token-goat 2.9.4 → 2.9.6

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,10 +18,11 @@ import {
18
18
  storeWebOutput,
19
19
  summarizeResidentContext,
20
20
  taskListPruneHint
21
- } from "./token-goat-chunk-FSC44QOH.mjs";
21
+ } from "./token-goat-chunk-7GJBID7S.mjs";
22
22
  import {
23
23
  BASH_OUTPUT_SUBDIR,
24
- UNTRUSTED_TOOL_TAG,
24
+ OUTLINE_MAX_REPLACEMENT_RATIO,
25
+ OUTLINE_MIN_HEADINGS,
25
26
  WEB_FETCH_KEY_SEP,
26
27
  appendDirtyPath,
27
28
  applyHintTracking,
@@ -38,8 +39,13 @@ import {
38
39
  ensureWorkerAlive,
39
40
  estimateTokens,
40
41
  extractCompactFromMarker,
42
+ extractMarkdownHeadings,
41
43
  fenceUntrusted,
42
44
  fenceWithMatches,
45
+ foldDelivery,
46
+ foldDetail,
47
+ foldingEnabled,
48
+ formatHeadingTreeParts,
43
49
  formatProjectMap,
44
50
  formatShrinkSummary,
45
51
  getBashOutput,
@@ -61,10 +67,12 @@ import {
61
67
  imageQualifiesForShrink,
62
68
  incrementSkillHit,
63
69
  indexRecallEntry,
70
+ indexServedBody,
64
71
  installedSkillPath,
65
72
  isBashEntryStale,
66
73
  isBuildCommand,
67
74
  isScopedGitStatusOrDiffStatCommand,
75
+ isStructuralRewriteAccepted,
68
76
  isTestRunnerCommand,
69
77
  lastTabContextMatches,
70
78
  listSiblingSessionStates,
@@ -78,6 +86,9 @@ import {
78
86
  matchesDenyPattern,
79
87
  meetsSavingsFloor,
80
88
  metadataEndpointRefusal,
89
+ planMarkdownOutline,
90
+ planServedElisions,
91
+ planSourceSkeleton,
81
92
  probeImageMeta,
82
93
  recordBashOutput,
83
94
  recordBashRerun,
@@ -98,6 +109,7 @@ import {
98
109
  removeOutstandingAgentSpawn,
99
110
  saveSessionState,
100
111
  scanAndRecord,
112
+ servedRunNotice,
101
113
  sessionOutputBodyBytes,
102
114
  sessionSidecarPath,
103
115
  setLastTabContext,
@@ -112,17 +124,19 @@ import {
112
124
  wasCliReadThisSession,
113
125
  wasFileReadThisSession,
114
126
  wasHintShown
115
- } from "./token-goat-chunk-CBO45DYZ.mjs";
127
+ } from "./token-goat-chunk-3HJQR4OO.mjs";
116
128
  import {
129
+ bashOutputCapBytes,
117
130
  canRunWrappedShell,
118
131
  deliveredOutputBytes
119
- } from "./token-goat-chunk-EPX5DWGM.mjs";
132
+ } from "./token-goat-chunk-LXIC7MTW.mjs";
120
133
  import {
121
134
  BODY_FIRST_TOOL_RESPONSE_KEYS,
122
135
  ENV_KEYS,
123
136
  IDENTICAL_READ_MIN_BODY_BYTES,
124
137
  OUTPUT_FIRST_TOOL_RESPONSE_KEYS,
125
138
  PER_FILE_COUNTERFACTUAL_CEILING,
139
+ UNTRUSTED_TOOL_TAG,
126
140
  VERSION,
127
141
  compressOutput,
128
142
  containsLineRun,
@@ -134,6 +148,7 @@ import {
134
148
  detectHarness,
135
149
  detectLanguage,
136
150
  displaySafePath,
151
+ displaySafeText,
137
152
  emitRewrite,
138
153
  emitRewriteIfChanged,
139
154
  envBool,
@@ -141,7 +156,9 @@ import {
141
156
  extractErrorMessage,
142
157
  extractToolResponseField,
143
158
  extractToolResultText,
159
+ fenceUntrustedFileContent,
144
160
  filterByName,
161
+ findProject,
145
162
  foldPath,
146
163
  getCwd,
147
164
  getDb,
@@ -172,8 +189,9 @@ import {
172
189
  shortFingerprint,
173
190
  splitOwnTrailingNotices,
174
191
  stripAnsiEscapes,
192
+ toDisplayPath,
175
193
  toKB
176
- } from "./token-goat-chunk-3ZR4NLB3.mjs";
194
+ } from "./token-goat-chunk-T2OE7MYM.mjs";
177
195
  import {
178
196
  init_define_import_meta_env
179
197
  } from "./token-goat-chunk-A37V4PBF.mjs";
@@ -498,10 +516,10 @@ function renderReadRow(entry) {
498
516
  const kb = Math.max(1, toKB(entry.sizeBytes));
499
517
  const plural = entry.readCount === 1 ? "read" : "reads";
500
518
  const edited = entry.wasEdited ? ", edited" : "";
501
- return `- ${entry.path} (${kb}kb, ${entry.readCount} ${plural}${edited})`;
519
+ return `- ${displaySafePath(entry.path)} (${kb}kb, ${entry.readCount} ${plural}${edited})`;
502
520
  }
503
521
  function renderSymbolReadRow(entry) {
504
- return `- ${entry.path} (symbols: ${(entry.symbols_read ?? []).join(", ")})`;
522
+ return `- ${displaySafePath(entry.path)} (symbols: ${(entry.symbols_read ?? []).map(displaySafeText).join(", ")})`;
505
523
  }
506
524
  function mergeManifestFiles(parent, siblingFiles) {
507
525
  const byPath = /* @__PURE__ */ new Map();
@@ -540,7 +558,7 @@ function buildManifest(sessionId, cwd) {
540
558
  appendCappedSection(
541
559
  lines,
542
560
  "### Edited files",
543
- editedFiles.map((entry) => `- ${entry.path}`),
561
+ editedFiles.map((entry) => `- ${displaySafePath(entry.path)}`),
544
562
  MAX_ROWS
545
563
  );
546
564
  appendCappedSection(lines, "### Surgically read files (symbol/section reads, never read whole)", symbolOnlyFiles.map(renderSymbolReadRow), MAX_ROWS);
@@ -607,7 +625,7 @@ function buildSafeToDiscardSection(files) {
607
625
  const entry = getBashOutput(id);
608
626
  if (entry === null) continue;
609
627
  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 + "`");
628
+ rerunRows.push("- `" + flatCommand + "` \u2014 an older run of this exact command was superseded; recall the surviving copy with `bash-output " + id + " --full`");
611
629
  }
612
630
  const supersededReadRows = [];
613
631
  for (const f of files) {
@@ -622,7 +640,7 @@ function buildSafeToDiscardSection(files) {
622
640
  const entry = getBashOutput(id);
623
641
  if (entry === null) continue;
624
642
  const flatCommand = entry.command.replace(/[\t\r\n]+/g, " ");
625
- cachedOutputRows.push("- `" + flatCommand + "` \u2014 recallable via `bash-output " + id + "`");
643
+ cachedOutputRows.push("- `" + flatCommand + "` \u2014 recallable via `bash-output " + id + " --full`");
626
644
  }
627
645
  const total = rerunRows.length + supersededReadRows.length + cachedOutputRows.length;
628
646
  if (total === 0) return [];
@@ -703,7 +721,7 @@ init_define_import_meta_env();
703
721
  var TRACKED_SKILL = "token-goat";
704
722
  var MAX_COMMANDS_SHOWN = 8;
705
723
  async function currentCommandNames() {
706
- const { buildProgram } = await import("./token-goat-chunk-GEXHIIKM.mjs");
724
+ const { buildProgram } = await import("./token-goat-chunk-3ZAMYHQG.mjs");
707
725
  return flattenCommandNames(buildCommandManifest(buildProgram()));
708
726
  }
709
727
  async function recordSkillVersionSnapshot(sessionId, skillName) {
@@ -7205,7 +7223,7 @@ function preFetchHandler(event) {
7205
7223
  if (cachedBytes >= loadConfig().hints.web_dedup_min_bytes) {
7206
7224
  recordStat("webfetch:recall", cachedBytes, savedTokensFromBytes(cachedBytes));
7207
7225
  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."
7226
+ "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
7227
  );
7210
7228
  }
7211
7229
  }
@@ -7340,6 +7358,21 @@ async function preSkillHandler(event) {
7340
7358
  return denyOutput(
7341
7359
  "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
7360
  );
7361
+ } else if (bodyBytes > OVERSIZED_FIRST_LOAD_THRESHOLD_BYTES) {
7362
+ const headings = extractMarkdownHeadings(body);
7363
+ if (headings.length >= OUTLINE_MIN_HEADINGS) {
7364
+ const { sectionsList } = formatHeadingTreeParts(headings, skillName);
7365
+ const treeBytes = Buffer.byteLength(sectionsList, "utf-8");
7366
+ if (treeBytes <= bodyBytes * OUTLINE_MAX_REPLACEMENT_RATIO) {
7367
+ const savedBytes = bodyBytes - treeBytes;
7368
+ recordStat("skill_heading_tree_inlined", savedBytes, savedTokensFromBytes(savedBytes));
7369
+ const totalHeadings = extractMarkdownHeadings(body, Number.MAX_SAFE_INTEGER).length;
7370
+ 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.";
7371
+ return denyOutput(
7372
+ "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)
7373
+ );
7374
+ }
7375
+ }
7343
7376
  }
7344
7377
  } catch {
7345
7378
  }
@@ -7376,8 +7409,9 @@ registerHook("post_tool_use", postSkillHandler, { toolName: "Skill" });
7376
7409
  // src/hooks_bash.ts
7377
7410
  init_define_import_meta_env();
7378
7411
  import { statSync as statSync3, existsSync, openSync, readSync, closeSync } from "node:fs";
7412
+ var CD_PREFIX_RE = /^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)[ \t]*(?:&&|;|\r?\n)\s*)+/;
7379
7413
  function stripCdPrefix(cmd) {
7380
- const stripped = cmd.replace(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/, "");
7414
+ const stripped = cmd.replace(CD_PREFIX_RE, "");
7381
7415
  return stripped.trim() || cmd;
7382
7416
  }
7383
7417
  function stripTrailingStderrRedirect(cmd) {
@@ -7393,10 +7427,10 @@ function isDirectTestRunnerCommand(cmd) {
7393
7427
  return /^(?:pytest|(?:npx\s+)?(?:jest|vitest)|go\s+test|cargo\s+test)\b/i.test(cmd);
7394
7428
  }
7395
7429
  function extractCdPrefixDirs(rawCmd) {
7396
- const prefixMatch = rawCmd.match(/^(?:cd\s+(?:"[^"]*"|'[^']*'|\S+)\s*&&\s*)+/);
7430
+ const prefixMatch = rawCmd.match(CD_PREFIX_RE);
7397
7431
  if (prefixMatch === null) return [];
7398
7432
  const dirs = [];
7399
- const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))\s*&&/g;
7433
+ const segmentPattern = /cd\s+(?:"([^"]*)"|'([^']*)'|(\S+))[ \t]*(?:&&|;|\r?\n)/g;
7400
7434
  let match;
7401
7435
  while ((match = segmentPattern.exec(prefixMatch[0])) !== null) {
7402
7436
  const dir = match[1] ?? match[2] ?? match[3];
@@ -7405,13 +7439,13 @@ function extractCdPrefixDirs(rawCmd) {
7405
7439
  return dirs;
7406
7440
  }
7407
7441
  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);
7442
+ if (extractCdPrefixDirs(rawCmd).length === 0) return filePath;
7443
+ return resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd));
7444
+ }
7445
+ function cdPrefixCwd(rawCmd, cwd) {
7446
+ let dir = cwd;
7447
+ for (const target of extractCdPrefixDirs(rawCmd)) dir = resolveIndexPath(target, dir);
7448
+ return dir;
7415
7449
  }
7416
7450
  function surgicalHintFor(hintPath, isEnv, isConfig, isDoc) {
7417
7451
  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 +8607,110 @@ function maybeCompressRewrite(event, rawCmd, cmd) {
8573
8607
  return { hookType: "rewriteInput", updatedInput: { ...event.toolInput, command: wrapped } };
8574
8608
  }
8575
8609
  function pureFileReadPath(cmd) {
8576
- return extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath ?? null;
8610
+ const single = extractCatFile(cmd)?.filePath ?? extractHeadFile(cmd)?.filePath ?? extractTailFile(cmd)?.filePath ?? extractLineRangeRead(cmd)?.filePath;
8611
+ if (single !== void 0) return single;
8612
+ return singleFileCompoundReadPath(cmd);
8613
+ }
8614
+ function singleFileCompoundReadPath(cmd) {
8615
+ const reads = extractLineRangeReadsCompound(cmd);
8616
+ if (reads === null || reads.length !== 1) return null;
8617
+ return reads[0]?.filePath ?? null;
8618
+ }
8619
+ function deliveredLineNumbers(cmd, lineCount) {
8620
+ const ranged = extractLineRangeRead(cmd);
8621
+ if (ranged !== null) {
8622
+ const nums = [];
8623
+ for (const [lo, hi] of ranged.ranges) for (let n = lo; n <= hi; n++) nums.push(n);
8624
+ if (nums.length === 0) return null;
8625
+ while (nums.length < lineCount) nums.push((nums[nums.length - 1] ?? 0) + 1);
8626
+ return nums;
8627
+ }
8628
+ if (extractCatFile(cmd) !== null || extractHeadFile(cmd) !== null) return Array.from({ length: lineCount }, (_, i) => i + 1);
8629
+ if (singleFileCompoundReadPath(cmd) !== null) return Array.from({ length: lineCount }, () => null);
8630
+ return null;
8577
8631
  }
8578
- async function maybeCollapseIdenticalRead(cmd, output, exitCode, cwd, cacheMinBytes) {
8632
+ function elideServedShellLines(cmd, output, priorIds) {
8633
+ if (priorIds.length === 0) return null;
8634
+ const lines = output.split("\n");
8635
+ const numbers = deliveredLineNumbers(cmd, lines.length);
8636
+ if (numbers === null) return null;
8637
+ const rows = lines.map((text, i) => ({ no: numbers[i] ?? null, text, raw: text }));
8638
+ const bodies = [];
8639
+ for (let i = priorIds.length - 1; i >= 0; i--) {
8640
+ const id = priorIds[i];
8641
+ if (id === void 0) continue;
8642
+ const prior = getBashOutput(id);
8643
+ if (prior !== null) bodies.push(indexServedBody(id, prior.output));
8644
+ }
8645
+ const cuts = planServedElisions(rows, bodies);
8646
+ if (cuts.length === 0) return null;
8647
+ const out = [];
8648
+ let at = 0;
8649
+ for (const cut of cuts) {
8650
+ for (let i = at; i < cut.start; i++) out.push(rows[i]?.raw ?? "");
8651
+ const first2 = rows[cut.start];
8652
+ const last = rows[cut.start + cut.len - 1];
8653
+ if (first2 === void 0 || last === void 0) return null;
8654
+ out.push(servedRunNotice(first2.no, last.no, cut.id, cut.len));
8655
+ at = cut.start + cut.len;
8656
+ }
8657
+ for (let i = at; i < rows.length; i++) out.push(rows[i]?.raw ?? "");
8658
+ return out.join("\n");
8659
+ }
8660
+ function foldShellReadBodies(cmd, output, fileKey, cwd) {
8661
+ if (!foldingEnabled()) return null;
8662
+ if (redactSecrets(output).count > 0) return null;
8663
+ const lines = output.split("\n");
8664
+ const numbers = deliveredLineNumbers(cmd, lines.length);
8665
+ if (numbers === null) return null;
8666
+ const rows = [];
8667
+ for (let i = 0; i < lines.length; i++) {
8668
+ const no = numbers[i];
8669
+ if (no === void 0 || no === null) return null;
8670
+ rows.push({ no, text: lines[i] ?? "", raw: lines[i] ?? "" });
8671
+ }
8672
+ const headRead = extractHeadFile(cmd);
8673
+ const windowed = extractLineRangeRead(cmd) !== null || headRead !== null && lines.length >= headRead.n;
8674
+ const folded = foldDelivery(rows, fileKey, displaySafePath(toDisplayPath(findProject(cwd ?? process.cwd())?.root, fileKey)), windowed);
8675
+ if (folded === null) return null;
8676
+ return { text: folded.numbered.join("\n"), folds: folded.folds };
8677
+ }
8678
+ 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;
8679
+ function isWholeFileDump(cmd, extractedPath) {
8680
+ const m = WHOLE_FILE_DUMP_RE.exec(cmd);
8681
+ if (m === null) return false;
8682
+ const shaped = m[1] ?? m[2] ?? m[3];
8683
+ return shaped !== void 0 && shaped === extractedPath;
8684
+ }
8685
+ function foldShellReadStructure(cmd, filePath, output, fileKey, cwd) {
8686
+ if (!isWholeFileDump(cmd, filePath)) return null;
8687
+ const originalBytes = Buffer.byteLength(output, "utf-8");
8688
+ let onDisk;
8689
+ try {
8690
+ onDisk = statSync3(fileKey).size;
8691
+ } catch {
8692
+ return null;
8693
+ }
8694
+ if (onDisk !== originalBytes) return null;
8695
+ if (redactSecrets(output).count > 0) return null;
8696
+ const lines = output.split("\n");
8697
+ if (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
8698
+ const rows = lines.map((text2, i) => ({ no: i + 1, text: text2, raw: text2 }));
8699
+ const shown = displaySafePath(toDisplayPath(findProject(cwd ?? process.cwd())?.root, fileKey));
8700
+ const fold = planMarkdownOutline(rows, fileKey, shown, originalBytes) ?? planSourceSkeleton(rows, fileKey, shown, originalBytes);
8701
+ if (fold === null) return null;
8702
+ const text = fold.numbered.join("\n");
8703
+ if (!isStructuralRewriteAccepted(originalBytes, Buffer.byteLength(text, "utf-8"), fold.ratioCap)) return null;
8704
+ return { text, kind: fold.kind === "read:markdown_outline" ? "bash_compress:markdown-outline" : "bash_compress:source-skeleton", detail: fold.detail };
8705
+ }
8706
+ async function maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, cacheMinBytes) {
8579
8707
  if (process.env["TOKEN_GOAT_BASH_COMPRESS"] === "0") return null;
8580
8708
  if (exitCode !== null && exitCode !== 0) return null;
8581
8709
  const filePath = pureFileReadPath(cmd);
8582
8710
  if (filePath === null) return null;
8583
8711
  const originalBytes = Buffer.byteLength(output, "utf-8");
8584
8712
  if (originalBytes < Math.max(cacheMinBytes, IDENTICAL_READ_MIN_BODY_BYTES)) return null;
8585
- const fileKey = resolveIndexPath(filePath, cwd ?? process.cwd());
8713
+ const fileKey = resolveIndexPath(filePath, cdPrefixCwd(rawCmd, cwd ?? process.cwd()));
8586
8714
  const priorIds = getFileServedOutputs(fileKey);
8587
8715
  let containerId = null;
8588
8716
  let identical = false;
@@ -8597,12 +8725,28 @@ async function maybeCollapseIdenticalRead(cmd, output, exitCode, cwd, cacheMinBy
8597
8725
  }
8598
8726
  const sessionKey = shortFingerprint(stripOutputPipeline(cmd));
8599
8727
  if (containerId === null) {
8600
- const storedId = await storeBashOutput(cmd, output, exitCode ?? 0, cwd);
8728
+ const elided = elideServedShellLines(cmd, output, priorIds);
8729
+ const priced = (text) => isRewriteWorthwhile({ originalBytes, rewrittenBytes: Buffer.byteLength(text, "utf-8"), noticeBytes: 0, minNetSavingsBytes: resolveMinNetSavingsBytes() });
8730
+ let rewrite = null;
8731
+ if (elided !== null) {
8732
+ if (priced(elided)) rewrite = { text: elided, reason: "already-served file lines withheld", kind: "bash_compress:served-elide" };
8733
+ } else {
8734
+ const structural = foldShellReadStructure(cmd, filePath, output, fileKey, cwd);
8735
+ if (structural !== null && priced(structural.text)) {
8736
+ 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 };
8737
+ }
8738
+ if (rewrite === null) {
8739
+ const folded = foldShellReadBodies(cmd, output, fileKey, cwd);
8740
+ if (folded !== null && priced(folded.text)) rewrite = { text: folded.text, reason: "code bodies folded", kind: "bash_compress:body-fold", detail: foldDetail(fileKey, folded.folds) };
8741
+ }
8742
+ }
8743
+ const storedId = await storeBashOutput(cmd, rewrite?.text ?? output, exitCode ?? 0, cwd);
8601
8744
  recordBashOutput(sessionKey, storedId, originalBytes);
8602
8745
  recordFileServedOutput(fileKey, storedId);
8603
- return null;
8746
+ if (rewrite === null) return null;
8747
+ return emitRewrite(rewrite.text, rewrite.reason, { kind: rewrite.kind, originalBytes: deliveredOutputBytes(originalBytes), ...rewrite.detail === void 0 ? {} : { detail: rewrite.detail } });
8604
8748
  }
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 + "`.";
8749
+ 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
8750
  if (!isRewriteWorthwhile({ originalBytes, rewrittenBytes: Buffer.byteLength(pointer, "utf-8"), noticeBytes: 0, minNetSavingsBytes: resolveMinNetSavingsBytes() })) return null;
8607
8751
  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
8752
  }
@@ -8663,6 +8807,48 @@ function maybeStripAnsiOnly(output) {
8663
8807
  }
8664
8808
  return emitRewrite(stripped, "ansi escapes stripped", { kind: "bash_compress:ansi", originalBytes: deliveredOutputBytes(originalBytes) }, "counted-elsewhere");
8665
8809
  }
8810
+ function maybeFoldCurlHtml(cmd, output, id) {
8811
+ if (!isCurlGetCommand(cmd) || !looksLikeHtml(output)) return null;
8812
+ let cfg;
8813
+ try {
8814
+ cfg = loadConfig().bash_compress;
8815
+ } catch {
8816
+ return null;
8817
+ }
8818
+ if (!cfg.enabled || cfg.disabled_filters.includes("curl-html")) return null;
8819
+ let cleaned;
8820
+ try {
8821
+ cleaned = extractCleanText(output);
8822
+ } catch {
8823
+ return null;
8824
+ }
8825
+ const originalBytes = Buffer.byteLength(output, "utf-8");
8826
+ const notice = `[token-goat: curl HTML body cleaned via extractCleanText; use \`token-goat bash-output ${id} --full\` to recall the raw markup]
8827
+ `;
8828
+ const noticeBytes = Buffer.byteLength(notice, "utf-8");
8829
+ 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]";
8830
+ const cap = bashOutputCapBytes();
8831
+ const fenceOverhead = Buffer.byteLength(fenceUntrusted(cleaned, UNTRUSTED_TOOL_TAG), "utf-8") - Buffer.byteLength(cleaned, "utf-8");
8832
+ const room = cap === null ? null : cap - noticeBytes - fenceOverhead - Buffer.byteLength(clipNote, "utf-8");
8833
+ let body = cleaned;
8834
+ let clipped = "";
8835
+ if (room !== null && room > 0 && Buffer.byteLength(cleaned, "utf-8") > room) {
8836
+ const sliced = Buffer.from(cleaned, "utf-8").subarray(0, room).toString("utf-8");
8837
+ const lastNewline = sliced.lastIndexOf("\n");
8838
+ body = lastNewline > 0 ? sliced.slice(0, lastNewline) : sliced;
8839
+ clipped = clipNote;
8840
+ }
8841
+ const fenced = fenceUntrusted(body, UNTRUSTED_TOOL_TAG) + clipped;
8842
+ if (!isRewriteWorthwhile({
8843
+ originalBytes,
8844
+ rewrittenBytes: Buffer.byteLength(fenced, "utf-8"),
8845
+ noticeBytes,
8846
+ minNetSavingsBytes: resolveMinNetSavingsBytes()
8847
+ })) {
8848
+ return null;
8849
+ }
8850
+ return emitRewrite(notice + fenced, "curl HTML body cleaned", { kind: "bash_compress:curl-html", originalBytes: deliveredOutputBytes(originalBytes) });
8851
+ }
8666
8852
  function unwrapCompressCommand(executed) {
8667
8853
  const t = executed.trim();
8668
8854
  if (!/^(?:token-goat|tg)\s+compress\b/.test(t)) return null;
@@ -9347,8 +9533,9 @@ async function postBashHandler(event) {
9347
9533
  recordBashOutput(gitScopedCacheHash, gitScopedCacheId, Buffer.byteLength(output, "utf-8"));
9348
9534
  }
9349
9535
  const isMonitoring = getMonitoringRecallHint(cmd) !== null;
9350
- if (!isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) {
9351
- const identical = await maybeCollapseIdenticalRead(cmd, output, exitCode, cwd, cacheMinBytes);
9536
+ const isFileRead = pureFileReadPath(cmd) !== null;
9537
+ if (isFileRead || !isMonitoring && !isBuildCommand(cmd) && !isCurlGetCommand(cmd)) {
9538
+ const identical = await maybeCollapseIdenticalRead(cmd, rawCmd, output, exitCode, cwd, cacheMinBytes);
9352
9539
  if (identical !== null) return identical;
9353
9540
  const compound = await maybeCompressCompoundOutput(cmd, output, exitCode, cwd, cacheMinBytes);
9354
9541
  if (compound !== null) return compound;
@@ -9366,9 +9553,11 @@ async function postBashHandler(event) {
9366
9553
  const delta = summarizeOutputDelta(priorEntry.output, output);
9367
9554
  if (delta !== null) {
9368
9555
  recordStat("session_hint", 0, 0);
9369
- return contextOutput(delta + " \u2014 full output: bash-output " + id);
9556
+ return contextOutput(delta + " \u2014 full output: bash-output " + id + " --full");
9370
9557
  }
9371
9558
  }
9559
+ const curlHtmlFold = maybeFoldCurlHtml(cmd, output, id);
9560
+ if (curlHtmlFold !== null) return curlHtmlFold;
9372
9561
  const ansiOnly = maybeStripAnsiOnly(output);
9373
9562
  if (ansiOnly !== null) return ansiOnly;
9374
9563
  } catch {