context-compress 2026.5.0 → 2026.7.0

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.
Files changed (79) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/.codex-plugin/plugin.json +40 -0
  4. package/.mcp.json +11 -0
  5. package/README.md +43 -12
  6. package/dist/bench/quality.d.ts +46 -0
  7. package/dist/bench/quality.d.ts.map +1 -0
  8. package/dist/bench/quality.js +118 -0
  9. package/dist/bench/quality.js.map +1 -0
  10. package/dist/bench/run.d.ts +2 -0
  11. package/dist/bench/run.d.ts.map +1 -0
  12. package/dist/bench/run.js +4 -0
  13. package/dist/bench/run.js.map +1 -0
  14. package/dist/cli/filter.d.ts.map +1 -1
  15. package/dist/cli/filter.js +13 -1
  16. package/dist/cli/filter.js.map +1 -1
  17. package/dist/cli/index.js +0 -0
  18. package/dist/cli/uninstall.d.ts.map +1 -1
  19. package/dist/cli/uninstall.js +4 -2
  20. package/dist/cli/uninstall.js.map +1 -1
  21. package/dist/config.d.ts +2 -0
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/config.js +11 -0
  24. package/dist/config.js.map +1 -1
  25. package/dist/executor.d.ts.map +1 -1
  26. package/dist/executor.js +32 -8
  27. package/dist/executor.js.map +1 -1
  28. package/dist/format-filter.d.ts +63 -0
  29. package/dist/format-filter.d.ts.map +1 -0
  30. package/dist/format-filter.js +331 -0
  31. package/dist/format-filter.js.map +1 -0
  32. package/dist/logger.d.ts +4 -0
  33. package/dist/logger.d.ts.map +1 -1
  34. package/dist/logger.js +14 -1
  35. package/dist/logger.js.map +1 -1
  36. package/dist/runtime/index.d.ts +2 -2
  37. package/dist/runtime/index.d.ts.map +1 -1
  38. package/dist/runtime/index.js +37 -20
  39. package/dist/runtime/index.js.map +1 -1
  40. package/dist/server.bundle.mjs +479 -110
  41. package/dist/server.bundle.mjs.map +4 -4
  42. package/dist/server.d.ts.map +1 -1
  43. package/dist/server.js +5 -2
  44. package/dist/server.js.map +1 -1
  45. package/dist/stats.d.ts.map +1 -1
  46. package/dist/stats.js +19 -1
  47. package/dist/stats.js.map +1 -1
  48. package/dist/store.d.ts +1 -0
  49. package/dist/store.d.ts.map +1 -1
  50. package/dist/store.js +20 -2
  51. package/dist/store.js.map +1 -1
  52. package/dist/tools/batch-execute.d.ts.map +1 -1
  53. package/dist/tools/batch-execute.js +20 -2
  54. package/dist/tools/batch-execute.js.map +1 -1
  55. package/dist/util/auto-mode.d.ts +8 -0
  56. package/dist/util/auto-mode.d.ts.map +1 -1
  57. package/dist/util/auto-mode.js +40 -26
  58. package/dist/util/auto-mode.js.map +1 -1
  59. package/dist/util/fetch-code.d.ts.map +1 -1
  60. package/dist/util/fetch-code.js +2 -48
  61. package/dist/util/fetch-code.js.map +1 -1
  62. package/dist/util/html-to-markdown.d.ts +17 -0
  63. package/dist/util/html-to-markdown.d.ts.map +1 -0
  64. package/dist/util/html-to-markdown.js +66 -0
  65. package/dist/util/html-to-markdown.js.map +1 -0
  66. package/dist/util/intent-filter.d.ts +12 -3
  67. package/dist/util/intent-filter.d.ts.map +1 -1
  68. package/dist/util/intent-filter.js +61 -8
  69. package/dist/util/intent-filter.js.map +1 -1
  70. package/dist/util/regret.d.ts +54 -0
  71. package/dist/util/regret.d.ts.map +1 -0
  72. package/dist/util/regret.js +107 -0
  73. package/dist/util/regret.js.map +1 -0
  74. package/docs/agentic-benchmark.md +110 -0
  75. package/docs/quality-regression-report.md +20 -0
  76. package/hooks/claude-codex-hooks.json +19 -0
  77. package/package.json +9 -5
  78. package/skills/context-compress-audit/SKILL.md +49 -0
  79. package/skills/context-compress-audit/agents/openai.yaml +13 -0
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Single source of truth for the HTML→markdown conversion snippet.
3
+ *
4
+ * Returns a dependency-free JS snippet that assumes a `html` string variable is
5
+ * already in scope, converts it to markdown, and `console.log`s the result.
6
+ * Shared by the fetch tool (which runs it inside the sandbox subprocess) and its
7
+ * tests, so the two copies can never drift.
8
+ *
9
+ * NOTE: This is a regex pipeline, not a full HTML parser — a real parser cannot
10
+ * be used because the snippet must run in the dependency-free sandbox. To bound
11
+ * worst-case backtracking, callers MUST size-limit `html` before running this
12
+ * (see `buildFetchCode`, which caps at 10MB). All patterns use bounded character
13
+ * classes (`[^>]`, `[^"]`) or lazy quantifiers separated by required literals,
14
+ * so none exhibit catastrophic (exponential) backtracking.
15
+ */
16
+ export function htmlToMarkdownSnippet() {
17
+ return `
18
+ // Strip unwanted tags
19
+ let md = html
20
+ .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, "")
21
+ .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, "")
22
+ .replace(/<nav[^>]*>[\\s\\S]*?<\\/nav>/gi, "")
23
+ .replace(/<header[^>]*>[\\s\\S]*?<\\/header>/gi, "")
24
+ .replace(/<footer[^>]*>[\\s\\S]*?<\\/footer>/gi, "");
25
+
26
+ // Convert headings
27
+ md = md.replace(/<h1[^>]*>(.*?)<\\/h1>/gi, "# $1\\n");
28
+ md = md.replace(/<h2[^>]*>(.*?)<\\/h2>/gi, "## $1\\n");
29
+ md = md.replace(/<h3[^>]*>(.*?)<\\/h3>/gi, "### $1\\n");
30
+ md = md.replace(/<h4[^>]*>(.*?)<\\/h4>/gi, "#### $1\\n");
31
+
32
+ // Convert code blocks
33
+ md = md.replace(/<pre[^>]*><code[^>]*>(.*?)<\\/code><\\/pre>/gis, "\`\`\`\\n$1\\n\`\`\`\\n");
34
+ md = md.replace(/<code[^>]*>(.*?)<\\/code>/gi, "\`$1\`");
35
+
36
+ // Convert links
37
+ md = md.replace(/<a[^>]*href="([^"]*)"[^>]*>(.*?)<\\/a>/gi, "[$2]($1)");
38
+
39
+ // Convert lists
40
+ md = md.replace(/<li[^>]*>(.*?)<\\/li>/gi, "- $1\\n");
41
+
42
+ // Convert paragraphs
43
+ md = md.replace(/<p[^>]*>(.*?)<\\/p>/gis, "$1\\n\\n");
44
+ md = md.replace(/<br\\s*\\/?>/gi, "\\n");
45
+
46
+ // Strip remaining tags
47
+ md = md.replace(/<[^>]+>/g, "");
48
+
49
+ // Decode entities (&amp; LAST so we don't double-decode)
50
+ md = md.replace(/&lt;/g, "<")
51
+ .replace(/&gt;/g, ">")
52
+ .replace(/&quot;/g, '"')
53
+ .replace(/&#39;/g, "'")
54
+ .replace(/&apos;/g, "'")
55
+ .replace(/&nbsp;/g, " ")
56
+ .replace(/&#(\\d+);/g, (_, n) => { const c = parseInt(n, 10); return c > 0 && c <= 0x10FFFF ? String.fromCodePoint(c) : ''; })
57
+ .replace(/&#x([0-9a-fA-F]+);/g, (_, h) => { const c = parseInt(h, 16); return c > 0 && c <= 0x10FFFF ? String.fromCodePoint(c) : ''; })
58
+ .replace(/&amp;/g, "&");
59
+
60
+ // Clean whitespace
61
+ md = md.replace(/\\n{3,}/g, "\\n\\n").trim();
62
+
63
+ console.log(md);
64
+ `;
65
+ }
66
+ //# sourceMappingURL=html-to-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-to-markdown.js","sourceRoot":"","sources":["../../src/util/html-to-markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB;IACpC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+CP,CAAC;AACF,CAAC"}
@@ -7,9 +7,18 @@ interface IntentFilterDeps {
7
7
  tracker: SessionTracker;
8
8
  }
9
9
  /**
10
- * Index large output and return a compact summary keyed to `intent`.
11
- * For small output (<= config.intentSearchThreshold bytes), returns the
12
- * original output unchanged so callers don't pay for trivial filtering.
10
+ * Index large output and return a compact, *query-conditioned* summary keyed to
11
+ * `intent`. For small output (<= config.intentSearchThreshold bytes), returns
12
+ * the original output unchanged so callers don't pay for trivial filtering.
13
+ *
14
+ * Unlike a fixed-mode filter, this is variable-rate and query-aware (cf.
15
+ * ACC-RAG / AttnComp): the retrieval layer already produces query-focused
16
+ * excerpts, so instead of throwing them away and forcing a follow-up search()
17
+ * round-trip, we inline the top-ranked content up to `config.intentBudgetBytes`.
18
+ * Best-scoring sections get the most room; each is capped so one section can't
19
+ * crowd out the rest. Error/warning lines are always surfaced verbatim — they're
20
+ * usually the whole reason an intent was specified — so a summary never silently
21
+ * drops the one line the agent needed.
13
22
  */
14
23
  export declare function createIntentFilter(deps: IntentFilterDeps): (output: string, intent: string, sourceLabel: string) => string;
15
24
  export type ApplyIntentFilter = ReturnType<typeof createIntentFilter>;
@@ -1 +1 @@
1
- {"version":3,"file":"intent-filter.d.ts","sourceRoot":"","sources":["../../src/util/intent-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,UAAU,gBAAgB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,IAGtB,QAAQ,MAAM,EAAE,QAAQ,MAAM,EAAE,aAAa,MAAM,KAAG,MAAM,CAoB9F;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
1
+ {"version":3,"file":"intent-filter.d.ts","sourceRoot":"","sources":["../../src/util/intent-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,UAAU,gBAAgB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;CACxB;AASD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,IAGtB,QAAQ,MAAM,EAAE,QAAQ,MAAM,EAAE,aAAa,MAAM,KAAG,MAAM,CA0B9F;AAmCD,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -1,22 +1,43 @@
1
+ import { extractErrorLines } from "../format-filter.js";
1
2
  import { compactLabel } from "./label.js";
3
+ /** How many matching sections to consider (before the byte budget trims them). */
4
+ const INTENT_SEARCH_LIMIT = 6;
5
+ /** No single hit may consume more than this share of the budget. */
6
+ const PER_HIT_CAP = 700;
7
+ /** Minimum snippet bytes worth showing for a hit. */
8
+ const PER_HIT_FLOOR = 120;
2
9
  /**
3
- * Index large output and return a compact summary keyed to `intent`.
4
- * For small output (<= config.intentSearchThreshold bytes), returns the
5
- * original output unchanged so callers don't pay for trivial filtering.
10
+ * Index large output and return a compact, *query-conditioned* summary keyed to
11
+ * `intent`. For small output (<= config.intentSearchThreshold bytes), returns
12
+ * the original output unchanged so callers don't pay for trivial filtering.
13
+ *
14
+ * Unlike a fixed-mode filter, this is variable-rate and query-aware (cf.
15
+ * ACC-RAG / AttnComp): the retrieval layer already produces query-focused
16
+ * excerpts, so instead of throwing them away and forcing a follow-up search()
17
+ * round-trip, we inline the top-ranked content up to `config.intentBudgetBytes`.
18
+ * Best-scoring sections get the most room; each is capped so one section can't
19
+ * crowd out the rest. Error/warning lines are always surfaced verbatim — they're
20
+ * usually the whole reason an intent was specified — so a summary never silently
21
+ * drops the one line the agent needed.
6
22
  */
7
23
  export function createIntentFilter(deps) {
8
24
  const { config, store, tracker } = deps;
9
25
  return function applyIntentFilter(output, intent, sourceLabel) {
10
- if (Buffer.byteLength(output) <= config.intentSearchThreshold)
26
+ const outputBytes = Buffer.byteLength(output);
27
+ if (outputBytes <= config.intentSearchThreshold)
11
28
  return output;
12
29
  const indexed = store.index(output, sourceLabel);
13
- tracker.trackIndexed(Buffer.byteLength(output));
14
- const searchResults = store.search(intent, { limit: 3 });
30
+ tracker.trackIndexed(outputBytes);
31
+ const searchResults = store.search(intent, { limit: INTENT_SEARCH_LIMIT });
15
32
  const terms = store.getDistinctiveTerms(indexed.sourceId);
33
+ const errorLines = extractErrorLines(output);
16
34
  let filtered = `Indexed ${indexed.totalChunks} sections from ${sourceLabel}.\n`;
17
35
  filtered += `${searchResults.results.length} sections matched "${intent}":\n\n`;
18
- for (const hit of searchResults.results) {
19
- filtered += ` - **${hit.title}**: ${hit.snippet.slice(0, 200)}\n`;
36
+ filtered += renderHits(searchResults.results, config.intentBudgetBytes);
37
+ if (errorLines.length > 0) {
38
+ filtered += `\n⚠ ${errorLines.length} error/warning line(s) in output:\n`;
39
+ filtered += errorLines.map((l) => ` ${l}`).join("\n");
40
+ filtered += "\n";
20
41
  }
21
42
  if (terms.length > 0 && config.compressionLevel !== "ultra") {
22
43
  filtered += `\nSearchable terms: ${terms.join(", ")}\n`;
@@ -25,4 +46,36 @@ export function createIntentFilter(deps) {
25
46
  return compactLabel(filtered, config.compressionLevel);
26
47
  };
27
48
  }
49
+ /**
50
+ * Render hits within a byte budget. Hits arrive best-first; each gets an even
51
+ * share of the remaining budget (capped and floored), and any unspent bytes
52
+ * roll forward to later hits. A hit skipped for lack of budget is still listed
53
+ * by title so the agent knows it exists and can search() for it.
54
+ */
55
+ function renderHits(hits, budget) {
56
+ if (hits.length === 0)
57
+ return " (no matching sections — try search() with different terms)\n";
58
+ const lines = [];
59
+ let remaining = budget;
60
+ for (let i = 0; i < hits.length; i++) {
61
+ const hit = hits[i];
62
+ const hitsLeft = hits.length - i;
63
+ const share = Math.min(PER_HIT_CAP, Math.floor(remaining / hitsLeft));
64
+ if (share < PER_HIT_FLOOR) {
65
+ lines.push(` - **${hit.title}** (search to view)`);
66
+ continue;
67
+ }
68
+ const snippet = clip(hit.snippet.trim(), share);
69
+ remaining -= Buffer.byteLength(snippet);
70
+ lines.push(` - **${hit.title}**: ${snippet}`);
71
+ }
72
+ return `${lines.join("\n")}\n`;
73
+ }
74
+ /** Trim a snippet to at most `max` bytes, marking truncation. */
75
+ function clip(s, max) {
76
+ if (Buffer.byteLength(s) <= max)
77
+ return s;
78
+ // Slice on characters; good enough for mostly-ASCII tool output.
79
+ return `${s.slice(0, Math.max(0, max - 1))}…`;
80
+ }
28
81
  //# sourceMappingURL=intent-filter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"intent-filter.js","sourceRoot":"","sources":["../../src/util/intent-filter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQ1C;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACxD,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAExC,OAAO,SAAS,iBAAiB,CAAC,MAAc,EAAE,MAAc,EAAE,WAAmB;QACpF,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,qBAAqB;YAAE,OAAO,MAAM,CAAC;QAE7E,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;QACjD,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAEhD,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,KAAK,CAAC,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE1D,IAAI,QAAQ,GAAG,WAAW,OAAO,CAAC,WAAW,kBAAkB,WAAW,KAAK,CAAC;QAChF,QAAQ,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,sBAAsB,MAAM,QAAQ,CAAC;QAChF,KAAK,MAAM,GAAG,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;YACzC,QAAQ,IAAI,SAAS,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC;QACpE,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,gBAAgB,KAAK,OAAO,EAAE,CAAC;YAC7D,QAAQ,IAAI,uBAAuB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QACzD,CAAC;QACD,QAAQ,IAAI,uEAAuE,CAAC;QACpF,OAAO,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACxD,CAAC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"intent-filter.js","sourceRoot":"","sources":["../../src/util/intent-filter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * ACON-style self-improving compression policy.
3
+ *
4
+ * ACON (Kang et al., 2026) optimizes a compression *guideline* in natural-language
5
+ * space by analyzing cases where the full context succeeds but the compressed
6
+ * context fails, then refines the guideline — no model fine-tuning. We can't run
7
+ * paired trajectories at runtime, but we can approximate the failure signal from
8
+ * the agent's own behavior: if a command is compressed aggressively and then
9
+ * *re-run almost immediately*, the aggressive summary probably hid something the
10
+ * agent needed. That's a "compression regret".
11
+ *
12
+ * The policy this drives is deliberately narrow and safe:
13
+ * - We only count a regret for a FAST re-run (default ≤ 30s) that followed an
14
+ * AGGRESSIVE compression. Balanced/conservative are never blamed — they
15
+ * rarely drop task-critical data, and normal edit→rerun loops (which are
16
+ * slower and usually ran under balanced) shouldn't be misread as regret.
17
+ * - The only adjustment is a one-step DOWNGRADE (aggressive → balanced) once a
18
+ * fingerprint's regret rate is high over enough samples. Downgrading only
19
+ * ever *reduces* compression, so a false positive costs a few tokens, never
20
+ * correctness. It never makes anything more aggressive on its own.
21
+ *
22
+ * State is a small JSON map at ~/.context-compress/regret.json, keyed by the same
23
+ * command fingerprint the auto-mode cache uses, so it persists across sessions.
24
+ */
25
+ import type { FilterMode } from "../filters.js";
26
+ export interface RegretOptions {
27
+ path?: string;
28
+ /** Injectable clock (epoch ms) for deterministic tests. */
29
+ now?: number;
30
+ /** Re-run window in ms; a re-run within this counts toward regret. */
31
+ windowMs?: number;
32
+ }
33
+ export interface RegretDecision {
34
+ /** Mode after any regret-driven adjustment. */
35
+ mode: FilterMode;
36
+ /** True when the chosen mode was downgraded due to regret. */
37
+ adjusted: boolean;
38
+ /** Current regret rate for this fingerprint (0–1). */
39
+ regretRate: number;
40
+ }
41
+ /**
42
+ * Record this compression decision, detect regret from a fast re-run, and return
43
+ * a possibly-downgraded mode. Call this once per auto-mode decision (including
44
+ * cache hits) so the re-run timeline stays accurate.
45
+ */
46
+ export declare function observeAndAdjust(fingerprint: string, chosenMode: FilterMode, opts?: RegretOptions): RegretDecision;
47
+ /** Read-only view of regret stats, for the `stats` tool / observability. */
48
+ export declare function regretSummary(opts?: RegretOptions): Array<{
49
+ fingerprint: string;
50
+ observations: number;
51
+ regrets: number;
52
+ regretRate: number;
53
+ }>;
54
+ //# sourceMappingURL=regret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regret.d.ts","sourceRoot":"","sources":["../../src/util/regret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAyBhD,MAAM,WAAW,aAAa;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC9B,+CAA+C;IAC/C,IAAI,EAAE,UAAU,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;CACnB;AA0BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC/B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,UAAU,EACtB,IAAI,GAAE,aAAkB,GACtB,cAAc,CA8BhB;AAED,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,KAAK,CAAC;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACnB,CAAC,CAWD"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * ACON-style self-improving compression policy.
3
+ *
4
+ * ACON (Kang et al., 2026) optimizes a compression *guideline* in natural-language
5
+ * space by analyzing cases where the full context succeeds but the compressed
6
+ * context fails, then refines the guideline — no model fine-tuning. We can't run
7
+ * paired trajectories at runtime, but we can approximate the failure signal from
8
+ * the agent's own behavior: if a command is compressed aggressively and then
9
+ * *re-run almost immediately*, the aggressive summary probably hid something the
10
+ * agent needed. That's a "compression regret".
11
+ *
12
+ * The policy this drives is deliberately narrow and safe:
13
+ * - We only count a regret for a FAST re-run (default ≤ 30s) that followed an
14
+ * AGGRESSIVE compression. Balanced/conservative are never blamed — they
15
+ * rarely drop task-critical data, and normal edit→rerun loops (which are
16
+ * slower and usually ran under balanced) shouldn't be misread as regret.
17
+ * - The only adjustment is a one-step DOWNGRADE (aggressive → balanced) once a
18
+ * fingerprint's regret rate is high over enough samples. Downgrading only
19
+ * ever *reduces* compression, so a false positive costs a few tokens, never
20
+ * correctness. It never makes anything more aggressive on its own.
21
+ *
22
+ * State is a small JSON map at ~/.context-compress/regret.json, keyed by the same
23
+ * command fingerprint the auto-mode cache uses, so it persists across sessions.
24
+ */
25
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
26
+ import { homedir } from "node:os";
27
+ import { dirname, join } from "node:path";
28
+ const DEFAULT_PATH = join(homedir(), ".context-compress", "regret.json");
29
+ const RERUN_WINDOW_MS = 30_000;
30
+ /**
31
+ * Absolute number of aggressive-mode fast re-runs before we downgrade. Using a
32
+ * count (not a decaying rate) gives hysteresis: once a fingerprint has proven
33
+ * aggressive is a bad fit, it stays downgraded instead of oscillating back the
34
+ * moment the re-runs stop (they stop *because* we downgraded).
35
+ */
36
+ const REGRET_MIN_COUNT = 3;
37
+ function load(path) {
38
+ if (!existsSync(path))
39
+ return {};
40
+ try {
41
+ return JSON.parse(readFileSync(path, "utf-8"));
42
+ }
43
+ catch {
44
+ return {};
45
+ }
46
+ }
47
+ function save(path, map) {
48
+ try {
49
+ mkdirSync(dirname(path), { recursive: true });
50
+ writeFileSync(path, JSON.stringify(map, null, 2));
51
+ }
52
+ catch {
53
+ /* best-effort */
54
+ }
55
+ }
56
+ function downgrade(mode) {
57
+ if (mode === "aggressive")
58
+ return "balanced";
59
+ if (mode === "balanced")
60
+ return "conservative";
61
+ return "conservative";
62
+ }
63
+ /**
64
+ * Record this compression decision, detect regret from a fast re-run, and return
65
+ * a possibly-downgraded mode. Call this once per auto-mode decision (including
66
+ * cache hits) so the re-run timeline stays accurate.
67
+ */
68
+ export function observeAndAdjust(fingerprint, chosenMode, opts = {}) {
69
+ const path = opts.path ?? DEFAULT_PATH;
70
+ const now = opts.now ?? Date.now();
71
+ const window = opts.windowMs ?? RERUN_WINDOW_MS;
72
+ const map = load(path);
73
+ const rec = map[fingerprint] ?? {
74
+ lastMode: chosenMode,
75
+ lastSeen: 0,
76
+ observations: 0,
77
+ regrets: 0,
78
+ };
79
+ // A fast re-run after an aggressive compression is our regret signal.
80
+ const isFastRerun = rec.lastSeen > 0 && now - rec.lastSeen <= window;
81
+ if (isFastRerun && rec.lastMode === "aggressive") {
82
+ rec.regrets++;
83
+ }
84
+ rec.observations++;
85
+ const regretRate = rec.observations > 0 ? rec.regrets / rec.observations : 0;
86
+ const shouldAdjust = chosenMode === "aggressive" && rec.regrets >= REGRET_MIN_COUNT;
87
+ const mode = shouldAdjust ? downgrade(chosenMode) : chosenMode;
88
+ rec.lastMode = mode;
89
+ rec.lastSeen = now;
90
+ map[fingerprint] = rec;
91
+ save(path, map);
92
+ return { mode, adjusted: mode !== chosenMode, regretRate };
93
+ }
94
+ /** Read-only view of regret stats, for the `stats` tool / observability. */
95
+ export function regretSummary(opts = {}) {
96
+ const map = load(opts.path ?? DEFAULT_PATH);
97
+ return Object.entries(map)
98
+ .map(([fingerprint, r]) => ({
99
+ fingerprint,
100
+ observations: r.observations,
101
+ regrets: r.regrets,
102
+ regretRate: r.observations > 0 ? r.regrets / r.observations : 0,
103
+ }))
104
+ .filter((r) => r.regrets > 0)
105
+ .sort((a, b) => b.regretRate - a.regretRate);
106
+ }
107
+ //# sourceMappingURL=regret.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regret.js","sourceRoot":"","sources":["../../src/util/regret.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ # Agentic Benchmark Plan
2
+
3
+ This benchmark measures context-compress in real agent sessions, not synthetic command output alone.
4
+
5
+ The claim to test:
6
+
7
+ > Large tool output should stay searchable outside the conversation, while the agent still solves the same task with less context pressure.
8
+
9
+ ## Why This Exists
10
+
11
+ `docs/token-reduction-report.md` measures byte and token reduction for common operations. That is necessary, but it does not fully answer whether an agent remains effective across a real coding task.
12
+
13
+ This benchmark adds the missing layer: run the same task with and without context-compress, isolate each arm, and compare context usage, task success, cost, and time.
14
+
15
+ ## Arms
16
+
17
+ | Arm | Setup | Purpose |
18
+ | --- | --- | --- |
19
+ | `baseline` | No context-compress MCP, no hook | Measures normal agent behavior. |
20
+ | `mcp-only` | MCP server registered, no PreToolUse hook | Measures explicit tool adoption. |
21
+ | `hook-balanced` | MCP plus PreToolUse hook, `CONTEXT_COMPRESS_MODE=balanced` | Default recommended setup. |
22
+ | `hook-aggressive` | MCP plus PreToolUse hook, `CONTEXT_COMPRESS_MODE=aggressive` | Maximum compression trade-off. |
23
+
24
+ Each arm must run in a fresh workspace with isolated agent settings. Do not allow global plugins, global MCP servers, or previous conversation state to leak into the run.
25
+
26
+ ## Task Set
27
+
28
+ Use tasks that naturally produce large outputs:
29
+
30
+ 1. Diagnose a failing test suite and patch the root cause.
31
+ 2. Review a multi-commit diff and summarize risky changes.
32
+ 3. Inspect a large API response and implement one missing field mapping.
33
+ 4. Analyze a generated Playwright snapshot and fix one selector bug.
34
+ 5. Audit dependency output and identify one vulnerable or outdated package.
35
+ 6. Search a large log file and explain the first recurring failure.
36
+
37
+ Pin every input repository and fixture by commit hash. Preserve every run directory so metrics can be recomputed.
38
+
39
+ ## Metrics
40
+
41
+ | Metric | How to collect |
42
+ | --- | --- |
43
+ | Context bytes returned by tools | Sum raw tool payloads in agent logs. |
44
+ | Compressed bytes returned | Sum context-compress tool responses. |
45
+ | Indexed bytes | Use `stats` output and session DB stats. |
46
+ | Task success | Deterministic test, assertion, or scorer per task. |
47
+ | Cost/time | Agent runner JSON output when available. |
48
+ | Follow-up retrieval quality | Count whether the final answer cites indexed/search results when needed. |
49
+
50
+ Report raw numbers and relative deltas. Do not only report the best percentage.
51
+
52
+ ## Isolation Rules
53
+
54
+ - Use a new temp workspace for every `(task, arm, run)` cell.
55
+ - Disable user/global plugin sources for the baseline arm.
56
+ - Install exactly the intended plugin or MCP config for non-baseline arms.
57
+ - Clear persistent context-compress DBs between runs unless the task explicitly tests persistence.
58
+ - Keep model, prompt, timeout, and working tree identical across arms.
59
+ - Record the exact agent version, model, OS, Node version, and context-compress version.
60
+
61
+ ## Safety Checks
62
+
63
+ Compression must not hide important failures. Every task needs one deterministic scorer:
64
+
65
+ - tests pass after the agent patch,
66
+ - expected files changed and unrelated files did not,
67
+ - security-relevant details are still retrievable with `search`,
68
+ - final answer includes the actual root cause, not just a compressed summary.
69
+
70
+ If an arm uses fewer tokens but fails the scorer, mark it as a failure, not a win.
71
+
72
+ ## Reporting Template
73
+
74
+ ```md
75
+ # Agentic benchmark: context-compress on real coding tasks
76
+
77
+ Date:
78
+ Agent:
79
+ Model:
80
+ context-compress:
81
+ Repo/fixture commits:
82
+
83
+ ## Summary
84
+
85
+ | Arm | Success | Tool bytes in context | Indexed bytes | Cost | Time |
86
+ | --- | ---: | ---: | ---: | ---: | ---: |
87
+
88
+ ## Per-task Results
89
+
90
+ | Task | Arm | Success | Tool bytes | Indexed bytes | Notes |
91
+ | --- | --- | ---: | ---: | ---: | --- |
92
+
93
+ ## Failures And Limits
94
+
95
+ - What failed:
96
+ - What this benchmark does not prove:
97
+ - Known nondeterminism:
98
+ ```
99
+
100
+ ## Reproduce
101
+
102
+ Until this harness is automated, run the benchmark manually with:
103
+
104
+ ```bash
105
+ npm run build
106
+ context-compress setup --auto
107
+ CONTEXT_COMPRESS_MODE=balanced context-compress doctor
108
+ ```
109
+
110
+ Then run each task in isolated agent settings and attach the resulting logs plus `context-compress stats` output to the benchmark result.
@@ -0,0 +1,20 @@
1
+ <!-- Generated by `npm run bench:quality` — do not edit by hand. -->
2
+
3
+ # Quality-Regression Benchmark
4
+
5
+ | Case | Mode | Reduction | Survival | Valid JSON |
6
+ |------|------|-----------|----------|------------|
7
+ | pretty-json (unrecognized cmd) | conservative | 0% | 100% | — |
8
+ | pretty-json (unrecognized cmd) | balanced | 37% | 100% | ✓ |
9
+ | pretty-json (unrecognized cmd) | aggressive | 96% | 100% | — |
10
+ | ndjson stream | conservative | 0% | 100% | — |
11
+ | ndjson stream | balanced | 93% | 100% | — |
12
+ | ndjson stream | aggressive | 93% | 100% | — |
13
+ | app logs with an error | conservative | 0% | 100% | — |
14
+ | app logs with an error | balanced | 98% | 100% | — |
15
+ | app logs with an error | aggressive | 98% | 100% | — |
16
+ | git log | conservative | 0% | 100% | — |
17
+ | git log | balanced | 0% | 100% | — |
18
+ | git log | aggressive | 59% | 100% | — |
19
+
20
+ _Reduction = bytes removed. Survival = fraction of task-critical substrings still present after compression. Valid JSON = balanced-mode output still parses._
@@ -0,0 +1,19 @@
1
+ {
2
+ "description": "context-compress PreToolUse hooks for Claude-compatible plugin hosts",
3
+ "hooks": {
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "Bash|Read|Grep|WebFetch|Task",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "command -v node >/dev/null 2>&1 && CONTEXT_COMPRESS_FILTER_BASH=1 CONTEXT_COMPRESS_BIN=\"node ${CLAUDE_PLUGIN_ROOT}/dist/cli/index.js\" node \"${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.mjs\" || exit 0",
11
+ "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $env:CONTEXT_COMPRESS_FILTER_BASH='1'; $env:CONTEXT_COMPRESS_BIN='context-compress'; node \"$env:CLAUDE_PLUGIN_ROOT\\hooks\\pretooluse.mjs\" }",
12
+ "timeout": 5,
13
+ "statusMessage": "Protecting context window..."
14
+ }
15
+ ]
16
+ }
17
+ ]
18
+ }
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-compress",
3
- "version": "2026.5.0",
3
+ "version": "2026.7.0",
4
4
  "description": "Context-aware MCP server that compresses tool output for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",
@@ -17,6 +17,7 @@
17
17
  "test": "node --import tsx --test tests/**/*.test.ts",
18
18
  "test:unit": "node --import tsx --test tests/unit/*.test.ts",
19
19
  "test:integration": "node --import tsx --test tests/integration/*.test.ts",
20
+ "bench:quality": "tsx src/bench/run.ts",
20
21
  "clean": "rm -rf dist dist-bin",
21
22
  "build:bin": "bun build --compile --target=bun-darwin-arm64 ./src/cli/lite.ts --outfile=./dist-bin/cc-lite-darwin-arm64 && bun build --compile --target=bun-darwin-x64 ./src/cli/lite.ts --outfile=./dist-bin/cc-lite-darwin-x64 && bun build --compile --target=bun-linux-x64 ./src/cli/lite.ts --outfile=./dist-bin/cc-lite-linux-x64 && bun build --compile --target=bun-linux-arm64 ./src/cli/lite.ts --outfile=./dist-bin/cc-lite-linux-arm64",
22
23
  "prepublishOnly": "npm run lint && npm run test && npm run build"
@@ -38,6 +39,9 @@
38
39
  "typescript": "^5.7.0"
39
40
  },
40
41
  "files": [
42
+ ".codex-plugin/",
43
+ ".claude-plugin/",
44
+ ".mcp.json",
41
45
  "dist/",
42
46
  "docs/",
43
47
  "hooks/",
@@ -46,10 +50,10 @@
46
50
  "README.md"
47
51
  ],
48
52
  "license": "MIT",
49
- "repository": {
50
- "type": "git",
51
- "url": "https://github.com/Open330/context-compress"
52
- },
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "git+https://github.com/Open330/context-compress.git"
56
+ },
53
57
  "keywords": [
54
58
  "mcp",
55
59
  "claude",
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: context-compress-audit
3
+ description: Audit a repository, plugin setup, or agent session for raw Bash, Read, WebFetch, and MCP outputs that should be routed through context-compress instead. Use when asked to find context waste, raw tool output waste, missing plugin routing, or places where large command/file/web output still enters the agent context window.
4
+ ---
5
+
6
+ # Context-Compress Audit
7
+
8
+ Find places where large raw output can still enter the agent context window.
9
+
10
+ ## Procedure
11
+
12
+ 1. Run `mcp__context-compress__stats` first if the tool is available.
13
+ 2. Inspect setup surfaces that control routing:
14
+ - `.codex-plugin/plugin.json`
15
+ - `.claude-plugin/plugin.json`
16
+ - `.mcp.json`
17
+ - `hooks/`
18
+ - `skills/`
19
+ - README install instructions
20
+ 3. Search for risky guidance or examples that encourage raw output:
21
+ - `Bash` for tests, logs, `git log`, `git diff`, `curl`, `kubectl`, `docker`, `npm test`
22
+ - `Read` for large logs, bundled files, snapshots, CSV/JSON dumps
23
+ - `WebFetch` for documentation pages that should use `fetch_and_index`
24
+ - Playwright snapshots without a file/index/search path
25
+ 4. Report only actionable findings. Prefer one-line fixes that route work through:
26
+ - `batch_execute` for several commands plus searches
27
+ - `execute` for command/API output that must be analyzed first
28
+ - `execute_file` for large local files
29
+ - `fetch_and_index` plus `search` for web documentation
30
+
31
+ ## Output
32
+
33
+ Use this format:
34
+
35
+ ```md
36
+ ## Context-Compress Audit
37
+
38
+ - [severity] file:line - raw-output risk. Replace with <tool/workflow>.
39
+ - [severity] file:line - missing install/routing coverage. Add <specific fix>.
40
+
41
+ Summary: <N> findings, estimated impact <low|medium|high>.
42
+ ```
43
+
44
+ Severity:
45
+ - `high`: large output can enter context by default.
46
+ - `medium`: docs/examples teach a wasteful path.
47
+ - `low`: minor wording, missing cross-link, or optional setup gap.
48
+
49
+ If nothing meaningful is found, say `No raw-output waste found. Routing looks covered.`
@@ -0,0 +1,13 @@
1
+ interface:
2
+ display_name: "Context Compress Audit"
3
+ short_description: "Find raw-output context waste."
4
+ default_prompt: "Use $context-compress-audit to audit this project for raw tool output waste and missing routing."
5
+
6
+ dependencies:
7
+ tools:
8
+ - type: "mcp"
9
+ value: "context-compress"
10
+ description: "Context-compress MCP server for stats and indexed search."
11
+
12
+ policy:
13
+ allow_implicit_invocation: true