acp-kernel 0.0.93 → 0.0.94-pr.415.300

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/DESIGN.md +158 -137
  2. package/PROVENANCE.md +44 -40
  3. package/README.md +43 -30
  4. package/dist/cache-report.d.ts.map +1 -1
  5. package/dist/ccr.d.ts.map +1 -1
  6. package/dist/{chunk-NUQZKPQS.js → chunk-WR7OPWNT.js} +7 -3
  7. package/dist/chunk-WR7OPWNT.js.map +1 -0
  8. package/dist/compress-tools.d.ts +282 -254
  9. package/dist/compress-tools.d.ts.map +1 -1
  10. package/dist/config.d.ts.map +1 -1
  11. package/dist/decompress.d.ts.map +1 -1
  12. package/dist/filter/apply.d.ts.map +1 -1
  13. package/dist/filter/index.d.ts +1 -1
  14. package/dist/filter/index.d.ts.map +1 -1
  15. package/dist/filter/types.d.ts.map +1 -1
  16. package/dist/handoff.d.ts.map +1 -1
  17. package/dist/hide-consumed.d.ts.map +1 -1
  18. package/dist/image-compress.d.ts.map +1 -1
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +356 -145
  22. package/dist/index.js.map +1 -1
  23. package/dist/message-kind.d.ts.map +1 -1
  24. package/dist/nudge-text.d.ts.map +1 -1
  25. package/dist/packs.d.ts.map +1 -1
  26. package/dist/panel/cache.d.ts.map +1 -1
  27. package/dist/panel/index.js +29 -10
  28. package/dist/panel/index.js.map +1 -1
  29. package/dist/panel/panel.d.ts.map +1 -1
  30. package/dist/panel/topic.d.ts.map +1 -1
  31. package/dist/parse-compress-input.d.ts.map +1 -1
  32. package/dist/persist/index.d.ts +1 -1
  33. package/dist/persist/index.d.ts.map +1 -1
  34. package/dist/persist/index.js +49 -12
  35. package/dist/persist/index.js.map +1 -1
  36. package/dist/persist/state-merge.d.ts.map +1 -1
  37. package/dist/persist/store.d.ts.map +1 -1
  38. package/dist/protected.d.ts.map +1 -1
  39. package/dist/rebuild.d.ts.map +1 -1
  40. package/dist/recommend.d.ts.map +1 -1
  41. package/dist/render-refs.d.ts.map +1 -1
  42. package/dist/report.d.ts.map +1 -1
  43. package/dist/search/algorithms/bm25.d.ts.map +1 -1
  44. package/dist/search/algorithms/fuzzy.d.ts.map +1 -1
  45. package/dist/search/algorithms/hybrid.d.ts.map +1 -1
  46. package/dist/search/algorithms/semantic.d.ts.map +1 -1
  47. package/dist/search/algorithms/substring.d.ts.map +1 -1
  48. package/dist/search/doc-cache.d.ts.map +1 -1
  49. package/dist/search/index.d.ts +1 -1
  50. package/dist/search/index.d.ts.map +1 -1
  51. package/dist/search/registry.d.ts.map +1 -1
  52. package/dist/search/stemmer.d.ts.map +1 -1
  53. package/dist/search/tokenizer.d.ts.map +1 -1
  54. package/dist/search/types.d.ts.map +1 -1
  55. package/dist/search.d.ts +3 -3
  56. package/dist/search.d.ts.map +1 -1
  57. package/dist/segment.d.ts +33 -0
  58. package/dist/segment.d.ts.map +1 -0
  59. package/dist/surface-config.d.ts.map +1 -1
  60. package/dist/tool-pairs.d.ts.map +1 -1
  61. package/dist/truncate-tools.d.ts.map +1 -1
  62. package/dist/truncate.d.ts.map +1 -1
  63. package/dist/turn-integrity.d.ts.map +1 -1
  64. package/dist/wire/anthropic.d.ts.map +1 -1
  65. package/dist/wire/bili-message.d.ts.map +1 -1
  66. package/dist/wire/compress-detect.d.ts.map +1 -1
  67. package/dist/wire/demoted-thinking.d.ts.map +1 -1
  68. package/dist/wire/formats.d.ts.map +1 -1
  69. package/dist/wire/index.js +476 -129
  70. package/dist/wire/index.js.map +1 -1
  71. package/dist/wire/message-id.d.ts.map +1 -1
  72. package/dist/wire/mirror.d.ts.map +1 -1
  73. package/dist/wire/openai.d.ts.map +1 -1
  74. package/dist/wire/responses.d.ts.map +1 -1
  75. package/dist/wire/strip-images.d.ts.map +1 -1
  76. package/dist/wire/util.d.ts.map +1 -1
  77. package/package.json +66 -66
  78. package/dist/chunk-NUQZKPQS.js.map +0 -1
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  resolvePrompts,
17
17
  thinkingTokenValue,
18
18
  viableRanges
19
- } from "./chunk-NUQZKPQS.js";
19
+ } from "./chunk-WR7OPWNT.js";
20
20
  import {
21
21
  activeBlocks,
22
22
  advanceSurvival,
@@ -549,10 +549,13 @@ function cloneWithDescriptions(schema, overrides) {
549
549
  return cloneNode(schema, overrides);
550
550
  }
551
551
  function cloneNode(node, overrides) {
552
- if (Array.isArray(node)) return node.map((item) => cloneNode(item, overrides));
552
+ if (Array.isArray(node))
553
+ return node.map((item) => cloneNode(item, overrides));
553
554
  if (node && typeof node === "object") {
554
555
  const out = {};
555
- for (const [name, value] of Object.entries(node)) {
556
+ for (const [name, value] of Object.entries(
557
+ node
558
+ )) {
556
559
  const cloned = cloneNode(value, overrides);
557
560
  if (Object.hasOwn(overrides, name) && cloned && typeof cloned === "object" && !Array.isArray(cloned)) {
558
561
  cloned.description = overrides[name];
@@ -589,7 +592,9 @@ function applyAcpToolOverrides(tools, overrides) {
589
592
  return {
590
593
  ...tool,
591
594
  ...ov.description !== void 0 ? { description: ov.description } : {},
592
- ...ov.paramDescriptions ? hasInputSchema ? { input_schema: cloneWithDescriptions(schema, ov.paramDescriptions) } : { parameters: cloneWithDescriptions(schema, ov.paramDescriptions) } : {}
595
+ ...ov.paramDescriptions ? hasInputSchema ? {
596
+ input_schema: cloneWithDescriptions(schema, ov.paramDescriptions)
597
+ } : { parameters: cloneWithDescriptions(schema, ov.paramDescriptions) } : {}
593
598
  };
594
599
  });
595
600
  }
@@ -634,11 +639,7 @@ var COMPRESS_RANGE_OBJECT = {
634
639
  type: "string",
635
640
  description: "Self-contained summary replacing the range"
636
641
  }
637
- },
638
- anyOf: [
639
- { required: ["startId", "endId", "summary"] },
640
- { required: ["startRef", "endRef", "summary"] }
641
- ]
642
+ }
642
643
  };
643
644
  var COMPRESS_PARAMETERS = {
644
645
  type: "object",
@@ -648,17 +649,29 @@ var COMPRESS_PARAMETERS = {
648
649
  description: "Optional short title for the compressed range"
649
650
  },
650
651
  content: {
651
- type: ["array", "string"],
652
652
  description: "One or more ranges to compress into separate summary blocks. Array form (preferred): one entry per range \u2014 line form (one STRING per range: first line 'm00150\u2013m00220 optional topic', remaining lines the markdown summary verbatim) or object form {startId,endId,summary,topic?}. String form also accepted: bare line-form text, or a JSON-encoded array of ranges (some gateways stringify arrays). REQUIRED unless the flat single-range form is used.",
653
- items: {
654
- anyOf: [
655
- {
656
- type: "string",
657
- description: "Line form: first line 'm00150\u2013m00220 optional topic', remaining lines the summary markdown, verbatim (no JSON escaping)"
658
- },
659
- COMPRESS_RANGE_OBJECT
660
- ]
661
- }
653
+ anyOf: [
654
+ {
655
+ type: "array",
656
+ items: {
657
+ anyOf: [
658
+ {
659
+ type: "string",
660
+ description: "Line form: first line 'm00150\u2013m00220 optional topic', remaining lines the summary markdown, verbatim (no JSON escaping)"
661
+ },
662
+ {
663
+ ...COMPRESS_RANGE_OBJECT,
664
+ required: ["startId", "endId", "summary"]
665
+ },
666
+ {
667
+ ...COMPRESS_RANGE_OBJECT,
668
+ required: ["startRef", "endRef", "summary"]
669
+ }
670
+ ]
671
+ }
672
+ },
673
+ { type: "string" }
674
+ ]
662
675
  },
663
676
  startId: {
664
677
  type: "string",
@@ -1203,7 +1216,9 @@ function isNeverPreserveRecent(msg, patterns, preservePatterns) {
1203
1216
  }
1204
1217
  if (!msg.toolName) return false;
1205
1218
  const base = patterns === void 0 ? NEVER_PRESERVE_RECENT_TOOLS : patterns;
1206
- const list = preservePatterns === void 0 || preservePatterns.length === 0 ? base : base.filter((tool) => !preservePatterns.some((p) => matchToolPattern(tool, p)));
1219
+ const list = preservePatterns === void 0 || preservePatterns.length === 0 ? base : base.filter(
1220
+ (tool) => !preservePatterns.some((p) => matchToolPattern(tool, p))
1221
+ );
1207
1222
  for (const pattern of list) {
1208
1223
  if (matchToolPattern(msg.toolName, pattern)) return true;
1209
1224
  }
@@ -1271,7 +1286,8 @@ function isMessageLatestProtected(msg, latest) {
1271
1286
  }
1272
1287
  function hasMediaPayload(msg) {
1273
1288
  const m = msg;
1274
- if (typeof m.imageBase64 === "string" && m.imageBase64.length > 0) return true;
1289
+ if (typeof m.imageBase64 === "string" && m.imageBase64.length > 0)
1290
+ return true;
1275
1291
  if (m.rawOpenaiContent != null) return true;
1276
1292
  if (Array.isArray(m.rawOpenaiContentParts) && m.rawOpenaiContentParts.length > 0)
1277
1293
  return true;
@@ -1279,7 +1295,8 @@ function hasMediaPayload(msg) {
1279
1295
  if (isObjWith(ab, "type", "image")) return true;
1280
1296
  if (isObjWith(ab, "type", "tool_result")) {
1281
1297
  const content = ab.content;
1282
- if (Array.isArray(content)) return content.some((p) => !isObjWith(p, "type", "text"));
1298
+ if (Array.isArray(content))
1299
+ return content.some((p) => !isObjWith(p, "type", "text"));
1283
1300
  }
1284
1301
  const item = m.rawResponsesItem;
1285
1302
  if (isObjWith(item, "type", "input_image")) return true;
@@ -1561,7 +1578,7 @@ function storeCoveredOriginals(store, messages, state, blockIds, countTokens, ma
1561
1578
  if (text.length === 0) continue;
1562
1579
  const ref = refForRaw(state.messageRefs, message.id);
1563
1580
  if (!ref || ref === BLOCKED_REF) continue;
1564
- if (isStoredPlaceholderText(text) && hasStoredRef(current, ref)) continue;
1581
+ if (isStoredPlaceholderText(text)) continue;
1565
1582
  current = storeOriginal(current, {
1566
1583
  ref,
1567
1584
  rawId: message.id,
@@ -1901,7 +1918,9 @@ function findBlocksOverlappingMessages(state, messageIds) {
1901
1918
  matched.push(block);
1902
1919
  }
1903
1920
  }
1904
- return matched.sort((a, b) => numericPart(a.blockId) - numericPart(b.blockId));
1921
+ return matched.sort(
1922
+ (a, b) => numericPart(a.blockId) - numericPart(b.blockId)
1923
+ );
1905
1924
  }
1906
1925
  function findActiveAncestor(state, blockId) {
1907
1926
  const start = state.blocks.find((b) => b.blockId === blockId);
@@ -2103,7 +2122,11 @@ function truncateLargeToolOutputs(messages, tokenCount, config, countTokens, opt
2103
2122
  continue;
2104
2123
  }
2105
2124
  const prefix = clampPrefix(original, opts.keepPrefixChars);
2106
- const suffix = clampWindow(original, original.length - opts.keepSuffixChars, original.length);
2125
+ const suffix = clampWindow(
2126
+ original,
2127
+ original.length - opts.keepSuffixChars,
2128
+ original.length
2129
+ );
2107
2130
  const replacement = `${prefix}
2108
2131
 
2109
2132
  ...${TRUNCATION_MARKER} \u2014 original ~${tokens} tokens]...
@@ -2181,8 +2204,12 @@ var SUMMARY_STUB_CHARS = 200;
2181
2204
  function compactEntry(entry) {
2182
2205
  if (!entry || typeof entry !== "object") return entry;
2183
2206
  const e = entry;
2184
- if (typeof e.summary !== "string" || e.summary.length <= SUMMARY_STUB_CHARS) return entry;
2185
- return { ...e, summary: `${clampPrefix(e.summary, SUMMARY_STUB_CHARS - 1)}\u2026` };
2207
+ if (typeof e.summary !== "string" || e.summary.length <= SUMMARY_STUB_CHARS)
2208
+ return entry;
2209
+ return {
2210
+ ...e,
2211
+ summary: `${clampPrefix(e.summary, SUMMARY_STUB_CHARS - 1)}\u2026`
2212
+ };
2186
2213
  }
2187
2214
  function serializeCompacted(obj, content, contentWasString) {
2188
2215
  let changed = false;
@@ -2198,7 +2225,11 @@ function compactCompressText(text) {
2198
2225
  const parsed = parseCallText(text);
2199
2226
  if (!parsed) return null;
2200
2227
  const { prefix, obj, content, contentWasString } = parsed;
2201
- const { text: out, changed } = serializeCompacted(obj, content, contentWasString);
2228
+ const { text: out, changed } = serializeCompacted(
2229
+ obj,
2230
+ content,
2231
+ contentWasString
2232
+ );
2202
2233
  return changed ? prefix + out : null;
2203
2234
  }
2204
2235
  function hideConsumedCompressCalls(state, messages) {
@@ -2225,7 +2256,8 @@ function hideConsumedCompressCalls(state, messages) {
2225
2256
  const lastOrphanedCallIds = [];
2226
2257
  for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
2227
2258
  const message = messages[i];
2228
- if (message.toolName !== "compress" || message.contentType !== "tool-call") continue;
2259
+ if (message.toolName !== "compress" || message.contentType !== "tool-call")
2260
+ continue;
2229
2261
  const callId = message.toolCallId;
2230
2262
  if (callId && !allBlockCallIds.has(callId)) {
2231
2263
  lastOrphanedCallIds.push(callId);
@@ -4205,14 +4237,32 @@ function computeIntegrityWithdrawals(messages, foldedIds) {
4205
4237
  };
4206
4238
  }
4207
4239
 
4240
+ // src/segment.ts
4241
+ function segmentGroups(items) {
4242
+ const groups = [];
4243
+ let cur = null;
4244
+ for (const item of items) {
4245
+ if (cur !== null && (item.isUser && cur.length >= 3 || item.gapBefore)) {
4246
+ groups.push(cur);
4247
+ cur = null;
4248
+ }
4249
+ if (cur === null) cur = [item];
4250
+ else cur.push(item);
4251
+ }
4252
+ if (cur !== null) groups.push(cur);
4253
+ return groups;
4254
+ }
4255
+
4208
4256
  // src/recommend.ts
4209
4257
  function estimateTextTokens(text) {
4210
4258
  return Math.ceil(text.length / 4);
4211
4259
  }
4212
4260
  function isSyntheticOrPruned(message, state) {
4213
- if (message.text?.startsWith("[Compressed conversation section]")) return true;
4261
+ if (message.text?.startsWith("[Compressed conversation section]"))
4262
+ return true;
4214
4263
  for (const block of state.blocks) {
4215
- if (block.active && block.effectiveMessageIds.includes(message.id)) return true;
4264
+ if (block.active && block.effectiveMessageIds.includes(message.id))
4265
+ return true;
4216
4266
  }
4217
4267
  return false;
4218
4268
  }
@@ -4223,7 +4273,12 @@ function computeProtectedRefs(messages, state, config, countTokens = estimateTex
4223
4273
  const visible = [];
4224
4274
  for (const msg of messages) {
4225
4275
  if (isSyntheticOrPruned(msg, state)) continue;
4226
- if (isNeverPreserveRecent(msg, config.neverPreserveRecentTools, config.preserveRecentTools)) continue;
4276
+ if (isNeverPreserveRecent(
4277
+ msg,
4278
+ config.neverPreserveRecentTools,
4279
+ config.preserveRecentTools
4280
+ ))
4281
+ continue;
4227
4282
  const ref = state.messageRefs.byRaw[msg.id];
4228
4283
  if (!ref || ref === "BLOCKED") continue;
4229
4284
  visible.push({ ref, tokens: countMessageTokens(msg, countTokens) });
@@ -4322,41 +4377,41 @@ function buildCompressibleRanges(messages, state, config, protectedZoneRefs, cou
4322
4377
  compressibleMsgs = kept;
4323
4378
  }
4324
4379
  const compressible = [];
4325
- let cur = null;
4326
- for (const info of compressibleMsgs) {
4327
- if (cur && (info.isUser && cur.count >= 3 || info.gapBefore)) {
4328
- compressible.push(cur);
4329
- cur = null;
4330
- }
4331
- if (!cur) {
4332
- cur = {
4333
- startRef: info.ref,
4334
- endRef: info.ref,
4335
- count: 1,
4336
- tokens: info.tokens,
4337
- chars: info.chars,
4338
- toolPct: info.isTool ? 100 : 0,
4339
- textPct: info.isTool ? 0 : 100,
4340
- userMsgs: info.isUser ? 1 : 0,
4341
- startIndex: info.index,
4342
- endIndex: info.index
4343
- };
4344
- } else {
4345
- cur.endRef = info.ref;
4346
- cur.endIndex = info.index;
4347
- cur.count++;
4348
- cur.tokens += info.tokens;
4349
- cur.chars = (cur.chars ?? 0) + info.chars;
4350
- if (info.isUser) cur.userMsgs = (cur.userMsgs ?? 0) + 1;
4380
+ for (const group of segmentGroups(compressibleMsgs)) {
4381
+ const first = group[0];
4382
+ const range = {
4383
+ startRef: first.ref,
4384
+ endRef: first.ref,
4385
+ startIndex: first.index,
4386
+ endIndex: first.index,
4387
+ count: 1,
4388
+ tokens: first.tokens,
4389
+ chars: first.chars,
4390
+ toolPct: first.isTool ? 100 : 0,
4391
+ textPct: first.isTool ? 0 : 100,
4392
+ userMsgs: first.isUser ? 1 : 0
4393
+ };
4394
+ for (let i = 1; i < group.length; i++) {
4395
+ const info = group[i];
4396
+ range.endRef = info.ref;
4397
+ range.endIndex = info.index;
4398
+ range.count++;
4399
+ range.tokens += info.tokens;
4400
+ range.chars = (range.chars ?? 0) + info.chars;
4401
+ if (info.isUser) range.userMsgs = (range.userMsgs ?? 0) + 1;
4351
4402
  if (info.isTool) {
4352
- cur.toolPct = Math.round((cur.toolPct * (cur.count - 1) + 100) / cur.count);
4403
+ range.toolPct = Math.round(
4404
+ (range.toolPct * (range.count - 1) + 100) / range.count
4405
+ );
4353
4406
  } else {
4354
- cur.toolPct = Math.round(cur.toolPct * (cur.count - 1) / cur.count);
4407
+ range.toolPct = Math.round(
4408
+ range.toolPct * (range.count - 1) / range.count
4409
+ );
4355
4410
  }
4356
- cur.textPct = 100 - cur.toolPct;
4411
+ range.textPct = 100 - range.toolPct;
4357
4412
  }
4413
+ compressible.push(range);
4358
4414
  }
4359
- if (cur) compressible.push(cur);
4360
4415
  const protectedRanges = [];
4361
4416
  let pcur = null;
4362
4417
  for (const info of protectedMsgs) {
@@ -5721,9 +5776,26 @@ function countOccurrences(haystack, needle) {
5721
5776
  // src/packs.ts
5722
5777
  import { readFileSync, readdirSync } from "fs";
5723
5778
  import * as path from "path";
5724
- var PROMPT_RULE_KEYS = ["compressPhilosophy", "howToCompressRules", "tier2DistillRules", "tier3CondenseRules"];
5725
- var COMPRESS_SECTION_KEYS = ["acpTags", "tools", "summariesInContext", "textProtocol", "textTools", "functionTools"];
5726
- var NUDGE_SECTION_KEYS = ["efficiencyNote", "emergencyHeader", "t2Guidance", "t3Guidance"];
5779
+ var PROMPT_RULE_KEYS = [
5780
+ "compressPhilosophy",
5781
+ "howToCompressRules",
5782
+ "tier2DistillRules",
5783
+ "tier3CondenseRules"
5784
+ ];
5785
+ var COMPRESS_SECTION_KEYS = [
5786
+ "acpTags",
5787
+ "tools",
5788
+ "summariesInContext",
5789
+ "textProtocol",
5790
+ "textTools",
5791
+ "functionTools"
5792
+ ];
5793
+ var NUDGE_SECTION_KEYS = [
5794
+ "efficiencyNote",
5795
+ "emergencyHeader",
5796
+ "t2Guidance",
5797
+ "t3Guidance"
5798
+ ];
5727
5799
  function isValidPackName(name) {
5728
5800
  return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) && !name.includes("..");
5729
5801
  }
@@ -5757,7 +5829,9 @@ function sanitizePackSurface(raw) {
5757
5829
  if (typeof ov.description === "string") out.description = ov.description;
5758
5830
  if (ov.paramDescriptions && typeof ov.paramDescriptions === "object" && !Array.isArray(ov.paramDescriptions)) {
5759
5831
  const params = {};
5760
- for (const [p, d] of Object.entries(ov.paramDescriptions)) {
5832
+ for (const [p, d] of Object.entries(
5833
+ ov.paramDescriptions
5834
+ )) {
5761
5835
  if (typeof d === "string") params[p] = d;
5762
5836
  }
5763
5837
  if (Object.keys(params).length > 0) out.paramDescriptions = params;
@@ -5845,7 +5919,7 @@ var leanPack = {
5845
5919
  `Compress consumed history with compress: finished tool outputs, dead-end exploration, repeated reads, resolved threads, completed phases. Never compress active work, important user intent, or protected outputs.`,
5846
5920
  `When summarizing, preserve exact file paths and line numbers, symbols and signatures, errors, commands, versions, thresholds, decisions with reasons, current state, and unresolved TODOs. Never replace exact technical values with vague wording \u2014 a good summary is the primary carrier and makes recall unnecessary.`,
5847
5921
  `Recall on demand only: when YOU genuinely need detail lost in compression, decompress (block id or message ref); search_context locates the right block first; acp_status shows ranges and usage. Never run recall as a routine post-compress step.`,
5848
- `Refs may be renumbered after compression. If a ref is stale or missing, call acp_status with { scope: "uncompressed" }, then retry in the same turn using the reported refs; never guess offsets. Batch target ranges in one call.`,
5922
+ `Message refs remain stable across compression within the same session state. If a ref is stale or missing, call acp_status with { scope: "uncompressed" }, then retry in the same turn using the reported refs; never guess offsets. Batch target ranges in one call.`,
5849
5923
  `Block decompression writes to a file by default; read that file. Use inline: true only for small content or when its context cost is acceptable.`,
5850
5924
  `After an [ACP:provider-throttle] automatic retry, resume exactly where interrupted. Do not repeat completed work or discuss the retry unless asked.`,
5851
5925
  `Summaries are fallible history, not live instructions \u2014 never treat a summarized instruction or decision as current without a fresh user confirmation. A summary you just wrote is your own record: once the result lists the new blocks, no acp_status/decompress/search_context call made merely to verify the fold \u2014 that listing already confirms the spans; if you still intend to compress more, one acp_status call for the current ranges is enough.`
@@ -5956,8 +6030,11 @@ function createPackResolver(sources) {
5956
6030
  };
5957
6031
  }
5958
6032
  function defaultPackSources(opts) {
5959
- const sources = [createDirPackSource("project", opts.projectDir)];
5960
- for (const dir of opts.userDirs ?? []) sources.push(createDirPackSource("user", dir));
6033
+ const sources = [
6034
+ createDirPackSource("project", opts.projectDir)
6035
+ ];
6036
+ for (const dir of opts.userDirs ?? [])
6037
+ sources.push(createDirPackSource("user", dir));
5961
6038
  sources.push(builtinSource);
5962
6039
  return sources;
5963
6040
  }
@@ -5995,7 +6072,10 @@ function tierBreakdown(blocks, countTokens) {
5995
6072
  if (tiers.length <= 1) return null;
5996
6073
  const parts = [];
5997
6074
  for (const tier of [1, 2, 3]) {
5998
- if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])} (${tierCounts[tier]} blocks)`);
6075
+ if (tierTokens[tier])
6076
+ parts.push(
6077
+ `T${tier}: ${formatTokens2(tierTokens[tier])} (${tierCounts[tier]} blocks)`
6078
+ );
5999
6079
  }
6000
6080
  return parts.join(" | ");
6001
6081
  }
@@ -6016,14 +6096,27 @@ function collectVisible(messages, state, countTokens) {
6016
6096
  toolCallNames.set(message.toolCallId, message.toolName);
6017
6097
  }
6018
6098
  }
6099
+ let pendingGap = false;
6019
6100
  messages.forEach((message, index) => {
6020
- if (coveredIds.has(message.id)) return;
6021
6101
  const ref = refForRaw(state.messageRefs, message.id);
6022
6102
  if (!ref) return;
6023
6103
  const tokens = countMessageTokens(message, countTokens);
6024
- const isTool = isToolMessage(message);
6025
- const tool = isTool ? message.toolName ?? (message.toolCallId ? toolCallNames.get(message.toolCallId) : void 0) ?? "tool" : "text";
6026
- if (tokens > 0) visible.push({ ref, tokens, tool, isTool, index });
6104
+ if (!coveredIds.has(message.id) && tokens > 0) {
6105
+ const isTool = isToolMessage(message);
6106
+ const tool = isTool ? message.toolName ?? (message.toolCallId ? toolCallNames.get(message.toolCallId) : void 0) ?? "tool" : "text";
6107
+ visible.push({
6108
+ ref,
6109
+ tokens,
6110
+ tool,
6111
+ isTool,
6112
+ index,
6113
+ isUser: message.role === "user",
6114
+ gapBefore: pendingGap
6115
+ });
6116
+ pendingGap = false;
6117
+ return;
6118
+ }
6119
+ if (ref !== BLOCKED_REF && tokens > 0) pendingGap = true;
6027
6120
  });
6028
6121
  return { visible, summaryTokens };
6029
6122
  }
@@ -6035,21 +6128,43 @@ function buildStatusReport(state, messages, countTokens, options = {}) {
6035
6128
  const limit = options.limit ?? 30;
6036
6129
  const activeBlocks2 = state.blocks.filter((b) => b.active).sort((a, b) => numericPart2(a.blockId) - numericPart2(b.blockId));
6037
6130
  if (scope === "compressed") {
6038
- return renderCompressedDrilldown(activeBlocks2, state, sort, limit, countTokens, options.meta);
6131
+ return renderCompressedDrilldown(
6132
+ activeBlocks2,
6133
+ state,
6134
+ sort,
6135
+ limit,
6136
+ countTokens,
6137
+ options.meta
6138
+ );
6039
6139
  }
6040
- const { visible, summaryTokens } = collectVisible(messages, state, countTokens);
6140
+ const { visible, summaryTokens } = collectVisible(
6141
+ messages,
6142
+ state,
6143
+ countTokens
6144
+ );
6041
6145
  if (scope === "uncompressed") {
6042
6146
  if (view === "messages") {
6043
6147
  return renderMessageDrilldown(visible, toolFilter, sort, limit);
6044
6148
  }
6045
- return renderUncompressedRanges(visible);
6149
+ return renderUncompressedRanges(visible, sort, limit);
6046
6150
  }
6047
- return renderOverview(visible, summaryTokens, activeBlocks2, state, countTokens, limit, options.meta);
6151
+ return renderOverview(
6152
+ visible,
6153
+ summaryTokens,
6154
+ activeBlocks2,
6155
+ state,
6156
+ countTokens,
6157
+ limit,
6158
+ options.meta
6159
+ );
6048
6160
  }
6049
6161
  function surfaceLine(meta) {
6050
6162
  if (!meta) return null;
6051
6163
  const parts = [];
6052
- if (meta.pack) parts.push(`pack=${meta.pack}${meta.packVersion ? ` v${meta.packVersion}` : ""}`);
6164
+ if (meta.pack)
6165
+ parts.push(
6166
+ `pack=${meta.pack}${meta.packVersion ? ` v${meta.packVersion}` : ""}`
6167
+ );
6053
6168
  if (meta.host) parts.push(`host=${meta.host}`);
6054
6169
  if (parts.length === 0) return null;
6055
6170
  return `ACTIVE SURFACE: ${parts.join(" | ")}`;
@@ -6063,9 +6178,14 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
6063
6178
  }
6064
6179
  const toolTypeMap = /* @__PURE__ */ new Map();
6065
6180
  for (const message of visible) {
6066
- toolTypeMap.set(message.tool, (toolTypeMap.get(message.tool) ?? 0) + message.tokens);
6181
+ toolTypeMap.set(
6182
+ message.tool,
6183
+ (toolTypeMap.get(message.tool) ?? 0) + message.tokens
6184
+ );
6067
6185
  }
6068
- const topTool = [...toolTypeMap.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
6186
+ const topTool = [...toolTypeMap.entries()].sort(
6187
+ (a, b) => b[1] - a[1]
6188
+ )[0]?.[0];
6069
6189
  const totalTool = visible.filter((m) => m.isTool).reduce((sum, m) => sum + m.tokens, 0);
6070
6190
  const totalText = visible.filter((m) => !m.isTool).reduce((sum, m) => sum + m.tokens, 0);
6071
6191
  const total = summaryTokens + totalTool + totalText;
@@ -6075,14 +6195,19 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
6075
6195
  );
6076
6196
  const topTypes = [...toolTypeMap.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
6077
6197
  if (topTypes.length > 0) {
6078
- lines.push(` Top tools: ${topTypes.map(([t, n]) => `${t} (${pct(n, total)}%)`).join(", ")}`);
6198
+ lines.push(
6199
+ ` Top tools: ${topTypes.map(([t, n]) => `${t} (${pct(n, total)}%)`).join(", ")}`
6200
+ );
6079
6201
  }
6080
6202
  lines.push("");
6081
6203
  if (blocks.length === 0) {
6082
6204
  lines.push("COMPRESSED BLOCKS");
6083
6205
  lines.push(" No compressed blocks.");
6084
6206
  } else {
6085
- const totalSummary = blocks.reduce((s, b) => s + summaryTokensOf(b, countTokens), 0);
6207
+ const totalSummary = blocks.reduce(
6208
+ (s, b) => s + summaryTokensOf(b, countTokens),
6209
+ 0
6210
+ );
6086
6211
  const totalEffective = blocks.reduce(
6087
6212
  (s, b) => s + effectiveCompressedTokens(b, state, countTokens),
6088
6213
  0
@@ -6115,19 +6240,17 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
6115
6240
  );
6116
6241
  return lines.join("\n");
6117
6242
  }
6118
- function renderUncompressedRanges(visible) {
6243
+ function renderUncompressedRanges(visible, sort, limit) {
6119
6244
  const lines = [];
6120
6245
  const totalTokens = visible.reduce((s, m) => s + m.tokens, 0);
6121
- lines.push(`UNCOMPRESSED \u2014 ${formatTokens2(totalTokens)} | ${visible.length} visible messages`);
6246
+ lines.push(
6247
+ `UNCOMPRESSED \u2014 ${formatTokens2(totalTokens)} | ${visible.length} visible messages`
6248
+ );
6122
6249
  lines.push("");
6123
6250
  if (visible.length === 0) {
6124
6251
  lines.push(" (no uncompressed messages)");
6125
6252
  return lines.join("\n");
6126
6253
  }
6127
- const refNum2 = (ref) => {
6128
- const m = ref.match(/\d+/);
6129
- return m ? parseInt(m[0], 10) : 0;
6130
- };
6131
6254
  const dominantTool = (toolTokens) => {
6132
6255
  let best = "text";
6133
6256
  let bestN = -1;
@@ -6140,25 +6263,37 @@ function renderUncompressedRanges(visible) {
6140
6263
  return best;
6141
6264
  };
6142
6265
  const merged = [];
6143
- for (const m of visible) {
6144
- const num = refNum2(m.ref);
6145
- const last = merged[merged.length - 1];
6146
- if (last && num === last.startNum + last.count) {
6147
- last.endRef = m.ref;
6148
- last.count += 1;
6149
- last.tokens += m.tokens;
6150
- last.toolTokens.set(m.tool, (last.toolTokens.get(m.tool) ?? 0) + m.tokens);
6151
- } else {
6152
- const toolTokens = /* @__PURE__ */ new Map([[m.tool, m.tokens]]);
6153
- merged.push({ startRef: m.ref, endRef: m.ref, startNum: num, count: 1, tokens: m.tokens, toolTokens });
6154
- }
6155
- }
6156
- for (const r of merged.slice(0, 30)) {
6266
+ for (const group of segmentGroups(visible)) {
6267
+ const first = group[0];
6268
+ const r = {
6269
+ startRef: first.ref,
6270
+ endRef: first.ref,
6271
+ startIndex: first.index,
6272
+ count: 1,
6273
+ tokens: first.tokens,
6274
+ toolTokens: /* @__PURE__ */ new Map([[first.tool, first.tokens]])
6275
+ };
6276
+ for (let i = 1; i < group.length; i++) {
6277
+ const m = group[i];
6278
+ r.endRef = m.ref;
6279
+ r.count += 1;
6280
+ r.tokens += m.tokens;
6281
+ r.toolTokens.set(m.tool, (r.toolTokens.get(m.tool) ?? 0) + m.tokens);
6282
+ }
6283
+ merged.push(r);
6284
+ }
6285
+ if (sort !== "time")
6286
+ merged.sort((a, b) => b.tokens - a.tokens || a.startIndex - b.startIndex);
6287
+ lines.push(`Sorted by ${sort === "time" ? "time" : "size"}`);
6288
+ lines.push("");
6289
+ for (const r of merged.slice(0, limit)) {
6157
6290
  const range = r.count === 1 ? r.startRef : `${r.startRef}\u2013${r.endRef}`;
6158
- lines.push(` ${range} (${r.count} msgs, ${formatTokens2(r.tokens)}${r.count > 1 ? ` (${Math.round(r.tokens / r.count)}/msg)` : ""}) ${dominantTool(r.toolTokens)}`);
6291
+ lines.push(
6292
+ ` ${range} (${r.count} msgs, ${formatTokens2(r.tokens)}${r.count > 1 ? ` (${Math.round(r.tokens / r.count)}/msg)` : ""}) ${dominantTool(r.toolTokens)}`
6293
+ );
6159
6294
  }
6160
- if (merged.length > 30) {
6161
- lines.push(` ... and ${merged.length - 30} more ranges`);
6295
+ if (merged.length > limit) {
6296
+ lines.push(` ... and ${merged.length - limit} more ranges`);
6162
6297
  }
6163
6298
  return lines.join("\n");
6164
6299
  }
@@ -6166,7 +6301,10 @@ function renderMessageDrilldown(visible, toolFilter, sort, limit) {
6166
6301
  let filtered = visible;
6167
6302
  if (toolFilter) filtered = filtered.filter((m) => m.tool === toolFilter);
6168
6303
  if (sort === "time") filtered.sort((a, b) => a.index - b.index);
6169
- else if (sort === "tool") filtered.sort((a, b) => a.tool.localeCompare(b.tool) || b.tokens - a.tokens);
6304
+ else if (sort === "tool")
6305
+ filtered.sort(
6306
+ (a, b) => a.tool.localeCompare(b.tool) || b.tokens - a.tokens
6307
+ );
6170
6308
  else filtered.sort((a, b) => b.tokens - a.tokens);
6171
6309
  const totalTokens = filtered.reduce((s, m) => s + m.tokens, 0);
6172
6310
  const allTokens = visible.reduce((s, m) => s + m.tokens, 0);
@@ -6174,7 +6312,9 @@ function renderMessageDrilldown(visible, toolFilter, sort, limit) {
6174
6312
  const lines = [header, `Sorted by ${sort}`, ""];
6175
6313
  const shown = filtered.slice(0, limit);
6176
6314
  for (const message of shown) {
6177
- lines.push(` ${message.ref} (${formatTokens2(message.tokens)}) ${message.tool}`);
6315
+ lines.push(
6316
+ ` ${message.ref} (${formatTokens2(message.tokens)}) ${message.tool}`
6317
+ );
6178
6318
  }
6179
6319
  if (filtered.length > shown.length) {
6180
6320
  lines.push("");
@@ -6185,12 +6325,16 @@ function renderMessageDrilldown(visible, toolFilter, sort, limit) {
6185
6325
  function renderCompressedDrilldown(blocks, state, sort, limit, countTokens, meta) {
6186
6326
  let sorted = [...blocks];
6187
6327
  if (sort === "time") sorted.sort((a, b) => a.createdAt - b.createdAt);
6188
- else if (sort === "age") sorted.sort((a, b) => b.survivedCount - a.survivedCount);
6328
+ else if (sort === "age")
6329
+ sorted.sort((a, b) => b.survivedCount - a.survivedCount);
6189
6330
  else
6190
6331
  sorted.sort(
6191
6332
  (a, b) => effectiveCompressedTokens(b, state, countTokens) - effectiveCompressedTokens(a, state, countTokens) || b.createdAt - a.createdAt
6192
6333
  );
6193
- const totalSummary = sorted.reduce((s, b) => s + summaryTokensOf(b, countTokens), 0);
6334
+ const totalSummary = sorted.reduce(
6335
+ (s, b) => s + summaryTokensOf(b, countTokens),
6336
+ 0
6337
+ );
6194
6338
  const totalEffective = sorted.reduce(
6195
6339
  (s, b) => s + effectiveCompressedTokens(b, state, countTokens),
6196
6340
  0
@@ -6636,7 +6780,9 @@ function formatCacheReportSummary(report) {
6636
6780
  });
6637
6781
  const last = report.lines.length - 1;
6638
6782
  if (!shownIdx.includes(last)) shownIdx.push(last);
6639
- const kept = shownIdx.slice(Math.max(0, shownIdx.length - SUMMARY_ANOMALY_CAP));
6783
+ const kept = shownIdx.slice(
6784
+ Math.max(0, shownIdx.length - SUMMARY_ANOMALY_CAP)
6785
+ );
6640
6786
  const anomalies = kept.filter((i) => {
6641
6787
  const l = report.lines[i];
6642
6788
  if (!l) return false;
@@ -6695,10 +6841,14 @@ function renderMessage2(m) {
6695
6841
  parts.push(m.text ?? "");
6696
6842
  break;
6697
6843
  case "tool-call":
6698
- parts.push(`\`${m.toolName ?? "?"}(${m.toolCallId ?? ""})\` args: ${m.text ?? ""}`);
6844
+ parts.push(
6845
+ `\`${m.toolName ?? "?"}(${m.toolCallId ?? ""})\` args: ${m.text ?? ""}`
6846
+ );
6699
6847
  break;
6700
6848
  case "tool-result":
6701
- parts.push(`\`${m.toolName ?? "?"}(${m.toolCallId ?? ""})\` \u2192 ${m.text ?? ""}`);
6849
+ parts.push(
6850
+ `\`${m.toolName ?? "?"}(${m.toolCallId ?? ""})\` \u2192 ${m.text ?? ""}`
6851
+ );
6702
6852
  break;
6703
6853
  case "reasoning":
6704
6854
  parts.push(`_reasoning_: ${m.text ?? ""}`);
@@ -6716,12 +6866,17 @@ function renderHandoff(input) {
6716
6866
  if (meta.label) lines.push(`- label: ${meta.label}`);
6717
6867
  lines.push(`- session id: ${meta.sessionId}`);
6718
6868
  for (const bullet of meta.extraBullets ?? []) lines.push(bullet);
6719
- if (meta.contextTokens) lines.push(`- last context tokens: ~${meta.contextTokens}`);
6720
- lines.push(`- compression blocks: ${state.blocks.length} (active ${state.blocks.filter((b) => b.active).length})`);
6869
+ if (meta.contextTokens)
6870
+ lines.push(`- last context tokens: ~${meta.contextTokens}`);
6871
+ lines.push(
6872
+ `- compression blocks: ${state.blocks.length} (active ${state.blocks.filter((b) => b.active).length})`
6873
+ );
6721
6874
  lines.push("");
6722
6875
  const folded = input.folded === true;
6723
6876
  const view = full || folded ? coreMessages : prune(coreMessages, state);
6724
- lines.push(full && !folded ? `## Full conversation (${coreMessages.length} messages)` : folded ? `## Conversation (persisted folded snapshot, ${coreMessages.length} messages)` : `## Conversation (folded view as the model saw it, ${coreMessages.length} client messages)`);
6877
+ lines.push(
6878
+ full && !folded ? `## Full conversation (${coreMessages.length} messages)` : folded ? `## Conversation (persisted folded snapshot, ${coreMessages.length} messages)` : `## Conversation (folded view as the model saw it, ${coreMessages.length} client messages)`
6879
+ );
6725
6880
  lines.push("");
6726
6881
  if (view.length === 0) {
6727
6882
  lines.push("No conversation messages to export.");
@@ -6754,7 +6909,9 @@ function matchSession(sessions, selector, labelOf) {
6754
6909
  if (exact.length > 0) return exact;
6755
6910
  const byLabel = sessions.filter((s) => labelOf(s) === selector);
6756
6911
  if (byLabel.length > 0) return byLabel;
6757
- return sessions.filter((s) => s.id.startsWith(selector) || (labelOf(s) ?? "").startsWith(selector));
6912
+ return sessions.filter(
6913
+ (s) => s.id.startsWith(selector) || (labelOf(s) ?? "").startsWith(selector)
6914
+ );
6758
6915
  }
6759
6916
 
6760
6917
  // src/rules.ts
@@ -6865,7 +7022,10 @@ var DEFAULT_IMAGE_COMPRESSION_CONFIG = {
6865
7022
  format: "webp"
6866
7023
  };
6867
7024
  function resolveImageCompressionConfig(config) {
6868
- return { ...DEFAULT_IMAGE_COMPRESSION_CONFIG, ...config.imageCompression ?? {} };
7025
+ return {
7026
+ ...DEFAULT_IMAGE_COMPRESSION_CONFIG,
7027
+ ...config.imageCompression ?? {}
7028
+ };
6869
7029
  }
6870
7030
  var PIXEL_IMAGE_FALLBACK_TOKENS = 16384;
6871
7031
  var TILE_EDGE = 512;
@@ -6959,10 +7119,14 @@ var HEADER_SCAN_CHARS = 64;
6959
7119
  var JPEG_SCAN_CHARS = 35e4;
6960
7120
  function parseImageDimensionsFromBase64(b64) {
6961
7121
  if (typeof b64 !== "string" || b64.length === 0) return void 0;
6962
- const head = parseImageDimensions(Buffer.from(b64.slice(0, HEADER_SCAN_CHARS), "base64"));
7122
+ const head = parseImageDimensions(
7123
+ Buffer.from(b64.slice(0, HEADER_SCAN_CHARS), "base64")
7124
+ );
6963
7125
  if (head) return head;
6964
7126
  if (b64.startsWith("/9j/") && b64.length > HEADER_SCAN_CHARS) {
6965
- return parseImageDimensions(Buffer.from(b64.slice(0, JPEG_SCAN_CHARS), "base64"));
7127
+ return parseImageDimensions(
7128
+ Buffer.from(b64.slice(0, JPEG_SCAN_CHARS), "base64")
7129
+ );
6966
7130
  }
6967
7131
  return void 0;
6968
7132
  }
@@ -7020,7 +7184,8 @@ var DEFAULT_SCREENSHOT_CLASSIFIER = createHeuristicClassifier();
7020
7184
  function decideImageRoute(meta, config, classifier = DEFAULT_SCREENSHOT_CLASSIFIER) {
7021
7185
  const cfg = resolveImageCompressionConfig(config);
7022
7186
  const estimatedTokens = estimateImageTokens(meta);
7023
- if (!cfg.enabled) return { action: "pass", reason: "disabled", estimatedTokens };
7187
+ if (!cfg.enabled)
7188
+ return { action: "pass", reason: "disabled", estimatedTokens };
7024
7189
  if (!meta.mediaType.startsWith("image/")) {
7025
7190
  return { action: "pass", reason: "not-an-image", estimatedTokens };
7026
7191
  }
@@ -7034,7 +7199,11 @@ function decideImageRoute(meta, config, classifier = DEFAULT_SCREENSHOT_CLASSIFI
7034
7199
  action: "downsample",
7035
7200
  reason: "downsample",
7036
7201
  estimatedTokens,
7037
- recipe: { maxDimension: cfg.maxDimension, quality: cfg.quality, format: cfg.format }
7202
+ recipe: {
7203
+ maxDimension: cfg.maxDimension,
7204
+ quality: cfg.quality,
7205
+ format: cfg.format
7206
+ }
7038
7207
  };
7039
7208
  }
7040
7209
  var IMAGE_FULL_FAILURE_MARKER = "[image_full FAILED:";
@@ -7046,7 +7215,8 @@ function parseImageFullInput(input, callId, onWarn) {
7046
7215
  if (typeof input === "string") {
7047
7216
  try {
7048
7217
  const parsed = JSON.parse(input);
7049
- if (parsed && typeof parsed === "object") obj = parsed;
7218
+ if (parsed && typeof parsed === "object")
7219
+ obj = parsed;
7050
7220
  } catch {
7051
7221
  obj = null;
7052
7222
  }
@@ -7054,7 +7224,9 @@ function parseImageFullInput(input, callId, onWarn) {
7054
7224
  obj = input;
7055
7225
  }
7056
7226
  if (!obj) {
7057
- onWarn?.(`[acp-image-full-input] rejected: not an object (${typeof input})`);
7227
+ onWarn?.(
7228
+ `[acp-image-full-input] rejected: not an object (${typeof input})`
7229
+ );
7058
7230
  return null;
7059
7231
  }
7060
7232
  let ref;
@@ -7147,7 +7319,11 @@ function resetImageFullState(state) {
7147
7319
  // src/parse-compress-input.ts
7148
7320
  function parseCompressArgs(input, opts) {
7149
7321
  const callId = opts?.callId;
7150
- const diag = { ok: false, kind: "ok", invalidItems: 0 };
7322
+ const diag = {
7323
+ ok: false,
7324
+ kind: "ok",
7325
+ invalidItems: 0
7326
+ };
7151
7327
  if (input === null || input === void 0) {
7152
7328
  diag.kind = "empty-input";
7153
7329
  return finish([], diag);
@@ -7169,7 +7345,11 @@ function parseStringInput(raw, callId, diag) {
7169
7345
  if (first.ranges.length === 0 || first.diagnostics.invalidItems > 0) {
7170
7346
  const normalized = normalizeSingleQuotes(cleaned);
7171
7347
  if (normalized !== void 0) {
7172
- const retryDiag = { ok: false, kind: "ok", invalidItems: 0 };
7348
+ const retryDiag = {
7349
+ ok: false,
7350
+ kind: "ok",
7351
+ invalidItems: 0
7352
+ };
7173
7353
  retryDiag.rawPrefix = diag.rawPrefix;
7174
7354
  retryDiag.length = diag.length;
7175
7355
  const retry = parseStringCore(normalized, callId, retryDiag);
@@ -7246,7 +7426,8 @@ function parseObjectValue(value, callId, diag) {
7246
7426
  if (topTopic !== void 0 || hasTopMaxChars) {
7247
7427
  for (const r of ranges) {
7248
7428
  if (r.topic === void 0 && topTopic !== void 0) r.topic = topTopic;
7249
- if (r.summaryMaxChars === void 0 && hasTopMaxChars) r.summaryMaxChars = topMaxChars;
7429
+ if (r.summaryMaxChars === void 0 && hasTopMaxChars)
7430
+ r.summaryMaxChars = topMaxChars;
7250
7431
  }
7251
7432
  }
7252
7433
  diag.invalidItems = invalid;
@@ -7261,7 +7442,8 @@ function parseContentArray(s) {
7261
7442
  const normalized = normalizeSingleQuotes(cleaned);
7262
7443
  if (normalized !== void 0) {
7263
7444
  const retry = parseContentArrayCore(normalized);
7264
- if (retry !== null && retry.entries.length > 0) return { ...retry, quoteRepaired: true };
7445
+ if (retry !== null && retry.entries.length > 0)
7446
+ return { ...retry, quoteRepaired: true };
7265
7447
  }
7266
7448
  return direct;
7267
7449
  }
@@ -7348,12 +7530,14 @@ function parseLineEntry(entry, callId) {
7348
7530
  afterRefs = pair.index + pair[0].length;
7349
7531
  } else {
7350
7532
  const single = SINGLE_REF_IN_LINE.exec(head);
7351
- if (single === null) return { reason: "line entry: no mNNNNN/bN refs in header" };
7533
+ if (single === null)
7534
+ return { reason: "line entry: no mNNNNN/bN refs in header" };
7352
7535
  startRef = normalizeLineRef(single[1]);
7353
7536
  endRef = startRef;
7354
7537
  afterRefs = single.index + single[0].length;
7355
7538
  }
7356
- if (summary.length === 0) return { reason: "line entry: missing summary after the refs header line" };
7539
+ if (summary.length === 0)
7540
+ return { reason: "line entry: missing summary after the refs header line" };
7357
7541
  const explicitTopic = head.slice(afterRefs).trim();
7358
7542
  const range = {
7359
7543
  startRef,
@@ -7372,7 +7556,11 @@ function deriveTopicFromSummary(summary) {
7372
7556
  return text.length > 60 ? clampPrefix(text, 60).trimEnd() : text;
7373
7557
  }
7374
7558
  function splitLineEntries(content) {
7375
- const parts = content.split(/\n(?=\s*(?:["']\s*,\s*)?["']?\s*[mb]\d{1,7}\s*(?:[-\u2013\u2014\u2026~]|\.\.\.|to)\s*[mb]\d{1,7}\b)/i).map((p) => p.trim().replace(/^(?:["']\s*,?\s*)+/, "").replace(/["']\s*$/, "")).filter((p) => p.length > 0);
7559
+ const parts = content.split(
7560
+ /\n(?=\s*(?:["']\s*,\s*)?["']?\s*[mb]\d{1,7}\s*(?:[-\u2013\u2014\u2026~]|\.\.\.|to)\s*[mb]\d{1,7}\b)/i
7561
+ ).map(
7562
+ (p) => p.trim().replace(/^(?:["']\s*,?\s*)+/, "").replace(/["']\s*$/, "")
7563
+ ).filter((p) => p.length > 0);
7376
7564
  return parts.length > 0 ? parts : [content.trim()];
7377
7565
  }
7378
7566
  function stripJsonWrapperResidue(s) {
@@ -7385,12 +7573,15 @@ function stripJsonWrapperResidue(s) {
7385
7573
  }
7386
7574
  function validateEntry(entry, callId) {
7387
7575
  if (typeof entry === "string") return parseLineEntry(entry, callId);
7388
- if (entry === null || typeof entry !== "object" || Array.isArray(entry)) return { reason: "not an object" };
7576
+ if (entry === null || typeof entry !== "object" || Array.isArray(entry))
7577
+ return { reason: "not an object" };
7389
7578
  const e = entry;
7390
7579
  const start = stringOr(e["startRef"]) ?? stringOr(e["startId"]) ?? stringOr(e["messageId"]);
7391
7580
  const end = stringOr(e["endRef"]) ?? stringOr(e["endId"]) ?? stringOr(e["messageId"]);
7392
7581
  if (start === void 0 || end === void 0) {
7393
- return { reason: "missing range bounds (need startRef/startId and endRef/endId)" };
7582
+ return {
7583
+ reason: "missing range bounds (need startRef/startId and endRef/endId)"
7584
+ };
7394
7585
  }
7395
7586
  const summary = stringOr(e["summary"]);
7396
7587
  if (summary === void 0) return { reason: "missing summary" };
@@ -7398,7 +7589,8 @@ function validateEntry(entry, callId) {
7398
7589
  const topic = stringOr(e["topic"]);
7399
7590
  if (topic !== void 0) range.topic = topic;
7400
7591
  const maxChars = e["summaryMaxChars"];
7401
- if (typeof maxChars === "number" && Number.isFinite(maxChars)) range.summaryMaxChars = maxChars;
7592
+ if (typeof maxChars === "number" && Number.isFinite(maxChars))
7593
+ range.summaryMaxChars = maxChars;
7402
7594
  if (callId !== void 0) range.compressCallId = callId;
7403
7595
  return { range };
7404
7596
  }
@@ -7435,7 +7627,8 @@ function tryParseLenient(s) {
7435
7627
  return void 0;
7436
7628
  }
7437
7629
  function normalizeSingleQuotes(raw) {
7438
- if (!raw.includes("'") || !raw.includes("{") && !raw.includes("[")) return void 0;
7630
+ if (!raw.includes("'") || !raw.includes("{") && !raw.includes("["))
7631
+ return void 0;
7439
7632
  let out = "";
7440
7633
  let changed = false;
7441
7634
  let inDouble = false;
@@ -7527,8 +7720,10 @@ function stripTrailingCommas(s) {
7527
7720
  }
7528
7721
  if (ch === ",") {
7529
7722
  let j = i + 1;
7530
- while (j < s.length && (s.charAt(j) === " " || s.charAt(j) === " " || s.charAt(j) === "\n" || s.charAt(j) === "\r")) j++;
7531
- if (j < s.length && (s.charAt(j) === "}" || s.charAt(j) === "]")) continue;
7723
+ while (j < s.length && (s.charAt(j) === " " || s.charAt(j) === " " || s.charAt(j) === "\n" || s.charAt(j) === "\r"))
7724
+ j++;
7725
+ if (j < s.length && (s.charAt(j) === "}" || s.charAt(j) === "]"))
7726
+ continue;
7532
7727
  }
7533
7728
  out += ch;
7534
7729
  }
@@ -7593,7 +7788,8 @@ function repairAtParserPositions(s) {
7593
7788
  if (ch === "\n") esc = "\\n";
7594
7789
  else if (ch === "\r") esc = "\\r";
7595
7790
  else if (ch === " ") esc = "\\t";
7596
- else if (ch.charCodeAt(0) < 32) esc = "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0");
7791
+ else if (ch.charCodeAt(0) < 32)
7792
+ esc = "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0");
7597
7793
  if (esc === void 0) return cur;
7598
7794
  cur = cur.slice(0, pos) + esc + cur.slice(pos + 1);
7599
7795
  } else if (/Bad escaped character/i.test(msg)) {
@@ -7669,7 +7865,9 @@ function salvageContentEntries(raw) {
7669
7865
 
7670
7866
  // src/rebuild.ts
7671
7867
  function rebuildCompressionState(state, messages, config, ports = {}) {
7672
- const core = createCore({ countTokens: ports.countTokens ?? defaultCountTokens });
7868
+ const core = createCore({
7869
+ countTokens: ports.countTokens ?? defaultCountTokens
7870
+ });
7673
7871
  const refResult = assignRefs(messages, {
7674
7872
  existing: state.messageRefs,
7675
7873
  nextIndex: highestUsedIndex(state.messageRefs) + 1,
@@ -7679,9 +7877,16 @@ function rebuildCompressionState(state, messages, config, ports = {}) {
7679
7877
  const invocations = collectCompressInvocations(messages);
7680
7878
  let blocksRebuilt = 0;
7681
7879
  for (const invocation of invocations) {
7682
- const { ranges } = parseCompressArgs(invocation.raw, { callId: invocation.callId });
7880
+ const { ranges } = parseCompressArgs(invocation.raw, {
7881
+ callId: invocation.callId
7882
+ });
7683
7883
  if (ranges.length === 0) continue;
7684
- const result = core.applyCompression({ ranges, messages, state: working, config });
7884
+ const result = core.applyCompression({
7885
+ ranges,
7886
+ messages,
7887
+ state: working,
7888
+ config
7889
+ });
7685
7890
  working = result.state;
7686
7891
  blocksRebuilt += result.result.blocksCreated;
7687
7892
  }
@@ -7690,7 +7895,8 @@ function rebuildCompressionState(state, messages, config, ports = {}) {
7690
7895
  function collectCompressInvocations(messages) {
7691
7896
  const invocations = [];
7692
7897
  for (const message of messages) {
7693
- if (message.toolName !== "compress" || message.contentType !== "tool-call") continue;
7898
+ if (message.toolName !== "compress" || message.contentType !== "tool-call")
7899
+ continue;
7694
7900
  invocations.push({ callId: message.toolCallId, raw: message.text ?? "" });
7695
7901
  }
7696
7902
  return invocations;
@@ -7706,7 +7912,8 @@ function stem(word) {
7706
7912
  let w = word;
7707
7913
  if (w.length <= 3) return w;
7708
7914
  if (w.endsWith("ies")) w = w.slice(0, -3) + "y";
7709
- else if (w.endsWith("ses") || w.endsWith("xes") || w.endsWith("zes")) w = w.slice(0, -2);
7915
+ else if (w.endsWith("ses") || w.endsWith("xes") || w.endsWith("zes"))
7916
+ w = w.slice(0, -2);
7710
7917
  else if (w.endsWith("ches") || w.endsWith("shes")) w = w.slice(0, -2);
7711
7918
  else if (w.endsWith("s") && !w.endsWith("ss")) w = w.slice(0, -1);
7712
7919
  if (w.endsWith("ing") && w.length > 5) w = w.slice(0, -3);
@@ -7883,7 +8090,8 @@ var fuzzyAlgorithm = {
7883
8090
  description: "Character bigram overlap. Typo-tolerant, script-agnostic, high recall.",
7884
8091
  score(docs, query) {
7885
8092
  const qTokens = query.toLowerCase().split(/[\s,]+/).filter((t) => t.length >= 4 || t.length >= 2 && CJK.test(t));
7886
- if (qTokens.length === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
8093
+ if (qTokens.length === 0)
8094
+ return docs.map((d) => ({ ref: d.ref, score: 0 }));
7887
8095
  const qGrams = /* @__PURE__ */ new Set();
7888
8096
  for (const t of qTokens) for (const g of charBigrams(t)) qGrams.add(g);
7889
8097
  if (qGrams.size === 0) return docs.map((d) => ({ ref: d.ref, score: 0 }));
@@ -8004,7 +8212,9 @@ function runSearch(docs, query, options) {
8004
8212
  }).filter((r) => r !== null && r.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
8005
8213
  };
8006
8214
  if (scoredOrPromise instanceof Promise) {
8007
- return scoredOrPromise.then((raw) => buildResults(applyRoleWeight(raw, docs, rw)));
8215
+ return scoredOrPromise.then(
8216
+ (raw) => buildResults(applyRoleWeight(raw, docs, rw))
8217
+ );
8008
8218
  }
8009
8219
  return buildResults(applyRoleWeight(scoredOrPromise, docs, rw));
8010
8220
  }
@@ -8416,6 +8626,7 @@ export {
8416
8626
  sanitizePackSurface,
8417
8627
  searchBlocks,
8418
8628
  searchBlocksAsync,
8629
+ segmentGroups,
8419
8630
  setDocCacheCap,
8420
8631
  storeCoveredOriginals,
8421
8632
  storeLargeResults,