acp-kernel 0.0.6 → 0.0.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAepB,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;CACpB;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;AAED,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CA0M7D;AAkmBD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAcpB,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;CACpB;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;AAED,wBAAgB,UAAU,CAAC,KAAK,GAAE,KAAU,GAAG,eAAe,CAgN7D;AA8lBD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wBAAgB,aAAa,CAC3B,iBAAiB,EAAE,MAAM,EACzB,SAAS,GAAE,OAAO,CAAC,MAAM,CAAM,GAC9B,MAAM,CAsCR;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCvD"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzC,wBAAgB,aAAa,CAC3B,iBAAiB,EAAE,MAAM,EACzB,SAAS,GAAE,OAAO,CAAC,MAAM,CAAM,GAC9B,MAAM,CAoCR;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CA0BvD"}
package/dist/index.d.ts CHANGED
@@ -22,8 +22,6 @@ export { buildStatusReport, buildRecap } from "./report.js";
22
22
  export type { StatusReportOptions } from "./report.js";
23
23
  export { hideConsumedCompressCalls } from "./hide-consumed.js";
24
24
  export type { HideConsumedResult } from "./hide-consumed.js";
25
- export { mergeMarkedBlocks, collectOldGenBlocks } from "./merge.js";
26
- export type { MergeResult } from "./merge.js";
27
25
  export { rebuildCompressionState } from "./rebuild.js";
28
26
  export type { RebuildResult, RebuildPorts } from "./rebuild.js";
29
27
  export { renderVisibleRefs, renderRefsNode } from "./render-refs.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EACV,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC3F,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC/H,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACH,eAAe,EACf,6BAA6B,EAC7B,kBAAkB,EAClB,eAAe,EACf,2BAA2B,GAC9B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,WAAW,EACX,MAAM,EACN,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC;AACvB,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EACV,KAAK,EACL,eAAe,EACf,gBAAgB,EAChB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,SAAS,EACT,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC3F,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC/H,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC/D,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACH,eAAe,EACf,6BAA6B,EAC7B,kBAAkB,EAClB,eAAe,EACf,2BAA2B,GAC9B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC5D,YAAY,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/D,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EACL,WAAW,EACX,MAAM,EACN,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC;AACvB,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -269,9 +269,9 @@ import { createRequire } from "module";
269
269
  var require2 = createRequire(import.meta.url);
270
270
  function defaultCountTokens(text) {
271
271
  if (!text) return 0;
272
- const ascii = text.match(/[a-zA-Z][a-zA-Z0-9_'-]*/g);
273
272
  const cjk = text.match(/[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]/g);
274
- return (ascii?.length ?? 0) + (cjk?.length ?? 0);
273
+ const cjkCount = cjk?.length ?? 0;
274
+ return cjkCount + Math.ceil((text.length - cjkCount) / 4);
275
275
  }
276
276
  function estimateTokensFast(text) {
277
277
  if (!text) return 0;
@@ -313,7 +313,6 @@ function defaultConfig(modelContextLimit, overrides = {}) {
313
313
  },
314
314
  promotionThreshold: 5,
315
315
  truncate: { threshold: 1 },
316
- merge: { maxSummaryLength: 3e3, minOldGenBlocks: 3 },
317
316
  compress: {
318
317
  minCompressRange: 5e3,
319
318
  maxSummaryLength: 2e4,
@@ -330,7 +329,6 @@ function defaultConfig(modelContextLimit, overrides = {}) {
330
329
  tiers: { ...base.tiers, ...overrides.tiers },
331
330
  nudge: { ...base.nudge, ...overrides.nudge },
332
331
  truncate: { ...base.truncate, ...overrides.truncate },
333
- merge: { ...base.merge, ...overrides.merge },
334
332
  compress: { ...base.compress, ...overrides.compress }
335
333
  };
336
334
  }
@@ -350,12 +348,6 @@ function validateConfig(config) {
350
348
  if (config.truncate.threshold <= 0 || config.truncate.threshold > 1) {
351
349
  errors.push("truncate.threshold must be in (0, 1]");
352
350
  }
353
- if (config.merge.maxSummaryLength < 1) {
354
- errors.push("merge.maxSummaryLength must be >= 1");
355
- }
356
- if (config.merge.minOldGenBlocks < 1) {
357
- errors.push("merge.minOldGenBlocks must be >= 1");
358
- }
359
351
  for (const tier of [config.tiers.tier2Trigger, config.tiers.tier3Trigger]) {
360
352
  if (tier < 1) errors.push("tier triggers must be >= 1");
361
353
  }
@@ -554,74 +546,6 @@ function hideConsumedCompressCalls(state, messages) {
554
546
  return { messages: result, hidden };
555
547
  }
556
548
 
557
- // src/merge.ts
558
- function numericPart(blockId) {
559
- const match = /^b(\d+)$/.exec(blockId);
560
- return match && match[1] !== void 0 ? Number(match[1]) : 0;
561
- }
562
- function truncateMergedSummary(merged, maxLength) {
563
- if (merged.length <= maxLength) return merged;
564
- const blocks = merged.split("\n---\n");
565
- const headers = blocks.map((b) => b.split("\n")[0] ?? "").filter((h) => h.trim().length > 0);
566
- const marker = "\n...\n[merged and truncated by batch cleanup]";
567
- const budget = Math.max(0, maxLength - marker.length);
568
- const headerJoin = headers.join("\n");
569
- return headerJoin.length <= budget ? headerJoin + marker : headerJoin.slice(0, budget) + marker;
570
- }
571
- function mergeMarkedBlocks(state, markedIds, maxMergedLength, countTokens) {
572
- const sortedIds = [...new Set(markedIds)].sort((a, b) => numericPart(a) - numericPart(b));
573
- const sourceBlocks = sortedIds.map((id) => state.blocks.find((b) => b.blockId === id && b.active)).filter((b) => b !== void 0);
574
- if (sourceBlocks.length < 2) {
575
- return { state, mergedCount: 0, savedTokens: 0 };
576
- }
577
- const next = {
578
- ...state,
579
- blocks: state.blocks.map((b) => ({ ...b }))
580
- };
581
- const newBlockId = allocateBlockId(next);
582
- const newRunId = allocateRunId(next);
583
- const bodies = sourceBlocks.map((block) => block.summary.trim());
584
- const mergedSummary = truncateMergedSummary(bodies.join("\n---\n"), maxMergedLength);
585
- const newSummaryTokens = countTokens(mergedSummary);
586
- const effectiveMessageIds = /* @__PURE__ */ new Set();
587
- const directMessageIds = /* @__PURE__ */ new Set();
588
- for (const block of sourceBlocks) {
589
- for (const id of block.effectiveMessageIds) effectiveMessageIds.add(id);
590
- for (const id of block.directMessageIds) directMessageIds.add(id);
591
- }
592
- const sourceIds = sourceBlocks.map((b) => b.blockId);
593
- const mergedBlock = {
594
- blockId: newBlockId,
595
- runId: newRunId,
596
- tier: sourceBlocks.reduce((min, b) => b.tier < min ? b.tier : min, 3),
597
- topic: "Batch merge cleanup",
598
- summary: mergedSummary,
599
- directMessageIds: [...directMessageIds],
600
- effectiveMessageIds: [...effectiveMessageIds],
601
- directBlockIds: [...sourceIds],
602
- compressedTokens: sourceBlocks.reduce((sum, b) => sum + b.compressedTokens, 0),
603
- createdAt: Date.now(),
604
- survivedCount: 0,
605
- generation: "old",
606
- active: true
607
- };
608
- for (const block of next.blocks) {
609
- if (sourceIds.includes(block.blockId)) block.active = false;
610
- }
611
- next.blocks.push(mergedBlock);
612
- const sourceTokens = sourceBlocks.reduce(
613
- (sum, block) => sum + countTokens(block.summary),
614
- 0
615
- );
616
- const savedTokens = Math.max(0, sourceTokens - newSummaryTokens);
617
- return { state: next, mergedCount: sourceBlocks.length, savedTokens };
618
- }
619
- function collectOldGenBlocks(state, maxOldGenSummaryLength) {
620
- return state.blocks.filter(
621
- (b) => b.active && (b.generation === "old" || b.summary.length > maxOldGenSummaryLength)
622
- ).sort((a, b) => numericPart(a.blockId) - numericPart(b.blockId));
623
- }
624
-
625
549
  // src/filter/registry.ts
626
550
  var registry = /* @__PURE__ */ new Map();
627
551
  function registerMessageFilter(filter) {
@@ -780,6 +704,7 @@ var renderRefsNode = {
780
704
  };
781
705
 
782
706
  // src/protected.ts
707
+ var ALWAYS_PROTECTED_TOOLS = ["compress"];
783
708
  function matchToolPattern(toolName, pattern) {
784
709
  if (pattern.endsWith("*")) {
785
710
  return toolName.startsWith(pattern.slice(0, -1));
@@ -787,13 +712,34 @@ function matchToolPattern(toolName, pattern) {
787
712
  return toolName === pattern;
788
713
  }
789
714
  function isMessageProtected(msg, config) {
790
- if (msg.contentType !== "tool-call" || !msg.toolName) return false;
715
+ if (msg.contentType !== "tool-call" && msg.contentType !== "tool-result" || !msg.toolName) {
716
+ return false;
717
+ }
718
+ if (ALWAYS_PROTECTED_TOOLS.includes(msg.toolName)) {
719
+ return true;
720
+ }
791
721
  for (const pattern of config.protectedTools) {
792
722
  if (matchToolPattern(msg.toolName, pattern)) return true;
793
723
  }
794
724
  if (config.isToolProtected?.(msg.toolName, msg.text)) return true;
795
725
  return false;
796
726
  }
727
+ function collectProtectedToolCallIds(messages, config) {
728
+ const ids = /* @__PURE__ */ new Set();
729
+ for (const m of messages) {
730
+ if (m.contentType === "tool-call" && m.toolCallId && isMessageProtected(m, config)) {
731
+ ids.add(m.toolCallId);
732
+ }
733
+ }
734
+ return ids;
735
+ }
736
+ function isMessageProtectedWithPairing(msg, config, protectedCallIds) {
737
+ if (isMessageProtected(msg, config)) return true;
738
+ if (msg.contentType === "tool-result" && msg.toolCallId && protectedCallIds.has(msg.toolCallId)) {
739
+ return true;
740
+ }
741
+ return false;
742
+ }
797
743
 
798
744
  // src/tool-pairs.ts
799
745
  function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan = 20) {
@@ -871,24 +817,27 @@ function computeProtectedRefs(messages, state, config) {
871
817
  tokenAccum += visible[i].tokens;
872
818
  }
873
819
  }
874
- for (let i = messages.length - 1; i >= 0; i--) {
875
- const msg = messages[i];
876
- if (msg.role !== "user" || isSyntheticOrPruned(msg, state)) continue;
877
- const ref = state.messageRefs.byRaw[msg.id];
878
- if (ref && ref !== "BLOCKED") result.add(ref);
879
- break;
820
+ if (preserveN > 0) {
821
+ for (let i = messages.length - 1; i >= 0; i--) {
822
+ const msg = messages[i];
823
+ if (msg.role !== "user" || isSyntheticOrPruned(msg, state)) continue;
824
+ const ref = state.messageRefs.byRaw[msg.id];
825
+ if (ref && ref !== "BLOCKED") result.add(ref);
826
+ break;
827
+ }
880
828
  }
881
829
  return result;
882
830
  }
883
831
  function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
884
832
  const compressibleMsgs = [];
885
833
  const protectedMsgs = [];
834
+ const protectedCallIds = collectProtectedToolCallIds(messages, config);
886
835
  for (const msg of messages) {
887
836
  if (isSyntheticOrPruned(msg, state)) continue;
888
837
  const ref = state.messageRefs.byRaw[msg.id];
889
838
  if (!ref || ref === "BLOCKED") continue;
890
839
  const rn = refNum(ref);
891
- if (isMessageProtected(msg, config)) {
840
+ if (isMessageProtectedWithPairing(msg, config, protectedCallIds)) {
892
841
  protectedMsgs.push({
893
842
  ref,
894
843
  refNum: rn,
@@ -904,7 +853,8 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
904
853
  ref,
905
854
  refNum: rn,
906
855
  tokens: estimateMessageTokens(msg),
907
- isTool: isToolMessage(msg)
856
+ isTool: isToolMessage(msg),
857
+ isUser: msg.role === "user"
908
858
  });
909
859
  }
910
860
  const compressible = [];
@@ -912,7 +862,7 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs) {
912
862
  let prevRefNum = -2;
913
863
  for (const info of compressibleMsgs) {
914
864
  const hasGap = info.refNum > prevRefNum + 1;
915
- if (cur && hasGap) {
865
+ if (cur && (info.isUser && cur.count >= 3 || hasGap)) {
916
866
  compressible.push(cur);
917
867
  cur = null;
918
868
  }
@@ -995,6 +945,7 @@ function createCore(ports = {}) {
995
945
  let blocksCreated = 0;
996
946
  let tokensCompressed = 0;
997
947
  const errors = [];
948
+ const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(input.messages, input.state, input.config);
998
949
  const preExistingCoverage = collectCoverage(state);
999
950
  const rangeIndexSets = [];
1000
951
  for (const spec of input.ranges) {
@@ -1082,7 +1033,7 @@ function createCore(ports = {}) {
1082
1033
  state,
1083
1034
  runId,
1084
1035
  config: input.config,
1085
- protectedMessageIds: input.protectedMessageIds,
1036
+ protectedMessageIds,
1086
1037
  countTokens,
1087
1038
  preExistingCoverage
1088
1039
  });
@@ -1148,7 +1099,6 @@ function createCore(ports = {}) {
1148
1099
  return [
1149
1100
  assignRefsNode,
1150
1101
  syncBlocksNode,
1151
- mergeBlocksNode,
1152
1102
  pruneNode,
1153
1103
  filterNode,
1154
1104
  hideCompressCallsNode,
@@ -1181,29 +1131,6 @@ var syncBlocksNode = {
1181
1131
  return { ...io, state: synced.state };
1182
1132
  }
1183
1133
  };
1184
- var mergeBlocksNode = {
1185
- name: "merge-blocks",
1186
- run(io, ctx) {
1187
- const oldGen = collectOldGenBlocks(
1188
- io.state,
1189
- ctx.config.merge.maxSummaryLength
1190
- );
1191
- if (oldGen.length < ctx.config.merge.minOldGenBlocks) return io;
1192
- const ids = oldGen.map((b) => b.blockId);
1193
- const merged = mergeMarkedBlocks(
1194
- io.state,
1195
- ids,
1196
- ctx.config.merge.maxSummaryLength,
1197
- ctx.countTokens
1198
- );
1199
- if (merged.mergedCount === 0) return io;
1200
- return {
1201
- ...io,
1202
- state: merged.state,
1203
- effects: { ...io.effects, mergedCount: merged.mergedCount }
1204
- };
1205
- }
1206
- };
1207
1134
  var pruneNode = {
1208
1135
  name: "prune",
1209
1136
  run(io) {
@@ -1348,11 +1275,25 @@ function applySingleRange(input) {
1348
1275
  const directMessageIds = [...effectiveMessageIds].filter(
1349
1276
  (id) => !input.preExistingCoverage.has(id)
1350
1277
  );
1351
- const { filteredIds, appendedProtectedText } = filterProtectedToolMessages(
1278
+ const filteredIds = filterProtectedToolMessages(
1352
1279
  directMessageIds,
1353
1280
  input.messages,
1354
1281
  input.config
1355
1282
  );
1283
+ const protectedRefs = input.protectedMessageIds;
1284
+ const hitProtectedRaw = protectedRefs ? filteredIds.filter((id) => {
1285
+ const ref = input.state.messageRefs.byRaw[id];
1286
+ return ref !== void 0 && protectedRefs.has(ref);
1287
+ }) : [];
1288
+ if (hitProtectedRaw.length > 0) {
1289
+ const recentN = input.config.preserveRecentMessages;
1290
+ const hitRefs = hitProtectedRaw.map((id) => input.state.messageRefs.byRaw[id]).filter(Boolean);
1291
+ throw new Error(
1292
+ `Range includes protected messages (the last ${recentN} messages and/or the most recent user message) which must not be compressed: ${hitRefs.join(
1293
+ ", "
1294
+ )}. Adjust startId/endId to exclude them.`
1295
+ );
1296
+ }
1356
1297
  validateCompressionRange(input, filteredIds, consumedBlockIds.length);
1357
1298
  let compressedTokens = 0;
1358
1299
  for (const id of filteredIds) {
@@ -1366,22 +1307,12 @@ function applySingleRange(input) {
1366
1307
  }
1367
1308
  }
1368
1309
  const blockId = allocateBlockId(input.state);
1369
- const finalSummary = appendedProtectedText.length > 0 ? `${input.spec.summary}
1370
-
1371
- The following protected tool calls were used in this conversation as well:
1372
- ${appendedProtectedText.join("")}` : input.spec.summary;
1373
- const maxLen = input.spec.summaryMaxChars ?? input.config.compress.maxSummaryLength;
1374
- if (maxLen > 0 && finalSummary.length > maxLen) {
1375
- throw new Error(
1376
- `Summary too long after appending protected content (${finalSummary.length} chars, max ${maxLen}). Reduce the summary or remove protected tools from the range.`
1377
- );
1378
- }
1379
1310
  const block = {
1380
1311
  blockId,
1381
1312
  runId: input.runId,
1382
1313
  tier: outputTier,
1383
1314
  topic: input.spec.topic,
1384
- summary: finalSummary,
1315
+ summary: input.spec.summary,
1385
1316
  directMessageIds: filteredIds,
1386
1317
  effectiveMessageIds: [...effectiveMessageIds],
1387
1318
  directBlockIds: [...consumedBlockIds],
@@ -1444,41 +1375,30 @@ function validateCompressionRange(input, directMessageIds, consumedBlockCount) {
1444
1375
  }
1445
1376
  }
1446
1377
  function filterProtectedToolMessages(directMessageIds, messages, config) {
1447
- const removedToolCallIds = /* @__PURE__ */ new Set();
1448
- const allProtectedCallIds = /* @__PURE__ */ new Set();
1378
+ const protectedCallIds = /* @__PURE__ */ new Set();
1449
1379
  const removedIds = /* @__PURE__ */ new Set();
1450
- const appendedProtectedText = [];
1451
1380
  for (const msg of messages) {
1452
1381
  if (isMessageProtected(msg, config) && msg.toolCallId) {
1453
- allProtectedCallIds.add(msg.toolCallId);
1382
+ protectedCallIds.add(msg.toolCallId);
1454
1383
  }
1455
1384
  }
1456
1385
  for (const id of directMessageIds) {
1457
1386
  const msg = messages.find((m) => m.id === id);
1458
1387
  if (!msg) continue;
1459
- if (!isMessageProtected(msg, config)) continue;
1460
- removedIds.add(id);
1461
- if (msg.toolCallId) removedToolCallIds.add(msg.toolCallId);
1462
- if (msg.text) {
1463
- appendedProtectedText.push(
1464
- `
1465
- ### Protected: ${msg.toolName ?? "tool"}
1466
- ${msg.text}`
1467
- );
1388
+ if (isMessageProtected(msg, config)) {
1389
+ removedIds.add(id);
1390
+ if (msg.toolCallId) protectedCallIds.add(msg.toolCallId);
1468
1391
  }
1469
1392
  }
1470
1393
  for (const id of directMessageIds) {
1471
1394
  if (removedIds.has(id)) continue;
1472
1395
  const msg = messages.find((m) => m.id === id);
1473
1396
  if (!msg) continue;
1474
- if (msg.contentType === "tool-result" && msg.toolCallId && (removedToolCallIds.has(msg.toolCallId) || allProtectedCallIds.has(msg.toolCallId))) {
1397
+ if (msg.contentType === "tool-result" && msg.toolCallId && protectedCallIds.has(msg.toolCallId)) {
1475
1398
  removedIds.add(id);
1476
1399
  }
1477
1400
  }
1478
- return {
1479
- filteredIds: directMessageIds.filter((id) => !removedIds.has(id)),
1480
- appendedProtectedText
1481
- };
1401
+ return directMessageIds.filter((id) => !removedIds.has(id));
1482
1402
  }
1483
1403
  function resolveTargetTier(state, nestedBlockIds, isBlockBoundary) {
1484
1404
  if (!isBlockBoundary) return 1;
@@ -1529,23 +1449,30 @@ function decideNudge(input) {
1529
1449
  shouldContextNudge = growthSinceReference >= growthFloor;
1530
1450
  }
1531
1451
  let tier = null;
1452
+ let tierTargetBlocks = [];
1532
1453
  if (config.tiers.enabled) {
1533
1454
  const active = activeBlocks(state);
1534
1455
  const activeT1 = active.filter((b) => b.tier === 1);
1535
1456
  const activeT2 = active.filter((b) => b.tier === 2);
1536
- if (activeT2.length >= config.tiers.tier3Trigger) tier = 3;
1537
- else if (activeT1.length >= config.tiers.tier2Trigger) tier = 2;
1457
+ if (activeT2.length >= config.tiers.tier3Trigger) {
1458
+ tier = 3;
1459
+ tierTargetBlocks = activeT2;
1460
+ } else if (activeT1.length >= config.tiers.tier2Trigger) {
1461
+ tier = 2;
1462
+ tierTargetBlocks = activeT1;
1463
+ }
1538
1464
  }
1539
1465
  const rec = recommendation;
1540
1466
  const nothingToCompress = rec ? rec.contextRanges.compressible.length === 0 : false;
1541
- const shouldInject = shouldContextNudge || tier !== null;
1467
+ const shouldInject = shouldContextNudge;
1542
1468
  let reason;
1543
1469
  if (!shouldInject) {
1544
- reason = `growth ${growthSinceReference} < threshold ${effectiveThreshold}${hasPendingNudge ? " (halved)" : ""} (floor ${growthFloor})`;
1470
+ const tierHint = tier !== null ? `, tier-${tier} ready (distill when growth reaches threshold)` : "";
1471
+ reason = `growth ${growthSinceReference} < threshold ${effectiveThreshold}${hasPendingNudge ? " (halved)" : ""} (floor ${growthFloor})${tierHint}`;
1545
1472
  } else if (emergencyOverride) {
1546
1473
  reason = `EMERGENCY: usage ${Math.round(usage * 100)}% >= ${Math.round(config.nudge.emergencyThresholdPct * 100)}%`;
1547
1474
  } else if (tier !== null) {
1548
- reason = `tier-${tier} distillation trigger`;
1475
+ reason = `growth ${growthSinceReference} >= ${effectiveThreshold}${hasPendingNudge ? " (halved)" : ""}, usage ${Math.round(usage * 100)}%, tier-${tier} distillation`;
1549
1476
  } else if (nothingToCompress) {
1550
1477
  reason = `growth ${growthSinceReference} >= ${effectiveThreshold} but no specific ranges worth compressing`;
1551
1478
  } else {
@@ -1557,6 +1484,7 @@ function decideNudge(input) {
1557
1484
  reason,
1558
1485
  compressibleRanges: rec?.recommendedRanges ?? [],
1559
1486
  protectedRanges: rec?.contextRanges.protected ?? [],
1487
+ tierTargetBlocks,
1560
1488
  contextUsage: usage,
1561
1489
  tier,
1562
1490
  breakdown: {
@@ -1833,6 +1761,18 @@ function mergeRanges(compressible, protected_) {
1833
1761
  }
1834
1762
  return merged;
1835
1763
  }
1764
+ function formatTierTargetBlocks(blocks) {
1765
+ if (blocks.length === 0) {
1766
+ return "Target blocks: (none \u2014 no tier blocks found)";
1767
+ }
1768
+ const lines = blocks.map((b) => {
1769
+ const summaryTokens = Math.ceil((b.summary ?? "").length / 4);
1770
+ const topic = b.topic ? ` "${b.topic}"` : "";
1771
+ return ` ${b.blockId} ${b.effectiveMessageIds.length} msgs ${formatK(b.compressedTokens)}\u2192${formatK(summaryTokens)}${topic}`;
1772
+ });
1773
+ return `Target ${blocks[0].tier === 1 ? "tier-1" : "tier-2"} blocks to distill (${blocks.length}):
1774
+ ${lines.join("\n")}`;
1775
+ }
1836
1776
  function formatRanges(compressible, protected_) {
1837
1777
  const merged = mergeRanges(compressible, protected_);
1838
1778
  if (merged.length === 0) {
@@ -1856,6 +1796,10 @@ function renderNudgeText(decision) {
1856
1796
  const rangesStr = formatRanges(decision.compressibleRanges, decision.protectedRanges ?? []);
1857
1797
  if (decision.tier !== null && decision.tier >= 2) {
1858
1798
  const isT2 = decision.tier === 2;
1799
+ const targets = decision.tierTargetBlocks ?? [];
1800
+ const blockList = formatTierTargetBlocks(targets);
1801
+ const startId = targets[0]?.blockId ?? "b1";
1802
+ const endId = targets[targets.length - 1]?.blockId ?? "b5";
1859
1803
  return {
1860
1804
  voice: "gentle",
1861
1805
  text: [
@@ -1864,8 +1808,9 @@ function renderNudgeText(decision) {
1864
1808
  breakdownStr,
1865
1809
  "",
1866
1810
  `[TIER ${decision.tier} ${isT2 ? "DISTILLATION" : "CONDENSATION"} TRIGGER]`,
1867
- isT2 ? "Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries." : "Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries.",
1868
- `Example: compress({ content: [{ startId: "b1", endId: "b5", summary: "..." }] })`,
1811
+ isT2 ? `Your tier-1 compression summaries have accumulated. Distill them into a single denser tier-2 summary. Use block IDs as boundaries.` : `Your tier-2 compression summaries have accumulated. Condense them further into a tier-3 ultra-condensed summary. Use block IDs as boundaries.`,
1812
+ blockList,
1813
+ `Example: compress({ content: [{ startId: "${startId}", endId: "${endId}", summary: "..." }] })`,
1869
1814
  "",
1870
1815
  HOW_TO_COMPRESS_RULES,
1871
1816
  "",
@@ -1984,7 +1929,7 @@ function findBlocksOverlappingMessages(state, messageIds) {
1984
1929
  matched.push(block);
1985
1930
  }
1986
1931
  }
1987
- return matched.sort((a, b) => numericPart2(a.blockId) - numericPart2(b.blockId));
1932
+ return matched.sort((a, b) => numericPart(a.blockId) - numericPart(b.blockId));
1988
1933
  }
1989
1934
  function findActiveAncestor(state, blockId) {
1990
1935
  const start = state.blocks.find((b) => b.blockId === blockId);
@@ -2060,7 +2005,7 @@ function buildRestoredContentPreview(messages, beforeActiveMessageIds, state) {
2060
2005
  }
2061
2006
  return { preview: lines.join("\n"), restoredCount: restored.length };
2062
2007
  }
2063
- function numericPart2(blockId) {
2008
+ function numericPart(blockId) {
2064
2009
  const match = /^b(\d+)$/.exec(blockId);
2065
2010
  return match && match[1] !== void 0 ? Number(match[1]) : 0;
2066
2011
  }
@@ -2074,7 +2019,7 @@ function pct(n, total) {
2074
2019
  if (n <= 0 || total <= 0) return 0;
2075
2020
  return Math.max(1, Math.round(n / total * 100));
2076
2021
  }
2077
- function numericPart3(blockId) {
2022
+ function numericPart2(blockId) {
2078
2023
  const match = /^b(\d+)$/.exec(blockId);
2079
2024
  return match && match[1] !== void 0 ? Number(match[1]) : 0;
2080
2025
  }
@@ -2134,7 +2079,7 @@ function buildStatusReport(state, messages, countTokens, options = {}) {
2134
2079
  const toolFilter = options.tool;
2135
2080
  const sort = options.sort ?? "size";
2136
2081
  const limit = options.limit ?? 30;
2137
- const activeBlocks2 = state.blocks.filter((b) => b.active).sort((a, b) => numericPart3(a.blockId) - numericPart3(b.blockId));
2082
+ const activeBlocks2 = state.blocks.filter((b) => b.active).sort((a, b) => numericPart2(a.blockId) - numericPart2(b.blockId));
2138
2083
  if (scope === "compressed") {
2139
2084
  return renderCompressedDrilldown(activeBlocks2, state, sort, limit, countTokens);
2140
2085
  }
@@ -2268,7 +2213,7 @@ function renderCompressedDrilldown(blocks, state, sort, limit, countTokens) {
2268
2213
  return lines.join("\n");
2269
2214
  }
2270
2215
  function buildRecap(state, blockId) {
2271
- const activeBlocks2 = state.blocks.filter((b) => b.active).sort((a, b) => numericPart3(a.blockId) - numericPart3(b.blockId));
2216
+ const activeBlocks2 = state.blocks.filter((b) => b.active).sort((a, b) => numericPart2(a.blockId) - numericPart2(b.blockId));
2272
2217
  if (blockId !== void 0) {
2273
2218
  const block = state.blocks.find((b) => b.blockId === blockId);
2274
2219
  if (!block) {
@@ -2406,7 +2351,6 @@ export {
2406
2351
  buildRestoredContentPreview,
2407
2352
  buildStatusReport,
2408
2353
  clearMessageFilters,
2409
- collectOldGenBlocks,
2410
2354
  coveredMessageIds,
2411
2355
  createBpeTokenizer,
2412
2356
  createCore,
@@ -2427,7 +2371,6 @@ export {
2427
2371
  listMessageFilters,
2428
2372
  makeIO,
2429
2373
  matchToolPattern,
2430
- mergeMarkedBlocks,
2431
2374
  parseBlockIdArg,
2432
2375
  parseBoundary,
2433
2376
  prune,