acp-kernel 0.0.63-pr.250.97 → 0.0.63-pr.253.100

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.
@@ -0,0 +1,19 @@
1
+ import type { BlockSpan, CompressionBlock, CompressionState, MessageRefMap } from "./types.js";
2
+ /** Resolve a block's current ref span. Prefers the stored startRef/endRef
3
+ * (assigned at creation from the requested range); falls back to the
4
+ * min/max of effectiveMessageIds' refs for blocks persisted before those
5
+ * fields existed. Stored refs are only trusted when both are message refs —
6
+ * block-boundary specs (startId "bN") are stored verbatim on tier-distilled
7
+ * blocks and must not render as spans. Returns null when nothing resolves. */
8
+ export declare function resolveBlockSpan(block: CompressionBlock, byRaw: MessageRefMap["byRaw"]): {
9
+ startRef: string;
10
+ endRef: string;
11
+ } | null;
12
+ export declare function activeBlockSpans(state: CompressionState): BlockSpan[];
13
+ /** Format newly created blocks for tool-result display, e.g.
14
+ * `blocks: b3=m00044–m00097, b4=m00103–m00123`. Adapters append this to their
15
+ * compress result line so the model's block ledger comes from the kernel
16
+ * instead of being inferred from its own arguments (#376). Empty string when
17
+ * no blocks were created. */
18
+ export declare function formatCreatedBlocks(state: CompressionState, newBlocks: CompressionBlock[]): string;
19
+ //# sourceMappingURL=block-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-map.d.ts","sourceRoot":"","sources":["../src/block-map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,EACd,MAAM,YAAY,CAAC;AASpB;;;;;+EAK+E;AAC/E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAC5B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAe7C;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,EAAE,CASrE;AAED;;;;8BAI8B;AAC9B,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,gBAAgB,EACvB,SAAS,EAAE,gBAAgB,EAAE,GAC5B,MAAM,CASR"}
@@ -169,17 +169,15 @@ function resolvePrompts(overrides, options = {}) {
169
169
  }
170
170
 
171
171
  // src/nudge-text.ts
172
- function efficiencyNote(prompts, includeGuidance) {
173
- const note = "This is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.";
174
- return includeGuidance ? `${note}
172
+ function efficiencyNote(prompts) {
173
+ return `This is an efficiency nudge to compress early and keep context lean \u2014 not an overflow warning. A separate, stronger alert will appear if the context is actually full.
175
174
 
176
- ${prompts.compressPhilosophy}` : note;
175
+ ${prompts.compressPhilosophy}`;
177
176
  }
178
- function emergencyHeader(prompts, includeGuidance) {
179
- const header = "\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.";
180
- return includeGuidance ? `${header}
177
+ function emergencyHeader(prompts) {
178
+ return `\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.
181
179
 
182
- ${prompts.compressPhilosophy}` : header;
180
+ ${prompts.compressPhilosophy}`;
183
181
  }
184
182
  function formatK(n) {
185
183
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
@@ -209,6 +207,17 @@ function formatTierTargetBlocks(blocks) {
209
207
  return `Target ${blocks[0].tier === 1 ? "tier-1" : "tier-2"} blocks to distill (${blocks.length}):
210
208
  ${lines.join("\n")}`;
211
209
  }
210
+ var BLOCK_MAP_MAX_SHOWN = 8;
211
+ function formatBlockMap(spans) {
212
+ if (spans.length === 0) return "";
213
+ const hidden = Math.max(0, spans.length - BLOCK_MAP_MAX_SHOWN);
214
+ const shown = hidden > 0 ? spans.slice(-BLOCK_MAP_MAX_SHOWN) : spans;
215
+ const items = shown.map(
216
+ (s) => `${s.blockId}=${s.startRef}\u2013${s.endRef}${s.tier > 1 ? ` t${s.tier}` : ""}`
217
+ );
218
+ const prefix = hidden > 0 ? `\u2026+${hidden} older \xB7 ` : "";
219
+ return `Active blocks (${spans.length}): ${prefix}${items.join(" \xB7 ")}`;
220
+ }
212
221
  function formatRanges(compressible, protectedRanges) {
213
222
  if (compressible.length === 0 && protectedRanges.length === 0) {
214
223
  return "[No specific ranges detected \u2014 compress any consumed content.]";
@@ -226,6 +235,7 @@ function formatRanges(compressible, protectedRanges) {
226
235
  endNum: refNum(r.endRef),
227
236
  count: r.count,
228
237
  tokens: r.tokens,
238
+ userMsgs: r.userMsgs ?? 0,
229
239
  toolPct: r.toolPct,
230
240
  textPct: r.textPct,
231
241
  compressibleTokens: r.tokens,
@@ -244,6 +254,7 @@ function formatRanges(compressible, protectedRanges) {
244
254
  endNum: refNum(r.endRef),
245
255
  count: r.count,
246
256
  tokens: r.tokens,
257
+ userMsgs: 0,
247
258
  toolPct: 0,
248
259
  textPct: 0,
249
260
  compressibleTokens: 0,
@@ -263,6 +274,7 @@ function formatRanges(compressible, protectedRanges) {
263
274
  last.endNum = Math.max(last.endNum, e.endNum);
264
275
  last.count += e.count;
265
276
  last.tokens += e.tokens;
277
+ last.userMsgs += e.userMsgs;
266
278
  last.compressibleTokens += e.compressibleTokens;
267
279
  last.compressibleCount += e.compressibleCount;
268
280
  last.protectedTokens += e.protectedTokens;
@@ -275,23 +287,24 @@ function formatRanges(compressible, protectedRanges) {
275
287
  merged.push({ ...e });
276
288
  }
277
289
  }
290
+ const userNote = (n) => n > 0 ? ` \xB7 ${n} user msg${n > 1 ? "s" : ""}` : "";
278
291
  const lines = merged.map((e) => {
279
292
  const suffix = e.dangerous && e.compressibleTokens > 0 ? " \u26A0\uFE0F NOT recommended unless you are certain." : "";
280
293
  if (e.protectedTokens > 0 && e.compressibleTokens === 0) {
281
294
  return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [PROTECTED: ${e.protectedTools.join(", ")} \u2014 not compressible]${suffix}`;
282
295
  }
283
296
  if (e.protectedTokens > 0 && e.compressibleTokens > 0) {
284
- return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [${formatK(e.compressibleTokens)} compressible | ${formatK(e.protectedTokens)} protected: ${e.protectedTools.join(", ")}]${suffix}`;
297
+ return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [${formatK(e.compressibleTokens)} compressible | ${formatK(e.protectedTokens)} protected: ${e.protectedTools.join(", ")}]${userNote(e.userMsgs)}${suffix}`;
285
298
  }
286
- return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${suffix}`;
299
+ return ` ${e.startRef}\u2013${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${userNote(e.userMsgs)}${suffix}`;
287
300
  });
288
301
  return `Compressible ranges (${merged.length}, oldest first):
289
302
  ${lines.join("\n")}`;
290
303
  }
291
- function renderNudgeText(decision, prompts = defaultPrompts, options = {}) {
292
- const includeGuidance = options.includeGuidance ?? true;
304
+ function renderNudgeText(decision, prompts = defaultPrompts) {
293
305
  const breakdownStr = formatBreakdown(decision.contextBreakdown);
294
306
  const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
307
+ const blockMapStr = formatBlockMap(decision.activeBlockSpans ?? []);
295
308
  const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;
296
309
  if (decision.tier !== null && decision.tier >= 2) {
297
310
  const isT2 = decision.tier === 2;
@@ -301,37 +314,57 @@ function renderNudgeText(decision, prompts = defaultPrompts, options = {}) {
301
314
  const endId = targets[targets.length - 1]?.blockId ?? "b5";
302
315
  const voice = isEmergency ? "emergency" : "gentle";
303
316
  const triggerLine = isEmergency ? `[EMERGENCY \u2014 TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"}] Context limit reached \u2014 distill NOW into a denser summary to reclaim tokens.` : `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`;
304
- const parts2 = [
305
- efficiencyNote(prompts, includeGuidance),
317
+ return {
318
+ voice,
319
+ text: [
320
+ efficiencyNote(prompts),
321
+ "",
322
+ breakdownStr,
323
+ "",
324
+ triggerLine,
325
+ isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
326
+ blockList,
327
+ `Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
328
+ "",
329
+ prompts.howToCompressRules,
330
+ "",
331
+ isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules
332
+ ].join("\n")
333
+ };
334
+ }
335
+ if (isEmergency) {
336
+ return {
337
+ voice: "emergency",
338
+ text: [
339
+ emergencyHeader(prompts),
340
+ "",
341
+ breakdownStr,
342
+ "",
343
+ prompts.howToCompressRules,
344
+ "",
345
+ `{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }`,
346
+ "Only use IDs from visible messages above. Compress older work first.",
347
+ "",
348
+ rangesStr,
349
+ ...blockMapStr ? ["", blockMapStr] : []
350
+ ].join("\n")
351
+ };
352
+ }
353
+ return {
354
+ voice: "gentle",
355
+ text: [
356
+ efficiencyNote(prompts),
306
357
  "",
307
358
  breakdownStr,
308
359
  "",
309
- triggerLine,
310
- isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well \u2014 apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,
311
- blockList,
312
- `Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`
313
- ];
314
- if (includeGuidance) {
315
- parts2.push("", prompts.howToCompressRules, "", isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules);
316
- }
317
- return { voice, text: parts2.join("\n") };
318
- }
319
- if (isEmergency) {
320
- const parts2 = [emergencyHeader(prompts, includeGuidance), "", breakdownStr];
321
- if (includeGuidance) parts2.push("", prompts.howToCompressRules);
322
- parts2.push(
360
+ prompts.howToCompressRules,
323
361
  "",
324
- `{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }`,
325
- "Only use IDs from visible messages above. Compress older work first.",
362
+ rangesStr,
363
+ ...blockMapStr ? ["", blockMapStr] : [],
326
364
  "",
327
- rangesStr
328
- );
329
- return { voice: "emergency", text: parts2.join("\n") };
330
- }
331
- const parts = [efficiencyNote(prompts, includeGuidance), "", breakdownStr];
332
- if (includeGuidance) parts.push("", prompts.howToCompressRules);
333
- parts.push("", rangesStr, "", `\u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`);
334
- return { voice: "gentle", text: parts.join("\n") };
365
+ `\u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`
366
+ ].join("\n")
367
+ };
335
368
  }
336
369
 
337
370
  // src/viable.ts
@@ -357,4 +390,4 @@ export {
357
390
  VIABLE_RANGE_MIN_TOKENS,
358
391
  viableRanges
359
392
  };
360
- //# sourceMappingURL=chunk-T6YQEHNL.js.map
393
+ //# sourceMappingURL=chunk-AJ4PV7FF.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tokenize.ts","../src/compression-rules.ts","../src/prompts.ts","../src/nudge-text.ts","../src/viable.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\n\nexport function defaultCountTokens(text: string): number {\n if (!text) return 0;\n // CJK chars tokenize ~1:1 (chars/4 badly underestimates them). Count them\n // directly, then estimate the non-CJK remainder with chars/4 so digits,\n // punctuation, and symbols in code/JSON are not dropped to zero.\n const cjk = text.match(/[\\u4e00-\\u9fff\\u3040-\\u30ff\\uac00-\\ud7af]/g);\n const cjkCount = cjk?.length ?? 0;\n return cjkCount + Math.ceil((text.length - cjkCount) / 4);\n}\n\nexport function estimateMessageTokens(text: string | undefined): number {\n return defaultCountTokens(text ?? \"\");\n}\n\n/** Guarded contribution of a host-projected thinking payload to the metered\n * message size. Non-finite or non-positive values are treated as absent (0),\n * so a bad host value can never poison the accounting. */\nexport function thinkingTokenValue(thinking: number | undefined): number {\n return typeof thinking === \"number\" &&\n Number.isFinite(thinking) &&\n thinking > 0\n ? thinking\n : 0;\n}\n\n/** Total metered size of a core message: visible text plus any host-projected\n * thinking payload (CoreMessage.thinkingTokens). Every per-message counting\n * site (range recommendations, block compressedTokens, status reports, context\n * breakdown) goes through this so all surfaces share one caliber. */\nexport function countMessageTokens(\n message: { text?: string; thinkingTokens?: number },\n countTokens: TokenCountFn = defaultCountTokens,\n): number {\n return (\n countTokens(message.text ?? \"\") + thinkingTokenValue(message.thinkingTokens)\n );\n}\n\nexport function estimateTokensFast(text: string): number {\n if (!text) return 0;\n return Math.ceil(text.length / 4);\n}\n\nexport type TokenCountFn = (text: string) => number;\n\nconst BPE_SIZE_GUARD = 100_000;\n\nexport function createBpeTokenizer(): TokenCountFn {\n try {\n const mod = require(\"@anthropic-ai/tokenizer\");\n const bpeCount = mod.countTokens ?? mod.default?.countTokens;\n if (typeof bpeCount !== \"function\") return defaultCountTokens;\n return (text: string) => {\n if (text.length > BPE_SIZE_GUARD) return defaultCountTokens(text);\n try {\n return bpeCount(text);\n } catch {\n return defaultCountTokens(text);\n }\n };\n } catch {\n return defaultCountTokens;\n }\n}\n","/**\n * Compression rule texts — VERBATIM copy from context-compress-algorithms (MIT, ours).\n * These were tuned over months of production use.\n *\n * 2026-09-06 amendment (owner-approved; billion-context-pi#309 incident): write-side\n * quote-fidelity rules merged INTO the tuned text. Verbatim user quotes now require\n * a message ref; recorded task state is labeled as history; unicode written directly.\n * Motivation: session 01a071dc blocks b60/b61 stored a fabricated\n * `user verbatim '合并了 下一个'` as a live \"CURRENT TASK\", causing loop relapses.\n */\n\nexport const COMPRESS_PHILOSOPHY = `Compression Philosophy:\n- All compression serves the primary task, but be frugal.\n- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.\n- Compress by need, not by percentage.\n- Work from summaries, not raw tool outputs. All listed ranges (user prompts, tool outputs, code, logs, exploration, intermediate steps) should be compressed to summary format — the ONLY exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct.`;\n\nexport const HOW_TO_COMPRESS_RULES = `HOW TO COMPRESS\n\nWhen you call \\`compress\\`, the summary you write becomes the only record of the replaced conversation. Make it self-contained and complete: every user request, experiment purpose, and work task in the range must be accurately captured. A later reader (or you, after decompressing) should be able to continue the task WITHOUT needing the original. The summary records the PAST as of this block's creation: label recorded task state as history (\"TASK AS OF THIS BLOCK: ...\") — never as a live instruction, so a later reader treats it as settled context, not something to re-execute. Write plain text with real unicode characters; never copy \\\\uXXXX escape sequences or JSON-escaped fragments out of tool output.\n\nKEEP VERBATIM — never paraphrase or abbreviate these:\n- Full file paths with line numbers, directory prefix on every mention (\\`lib/hooks.ts:347\\`, \\`src/index.ts:12-18\\`, \\`gatenet_v3/model.py:45\\`). Never abbreviate to a bare filename (\\`hooks.ts\\`, \\`model.py\\`) — they are ambiguous and cannot be grepped or decompressed-to later.\n- Function, class, and type signatures (exact names, params, return types) AND critical code lines that encode logic — the line that IS the finding, not just the function name (e.g. \\`kv_keys += define_gate * a_key[i](emb)\\` is more useful than \"see model_kvnet.py\").\n- Error messages and stack traces (exact text — you need the literal string to grep for it later).\n- Key details from reports and analyses — not just the conclusion. Keep the comparison numbers and the mechanism, not \"X is worse\" alone (write \"1.76× PPL gap because KV store is static\", not \"KVNet underperforms\").\n- Decisions and their rationale (\"chose X over Y because Z\" — the \"because\" is load-bearing; without it the decision looks arbitrary).\n- Constraints discovered (\"must support Node 22\", \"no new dependencies\", \"AGENTS.md forbids \\`as any\\`\").\n- Exact values: versions, config keys, thresholds, magic numbers.\n- User intent — quote short user messages verbatim ONLY WITH their message ref, e.g. \\`User said (m00132): \"ship it tonight\"\\`. Without a verifiable ref, paraphrase (\\`user previously asked (paraphrased): ...\\`) — this is the one exception to the verbatim rule above; never present a reconstructed or half-remembered phrase as a verbatim quote. When the message is too long to quote, preserve intent with extra care: do not change scope, constraints, priorities, acceptance criteria, or requested outcomes. Quotes are historical records, never current directives. Losing these changes the task itself.\n- The user's overall goal and any changes to it — the big-picture objective plus how it evolved during the compressed range. Each summary must reflect the goal as it stood at the end of the range, including pivots (e.g., \"initially: fix bug X → pivoted to: refactor module Y after discovering root cause\"). Losing the goal or its evolution makes all subsequent work appear unmotivated.\n- Purpose behind each significant action — preserve not just what was done but why: the hypothesis behind each experiment, the question behind each exploration, the task goal behind each work action. Without purpose, the summary reads as disconnected technical steps with no through-line.\n- Open questions and unresolved TODOs — losing these changes what work appears to remain.\n- Message refs of key anchors (\\`m00420\\`, \\`m00510–m00520\\`) — they let you or a later reader jump back via decompress to the exact original.\n\nDROP — extract the signal, discard the vessel:\n- Verbose logs (build/test/\\`npm\\` output) once you have captured the error line or the result.\n- Duplicate file reads once the needed content is recorded.\n- Consumed exploration — search hits, agent return values, successful tool outputs — once you have extracted the facts you need (same rule as dead-ends, but nothing went wrong; the content is simply spent).\n- Dead-end exploration — but PRESERVE the lesson in one line: \"tried X, failed because Y\".\n- Back-and-forth discussion and self-corrections once the final position is captured (keep the outcome, drop the journey to it).\n- Repeated status checks (\\`git status\\`, \\`ls\\`) once state is known.\n\nFor each significant item you DROP (scripts, reports, large analyses, long tool outputs), add a one-line CONTENT description of what it covers — not where it lives. Bad: \"probe script at /path/probe_kvnet.py\". Good: \"probe_kvnet.py: tests n-gram baseline, generation quality, long-range dependency, position sensitivity, op pipeline, QUERY attention.\" This lets a later decompress target the right block by relevance, not by guessing locations.\n\nPRIORITY — when the summary must be compact, preserve in this order:\n1. User's overall goal, goal evolution, intent, and hard constraints (losing these changes the task).\n2. Decisions and rationale.\n3. Exact technical artifacts: paths, signatures, errors, values.\n4. Conclusions and key findings.\n5. Lessons learned: what failed and why.\n\nWrite dense, scannable bullets — not narrative prose. If the range spans distinct concerns (request → findings → decision), group bullets under short thematic headers so a reader can scan to the part they need. Every line must earn its place. Do not mimic the style of existing summaries in context; follow these rules.`;\n\nexport const TIER2_DISTILL_RULES = `TIER 2 COMPRESSION — DISTILLATION\n\nYou are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them: extract only what matters for future work, discard the process.\n\nKEEP — these are the only things that survive distillation:\n- Decisions and their rationale (\"chose X over Y because Z\" — the \"because\" is load-bearing).\n- Final outcomes: version numbers shipped, PR numbers merged/closed, bugs fixed or deferred.\n- Key lessons: what failed and why (\"tried X, failed because Y\"). These prevent repeating mistakes.\n- Critical constraints discovered (\"must support Node 22\", \"AGENTS.md forbids as any\").\n- Design decisions with architectural impact (\"chose compress-as-anchor over synthetic messages because prefix cache\").\n- User quotes and task state only as attributed history: keep the source ref with any user quote; never carry a tier-1 \"CURRENT TASK\" claim forward as a live directive — relabel it \"TASK AS OF THIS BLOCK\".\n- Whether content is OBSOLETE or SUPERSEDED — mark with one line: \"[SUPERSEDED by PR #NNN]\" or \"[OBSOLETE: deleted in vX.Y.Z]\". Do NOT keep the obsolete content's details — just the marker and reason.\n- Function/class/type names and module paths that are the SUBJECT of the work — e.g., \"fixed filterCompressedRanges in prune.ts\", \"added SessionStateRegistry in state.ts\". Not exact line numbers or full signatures — just enough to LOCATE the code without searching.\n- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line (\"explored X, not viable because Y\"). Do not keep the exploration process.\n\nDROP — these were useful during the work but are no longer needed:\n- Exact line numbers, diffs, verbose function signatures, full code listings.\n- Build/deploy process details, test execution steps.\n- Review process details (who reviewed, what rounds, test counts).\n- Verbose logs, command output, intermediate debugging steps.\n\nFORMAT:\n- Start each distilled block with a source header line:\n \\`Source: bN+bM+... (XK→YK tok, Zx). [original topic]\\`\n Example: \\`Source: b5+b7 (56K+44K→268 tok, 375x). [Tool-result recap + publish]\\`\n- 3-5 bullet points per source block, each a self-contained fact.\n- Dense, scannable — no narrative prose.\n- Start with the outcome, not the process: \"v1.13.0 shipped (7 PRs bundled)\" not \"implemented 7 PRs then reviewed then merged\".\n- Cross-block synthesis: if multiple source blocks cover the same topic (same PR, same feature, same bug), MERGE them into a single group of bullets. Do not repeat the same fact from different blocks — keep it once under the most relevant source header.\n\nSIZE TARGET: 50-150 tokens per source block (excluding the header). If you can't fit it in 150 tokens, you're keeping too much process. If a block has nothing worth keeping (pure noise), output just the header followed by \"[no actionable content].\"`;\n\nexport const TIER3_CONDENSE_RULES = `TIER 3 COMPRESSION — ULTRA-CONDENSATION\n\nYou are compressing distilled summaries (Tier 2) into ultra-condensed facts (Tier 3). The distilled summaries already contain only decisions and outcomes. Your job is to reduce them to bare factual references.\n\nPRIORITY — when a source block has more facts than the size target allows, keep in this order:\n1. Shipped outcomes (versions released, PRs merged) — these are permanent record.\n2. Open work (PRs/issues still pending) — these may need follow-up.\n3. Key decisions with architectural impact (\"chose X over Y because Z\").\n4. Critical constraints (\"must support Node 22\").\nDrop everything else. Tier 3 is a lookup index, not a knowledge base.\n\nFORMAT:\n- Start with a source header line:\n \\`Source: bN+bM+... (XK→YK tok, Zx). [original topic]\\`\n- Output 1-3 facts per source block. Each fact is a single line: subject + outcome.\n- No explanations, no rationale, no process — just the fact.\n- Format: \"[PR/Issue/Version] — [outcome in ≤8 words]\"\n- Merge related facts from different source blocks if they concern the same topic.\n\nEXAMPLES:\n- \"v1.13.0 shipped — quality gate + GC fix (7 PRs)\"\n- \"PR #196 merged — preserve-first-user (supersedes #169)\"\n- \"Bug 1214 fixed — compress consumed all user messages\"\n- \"Chose compress-as-anchor — prefix cache benefit over synthetic injection\"\n- \"Constraint: AGENTS.md forbids as any — never suppress types\"\n\nDROP:\n- Multi-sentence context. If a fact needs >1 sentence, it's too detailed for Tier 3.\n- Lessons learned (\"tried X, failed because Y\") — drop UNLESS the failure is likely to recur and the block is <30 days old.\n- Design rationale details — keep the decision, drop the \"because\" unless it's a critical constraint.\n- Anything marked [OBSOLETE] or [SUPERSEDED] — drop entirely, note \"[N blocks obsolete]\" in the summary.\n\nSIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output ≈ N × 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;\n","import {\n COMPRESS_PHILOSOPHY,\n HOW_TO_COMPRESS_RULES,\n TIER2_DISTILL_RULES,\n TIER3_CONDENSE_RULES,\n} from \"./compression-rules.js\";\n\n/**\n * Overridable prompt text consumed by the kernel's nudge renderer and, via the\n * adapter, the system prompt. Every field here is LOAD-BEARING: these rules\n * were tuned over months of production use and are quality-critical. Overriding\n * them can degrade summary quality (loss of paths / signatures / decisions →\n * broken retrieval), so {@link resolvePrompts} requires `{ acknowledgeRisk: true }`.\n *\n * Surface-level text (summary section headers, status-report chrome, tool\n * descriptions) is intentionally NOT part of this interface — it is owned by\n * the adapter or a later \"prompt-set format\" layer and is safe to customize\n * freely. See DESIGN.md for the load-bearing vs surface classification.\n */\nexport interface Prompts {\n /** Core compression philosophy. Embedded in the system prompt + every nudge. */\n compressPhilosophy: string;\n /** Rules the model follows when writing a tier-1 summary. */\n howToCompressRules: string;\n /** Rules for tier-2 distillation of existing summaries. */\n tier2DistillRules: string;\n /** Rules for tier-3 ultra-condensation of distilled summaries. */\n tier3CondenseRules: string;\n}\n\n/**\n * The kernel's canonical prompt values (verbatim from compression-rules.ts).\n * Frozen so a buggy caller cannot mutate the shared singleton and corrupt\n * every other consumer of {@link defaultPrompts}.\n */\nexport const defaultPrompts: Prompts = Object.freeze({\n compressPhilosophy: COMPRESS_PHILOSOPHY,\n howToCompressRules: HOW_TO_COMPRESS_RULES,\n tier2DistillRules: TIER2_DISTILL_RULES,\n tier3CondenseRules: TIER3_CONDENSE_RULES,\n}) as Prompts;\n\nexport interface ResolvePromptsOptions {\n /**\n * Must be `true` to override any prompt field. Every {@link Prompts} field is\n * load-bearing; overriding without acknowledging the quality risk is a\n * programming error and throws.\n */\n acknowledgeRisk?: boolean;\n}\n\n/**\n * Merge prompt overrides onto the kernel defaults. All fields are load-bearing,\n * so ANY override requires `{ acknowledgeRisk: true }`.\n *\n * Only `string`-valued overrides take effect: an explicit `undefined`/`null` or\n * a wrong type is silently dropped (never clobbers a good default), so a\n * malformed partial never degrades the canonical rules. Resolve once at host\n * startup, then pass the resulting {@link Prompts} to {@link renderNudgeText}\n * and to the adapter's system-prompt composition so both layers stay consistent.\n *\n * To keep guidance in the system prompt but drop it from every nudge (hosts that\n * deliver these texts once at startup), do NOT override fields here — that would\n * change both surfaces at once. Pass `{ includeGuidance: false }` to\n * {@link renderNudgeText} instead; the system-prompt path is unaffected by it.\n */\nexport function resolvePrompts(\n overrides?: Partial<Prompts>,\n options: ResolvePromptsOptions = {},\n): Prompts {\n const clean: Partial<Prompts> = {};\n if (overrides) {\n for (const [key, value] of Object.entries(overrides)) {\n if (typeof value === \"string\") {\n (clean as Record<string, unknown>)[key] = value;\n }\n }\n }\n const keys = Object.keys(clean) as (keyof Prompts)[];\n if (keys.length > 0 && !options.acknowledgeRisk) {\n throw new Error(\n `resolvePrompts: overriding compression rules requires { acknowledgeRisk: true }. ` +\n `Overridden keys: ${keys.join(\", \")}. These rules are quality-critical (tuned over months of production use); ` +\n `changing them can degrade summary quality and break retrieval (summaries may lose paths, signatures, decisions).`,\n );\n }\n return { ...defaultPrompts, ...clean };\n}\n","import type { NudgeDecision, CompressibleRange, ProtectedRange, ContextBreakdown, CompressionBlock } from \"./types.js\";\nimport { defaultPrompts } from \"./prompts.js\";\nimport type { Prompts } from \"./prompts.js\";\n\nexport type NudgeVoice = \"gentle\" | \"emergency\";\n\nexport interface RenderedNudge {\n voice: NudgeVoice;\n text: string;\n}\n\nexport interface RenderNudgeOptions {\n /**\n * Embed the load-bearing guidance texts (compressPhilosophy, howToCompressRules,\n * and the active tier's distill/condense rules) in the nudge body. Defaults to\n * true, so existing callers render byte-identical output. Set to false when the\n * host already delivers these same texts once via its system prompt or tool\n * descriptions — re-billing them on every injection is pure repetition. Only\n * the four guidance texts are dropped; per-injection dynamic content\n * (breakdown, trigger line, ranges, tier target blocks) survives. The\n * system-prompt composition path reads {@link Prompts} directly and is\n * unaffected by this flag — this is how a host keeps guidance in the system\n * prompt while slimming every nudge.\n */\n includeGuidance?: boolean;\n}\n\nfunction efficiencyNote(prompts: Prompts, includeGuidance: boolean): string {\n const note = \"This is an efficiency nudge to compress early and keep context lean — not an overflow warning. A separate, stronger alert will appear if the context is actually full.\";\n return includeGuidance ? `${note}\\n\\n${prompts.compressPhilosophy}` : note;\n}\n\nfunction emergencyHeader(prompts: Prompts, includeGuidance: boolean): string {\n const header = \"⚠️ Context limit reached — compress now. Prioritize consumed tool outputs.\";\n return includeGuidance ? `${header}\\n\\n${prompts.compressPhilosophy}` : header;\n}\n\nfunction formatK(n: number): string {\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;\n return `${n}`;\n}\n\nfunction formatBreakdown(bd?: ContextBreakdown): string {\n if (!bd) return \"\";\n const parts: string[] = [];\n if (bd.system > 0) parts.push(`${formatK(bd.system)} system`);\n if (bd.tool > 0) parts.push(`${formatK(bd.tool)} tool`);\n if (bd.summaries > 0) parts.push(`${formatK(bd.summaries)} summaries`);\n if (bd.code > 0) parts.push(`${formatK(bd.code)} code`);\n if (bd.text > 0) parts.push(`${formatK(bd.text)} text`);\n const growth = bd.growth > 0 ? `\\n+${formatK(bd.growth)} since last nudge` : \"\";\n return `Context breakdown: ${parts.join(\" | \")}${growth}`;\n}\n\n\n\nfunction formatTierTargetBlocks(blocks: CompressionBlock[]): string {\n if (blocks.length === 0) {\n return \"Target blocks: (none — no tier blocks found)\";\n }\n const lines = blocks.map((b) => {\n const summaryTokens = Math.ceil((b.summary ?? \"\").length / 4);\n const topic = b.topic ? ` \"${b.topic}\"` : \"\";\n return ` ${b.blockId} ${b.effectiveMessageIds.length} msgs ${formatK(b.compressedTokens)}→${formatK(summaryTokens)}${topic}`;\n });\n return `Target ${blocks[0]!.tier === 1 ? \"tier-1\" : \"tier-2\"} blocks to distill (${blocks.length}):\\n${lines.join(\"\\n\")}`;\n}\n\nexport function formatRanges(compressible: CompressibleRange[], protectedRanges: ProtectedRange[]): string {\n if (compressible.length === 0 && protectedRanges.length === 0) {\n return \"[No specific ranges detected — compress any consumed content.]\";\n }\n\n // Merge compressible + protected into a single oldest-first list, mirroring\n // opencode-acp's formatCompressibleRanges. Splitting them into two sections\n // lost the time order and hid overlaps; a range can be partly compressible\n // and partly protected, which only the merged view shows correctly.\n interface Merged {\n startRef: string; endRef: string; startNum: number; endNum: number;\n count: number; tokens: number;\n compressibleTokens: number; compressibleCount: number;\n protectedTokens: number; protectedCount: number; protectedTools: string[];\n toolPct: number; textPct: number; dangerous: boolean;\n }\n const refNum = (ref: string): number => {\n const m = ref.match(/\\d+/);\n return m ? parseInt(m[0], 10) : 0;\n };\n const entries: Merged[] = [];\n for (const r of compressible) {\n entries.push({\n startRef: r.startRef, endRef: r.endRef, startNum: refNum(r.startRef), endNum: refNum(r.endRef),\n count: r.count, tokens: r.tokens, toolPct: r.toolPct, textPct: r.textPct,\n compressibleTokens: r.tokens, compressibleCount: r.count,\n protectedTokens: 0, protectedCount: 0, protectedTools: [], dangerous: r.dangerous ?? false,\n });\n }\n for (const r of protectedRanges) {\n entries.push({\n startRef: r.startRef, endRef: r.endRef, startNum: refNum(r.startRef), endNum: refNum(r.endRef),\n count: r.count, tokens: r.tokens, toolPct: 0, textPct: 0,\n compressibleTokens: 0, compressibleCount: 0,\n protectedTokens: r.tokens, protectedCount: r.count, protectedTools: [...r.tools], dangerous: false,\n });\n }\n entries.sort((a, b) => a.startNum - b.startNum);\n // Merge adjacent/overlapping ranges (gap ≤ 1 ref).\n const merged: Merged[] = [];\n for (const e of entries) {\n const last = merged[merged.length - 1];\n if (last && e.startNum <= last.endNum + 1) {\n last.endRef = e.endRef;\n last.endNum = Math.max(last.endNum, e.endNum);\n last.count += e.count;\n last.tokens += e.tokens;\n last.compressibleTokens += e.compressibleTokens;\n last.compressibleCount += e.compressibleCount;\n last.protectedTokens += e.protectedTokens;\n last.protectedCount += e.protectedCount;\n if (e.dangerous) last.dangerous = true;\n for (const t of e.protectedTools) {\n if (!last.protectedTools.includes(t)) last.protectedTools.push(t);\n }\n } else {\n merged.push({ ...e });\n }\n }\n const lines = merged.map((e) => {\n const suffix = e.dangerous && e.compressibleTokens > 0 ? \" ⚠️ NOT recommended unless you are certain.\" : \"\";\n if (e.protectedTokens > 0 && e.compressibleTokens === 0) {\n return ` ${e.startRef}–${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [PROTECTED: ${e.protectedTools.join(\", \")} — not compressible]${suffix}`;\n }\n if (e.protectedTokens > 0 && e.compressibleTokens > 0) {\n return ` ${e.startRef}–${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [${formatK(e.compressibleTokens)} compressible | ${formatK(e.protectedTokens)} protected: ${e.protectedTools.join(\", \")}]${suffix}`;\n }\n return ` ${e.startRef}–${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${suffix}`;\n });\n return `Compressible ranges (${merged.length}, oldest first):\\n${lines.join(\"\\n\")}`;\n}\n\nexport function renderNudgeText(\n decision: NudgeDecision,\n prompts: Prompts = defaultPrompts,\n options: RenderNudgeOptions = {},\n): RenderedNudge {\n const includeGuidance = options.includeGuidance ?? true;\n const breakdownStr = formatBreakdown(decision.contextBreakdown);\n const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);\n const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;\n\n if (decision.tier !== null && decision.tier >= 2) {\n const isT2 = decision.tier === 2;\n const targets = decision.tierTargetBlocks ?? [];\n const blockList = formatTierTargetBlocks(targets);\n const startId = targets[0]?.blockId ?? \"b1\";\n const endId = targets[targets.length - 1]?.blockId ?? \"b5\";\n const voice: NudgeVoice = isEmergency ? \"emergency\" : \"gentle\";\n const triggerLine = isEmergency\n ? `[EMERGENCY — TIER ${decision.tier} ${isT2 ? \"DISTILLATION\" : \"CONDENSATION\"}] Context limit reached — distill NOW into a denser summary to reclaim tokens.`\n : `[TIER ${decision.tier} ${isT2 ? \"DISTILLATION\" : \"CONDENSATION\"} TRIGGER]`;\n const parts = [\n efficiencyNote(prompts, includeGuidance),\n \"\",\n breakdownStr,\n \"\",\n triggerLine,\n isT2\n ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well — apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.`\n : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well — apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,\n blockList,\n `Example: compress({ content: [{ startId: \"${startId}\", endId: \"${endId}\", summary: \"...\" }] })`,\n ];\n if (includeGuidance) {\n parts.push(\"\", prompts.howToCompressRules, \"\", isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules);\n }\n return { voice, text: parts.join(\"\\n\") };\n }\n\n if (isEmergency) {\n const parts = [emergencyHeader(prompts, includeGuidance), \"\", breakdownStr];\n if (includeGuidance) parts.push(\"\", prompts.howToCompressRules);\n parts.push(\n \"\",\n `{ \"topic\": \"...\", \"content\": [{ \"startId\": \"<ID>\", \"endId\": \"<ID>\", \"summary\": \"...\" }] }`,\n \"Only use IDs from visible messages above. Compress older work first.\",\n \"\",\n rangesStr,\n );\n return { voice: \"emergency\", text: parts.join(\"\\n\") };\n }\n\n const parts = [efficiencyNote(prompts, includeGuidance), \"\", breakdownStr];\n if (includeGuidance) parts.push(\"\", prompts.howToCompressRules);\n parts.push(\"\", rangesStr, \"\", `💡 Compress all ranges in one call (pass multiple content entries: \\`content: [{...}, {...}]\\`).`);\n return { voice: \"gentle\", text: parts.join(\"\\n\") };\n}\n","/** Minimum size for a compressible range to be worth recommending. Ranges\n * below this are fragmented leftovers (a 16-token ack, a one-line tool\n * result): the model cannot write a meaningful >=50-char summary for them,\n * and a batched compress call that includes one gets atomically rejected\n * (the kernel validates the whole batch). Observed in the wild: a 14-range\n * recommendation list containing a 16-token range → every batch attempt\n * failed with \"Summary too short\". Apply on every surface that recommends\n * ranges: the injected nudge, acp_status, and the /acp panel. */\nexport const VIABLE_RANGE_MIN_TOKENS = 200;\n\nexport function viableRanges<T extends { tokens: number }>(ranges: T[]): T[] {\n return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);\n}\n"],"mappings":"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","names":["require","require","parts"]}
1
+ {"version":3,"sources":["../src/tokenize.ts","../src/compression-rules.ts","../src/prompts.ts","../src/nudge-text.ts","../src/viable.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\n\nconst require = createRequire(import.meta.url);\n\nexport function defaultCountTokens(text: string): number {\n if (!text) return 0;\n // CJK chars tokenize ~1:1 (chars/4 badly underestimates them). Count them\n // directly, then estimate the non-CJK remainder with chars/4 so digits,\n // punctuation, and symbols in code/JSON are not dropped to zero.\n const cjk = text.match(/[\\u4e00-\\u9fff\\u3040-\\u30ff\\uac00-\\ud7af]/g);\n const cjkCount = cjk?.length ?? 0;\n return cjkCount + Math.ceil((text.length - cjkCount) / 4);\n}\n\nexport function estimateMessageTokens(text: string | undefined): number {\n return defaultCountTokens(text ?? \"\");\n}\n\n/** Guarded contribution of a host-projected thinking payload to the metered\n * message size. Non-finite or non-positive values are treated as absent (0),\n * so a bad host value can never poison the accounting. */\nexport function thinkingTokenValue(thinking: number | undefined): number {\n return typeof thinking === \"number\" &&\n Number.isFinite(thinking) &&\n thinking > 0\n ? thinking\n : 0;\n}\n\n/** Total metered size of a core message: visible text plus any host-projected\n * thinking payload (CoreMessage.thinkingTokens). Every per-message counting\n * site (range recommendations, block compressedTokens, status reports, context\n * breakdown) goes through this so all surfaces share one caliber. */\nexport function countMessageTokens(\n message: { text?: string; thinkingTokens?: number },\n countTokens: TokenCountFn = defaultCountTokens,\n): number {\n return (\n countTokens(message.text ?? \"\") + thinkingTokenValue(message.thinkingTokens)\n );\n}\n\nexport function estimateTokensFast(text: string): number {\n if (!text) return 0;\n return Math.ceil(text.length / 4);\n}\n\nexport type TokenCountFn = (text: string) => number;\n\nconst BPE_SIZE_GUARD = 100_000;\n\nexport function createBpeTokenizer(): TokenCountFn {\n try {\n const mod = require(\"@anthropic-ai/tokenizer\");\n const bpeCount = mod.countTokens ?? mod.default?.countTokens;\n if (typeof bpeCount !== \"function\") return defaultCountTokens;\n return (text: string) => {\n if (text.length > BPE_SIZE_GUARD) return defaultCountTokens(text);\n try {\n return bpeCount(text);\n } catch {\n return defaultCountTokens(text);\n }\n };\n } catch {\n return defaultCountTokens;\n }\n}\n","/**\n * Compression rule texts — VERBATIM copy from context-compress-algorithms (MIT, ours).\n * These were tuned over months of production use.\n *\n * 2026-09-06 amendment (owner-approved; billion-context-pi#309 incident): write-side\n * quote-fidelity rules merged INTO the tuned text. Verbatim user quotes now require\n * a message ref; recorded task state is labeled as history; unicode written directly.\n * Motivation: session 01a071dc blocks b60/b61 stored a fabricated\n * `user verbatim '合并了 下一个'` as a live \"CURRENT TASK\", causing loop relapses.\n */\n\nexport const COMPRESS_PHILOSOPHY = `Compression Philosophy:\n- All compression serves the primary task, but be frugal.\n- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.\n- Compress by need, not by percentage.\n- Work from summaries, not raw tool outputs. All listed ranges (user prompts, tool outputs, code, logs, exploration, intermediate steps) should be compressed to summary format — the ONLY exceptions are protected content, content the current step is actively using, or critical content you cannot reconstruct.`;\n\nexport const HOW_TO_COMPRESS_RULES = `HOW TO COMPRESS\n\nWhen you call \\`compress\\`, the summary you write becomes the only record of the replaced conversation. Make it self-contained and complete: every user request, experiment purpose, and work task in the range must be accurately captured. A later reader (or you, after decompressing) should be able to continue the task WITHOUT needing the original. The summary records the PAST as of this block's creation: label recorded task state as history (\"TASK AS OF THIS BLOCK: ...\") — never as a live instruction, so a later reader treats it as settled context, not something to re-execute. Write plain text with real unicode characters; never copy \\\\uXXXX escape sequences or JSON-escaped fragments out of tool output.\n\nKEEP VERBATIM — never paraphrase or abbreviate these:\n- Full file paths with line numbers, directory prefix on every mention (\\`lib/hooks.ts:347\\`, \\`src/index.ts:12-18\\`, \\`gatenet_v3/model.py:45\\`). Never abbreviate to a bare filename (\\`hooks.ts\\`, \\`model.py\\`) — they are ambiguous and cannot be grepped or decompressed-to later.\n- Function, class, and type signatures (exact names, params, return types) AND critical code lines that encode logic — the line that IS the finding, not just the function name (e.g. \\`kv_keys += define_gate * a_key[i](emb)\\` is more useful than \"see model_kvnet.py\").\n- Error messages and stack traces (exact text — you need the literal string to grep for it later).\n- Key details from reports and analyses — not just the conclusion. Keep the comparison numbers and the mechanism, not \"X is worse\" alone (write \"1.76× PPL gap because KV store is static\", not \"KVNet underperforms\").\n- Decisions and their rationale (\"chose X over Y because Z\" — the \"because\" is load-bearing; without it the decision looks arbitrary).\n- Constraints discovered (\"must support Node 22\", \"no new dependencies\", \"AGENTS.md forbids \\`as any\\`\").\n- Exact values: versions, config keys, thresholds, magic numbers.\n- User intent — quote short user messages verbatim ONLY WITH their message ref, e.g. \\`User said (m00132): \"ship it tonight\"\\`. Without a verifiable ref, paraphrase (\\`user previously asked (paraphrased): ...\\`) — this is the one exception to the verbatim rule above; never present a reconstructed or half-remembered phrase as a verbatim quote. When the message is too long to quote, preserve intent with extra care: do not change scope, constraints, priorities, acceptance criteria, or requested outcomes. Quotes are historical records, never current directives. Losing these changes the task itself.\n- The user's overall goal and any changes to it — the big-picture objective plus how it evolved during the compressed range. Each summary must reflect the goal as it stood at the end of the range, including pivots (e.g., \"initially: fix bug X → pivoted to: refactor module Y after discovering root cause\"). Losing the goal or its evolution makes all subsequent work appear unmotivated.\n- Purpose behind each significant action — preserve not just what was done but why: the hypothesis behind each experiment, the question behind each exploration, the task goal behind each work action. Without purpose, the summary reads as disconnected technical steps with no through-line.\n- Open questions and unresolved TODOs — losing these changes what work appears to remain.\n- Message refs of key anchors (\\`m00420\\`, \\`m00510–m00520\\`) — they let you or a later reader jump back via decompress to the exact original.\n\nDROP — extract the signal, discard the vessel:\n- Verbose logs (build/test/\\`npm\\` output) once you have captured the error line or the result.\n- Duplicate file reads once the needed content is recorded.\n- Consumed exploration — search hits, agent return values, successful tool outputs — once you have extracted the facts you need (same rule as dead-ends, but nothing went wrong; the content is simply spent).\n- Dead-end exploration — but PRESERVE the lesson in one line: \"tried X, failed because Y\".\n- Back-and-forth discussion and self-corrections once the final position is captured (keep the outcome, drop the journey to it).\n- Repeated status checks (\\`git status\\`, \\`ls\\`) once state is known.\n\nFor each significant item you DROP (scripts, reports, large analyses, long tool outputs), add a one-line CONTENT description of what it covers — not where it lives. Bad: \"probe script at /path/probe_kvnet.py\". Good: \"probe_kvnet.py: tests n-gram baseline, generation quality, long-range dependency, position sensitivity, op pipeline, QUERY attention.\" This lets a later decompress target the right block by relevance, not by guessing locations.\n\nPRIORITY — when the summary must be compact, preserve in this order:\n1. User's overall goal, goal evolution, intent, and hard constraints (losing these changes the task).\n2. Decisions and rationale.\n3. Exact technical artifacts: paths, signatures, errors, values.\n4. Conclusions and key findings.\n5. Lessons learned: what failed and why.\n\nWrite dense, scannable bullets — not narrative prose. If the range spans distinct concerns (request → findings → decision), group bullets under short thematic headers so a reader can scan to the part they need. Every line must earn its place. Do not mimic the style of existing summaries in context; follow these rules.`;\n\nexport const TIER2_DISTILL_RULES = `TIER 2 COMPRESSION — DISTILLATION\n\nYou are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them: extract only what matters for future work, discard the process.\n\nKEEP — these are the only things that survive distillation:\n- Decisions and their rationale (\"chose X over Y because Z\" — the \"because\" is load-bearing).\n- Final outcomes: version numbers shipped, PR numbers merged/closed, bugs fixed or deferred.\n- Key lessons: what failed and why (\"tried X, failed because Y\"). These prevent repeating mistakes.\n- Critical constraints discovered (\"must support Node 22\", \"AGENTS.md forbids as any\").\n- Design decisions with architectural impact (\"chose compress-as-anchor over synthetic messages because prefix cache\").\n- User quotes and task state only as attributed history: keep the source ref with any user quote; never carry a tier-1 \"CURRENT TASK\" claim forward as a live directive — relabel it \"TASK AS OF THIS BLOCK\".\n- Whether content is OBSOLETE or SUPERSEDED — mark with one line: \"[SUPERSEDED by PR #NNN]\" or \"[OBSOLETE: deleted in vX.Y.Z]\". Do NOT keep the obsolete content's details — just the marker and reason.\n- Function/class/type names and module paths that are the SUBJECT of the work — e.g., \"fixed filterCompressedRanges in prune.ts\", \"added SessionStateRegistry in state.ts\". Not exact line numbers or full signatures — just enough to LOCATE the code without searching.\n- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line (\"explored X, not viable because Y\"). Do not keep the exploration process.\n\nDROP — these were useful during the work but are no longer needed:\n- Exact line numbers, diffs, verbose function signatures, full code listings.\n- Build/deploy process details, test execution steps.\n- Review process details (who reviewed, what rounds, test counts).\n- Verbose logs, command output, intermediate debugging steps.\n\nFORMAT:\n- Start each distilled block with a source header line:\n \\`Source: bN+bM+... (XK→YK tok, Zx). [original topic]\\`\n Example: \\`Source: b5+b7 (56K+44K→268 tok, 375x). [Tool-result recap + publish]\\`\n- 3-5 bullet points per source block, each a self-contained fact.\n- Dense, scannable — no narrative prose.\n- Start with the outcome, not the process: \"v1.13.0 shipped (7 PRs bundled)\" not \"implemented 7 PRs then reviewed then merged\".\n- Cross-block synthesis: if multiple source blocks cover the same topic (same PR, same feature, same bug), MERGE them into a single group of bullets. Do not repeat the same fact from different blocks — keep it once under the most relevant source header.\n\nSIZE TARGET: 50-150 tokens per source block (excluding the header). If you can't fit it in 150 tokens, you're keeping too much process. If a block has nothing worth keeping (pure noise), output just the header followed by \"[no actionable content].\"`;\n\nexport const TIER3_CONDENSE_RULES = `TIER 3 COMPRESSION — ULTRA-CONDENSATION\n\nYou are compressing distilled summaries (Tier 2) into ultra-condensed facts (Tier 3). The distilled summaries already contain only decisions and outcomes. Your job is to reduce them to bare factual references.\n\nPRIORITY — when a source block has more facts than the size target allows, keep in this order:\n1. Shipped outcomes (versions released, PRs merged) — these are permanent record.\n2. Open work (PRs/issues still pending) — these may need follow-up.\n3. Key decisions with architectural impact (\"chose X over Y because Z\").\n4. Critical constraints (\"must support Node 22\").\nDrop everything else. Tier 3 is a lookup index, not a knowledge base.\n\nFORMAT:\n- Start with a source header line:\n \\`Source: bN+bM+... (XK→YK tok, Zx). [original topic]\\`\n- Output 1-3 facts per source block. Each fact is a single line: subject + outcome.\n- No explanations, no rationale, no process — just the fact.\n- Format: \"[PR/Issue/Version] — [outcome in ≤8 words]\"\n- Merge related facts from different source blocks if they concern the same topic.\n\nEXAMPLES:\n- \"v1.13.0 shipped — quality gate + GC fix (7 PRs)\"\n- \"PR #196 merged — preserve-first-user (supersedes #169)\"\n- \"Bug 1214 fixed — compress consumed all user messages\"\n- \"Chose compress-as-anchor — prefix cache benefit over synthetic injection\"\n- \"Constraint: AGENTS.md forbids as any — never suppress types\"\n\nDROP:\n- Multi-sentence context. If a fact needs >1 sentence, it's too detailed for Tier 3.\n- Lessons learned (\"tried X, failed because Y\") — drop UNLESS the failure is likely to recur and the block is <30 days old.\n- Design rationale details — keep the decision, drop the \"because\" unless it's a critical constraint.\n- Anything marked [OBSOLETE] or [SUPERSEDED] — drop entirely, note \"[N blocks obsolete]\" in the summary.\n\nSIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output ≈ N × 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;\n","import {\n COMPRESS_PHILOSOPHY,\n HOW_TO_COMPRESS_RULES,\n TIER2_DISTILL_RULES,\n TIER3_CONDENSE_RULES,\n} from \"./compression-rules.js\";\n\n/**\n * Overridable prompt text consumed by the kernel's nudge renderer and, via the\n * adapter, the system prompt. Every field here is LOAD-BEARING: these rules\n * were tuned over months of production use and are quality-critical. Overriding\n * them can degrade summary quality (loss of paths / signatures / decisions →\n * broken retrieval), so {@link resolvePrompts} requires `{ acknowledgeRisk: true }`.\n *\n * Surface-level text (summary section headers, status-report chrome, tool\n * descriptions) is intentionally NOT part of this interface — it is owned by\n * the adapter or a later \"prompt-set format\" layer and is safe to customize\n * freely. See DESIGN.md for the load-bearing vs surface classification.\n */\nexport interface Prompts {\n /** Core compression philosophy. Embedded in the system prompt + every nudge. */\n compressPhilosophy: string;\n /** Rules the model follows when writing a tier-1 summary. */\n howToCompressRules: string;\n /** Rules for tier-2 distillation of existing summaries. */\n tier2DistillRules: string;\n /** Rules for tier-3 ultra-condensation of distilled summaries. */\n tier3CondenseRules: string;\n}\n\n/**\n * The kernel's canonical prompt values (verbatim from compression-rules.ts).\n * Frozen so a buggy caller cannot mutate the shared singleton and corrupt\n * every other consumer of {@link defaultPrompts}.\n */\nexport const defaultPrompts: Prompts = Object.freeze({\n compressPhilosophy: COMPRESS_PHILOSOPHY,\n howToCompressRules: HOW_TO_COMPRESS_RULES,\n tier2DistillRules: TIER2_DISTILL_RULES,\n tier3CondenseRules: TIER3_CONDENSE_RULES,\n}) as Prompts;\n\nexport interface ResolvePromptsOptions {\n /**\n * Must be `true` to override any prompt field. Every {@link Prompts} field is\n * load-bearing; overriding without acknowledging the quality risk is a\n * programming error and throws.\n */\n acknowledgeRisk?: boolean;\n}\n\n/**\n * Merge prompt overrides onto the kernel defaults. All fields are load-bearing,\n * so ANY override requires `{ acknowledgeRisk: true }`.\n *\n * Only `string`-valued overrides take effect: an explicit `undefined`/`null` or\n * a wrong type is silently dropped (never clobbers a good default), so a\n * malformed partial never degrades the canonical rules. Resolve once at host\n * startup, then pass the resulting {@link Prompts} to {@link renderNudgeText}\n * and to the adapter's system-prompt composition so both layers stay consistent.\n */\nexport function resolvePrompts(\n overrides?: Partial<Prompts>,\n options: ResolvePromptsOptions = {},\n): Prompts {\n const clean: Partial<Prompts> = {};\n if (overrides) {\n for (const [key, value] of Object.entries(overrides)) {\n if (typeof value === \"string\") {\n (clean as Record<string, unknown>)[key] = value;\n }\n }\n }\n const keys = Object.keys(clean) as (keyof Prompts)[];\n if (keys.length > 0 && !options.acknowledgeRisk) {\n throw new Error(\n `resolvePrompts: overriding compression rules requires { acknowledgeRisk: true }. ` +\n `Overridden keys: ${keys.join(\", \")}. These rules are quality-critical (tuned over months of production use); ` +\n `changing them can degrade summary quality and break retrieval (summaries may lose paths, signatures, decisions).`,\n );\n }\n return { ...defaultPrompts, ...clean };\n}\n","import type { NudgeDecision, CompressibleRange, ProtectedRange, ContextBreakdown, CompressionBlock, BlockSpan } from \"./types.js\";\nimport { defaultPrompts } from \"./prompts.js\";\nimport type { Prompts } from \"./prompts.js\";\n\nexport type NudgeVoice = \"gentle\" | \"emergency\";\n\nexport interface RenderedNudge {\n voice: NudgeVoice;\n text: string;\n}\n\nfunction efficiencyNote(prompts: Prompts): string {\n return `This is an efficiency nudge to compress early and keep context lean — not an overflow warning. A separate, stronger alert will appear if the context is actually full.\\n\\n${prompts.compressPhilosophy}`;\n}\n\nfunction emergencyHeader(prompts: Prompts): string {\n return `⚠️ Context limit reached — compress now. Prioritize consumed tool outputs.\\n\\n${prompts.compressPhilosophy}`;\n}\n\nfunction formatK(n: number): string {\n if (n >= 1000) return `${(n / 1000).toFixed(1)}K`;\n return `${n}`;\n}\n\nfunction formatBreakdown(bd?: ContextBreakdown): string {\n if (!bd) return \"\";\n const parts: string[] = [];\n if (bd.system > 0) parts.push(`${formatK(bd.system)} system`);\n if (bd.tool > 0) parts.push(`${formatK(bd.tool)} tool`);\n if (bd.summaries > 0) parts.push(`${formatK(bd.summaries)} summaries`);\n if (bd.code > 0) parts.push(`${formatK(bd.code)} code`);\n if (bd.text > 0) parts.push(`${formatK(bd.text)} text`);\n const growth = bd.growth > 0 ? `\\n+${formatK(bd.growth)} since last nudge` : \"\";\n return `Context breakdown: ${parts.join(\" | \")}${growth}`;\n}\n\n\n\nfunction formatTierTargetBlocks(blocks: CompressionBlock[]): string {\n if (blocks.length === 0) {\n return \"Target blocks: (none — no tier blocks found)\";\n }\n const lines = blocks.map((b) => {\n const summaryTokens = Math.ceil((b.summary ?? \"\").length / 4);\n const topic = b.topic ? ` \"${b.topic}\"` : \"\";\n return ` ${b.blockId} ${b.effectiveMessageIds.length} msgs ${formatK(b.compressedTokens)}→${formatK(summaryTokens)}${topic}`;\n });\n return `Target ${blocks[0]!.tier === 1 ? \"tier-1\" : \"tier-2\"} blocks to distill (${blocks.length}):\\n${lines.join(\"\\n\")}`;\n}\n\nconst BLOCK_MAP_MAX_SHOWN = 8;\n\nfunction formatBlockMap(spans: BlockSpan[]): string {\n if (spans.length === 0) return \"\";\n const hidden = Math.max(0, spans.length - BLOCK_MAP_MAX_SHOWN);\n const shown = hidden > 0 ? spans.slice(-BLOCK_MAP_MAX_SHOWN) : spans;\n const items = shown.map(\n (s) => `${s.blockId}=${s.startRef}–${s.endRef}${s.tier > 1 ? ` t${s.tier}` : \"\"}`,\n );\n const prefix = hidden > 0 ? `…+${hidden} older · ` : \"\";\n return `Active blocks (${spans.length}): ${prefix}${items.join(\" · \")}`;\n}\n\nexport function formatRanges(compressible: CompressibleRange[], protectedRanges: ProtectedRange[]): string {\n if (compressible.length === 0 && protectedRanges.length === 0) {\n return \"[No specific ranges detected — compress any consumed content.]\";\n }\n\n // Merge compressible + protected into a single oldest-first list, mirroring\n // opencode-acp's formatCompressibleRanges. Splitting them into two sections\n // lost the time order and hid overlaps; a range can be partly compressible\n // and partly protected, which only the merged view shows correctly.\n interface Merged {\n startRef: string; endRef: string; startNum: number; endNum: number;\n count: number; tokens: number; userMsgs: number;\n compressibleTokens: number; compressibleCount: number;\n protectedTokens: number; protectedCount: number; protectedTools: string[];\n toolPct: number; textPct: number; dangerous: boolean;\n }\n const refNum = (ref: string): number => {\n const m = ref.match(/\\d+/);\n return m ? parseInt(m[0], 10) : 0;\n };\n const entries: Merged[] = [];\n for (const r of compressible) {\n entries.push({\n startRef: r.startRef, endRef: r.endRef, startNum: refNum(r.startRef), endNum: refNum(r.endRef),\n count: r.count, tokens: r.tokens, userMsgs: r.userMsgs ?? 0, toolPct: r.toolPct, textPct: r.textPct,\n compressibleTokens: r.tokens, compressibleCount: r.count,\n protectedTokens: 0, protectedCount: 0, protectedTools: [], dangerous: r.dangerous ?? false,\n });\n }\n for (const r of protectedRanges) {\n entries.push({\n startRef: r.startRef, endRef: r.endRef, startNum: refNum(r.startRef), endNum: refNum(r.endRef),\n count: r.count, tokens: r.tokens, userMsgs: 0, toolPct: 0, textPct: 0,\n compressibleTokens: 0, compressibleCount: 0,\n protectedTokens: r.tokens, protectedCount: r.count, protectedTools: [...r.tools], dangerous: false,\n });\n }\n entries.sort((a, b) => a.startNum - b.startNum);\n // Merge adjacent/overlapping ranges (gap ≤ 1 ref).\n const merged: Merged[] = [];\n for (const e of entries) {\n const last = merged[merged.length - 1];\n if (last && e.startNum <= last.endNum + 1) {\n last.endRef = e.endRef;\n last.endNum = Math.max(last.endNum, e.endNum);\n last.count += e.count;\n last.tokens += e.tokens;\n last.userMsgs += e.userMsgs;\n last.compressibleTokens += e.compressibleTokens;\n last.compressibleCount += e.compressibleCount;\n last.protectedTokens += e.protectedTokens;\n last.protectedCount += e.protectedCount;\n if (e.dangerous) last.dangerous = true;\n for (const t of e.protectedTools) {\n if (!last.protectedTools.includes(t)) last.protectedTools.push(t);\n }\n } else {\n merged.push({ ...e });\n }\n }\n const userNote = (n: number): string =>\n n > 0 ? ` · ${n} user msg${n > 1 ? \"s\" : \"\"}` : \"\";\n const lines = merged.map((e) => {\n const suffix = e.dangerous && e.compressibleTokens > 0 ? \" ⚠️ NOT recommended unless you are certain.\" : \"\";\n if (e.protectedTokens > 0 && e.compressibleTokens === 0) {\n return ` ${e.startRef}–${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [PROTECTED: ${e.protectedTools.join(\", \")} — not compressible]${suffix}`;\n }\n if (e.protectedTokens > 0 && e.compressibleTokens > 0) {\n return ` ${e.startRef}–${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [${formatK(e.compressibleTokens)} compressible | ${formatK(e.protectedTokens)} protected: ${e.protectedTools.join(\", \")}]${userNote(e.userMsgs)}${suffix}`;\n }\n return ` ${e.startRef}–${e.endRef} ${e.count} msgs ${formatK(e.tokens)} [tool ${e.toolPct}% | text ${e.textPct}%]${userNote(e.userMsgs)}${suffix}`;\n });\n return `Compressible ranges (${merged.length}, oldest first):\\n${lines.join(\"\\n\")}`;\n}\n\nexport function renderNudgeText(decision: NudgeDecision, prompts: Prompts = defaultPrompts): RenderedNudge {\n const breakdownStr = formatBreakdown(decision.contextBreakdown);\n const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);\n const blockMapStr = formatBlockMap(decision.activeBlockSpans ?? []);\n const isEmergency = !!decision.breakdown?.emergencyOverride || !!decision.breakdown?.overLimit;\n\n if (decision.tier !== null && decision.tier >= 2) {\n const isT2 = decision.tier === 2;\n const targets = decision.tierTargetBlocks ?? [];\n const blockList = formatTierTargetBlocks(targets);\n const startId = targets[0]?.blockId ?? \"b1\";\n const endId = targets[targets.length - 1]?.blockId ?? \"b5\";\n const voice: NudgeVoice = isEmergency ? \"emergency\" : \"gentle\";\n const triggerLine = isEmergency\n ? `[EMERGENCY — TIER ${decision.tier} ${isT2 ? \"DISTILLATION\" : \"CONDENSATION\"}] Context limit reached — distill NOW into a denser summary to reclaim tokens.`\n : `[TIER ${decision.tier} ${isT2 ? \"DISTILLATION\" : \"CONDENSATION\"} TRIGGER]`;\n return {\n voice,\n text: [\n efficiencyNote(prompts),\n \"\",\n breakdownStr,\n \"\",\n triggerLine,\n isT2\n ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-2 block as well — apply HOW TO COMPRESS to those raw messages and the TIER 2 distillation rules to the existing summaries, so the whole span is covered and nothing is lost.`\n : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries (startId and endId as bN). Any raw (uncompressed) messages sitting between the boundary blocks are absorbed into the tier-3 block as well — apply HOW TO COMPRESS to those raw messages and the TIER 3 condensation rules to the existing summaries, so the whole span is covered and nothing is lost.`,\n blockList,\n `Example: compress({ content: [{ startId: \"${startId}\", endId: \"${endId}\", summary: \"...\" }] })`,\n \"\",\n prompts.howToCompressRules,\n \"\",\n isT2 ? prompts.tier2DistillRules : prompts.tier3CondenseRules,\n ].join(\"\\n\"),\n };\n }\n\n if (isEmergency) {\n return {\n voice: \"emergency\",\n text: [\n emergencyHeader(prompts),\n \"\",\n breakdownStr,\n \"\",\n prompts.howToCompressRules,\n \"\",\n `{ \"topic\": \"...\", \"content\": [{ \"startId\": \"<ID>\", \"endId\": \"<ID>\", \"summary\": \"...\" }] }`,\n \"Only use IDs from visible messages above. Compress older work first.\",\n \"\",\n rangesStr,\n ...(blockMapStr ? [\"\", blockMapStr] : []),\n ].join(\"\\n\"),\n };\n }\n\n return {\n voice: \"gentle\",\n text: [\n efficiencyNote(prompts),\n \"\",\n breakdownStr,\n \"\",\n prompts.howToCompressRules,\n \"\",\n rangesStr,\n ...(blockMapStr ? [\"\", blockMapStr] : []),\n \"\",\n `💡 Compress all ranges in one call (pass multiple content entries: \\`content: [{...}, {...}]\\`).`,\n ].join(\"\\n\"),\n };\n}\n","/** Minimum size for a compressible range to be worth recommending. Ranges\n * below this are fragmented leftovers (a 16-token ack, a one-line tool\n * result): the model cannot write a meaningful >=50-char summary for them,\n * and a batched compress call that includes one gets atomically rejected\n * (the kernel validates the whole batch). Observed in the wild: a 14-range\n * recommendation list containing a 16-token range → every batch attempt\n * failed with \"Summary too short\". Apply on every surface that recommends\n * ranges: the injected nudge, acp_status, and the /acp panel. */\nexport const VIABLE_RANGE_MIN_TOKENS = 200;\n\nexport function viableRanges<T extends { tokens: number }>(ranges: T[]): T[] {\n return ranges.filter((r) => r.tokens >= VIABLE_RANGE_MIN_TOKENS);\n}\n"],"mappings":"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","names":["require","require"]}
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkC,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAehF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAUvD,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAEhB,MAAM,EAEN,WAAW,EAGX,iBAAiB,EAEjB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAC;IACvE,YAAY,IAAI,YAAY,EAAE,CAAC;IAC/B,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,gBAAgB,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,CACJ,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,EAAE,CAAC;IACJ,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC;AA2DD,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAyU7D;AAwgCD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAAkC,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAgBhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAUvD,OAAO,EAGL,KAAK,YAAY,EAElB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAEhB,MAAM,EAEN,WAAW,EAGX,iBAAiB,EAEjB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,KAAK;IACpB,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAAC;IACxD,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CAAC;IACvE,YAAY,IAAI,YAAY,EAAE,CAAC;IAC/B,UAAU,CACR,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,gBAAgB,GACtB,gBAAgB,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,CACJ,KAAK,EAAE,gBAAgB,EACvB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,EAAE,CAAC;IACJ,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,EAAE,gBAAgB,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnC;AA2DD,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAyU7D;AAygCD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -11,7 +11,8 @@ export { resolveBoundaries, parseBoundary, BoundaryNotFoundError, visibleBlockAn
11
11
  export { defaultCountTokens, estimateTokensFast, createBpeTokenizer, countMessageTokens, } from "./tokenize.js";
12
12
  export type { TokenCountFn } from "./tokenize.js";
13
13
  export { renderNudgeText, formatRanges } from "./nudge-text.js";
14
- export type { NudgeVoice, RenderedNudge, RenderNudgeOptions } from "./nudge-text.js";
14
+ export type { NudgeVoice, RenderedNudge } from "./nudge-text.js";
15
+ export { resolveBlockSpan, activeBlockSpans, formatCreatedBlocks } from "./block-map.js";
15
16
  export { COMPRESS_PHILOSOPHY, HOW_TO_COMPRESS_RULES, TIER2_DISTILL_RULES, TIER3_CONDENSE_RULES, } from "./compression-rules.js";
16
17
  export { defaultPrompts, resolvePrompts } from "./prompts.js";
17
18
  export type { Prompts, ResolvePromptsOptions } from "./prompts.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  resolvePrompts,
15
15
  thinkingTokenValue,
16
16
  viableRanges
17
- } from "./chunk-T6YQEHNL.js";
17
+ } from "./chunk-AJ4PV7FF.js";
18
18
  import {
19
19
  activeBlocks,
20
20
  advanceSurvival,
@@ -1564,6 +1564,42 @@ function applyMessageFilters(messages, config) {
1564
1564
  return { messages: working, ...tally };
1565
1565
  }
1566
1566
 
1567
+ // src/block-map.ts
1568
+ function refNum(ref) {
1569
+ const m = ref.match(/\d+/);
1570
+ return m ? parseInt(m[0], 10) : 0;
1571
+ }
1572
+ var M_REF = /^m\d+$/;
1573
+ function resolveBlockSpan(block, byRaw) {
1574
+ if (block.startRef && block.endRef && M_REF.test(block.startRef) && M_REF.test(block.endRef)) {
1575
+ return { startRef: block.startRef, endRef: block.endRef };
1576
+ }
1577
+ const refs = block.effectiveMessageIds.map((id) => byRaw[id]).filter((r) => typeof r === "string" && r !== "BLOCKED");
1578
+ if (refs.length === 0) return null;
1579
+ const sorted = [...refs].sort((a, b) => refNum(a) - refNum(b));
1580
+ return { startRef: sorted[0], endRef: sorted[sorted.length - 1] };
1581
+ }
1582
+ function activeBlockSpans(state) {
1583
+ const spans = [];
1584
+ for (const block of state.blocks) {
1585
+ if (!block.active) continue;
1586
+ const span = resolveBlockSpan(block, state.messageRefs.byRaw);
1587
+ if (!span) continue;
1588
+ spans.push({ blockId: block.blockId, tier: block.tier, ...span });
1589
+ }
1590
+ return spans;
1591
+ }
1592
+ function formatCreatedBlocks(state, newBlocks) {
1593
+ const parts = [];
1594
+ for (const block of newBlocks) {
1595
+ const span = resolveBlockSpan(block, state.messageRefs.byRaw);
1596
+ parts.push(
1597
+ span ? `${block.blockId}=${span.startRef}\u2013${span.endRef}` : block.blockId
1598
+ );
1599
+ }
1600
+ return parts.length > 0 ? `blocks: ${parts.join(", ")}` : "";
1601
+ }
1602
+
1567
1603
  // src/render-refs.ts
1568
1604
  function formatTokens(tokens) {
1569
1605
  if (tokens < 1e3) return String(tokens);
@@ -1874,13 +1910,15 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
1874
1910
  tokens: info.tokens,
1875
1911
  chars: info.chars,
1876
1912
  toolPct: info.isTool ? 100 : 0,
1877
- textPct: info.isTool ? 0 : 100
1913
+ textPct: info.isTool ? 0 : 100,
1914
+ userMsgs: info.isUser ? 1 : 0
1878
1915
  };
1879
1916
  } else {
1880
1917
  cur.endRef = info.ref;
1881
1918
  cur.count++;
1882
1919
  cur.tokens += info.tokens;
1883
1920
  cur.chars = (cur.chars ?? 0) + info.chars;
1921
+ if (info.isUser) cur.userMsgs = (cur.userMsgs ?? 0) + 1;
1884
1922
  if (info.isTool) {
1885
1923
  cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
1886
1924
  } else {
@@ -1936,7 +1974,8 @@ function mergeBatch(batch) {
1936
1974
  tokens,
1937
1975
  chars,
1938
1976
  toolPct,
1939
- textPct: 100 - toolPct
1977
+ textPct: 100 - toolPct,
1978
+ userMsgs: batch.reduce((s, r) => s + (r.userMsgs ?? 0), 0)
1940
1979
  };
1941
1980
  if (batch.some((r) => r.dangerous === true)) {
1942
1981
  merged.dangerous = true;
@@ -2845,6 +2884,7 @@ function decideNudge(input) {
2845
2884
  reason,
2846
2885
  compressibleRanges: rec?.recommendedRanges ?? [],
2847
2886
  protectedRanges: rec?.contextRanges.protected ?? [],
2887
+ activeBlockSpans: activeBlockSpans(state),
2848
2888
  tierTargetBlocks: injectedTier ? tiers[injectedTier].targetBlocks : [],
2849
2889
  contextUsage: usage,
2850
2890
  tier: injectedTier,
@@ -3210,7 +3250,7 @@ function renderUncompressedRanges(visible) {
3210
3250
  lines.push(" (no uncompressed messages)");
3211
3251
  return lines.join("\n");
3212
3252
  }
3213
- const refNum = (ref) => {
3253
+ const refNum2 = (ref) => {
3214
3254
  const m = ref.match(/\d+/);
3215
3255
  return m ? parseInt(m[0], 10) : 0;
3216
3256
  };
@@ -3227,7 +3267,7 @@ function renderUncompressedRanges(visible) {
3227
3267
  };
3228
3268
  const merged = [];
3229
3269
  for (const m of visible) {
3230
- const num = refNum(m.ref);
3270
+ const num = refNum2(m.ref);
3231
3271
  const last = merged[merged.length - 1];
3232
3272
  if (last && num === last.startNum + last.count) {
3233
3273
  last.endRef = m.ref;
@@ -4214,6 +4254,7 @@ export {
4214
4254
  TIER2_DISTILL_RULES,
4215
4255
  TIER3_CONDENSE_RULES,
4216
4256
  VIABLE_RANGE_MIN_TOKENS,
4257
+ activeBlockSpans,
4217
4258
  activeBlocks,
4218
4259
  advanceSurvival,
4219
4260
  allocateBlockId,
@@ -4252,6 +4293,7 @@ export {
4252
4293
  estimateTokensFast,
4253
4294
  findActiveAncestor,
4254
4295
  findBlocksOverlappingMessages,
4296
+ formatCreatedBlocks,
4255
4297
  formatRanges,
4256
4298
  getMessageFilter,
4257
4299
  getSearchAlgorithm,
@@ -4288,6 +4330,7 @@ export {
4288
4330
  renderRefsNode,
4289
4331
  renderVisibleRefs,
4290
4332
  resolveAbsorbConfig,
4333
+ resolveBlockSpan,
4291
4334
  resolveBoundaries,
4292
4335
  resolvePrompts,
4293
4336
  resolveTransformChannel,