acp-kernel 0.0.88 → 0.0.89-pr.315.262

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.
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-OY6XYN4S.js";
20
20
  import {
21
21
  activeBlocks,
22
22
  advanceSurvival,
@@ -111,33 +111,45 @@ function summaryMessageId(blockId) {
111
111
  function isSummaryMessageId(id) {
112
112
  return id.startsWith(SUMMARY_ID_PREFIX);
113
113
  }
114
+ function baseIdOf(id) {
115
+ const hash = id.indexOf("#");
116
+ return hash > 0 ? id.substring(0, hash) : id;
117
+ }
118
+ function isCovered(id, coveredBases) {
119
+ return coveredBases.has(baseIdOf(id));
120
+ }
114
121
  function isRenderedSummaryMessage(message) {
115
122
  return isSummaryMessageId(message.id) && message.role === "system" && message.contentType === "text";
116
123
  }
117
124
  function prune(messages, state, options = {}) {
118
125
  const covered = coveredMessageIds(state);
119
126
  if (covered.size === 0) return [...messages];
127
+ const coveredBases = /* @__PURE__ */ new Set();
128
+ for (const id of covered) coveredBases.add(baseIdOf(id));
120
129
  const inject = options.injectSummaries ?? true;
121
130
  const firstUserIndex = messages.findIndex(
122
131
  (message) => message.role === "user"
123
132
  );
124
- const indexById = /* @__PURE__ */ new Map();
133
+ const baseIndexById = /* @__PURE__ */ new Map();
125
134
  const summaryIndexById = /* @__PURE__ */ new Map();
126
135
  messages.forEach((message, index) => {
127
- indexById.set(message.id, index);
136
+ const base = baseIdOf(message.id);
137
+ const existing = baseIndexById.get(base);
138
+ if (existing === void 0 || index < existing)
139
+ baseIndexById.set(base, index);
128
140
  if (isRenderedSummaryMessage(message))
129
141
  summaryIndexById.set(message.id, index);
130
142
  });
131
- const anchors = inject ? collectSummaryAnchors(state, indexById, summaryIndexById) : [];
143
+ const anchors = inject ? collectSummaryAnchors(state, baseIndexById, summaryIndexById) : [];
132
144
  return stripOrphanedReasoning(
133
145
  stripOrphanedToolResults(
134
146
  stripOrphanedToolCalls(
135
- rebuildMessages(messages, covered, firstUserIndex, anchors)
147
+ rebuildMessages(messages, coveredBases, firstUserIndex, anchors)
136
148
  )
137
149
  )
138
150
  );
139
151
  }
140
- function collectSummaryAnchors(state, indexById, summaryIndexById) {
152
+ function collectSummaryAnchors(state, baseIndexById, summaryIndexById) {
141
153
  const anchors = [];
142
154
  for (const block of activeBlocks(state)) {
143
155
  const existingIndex = summaryIndexById.get(summaryMessageId(block.blockId));
@@ -152,7 +164,7 @@ function collectSummaryAnchors(state, indexById, summaryIndexById) {
152
164
  }
153
165
  let earliest = null;
154
166
  for (const id of block.effectiveMessageIds) {
155
- const index = indexById.get(id);
167
+ const index = baseIndexById.get(baseIdOf(id));
156
168
  if (index !== void 0 && (earliest === null || index < earliest)) {
157
169
  earliest = index;
158
170
  }
@@ -197,7 +209,7 @@ function pairSafeAnchorIndex(messages, index) {
197
209
  }
198
210
  return safe;
199
211
  }
200
- function rebuildMessages(messages, covered, firstUserIndex, anchors) {
212
+ function rebuildMessages(messages, coveredBases, firstUserIndex, anchors) {
201
213
  const safeAnchors = anchors.map((anchor) => ({
202
214
  ...anchor,
203
215
  insertAt: pairSafeAnchorIndex(messages, anchor.insertAt)
@@ -215,7 +227,7 @@ function rebuildMessages(messages, covered, firstUserIndex, anchors) {
215
227
  result.push(messages[index]);
216
228
  continue;
217
229
  }
218
- if (covered.has(messages[index].id)) continue;
230
+ if (isCovered(messages[index].id, coveredBases)) continue;
219
231
  if (isRenderedSummaryMessage(messages[index]) && anchoredSummaryIds.has(messages[index].id))
220
232
  continue;
221
233
  result.push(messages[index]);
@@ -281,6 +293,8 @@ function stripOrphanedReasoning(messages) {
281
293
  // src/sync.ts
282
294
  function syncBlocks(messages, state) {
283
295
  const presentIds = new Set(messages.map((message) => message.id));
296
+ const presentBases = /* @__PURE__ */ new Set();
297
+ for (const message of messages) presentBases.add(baseIdOf(message.id));
284
298
  const deactivated = [];
285
299
  const result = {
286
300
  blocks: state.blocks.map((block) => ({
@@ -330,7 +344,7 @@ function syncBlocks(messages, state) {
330
344
  continue;
331
345
  }
332
346
  block.active = true;
333
- const stillPresent = block.effectiveMessageIds.some((id) => presentIds.has(id)) || presentIds.has(summaryMessageId(block.blockId));
347
+ const stillPresent = block.effectiveMessageIds.some((id) => presentBases.has(baseIdOf(id))) || presentIds.has(summaryMessageId(block.blockId));
334
348
  if (!stillPresent) {
335
349
  block.active = false;
336
350
  deactivated.push(block.blockId);
@@ -658,14 +672,7 @@ var COMPRESS_PARAMETERS = {
658
672
  type: "string",
659
673
  description: "Flat single-range form (no content): self-contained summary replacing the range"
660
674
  }
661
- },
662
- anyOf: [
663
- { required: ["content"] },
664
- {
665
- required: ["summary"],
666
- anyOf: [{ required: ["startId", "endId"] }, { required: ["startRef", "endRef"] }]
667
- }
668
- ]
675
+ }
669
676
  };
670
677
  var COMPRESS_TOOL = {
671
678
  name: COMPRESS_TOOL_NAME,
@@ -1255,7 +1262,12 @@ function hasMediaPayload(msg) {
1255
1262
  if (m.rawOpenaiContent != null) return true;
1256
1263
  if (Array.isArray(m.rawOpenaiContentParts) && m.rawOpenaiContentParts.length > 0)
1257
1264
  return true;
1258
- if (isObjWith(m.rawAnthropicBlock, "type", "image")) return true;
1265
+ const ab = m.rawAnthropicBlock;
1266
+ if (isObjWith(ab, "type", "image")) return true;
1267
+ if (isObjWith(ab, "type", "tool_result")) {
1268
+ const content = ab.content;
1269
+ if (Array.isArray(content)) return content.some((p) => !isObjWith(p, "type", "text"));
1270
+ }
1259
1271
  const item = m.rawResponsesItem;
1260
1272
  if (isObjWith(item, "type", "input_image")) return true;
1261
1273
  if (item && typeof item === "object") {
@@ -1619,8 +1631,10 @@ function resolveBoundaries(input) {
1619
1631
  if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
1620
1632
  let startIndex = startAnchor.index;
1621
1633
  let endIndex = endAnchor.index;
1634
+ let reversedNote;
1622
1635
  if (startIndex > endIndex) {
1623
1636
  [startIndex, endIndex] = [endIndex, startIndex];
1637
+ reversedNote = `note: refs were given reversed (${start.raw}\u2192${end.raw}), normalized to ${end.raw}..${start.raw}`;
1624
1638
  }
1625
1639
  const messageIds = [];
1626
1640
  for (let index = startIndex; index <= endIndex; index++) {
@@ -1647,7 +1661,8 @@ function resolveBoundaries(input) {
1647
1661
  nestedBlockIds,
1648
1662
  boundaryKind,
1649
1663
  protectedGaps,
1650
- snappedBoundaries
1664
+ snappedBoundaries,
1665
+ reversedNote
1651
1666
  };
1652
1667
  }
1653
1668
  function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
@@ -1672,6 +1687,27 @@ function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
1672
1687
  snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
1673
1688
  };
1674
1689
  }
1690
+ const paddedRef = formatPaddedRef(boundary.numericId);
1691
+ if (state.hiddenOrphanRefs?.includes(paddedRef)) {
1692
+ const neighbor = snapToNeighborVisible(
1693
+ state,
1694
+ indexByMessageId,
1695
+ boundary.numericId,
1696
+ endpoint
1697
+ );
1698
+ if (neighbor !== null) {
1699
+ const dir = endpoint === "start" ? "the next visible message after it" : "the previous visible message before it";
1700
+ return {
1701
+ index: neighbor,
1702
+ snapped: `${label}="${boundary.raw}" is a hidden orphan compress call (no matching block) \u2014 snapped to ${dir} instead.`
1703
+ };
1704
+ }
1705
+ throw new BoundaryNotFoundError(
1706
+ "consumed",
1707
+ endpoint,
1708
+ `${label}="${boundary.raw}" is a hidden orphan compress call with no adjacent visible message to anchor to \u2014 run acp_status for current refs.`
1709
+ );
1710
+ }
1675
1711
  throw new BoundaryNotFoundError(
1676
1712
  "consumed",
1677
1713
  endpoint,
@@ -1748,6 +1784,26 @@ function inheritedContentIds(state, block) {
1748
1784
  }
1749
1785
  return ids;
1750
1786
  }
1787
+ function snapToNeighborVisible(state, indexByMessageId, refNumber, endpoint) {
1788
+ let bestIndex = null;
1789
+ let bestRef = null;
1790
+ for (const [rawId, index] of indexByMessageId) {
1791
+ const refText = state.messageRefs.byRaw[rawId];
1792
+ if (!refText || refText === BLOCKED_REF) continue;
1793
+ const ref = refToIndex(refText);
1794
+ if (ref === null) continue;
1795
+ if (endpoint === "start") {
1796
+ if (ref > refNumber && (bestRef === null || ref < bestRef)) {
1797
+ bestRef = ref;
1798
+ bestIndex = index;
1799
+ }
1800
+ } else if (ref < refNumber && (bestRef === null || ref > bestRef)) {
1801
+ bestRef = ref;
1802
+ bestIndex = index;
1803
+ }
1804
+ }
1805
+ return bestIndex;
1806
+ }
1751
1807
  function formatPaddedRef(index) {
1752
1808
  return `m${String(index).padStart(5, "0")}`;
1753
1809
  }
@@ -1962,14 +2018,26 @@ function hideConsumedCompressCalls(state, messages) {
1962
2018
  }
1963
2019
  }
1964
2020
  let hidden = 0;
2021
+ const hiddenOrphanRefs = [];
2022
+ const rememberHiddenRef = (message) => {
2023
+ if (message.toolCallId && allBlockCallIds.has(message.toolCallId)) {
2024
+ return;
2025
+ }
2026
+ const ref = state.messageRefs.byRaw[message.id];
2027
+ if (ref && ref !== BLOCKED_REF && !hiddenOrphanRefs.includes(ref)) {
2028
+ hiddenOrphanRefs.push(ref);
2029
+ }
2030
+ };
1965
2031
  const result = [];
1966
2032
  for (const message of messages) {
1967
2033
  if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
1968
2034
  hidden++;
2035
+ rememberHiddenRef(message);
1969
2036
  continue;
1970
2037
  }
1971
2038
  if (message.contentType === "tool-result" && message.toolCallId && hiddenCallIds.has(message.toolCallId)) {
1972
2039
  hidden++;
2040
+ rememberHiddenRef(message);
1973
2041
  continue;
1974
2042
  }
1975
2043
  if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
@@ -1989,7 +2057,7 @@ function hideConsumedCompressCalls(state, messages) {
1989
2057
  }
1990
2058
  result.push(message);
1991
2059
  }
1992
- return { messages: result, hidden };
2060
+ return { messages: result, hidden, hiddenOrphanRefs };
1993
2061
  }
1994
2062
 
1995
2063
  // src/absorb.ts
@@ -4282,6 +4350,7 @@ function createCore(ports = {}) {
4282
4350
  let tokensCompressed = 0;
4283
4351
  const errors = [];
4284
4352
  const warnings = [];
4353
+ const notes = [];
4285
4354
  const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(
4286
4355
  input.messages,
4287
4356
  input.state,
@@ -4382,7 +4451,28 @@ function createCore(ports = {}) {
4382
4451
  const danglingRefs = consumedRanges.flatMap(
4383
4452
  (spec) => danglingMessageRefs(state, input.messages, spec)
4384
4453
  );
4385
- const gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref is unknown to this session. Refs are per-session snapshots, assigned once when a message is first rendered; no compress reassigns them, so unknown refs cannot come from an earlier compress in this session. They come from a different generation: a previous session instance (switching model or upstream mid-conversation starts a fresh session whose refs restart at m00001), the generation before a native-compaction rebase (which also resets refs to m00001), or a typo. ${diagnostics} Run acp_status, then call the compress tool again using only the refs it reports.` : consumedRanges.length > 0 ? danglingRefs.length > 0 ? `Requested range(s) cannot be anchored (e.g. ${firstConsumed.startRef}..${firstConsumed.endRef}) \u2014 the refs exist in this session's ref map, but the messages they point to are no longer in the visible context and no active block covers them: the message content changed (or the message was filtered out of the view) and now carries a new ref, leaving your old refs dangling. ${diagnostics} Run acp_status, then call the compress tool again using only the refs it reports.` : `Requested range(s) already compressed (e.g. ${firstConsumed.startRef}..${firstConsumed.endRef}) \u2014 ${coverDetail}. Nothing new to compress in that window. ${diagnostics} Continue the task, or run acp_status and target one of the CURRENT compressible ranges it reports.${tierActionHint(input.config, state)}` : `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.`;
4454
+ let gateMessage = resolvableCount === 0 && consumedRanges.length === 0 && unknownCount > 0 ? `None of the ${input.ranges.length} requested range(s) resolved \u2014 every ref is unknown to this session. Refs are per-session snapshots, assigned once when a message is first rendered; no compress reassigns them, so unknown refs cannot come from an earlier compress in this session. They come from a different generation: a previous session instance (switching model or upstream mid-conversation starts a fresh session whose refs restart at m00001), the generation before a native-compaction rebase (which also resets refs to m00001), or a typo. ${diagnostics} Run acp_status, then call the compress tool again using only the refs it reports.` : consumedRanges.length > 0 ? danglingRefs.length > 0 ? `Requested range(s) cannot be anchored (e.g. ${firstConsumed.startRef}..${firstConsumed.endRef}) \u2014 the refs exist in this session's ref map, but the messages they point to are no longer in the visible context and no active block covers them: the message content changed (or the message was filtered out of the view) and now carries a new ref, leaving your old refs dangling. ${diagnostics} Run acp_status, then call the compress tool again using only the refs it reports.` : `Requested range(s) already compressed (e.g. ${firstConsumed.startRef}..${firstConsumed.endRef}) \u2014 ${coverDetail}. Nothing new to compress in that window. ${diagnostics} Continue the task, or run acp_status and target one of the CURRENT compressible ranges it reports.${tierActionHint(input.config, state)}` : countedRanges > 0 ? `Total compressible content too small (${totalRangeChars} chars across ${countedRanges} range(s), min ${input.config.compress.minCompressRange}). Combine more messages into your range(s) to meet the threshold.` : null;
4455
+ if (gateMessage === null) {
4456
+ return {
4457
+ state: input.state,
4458
+ result: {
4459
+ blocksCreated: 0,
4460
+ tokensCompressed: 0,
4461
+ errors: [...classificationErrors],
4462
+ warnings: []
4463
+ }
4464
+ };
4465
+ }
4466
+ const reversalNotes = [];
4467
+ for (const [spec, resolution] of classifications) {
4468
+ if (resolution.status === "ok" && !skipSpecs.has(spec)) {
4469
+ const note = resolution.resolved.reversedNote;
4470
+ if (note) reversalNotes.push(note);
4471
+ }
4472
+ }
4473
+ if (reversalNotes.length > 0) {
4474
+ gateMessage += ` ${reversalNotes.join(" ")}`;
4475
+ }
4386
4476
  return {
4387
4477
  state: input.state,
4388
4478
  result: {
@@ -4409,6 +4499,8 @@ function createCore(ports = {}) {
4409
4499
  continue;
4410
4500
  }
4411
4501
  warnings.push(...resolution.resolved.snappedBoundaries);
4502
+ const note = resolution.resolved.reversedNote;
4503
+ if (note) notes.push(note);
4412
4504
  try {
4413
4505
  const outcome = applySingleRange({
4414
4506
  spec,
@@ -4442,7 +4534,13 @@ function createCore(ports = {}) {
4442
4534
  }
4443
4535
  return {
4444
4536
  state,
4445
- result: { blocksCreated, tokensCompressed, errors, warnings }
4537
+ result: {
4538
+ blocksCreated,
4539
+ tokensCompressed,
4540
+ errors,
4541
+ warnings,
4542
+ ...notes.length > 0 ? { notes } : {}
4543
+ }
4446
4544
  };
4447
4545
  }
4448
4546
  function processTurn(input) {
@@ -4626,7 +4724,11 @@ var hideCompressCallsNode = {
4626
4724
  name: "hide-compress-calls",
4627
4725
  run(io) {
4628
4726
  const hidden = hideConsumedCompressCalls(io.state, io.messages);
4629
- return { ...io, messages: hidden.messages };
4727
+ return {
4728
+ ...io,
4729
+ messages: hidden.messages,
4730
+ state: { ...io.state, hiddenOrphanRefs: hidden.hiddenOrphanRefs }
4731
+ };
4630
4732
  }
4631
4733
  };
4632
4734
  var recommendNode = {
@@ -5258,7 +5360,8 @@ function cloneState(state) {
5258
5360
  nextRuleId: state.nextRuleId,
5259
5361
  terminalStreak: state.terminalStreak,
5260
5362
  nextBlockId: state.nextBlockId,
5261
- nextRunId: state.nextRunId
5363
+ nextRunId: state.nextRunId,
5364
+ hiddenOrphanRefs: state.hiddenOrphanRefs ? [...state.hiddenOrphanRefs] : void 0
5262
5365
  };
5263
5366
  }
5264
5367
  function scoreRelevance(block, terms) {
@@ -5289,7 +5392,7 @@ import { readFileSync, readdirSync } from "fs";
5289
5392
  import * as path from "path";
5290
5393
  var PROMPT_RULE_KEYS = ["compressPhilosophy", "howToCompressRules", "tier2DistillRules", "tier3CondenseRules"];
5291
5394
  var COMPRESS_SECTION_KEYS = ["acpTags", "tools", "summariesInContext", "textProtocol", "textTools", "functionTools"];
5292
- var NUDGE_SECTION_KEYS = ["efficiencyNote", "emergencyHeader", "t2Guidance", "t3Guidance"];
5395
+ var NUDGE_SECTION_KEYS = ["efficiencyNote", "pressureHeader", "emergencyHeader", "t2Guidance", "t3Guidance"];
5293
5396
  function isValidPackName(name) {
5294
5397
  return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name) && !name.includes("..");
5295
5398
  }
@@ -5693,14 +5796,16 @@ function tierLabel(block) {
5693
5796
  }
5694
5797
  function tierBreakdown(blocks, countTokens) {
5695
5798
  const tierTokens = {};
5799
+ const tierCounts = {};
5696
5800
  for (const block of blocks) {
5697
5801
  tierTokens[block.tier] = (tierTokens[block.tier] ?? 0) + summaryTokensOf(block, countTokens);
5802
+ tierCounts[block.tier] = (tierCounts[block.tier] ?? 0) + 1;
5698
5803
  }
5699
5804
  const tiers = Object.keys(tierTokens).map(Number);
5700
5805
  if (tiers.length <= 1) return null;
5701
5806
  const parts = [];
5702
5807
  for (const tier of [1, 2, 3]) {
5703
- if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])}`);
5808
+ if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])} (${tierCounts[tier]} blocks)`);
5704
5809
  }
5705
5810
  return parts.join(" | ");
5706
5811
  }
@@ -5807,6 +5912,11 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
5807
5912
  ` ${block.blockId} (${tierLabel(block)}) ${formatTokens2(eff)}\u2192${formatTokens2(summaryTokensOf(block, countTokens))} ${block.effectiveMessageIds.length} msgs "${topic}"`
5808
5913
  );
5809
5914
  }
5915
+ if (blocks.length > limit) {
5916
+ lines.push(
5917
+ ` ... and ${blocks.length - limit} more blocks not shown (scope:"compressed", limit:${blocks.length} for full list)`
5918
+ );
5919
+ }
5810
5920
  }
5811
5921
  lines.push("");
5812
5922
  lines.push(
@@ -7489,7 +7599,11 @@ function build(text) {
7489
7599
  }
7490
7600
  function docFeatures(text) {
7491
7601
  const hit = cache.get(text);
7492
- if (hit) return hit;
7602
+ if (hit) {
7603
+ cache.delete(text);
7604
+ cache.set(text, hit);
7605
+ return hit;
7606
+ }
7493
7607
  const f = build(text);
7494
7608
  if (text.length > 0 && text.length <= capChars) {
7495
7609
  while (cachedChars + text.length > capChars && cache.size > 0) {
@@ -7600,8 +7714,8 @@ var hybridAlgorithm = {
7600
7714
  score(docs, query) {
7601
7715
  const bm = bm25Algorithm.score(docs, query);
7602
7716
  const fz = fuzzyAlgorithm.score(docs, query);
7603
- const maxBm = Math.max(...bm.map((r) => r.score), 1e-9);
7604
- const maxFz = Math.max(...fz.map((r) => r.score), 1e-9);
7717
+ const maxBm = bm.reduce((m, r) => r.score > m ? r.score : m, 1e-9);
7718
+ const maxFz = fz.reduce((m, r) => r.score > m ? r.score : m, 1e-9);
7605
7719
  const bmMap = new Map(bm.map((r) => [r.ref, r.score / maxBm]));
7606
7720
  const fzMap = new Map(fz.map((r) => [r.ref, r.score / maxFz]));
7607
7721
  return docs.map((d) => ({
@@ -7965,6 +8079,7 @@ export {
7965
8079
  applySectionOverrides,
7966
8080
  applySteeringToPrompt,
7967
8081
  assignRefs,
8082
+ baseIdOf,
7968
8083
  blockById,
7969
8084
  blockDocs,
7970
8085
  blockVisibleInRange,
@@ -8041,6 +8156,7 @@ export {
8041
8156
  imageShrinksForRef,
8042
8157
  indexToRef,
8043
8158
  isAbsorbCandidate,
8159
+ isCovered,
8044
8160
  isImageFullRestored,
8045
8161
  isMessageLatestProtected,
8046
8162
  isMessageProtected,