acp-kernel 0.0.89 → 0.0.90-pr.420.271
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/DESIGN.md +16 -0
- package/dist/boundaries.d.ts +1 -0
- package/dist/boundaries.d.ts.map +1 -1
- package/dist/compress.d.ts.map +1 -1
- package/dist/decompress.d.ts +25 -0
- package/dist/decompress.d.ts.map +1 -1
- package/dist/hide-consumed.d.ts +4 -0
- package/dist/hide-consumed.d.ts.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +519 -215
- package/dist/index.js.map +1 -1
- package/dist/message-kind.d.ts +11 -0
- package/dist/message-kind.d.ts.map +1 -0
- package/dist/persist/index.js +43 -4
- package/dist/persist/index.js.map +1 -1
- package/dist/persist/store.d.ts +13 -1
- package/dist/persist/store.d.ts.map +1 -1
- package/dist/protected.d.ts.map +1 -1
- package/dist/prune.d.ts +15 -0
- package/dist/prune.d.ts.map +1 -1
- package/dist/recommend.d.ts +0 -1
- package/dist/recommend.d.ts.map +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/search/algorithms/hybrid.d.ts.map +1 -1
- package/dist/search/doc-cache.d.ts +34 -10
- package/dist/search/doc-cache.d.ts.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wire/anthropic.d.ts +1 -1
- package/dist/wire/anthropic.d.ts.map +1 -1
- package/dist/wire/index.js +8 -2
- package/dist/wire/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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
|
|
133
|
+
const baseIndexById = /* @__PURE__ */ new Map();
|
|
125
134
|
const summaryIndexById = /* @__PURE__ */ new Map();
|
|
126
135
|
messages.forEach((message, index) => {
|
|
127
|
-
|
|
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,
|
|
143
|
+
const anchors = inject ? collectSummaryAnchors(state, baseIndexById, summaryIndexById) : [];
|
|
132
144
|
return stripOrphanedReasoning(
|
|
133
145
|
stripOrphanedToolResults(
|
|
134
146
|
stripOrphanedToolCalls(
|
|
135
|
-
rebuildMessages(messages,
|
|
147
|
+
rebuildMessages(messages, coveredBases, firstUserIndex, anchors)
|
|
136
148
|
)
|
|
137
149
|
)
|
|
138
150
|
);
|
|
139
151
|
}
|
|
140
|
-
function collectSummaryAnchors(state,
|
|
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 =
|
|
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,
|
|
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 (
|
|
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) =>
|
|
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);
|
|
@@ -1248,7 +1262,12 @@ function hasMediaPayload(msg) {
|
|
|
1248
1262
|
if (m.rawOpenaiContent != null) return true;
|
|
1249
1263
|
if (Array.isArray(m.rawOpenaiContentParts) && m.rawOpenaiContentParts.length > 0)
|
|
1250
1264
|
return true;
|
|
1251
|
-
|
|
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
|
+
}
|
|
1252
1271
|
const item = m.rawResponsesItem;
|
|
1253
1272
|
if (isObjWith(item, "type", "input_image")) return true;
|
|
1254
1273
|
if (item && typeof item === "object") {
|
|
@@ -1612,8 +1631,10 @@ function resolveBoundaries(input) {
|
|
|
1612
1631
|
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
1613
1632
|
let startIndex = startAnchor.index;
|
|
1614
1633
|
let endIndex = endAnchor.index;
|
|
1634
|
+
let reversedNote;
|
|
1615
1635
|
if (startIndex > endIndex) {
|
|
1616
1636
|
[startIndex, endIndex] = [endIndex, startIndex];
|
|
1637
|
+
reversedNote = `note: refs were given reversed (${start.raw}\u2192${end.raw}), normalized to ${end.raw}..${start.raw}`;
|
|
1617
1638
|
}
|
|
1618
1639
|
const messageIds = [];
|
|
1619
1640
|
for (let index = startIndex; index <= endIndex; index++) {
|
|
@@ -1640,7 +1661,8 @@ function resolveBoundaries(input) {
|
|
|
1640
1661
|
nestedBlockIds,
|
|
1641
1662
|
boundaryKind,
|
|
1642
1663
|
protectedGaps,
|
|
1643
|
-
snappedBoundaries
|
|
1664
|
+
snappedBoundaries,
|
|
1665
|
+
reversedNote
|
|
1644
1666
|
};
|
|
1645
1667
|
}
|
|
1646
1668
|
function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
@@ -1665,6 +1687,27 @@ function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
|
1665
1687
|
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
|
|
1666
1688
|
};
|
|
1667
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
|
+
}
|
|
1668
1711
|
throw new BoundaryNotFoundError(
|
|
1669
1712
|
"consumed",
|
|
1670
1713
|
endpoint,
|
|
@@ -1741,6 +1784,26 @@ function inheritedContentIds(state, block) {
|
|
|
1741
1784
|
}
|
|
1742
1785
|
return ids;
|
|
1743
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
|
+
}
|
|
1744
1807
|
function formatPaddedRef(index) {
|
|
1745
1808
|
return `m${String(index).padStart(5, "0")}`;
|
|
1746
1809
|
}
|
|
@@ -1771,6 +1834,214 @@ function earliestIndexOfIds(ids, indexByMessageId) {
|
|
|
1771
1834
|
return earliest;
|
|
1772
1835
|
}
|
|
1773
1836
|
|
|
1837
|
+
// src/block-map.ts
|
|
1838
|
+
function refNum(ref) {
|
|
1839
|
+
const m = ref.match(/\d+/);
|
|
1840
|
+
return m ? parseInt(m[0], 10) : 0;
|
|
1841
|
+
}
|
|
1842
|
+
var M_REF = /^m\d+$/;
|
|
1843
|
+
function resolveBlockSpan(block, byRaw) {
|
|
1844
|
+
if (block.startRef && block.endRef && M_REF.test(block.startRef) && M_REF.test(block.endRef)) {
|
|
1845
|
+
return { startRef: block.startRef, endRef: block.endRef };
|
|
1846
|
+
}
|
|
1847
|
+
const refs = block.effectiveMessageIds.map((id) => byRaw[id]).filter((r) => typeof r === "string" && r !== "BLOCKED");
|
|
1848
|
+
if (refs.length === 0) return null;
|
|
1849
|
+
const sorted = [...refs].sort((a, b) => refNum(a) - refNum(b));
|
|
1850
|
+
return { startRef: sorted[0], endRef: sorted[sorted.length - 1] };
|
|
1851
|
+
}
|
|
1852
|
+
function activeBlockSpans(state) {
|
|
1853
|
+
const spans = [];
|
|
1854
|
+
for (const block of state.blocks) {
|
|
1855
|
+
if (!block.active) continue;
|
|
1856
|
+
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
1857
|
+
if (!span) continue;
|
|
1858
|
+
spans.push({ blockId: block.blockId, tier: block.tier, ...span });
|
|
1859
|
+
}
|
|
1860
|
+
return spans;
|
|
1861
|
+
}
|
|
1862
|
+
function formatCreatedBlocks(state, newBlocks) {
|
|
1863
|
+
const parts = [];
|
|
1864
|
+
for (const block of newBlocks) {
|
|
1865
|
+
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
1866
|
+
parts.push(
|
|
1867
|
+
span ? `${block.blockId}=${span.startRef}\u2013${span.endRef}` : block.blockId
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
return parts.length > 0 ? `blocks: ${parts.join(", ")}` : "";
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
// src/decompress.ts
|
|
1874
|
+
function parseBlockIdArg(arg) {
|
|
1875
|
+
const normalized = arg.trim().toLowerCase();
|
|
1876
|
+
const refMatch = /^b0*(\d+)$/.exec(normalized);
|
|
1877
|
+
if (refMatch && refMatch[1] !== void 0) return `b${refMatch[1]}`;
|
|
1878
|
+
const numMatch = /^(\d+)$/.exec(normalized);
|
|
1879
|
+
if (numMatch && numMatch[1] !== void 0) return `b${numMatch[1]}`;
|
|
1880
|
+
return null;
|
|
1881
|
+
}
|
|
1882
|
+
function findBlocksOverlappingMessages(state, messageIds) {
|
|
1883
|
+
if (messageIds.size === 0) return [];
|
|
1884
|
+
const matched = [];
|
|
1885
|
+
for (const block of state.blocks) {
|
|
1886
|
+
if (!block.active) continue;
|
|
1887
|
+
if (block.effectiveMessageIds.some((id) => messageIds.has(id))) {
|
|
1888
|
+
matched.push(block);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return matched.sort((a, b) => numericPart(a.blockId) - numericPart(b.blockId));
|
|
1892
|
+
}
|
|
1893
|
+
function findActiveAncestor(state, blockId) {
|
|
1894
|
+
const start = state.blocks.find((b) => b.blockId === blockId);
|
|
1895
|
+
if (!start) return null;
|
|
1896
|
+
const queue = [...start.directBlockIds];
|
|
1897
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1898
|
+
while (queue.length > 0) {
|
|
1899
|
+
const currentId = queue.shift();
|
|
1900
|
+
if (visited.has(currentId)) continue;
|
|
1901
|
+
visited.add(currentId);
|
|
1902
|
+
const current = state.blocks.find((b) => b.blockId === currentId);
|
|
1903
|
+
if (!current) continue;
|
|
1904
|
+
if (current.active) return current.blockId;
|
|
1905
|
+
for (const ancestorId of current.directBlockIds) {
|
|
1906
|
+
if (!visited.has(ancestorId)) queue.push(ancestorId);
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
return null;
|
|
1910
|
+
}
|
|
1911
|
+
function deactivateBlock(state, blockIds, options = {}) {
|
|
1912
|
+
const targets = new Set(blockIds);
|
|
1913
|
+
const updated = state.blocks.map((block) => {
|
|
1914
|
+
if (!targets.has(block.blockId) || !block.active) return block;
|
|
1915
|
+
return {
|
|
1916
|
+
...block,
|
|
1917
|
+
active: false,
|
|
1918
|
+
durationMs: block.durationMs,
|
|
1919
|
+
createdAt: block.createdAt
|
|
1920
|
+
};
|
|
1921
|
+
});
|
|
1922
|
+
let final = updated;
|
|
1923
|
+
if (options.deep) {
|
|
1924
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1925
|
+
const queue = [];
|
|
1926
|
+
for (const id of blockIds) {
|
|
1927
|
+
const block = updated.find((b) => b.blockId === id);
|
|
1928
|
+
if (block) queue.push(...block.directBlockIds);
|
|
1929
|
+
}
|
|
1930
|
+
while (queue.length > 0) {
|
|
1931
|
+
const id = queue.shift();
|
|
1932
|
+
if (visited.has(id)) continue;
|
|
1933
|
+
visited.add(id);
|
|
1934
|
+
final = final.map((block) => {
|
|
1935
|
+
if (block.blockId !== id) return block;
|
|
1936
|
+
queue.push(...block.directBlockIds);
|
|
1937
|
+
return block.active ? { ...block, active: false } : block;
|
|
1938
|
+
});
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return { ...state, blocks: final };
|
|
1942
|
+
}
|
|
1943
|
+
function buildRestoredContentPreview(messages, beforeActiveMessageIds, state) {
|
|
1944
|
+
const restored = [];
|
|
1945
|
+
for (const message of messages) {
|
|
1946
|
+
if (!beforeActiveMessageIds.has(message.id)) continue;
|
|
1947
|
+
const stillCovered = state.blocks.some(
|
|
1948
|
+
(b) => b.active && b.effectiveMessageIds.includes(message.id)
|
|
1949
|
+
);
|
|
1950
|
+
if (!stillCovered) restored.push(message);
|
|
1951
|
+
}
|
|
1952
|
+
if (restored.length === 0) return { preview: "", restoredCount: 0 };
|
|
1953
|
+
const lines = [];
|
|
1954
|
+
let totalLength = 0;
|
|
1955
|
+
const MAX_PREVIEW = 2e3;
|
|
1956
|
+
const MAX_PER_MESSAGE = 200;
|
|
1957
|
+
for (const message of restored) {
|
|
1958
|
+
if (totalLength >= MAX_PREVIEW) break;
|
|
1959
|
+
const text = message.text ?? "";
|
|
1960
|
+
const truncated = text.length > MAX_PER_MESSAGE ? clampPrefix(text, MAX_PER_MESSAGE) + "..." : text;
|
|
1961
|
+
const label = message.toolName && message.contentType !== "text" ? `${message.toolName}: ${truncated}` : `[${message.role}] ${truncated}`;
|
|
1962
|
+
lines.push(label);
|
|
1963
|
+
totalLength += label.length + 1;
|
|
1964
|
+
}
|
|
1965
|
+
return { preview: lines.join("\n"), restoredCount: restored.length };
|
|
1966
|
+
}
|
|
1967
|
+
function collectBlockContent(state, block, messages, options = {}) {
|
|
1968
|
+
const full = options.full ?? false;
|
|
1969
|
+
const targetIds = new Set(block.effectiveMessageIds);
|
|
1970
|
+
if (full) {
|
|
1971
|
+
const msgs = messages.filter((m) => targetIds.has(m.id));
|
|
1972
|
+
if (msgs.length === 0) return { text: "", count: 0 };
|
|
1973
|
+
return { text: msgs.map(formatMessage).join("\n\n"), count: msgs.length };
|
|
1974
|
+
}
|
|
1975
|
+
const nestedChildren = [];
|
|
1976
|
+
const nestedCovered = /* @__PURE__ */ new Set();
|
|
1977
|
+
for (const childId of block.directBlockIds) {
|
|
1978
|
+
const child = state.blocks.find((b) => b.blockId === childId);
|
|
1979
|
+
if (!child?.active) continue;
|
|
1980
|
+
nestedChildren.push(child);
|
|
1981
|
+
for (const id of child.effectiveMessageIds) nestedCovered.add(id);
|
|
1982
|
+
}
|
|
1983
|
+
const parts = [];
|
|
1984
|
+
for (const child of nestedChildren) {
|
|
1985
|
+
const label = child.topic ? `${child.blockId}: ${child.topic}` : child.blockId;
|
|
1986
|
+
parts.push(`${SUMMARY_HEADER} \u2014 ${label}
|
|
1987
|
+
${child.summary}`);
|
|
1988
|
+
}
|
|
1989
|
+
let directCount = 0;
|
|
1990
|
+
for (const m of messages) {
|
|
1991
|
+
if (targetIds.has(m.id) && !nestedCovered.has(m.id)) {
|
|
1992
|
+
parts.push(formatMessage(m));
|
|
1993
|
+
directCount++;
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
const count = directCount + nestedChildren.length;
|
|
1997
|
+
if (count === 0) return { text: "", count: 0 };
|
|
1998
|
+
return { text: parts.join("\n\n"), count };
|
|
1999
|
+
}
|
|
2000
|
+
function formatMessage(message) {
|
|
2001
|
+
const text = message.text ?? "";
|
|
2002
|
+
if (message.toolName && message.contentType !== "text") {
|
|
2003
|
+
return `[${message.role} \u2022 ${message.toolName}]
|
|
2004
|
+
${text}`;
|
|
2005
|
+
}
|
|
2006
|
+
return `[${message.role}]
|
|
2007
|
+
${text}`;
|
|
2008
|
+
}
|
|
2009
|
+
function numericPart(blockId) {
|
|
2010
|
+
const match = /^b(\d+)$/.exec(blockId);
|
|
2011
|
+
return match && match[1] !== void 0 ? Number(match[1]) : 0;
|
|
2012
|
+
}
|
|
2013
|
+
function markBlockRestoredInline(state, blockId) {
|
|
2014
|
+
const existing = state.blocks.find((b) => b.blockId === blockId);
|
|
2015
|
+
if (!existing) return { state, result: null };
|
|
2016
|
+
const blocks = state.blocks.map(
|
|
2017
|
+
(block) => block.blockId === blockId ? { ...block, restoredInline: true } : block
|
|
2018
|
+
);
|
|
2019
|
+
const span = resolveBlockSpan(existing, state.messageRefs.byRaw);
|
|
2020
|
+
const result = {
|
|
2021
|
+
restored: true,
|
|
2022
|
+
blockId,
|
|
2023
|
+
...span ? { restoredStartRef: span.startRef, restoredEndRef: span.endRef } : {}
|
|
2024
|
+
};
|
|
2025
|
+
return { state: { ...state, blocks }, result };
|
|
2026
|
+
}
|
|
2027
|
+
function activeAncestorIds(state, blockId) {
|
|
2028
|
+
const out = [];
|
|
2029
|
+
const visited = /* @__PURE__ */ new Set([blockId]);
|
|
2030
|
+
let frontier = [blockId];
|
|
2031
|
+
while (frontier.length > 0) {
|
|
2032
|
+
const next = [];
|
|
2033
|
+
for (const block of state.blocks) {
|
|
2034
|
+
if (visited.has(block.blockId)) continue;
|
|
2035
|
+
if (!block.directBlockIds.some((id) => frontier.includes(id))) continue;
|
|
2036
|
+
visited.add(block.blockId);
|
|
2037
|
+
if (block.active) out.push(block.blockId);
|
|
2038
|
+
next.push(block.blockId);
|
|
2039
|
+
}
|
|
2040
|
+
frontier = next;
|
|
2041
|
+
}
|
|
2042
|
+
return out;
|
|
2043
|
+
}
|
|
2044
|
+
|
|
1774
2045
|
// src/truncate-tools.ts
|
|
1775
2046
|
var TRUNCATION_MARKER = "[truncated for context space]";
|
|
1776
2047
|
var DEFAULTS = {
|
|
@@ -1955,14 +2226,26 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
1955
2226
|
}
|
|
1956
2227
|
}
|
|
1957
2228
|
let hidden = 0;
|
|
2229
|
+
const hiddenOrphanRefs = [];
|
|
2230
|
+
const rememberHiddenRef = (message) => {
|
|
2231
|
+
if (message.toolCallId && allBlockCallIds.has(message.toolCallId)) {
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const ref = state.messageRefs.byRaw[message.id];
|
|
2235
|
+
if (ref && ref !== BLOCKED_REF && !hiddenOrphanRefs.includes(ref)) {
|
|
2236
|
+
hiddenOrphanRefs.push(ref);
|
|
2237
|
+
}
|
|
2238
|
+
};
|
|
1958
2239
|
const result = [];
|
|
1959
2240
|
for (const message of messages) {
|
|
1960
2241
|
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
1961
2242
|
hidden++;
|
|
2243
|
+
rememberHiddenRef(message);
|
|
1962
2244
|
continue;
|
|
1963
2245
|
}
|
|
1964
2246
|
if (message.contentType === "tool-result" && message.toolCallId && hiddenCallIds.has(message.toolCallId)) {
|
|
1965
2247
|
hidden++;
|
|
2248
|
+
rememberHiddenRef(message);
|
|
1966
2249
|
continue;
|
|
1967
2250
|
}
|
|
1968
2251
|
if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
|
|
@@ -1982,7 +2265,7 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
1982
2265
|
}
|
|
1983
2266
|
result.push(message);
|
|
1984
2267
|
}
|
|
1985
|
-
return { messages: result, hidden };
|
|
2268
|
+
return { messages: result, hidden, hiddenOrphanRefs };
|
|
1986
2269
|
}
|
|
1987
2270
|
|
|
1988
2271
|
// src/absorb.ts
|
|
@@ -3632,42 +3915,6 @@ function applyMessageFilters(messages, config) {
|
|
|
3632
3915
|
return { messages: working, ...tally };
|
|
3633
3916
|
}
|
|
3634
3917
|
|
|
3635
|
-
// src/block-map.ts
|
|
3636
|
-
function refNum(ref) {
|
|
3637
|
-
const m = ref.match(/\d+/);
|
|
3638
|
-
return m ? parseInt(m[0], 10) : 0;
|
|
3639
|
-
}
|
|
3640
|
-
var M_REF = /^m\d+$/;
|
|
3641
|
-
function resolveBlockSpan(block, byRaw) {
|
|
3642
|
-
if (block.startRef && block.endRef && M_REF.test(block.startRef) && M_REF.test(block.endRef)) {
|
|
3643
|
-
return { startRef: block.startRef, endRef: block.endRef };
|
|
3644
|
-
}
|
|
3645
|
-
const refs = block.effectiveMessageIds.map((id) => byRaw[id]).filter((r) => typeof r === "string" && r !== "BLOCKED");
|
|
3646
|
-
if (refs.length === 0) return null;
|
|
3647
|
-
const sorted = [...refs].sort((a, b) => refNum(a) - refNum(b));
|
|
3648
|
-
return { startRef: sorted[0], endRef: sorted[sorted.length - 1] };
|
|
3649
|
-
}
|
|
3650
|
-
function activeBlockSpans(state) {
|
|
3651
|
-
const spans = [];
|
|
3652
|
-
for (const block of state.blocks) {
|
|
3653
|
-
if (!block.active) continue;
|
|
3654
|
-
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
3655
|
-
if (!span) continue;
|
|
3656
|
-
spans.push({ blockId: block.blockId, tier: block.tier, ...span });
|
|
3657
|
-
}
|
|
3658
|
-
return spans;
|
|
3659
|
-
}
|
|
3660
|
-
function formatCreatedBlocks(state, newBlocks) {
|
|
3661
|
-
const parts = [];
|
|
3662
|
-
for (const block of newBlocks) {
|
|
3663
|
-
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
3664
|
-
parts.push(
|
|
3665
|
-
span ? `${block.blockId}=${span.startRef}\u2013${span.endRef}` : block.blockId
|
|
3666
|
-
);
|
|
3667
|
-
}
|
|
3668
|
-
return parts.length > 0 ? `blocks: ${parts.join(", ")}` : "";
|
|
3669
|
-
}
|
|
3670
|
-
|
|
3671
3918
|
// src/render-refs.ts
|
|
3672
3919
|
function formatTokens(tokens) {
|
|
3673
3920
|
if (tokens < 1e3) return String(tokens);
|
|
@@ -3740,6 +3987,11 @@ function createRenderRefsNode(strategy) {
|
|
|
3740
3987
|
}
|
|
3741
3988
|
var renderRefsNode = createRenderRefsNode("all");
|
|
3742
3989
|
|
|
3990
|
+
// src/message-kind.ts
|
|
3991
|
+
function isToolMessage(message) {
|
|
3992
|
+
return message.contentType === "tool-call" || message.contentType === "tool-result";
|
|
3993
|
+
}
|
|
3994
|
+
|
|
3743
3995
|
// src/tool-pairs.ts
|
|
3744
3996
|
function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan = 20) {
|
|
3745
3997
|
const callIdsInRange = /* @__PURE__ */ new Set();
|
|
@@ -3944,9 +4196,6 @@ function computeIntegrityWithdrawals(messages, foldedIds) {
|
|
|
3944
4196
|
function estimateTextTokens(text) {
|
|
3945
4197
|
return Math.ceil(text.length / 4);
|
|
3946
4198
|
}
|
|
3947
|
-
function isToolMessage(message) {
|
|
3948
|
-
return message.contentType === "tool-call" || message.contentType === "tool-result";
|
|
3949
|
-
}
|
|
3950
4199
|
function isSyntheticOrPruned(message, state) {
|
|
3951
4200
|
if (message.text?.startsWith("[Compressed conversation section]")) return true;
|
|
3952
4201
|
for (const block of state.blocks) {
|
|
@@ -4266,6 +4515,79 @@ function tierActionHint(config, state) {
|
|
|
4266
4515
|
}
|
|
4267
4516
|
return "";
|
|
4268
4517
|
}
|
|
4518
|
+
function requestedRefoldSpan(state, spec) {
|
|
4519
|
+
const nums = [];
|
|
4520
|
+
for (const ref of [spec.startRef, spec.endRef]) {
|
|
4521
|
+
const parsed = parseBoundary(ref);
|
|
4522
|
+
if (!parsed) return null;
|
|
4523
|
+
if (parsed.kind === "message") {
|
|
4524
|
+
nums.push(parsed.numericId);
|
|
4525
|
+
continue;
|
|
4526
|
+
}
|
|
4527
|
+
const block = blockById(state, `b${parsed.numericId}`);
|
|
4528
|
+
if (!block) return null;
|
|
4529
|
+
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
4530
|
+
if (!span) return null;
|
|
4531
|
+
const lo = parseBoundary(span.startRef);
|
|
4532
|
+
const hi = parseBoundary(span.endRef);
|
|
4533
|
+
if (!lo || !hi) return null;
|
|
4534
|
+
nums.push(lo.numericId, hi.numericId);
|
|
4535
|
+
}
|
|
4536
|
+
return { lo: Math.min(...nums), hi: Math.max(...nums) };
|
|
4537
|
+
}
|
|
4538
|
+
function evaluateRefold(state, spec) {
|
|
4539
|
+
const span = requestedRefoldSpan(state, spec);
|
|
4540
|
+
if (!span) return { kind: "blocked", reasons: [] };
|
|
4541
|
+
const reasons = [];
|
|
4542
|
+
const blocks = [];
|
|
4543
|
+
for (const block of state.blocks) {
|
|
4544
|
+
if (!block.active) continue;
|
|
4545
|
+
const resolved = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
4546
|
+
if (!resolved) {
|
|
4547
|
+
reasons.push(`partially covered ${block.blockId}`);
|
|
4548
|
+
continue;
|
|
4549
|
+
}
|
|
4550
|
+
const ownLo = parseBoundary(resolved.startRef)?.numericId;
|
|
4551
|
+
const ownHi = parseBoundary(resolved.endRef)?.numericId;
|
|
4552
|
+
if (ownLo === void 0 || ownHi === void 0) continue;
|
|
4553
|
+
if (ownHi < span.lo || ownLo > span.hi) continue;
|
|
4554
|
+
if (!block.restoredInline) {
|
|
4555
|
+
reasons.push(`blocked by ${block.blockId} (not restored)`);
|
|
4556
|
+
continue;
|
|
4557
|
+
}
|
|
4558
|
+
if (ownLo < span.lo || ownHi > span.hi) {
|
|
4559
|
+
reasons.push(`partially covered ${block.blockId}`);
|
|
4560
|
+
continue;
|
|
4561
|
+
}
|
|
4562
|
+
const blockers = activeAncestorIds(state, block.blockId).filter(
|
|
4563
|
+
(ancestorId) => !blockById(state, ancestorId)?.restoredInline
|
|
4564
|
+
);
|
|
4565
|
+
if (blockers.length > 0) {
|
|
4566
|
+
for (const id of blockers)
|
|
4567
|
+
reasons.push(`blocked by ${id} (not restored)`);
|
|
4568
|
+
continue;
|
|
4569
|
+
}
|
|
4570
|
+
blocks.push(block);
|
|
4571
|
+
}
|
|
4572
|
+
const uniqueReasons = [...new Set(reasons)];
|
|
4573
|
+
if (uniqueReasons.length > 0)
|
|
4574
|
+
return { kind: "blocked", reasons: uniqueReasons };
|
|
4575
|
+
if (blocks.length > 0) return { kind: "refold", blocks };
|
|
4576
|
+
return { kind: "blocked", reasons: [] };
|
|
4577
|
+
}
|
|
4578
|
+
function applyRefolds(input) {
|
|
4579
|
+
validateSummaryLength(input.spec, input.config.compress);
|
|
4580
|
+
const targets = new Set(input.blockIds);
|
|
4581
|
+
input.state.blocks = input.state.blocks.map(
|
|
4582
|
+
(block) => targets.has(block.blockId) ? {
|
|
4583
|
+
...block,
|
|
4584
|
+
summary: input.spec.summary,
|
|
4585
|
+
topic: input.spec.topic ?? block.topic,
|
|
4586
|
+
runId: input.runId,
|
|
4587
|
+
restoredInline: false
|
|
4588
|
+
} : block
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4269
4591
|
function createCore(ports = {}) {
|
|
4270
4592
|
const countTokens = ports.countTokens ?? defaultCountTokens;
|
|
4271
4593
|
function applyCompression(input) {
|
|
@@ -4275,6 +4597,7 @@ function createCore(ports = {}) {
|
|
|
4275
4597
|
let tokensCompressed = 0;
|
|
4276
4598
|
const errors = [];
|
|
4277
4599
|
const warnings = [];
|
|
4600
|
+
const notes = [];
|
|
4278
4601
|
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(
|
|
4279
4602
|
input.messages,
|
|
4280
4603
|
input.state,
|
|
@@ -4325,6 +4648,18 @@ function createCore(ports = {}) {
|
|
|
4325
4648
|
if (resolution.status === "ok") resolvableCount++;
|
|
4326
4649
|
else if (resolution.status === "unknown") unknownCount++;
|
|
4327
4650
|
}
|
|
4651
|
+
const refoldDecisions = /* @__PURE__ */ new Map();
|
|
4652
|
+
const isRefoldCandidate = (resolution) => resolution.status === "consumed" || resolution.status === "ok" && resolution.resolved.boundaryKind !== "block" && resolution.resolved.messageIds.every(
|
|
4653
|
+
(id) => preExistingCoverage.has(id)
|
|
4654
|
+
);
|
|
4655
|
+
for (const [spec, resolution] of classifications) {
|
|
4656
|
+
if (isRefoldCandidate(resolution)) {
|
|
4657
|
+
refoldDecisions.set(spec, evaluateRefold(state, spec));
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
const allRefold = input.ranges.length > 0 && unknownCount === 0 && [...classifications.entries()].every(
|
|
4661
|
+
([spec, resolution]) => isRefoldCandidate(resolution) && refoldDecisions.get(spec)?.kind === "refold"
|
|
4662
|
+
);
|
|
4328
4663
|
const rangeSpans = [];
|
|
4329
4664
|
for (const [spec, resolution] of classifications) {
|
|
4330
4665
|
if (resolution.status !== "ok") continue;
|
|
@@ -4363,7 +4698,7 @@ function createCore(ports = {}) {
|
|
|
4363
4698
|
totalRangeChars += msg?.text?.length ?? 0;
|
|
4364
4699
|
}
|
|
4365
4700
|
}
|
|
4366
|
-
if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
4701
|
+
if (!allRefold && !hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
4367
4702
|
const diagnostics = refGateDiagnostics(
|
|
4368
4703
|
state,
|
|
4369
4704
|
input.ranges.length,
|
|
@@ -4372,10 +4707,50 @@ function createCore(ports = {}) {
|
|
|
4372
4707
|
const firstConsumed = consumedRanges[0];
|
|
4373
4708
|
const covering = firstConsumed ? coveringBlockIds(state, firstConsumed) : [];
|
|
4374
4709
|
const coverDetail = covering.length > 0 ? `its content is already summarized in active block(s) ${covering.join(", ")}${covering.length === 1 ? ` \u2014 use search_context or decompress ${covering[0]} if you need details from it` : ""}` : `its refs no longer point to directly compressible content (stale block ref(s) distilled or consumed by higher-tier blocks)`;
|
|
4710
|
+
const refoldReasons = [
|
|
4711
|
+
...new Set(
|
|
4712
|
+
consumedRanges.flatMap((spec) => {
|
|
4713
|
+
const decision = refoldDecisions.get(spec);
|
|
4714
|
+
return decision?.kind === "blocked" ? decision.reasons : [];
|
|
4715
|
+
})
|
|
4716
|
+
)
|
|
4717
|
+
];
|
|
4718
|
+
const refoldSuffix = (reasons) => reasons.length > 0 ? ` Refold blocked: ${reasons.join("; ")}. Restore the affected block(s) inline (decompress with inline:true), then recompressing the same range updates them in place` : "";
|
|
4719
|
+
const refoldDetail = refoldSuffix(refoldReasons);
|
|
4720
|
+
const okBlockedReasons = [
|
|
4721
|
+
...new Set(
|
|
4722
|
+
[...classifications.entries()].flatMap(([spec, resolution]) => {
|
|
4723
|
+
if (skipSpecs.has(spec) || resolution.status !== "ok") return [];
|
|
4724
|
+
const decision = refoldDecisions.get(spec);
|
|
4725
|
+
return decision?.kind === "blocked" ? decision.reasons : [];
|
|
4726
|
+
})
|
|
4727
|
+
)
|
|
4728
|
+
];
|
|
4375
4729
|
const danglingRefs = consumedRanges.flatMap(
|
|
4376
4730
|
(spec) => danglingMessageRefs(state, input.messages, spec)
|
|
4377
4731
|
);
|
|
4378
|
-
|
|
4732
|
+
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}${refoldDetail}. 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.${refoldSuffix(okBlockedReasons)}` : null;
|
|
4733
|
+
if (gateMessage === null) {
|
|
4734
|
+
return {
|
|
4735
|
+
state: input.state,
|
|
4736
|
+
result: {
|
|
4737
|
+
blocksCreated: 0,
|
|
4738
|
+
tokensCompressed: 0,
|
|
4739
|
+
errors: [...classificationErrors],
|
|
4740
|
+
warnings: []
|
|
4741
|
+
}
|
|
4742
|
+
};
|
|
4743
|
+
}
|
|
4744
|
+
const reversalNotes = [];
|
|
4745
|
+
for (const [spec, resolution] of classifications) {
|
|
4746
|
+
if (resolution.status === "ok" && !skipSpecs.has(spec)) {
|
|
4747
|
+
const note = resolution.resolved.reversedNote;
|
|
4748
|
+
if (note) reversalNotes.push(note);
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
if (reversalNotes.length > 0) {
|
|
4752
|
+
gateMessage += ` ${reversalNotes.join(" ")}`;
|
|
4753
|
+
}
|
|
4379
4754
|
return {
|
|
4380
4755
|
state: input.state,
|
|
4381
4756
|
result: {
|
|
@@ -4392,8 +4767,30 @@ function createCore(ports = {}) {
|
|
|
4392
4767
|
const resolution = classifications.get(spec);
|
|
4393
4768
|
if (resolution === void 0) continue;
|
|
4394
4769
|
if (resolution.status === "consumed") {
|
|
4770
|
+
const decision = refoldDecisions.get(spec);
|
|
4771
|
+
if (decision?.kind === "refold") {
|
|
4772
|
+
try {
|
|
4773
|
+
applyRefolds({
|
|
4774
|
+
spec,
|
|
4775
|
+
state,
|
|
4776
|
+
runId,
|
|
4777
|
+
config: input.config,
|
|
4778
|
+
blockIds: decision.blocks.map((block) => block.blockId)
|
|
4779
|
+
});
|
|
4780
|
+
blocksCreated += decision.blocks.length;
|
|
4781
|
+
} catch (error) {
|
|
4782
|
+
errors.push(
|
|
4783
|
+
rangeError(
|
|
4784
|
+
spec,
|
|
4785
|
+
error instanceof Error ? error.message : String(error)
|
|
4786
|
+
)
|
|
4787
|
+
);
|
|
4788
|
+
}
|
|
4789
|
+
continue;
|
|
4790
|
+
}
|
|
4791
|
+
const reasons = decision?.kind === "blocked" ? decision.reasons : [];
|
|
4395
4792
|
warnings.push(
|
|
4396
|
-
`Skipped range (${spec.startRef}..${spec.endRef}) \u2014 already compressed (messages consumed by existing block(s)); nothing to compress.`
|
|
4793
|
+
`Skipped range (${spec.startRef}..${spec.endRef}) \u2014 already compressed${reasons.length > 0 ? `: ${reasons.join("; ")}` : " (messages consumed by existing block(s))"}; nothing to compress.`
|
|
4397
4794
|
);
|
|
4398
4795
|
continue;
|
|
4399
4796
|
}
|
|
@@ -4402,6 +4799,8 @@ function createCore(ports = {}) {
|
|
|
4402
4799
|
continue;
|
|
4403
4800
|
}
|
|
4404
4801
|
warnings.push(...resolution.resolved.snappedBoundaries);
|
|
4802
|
+
const note = resolution.resolved.reversedNote;
|
|
4803
|
+
if (note) notes.push(note);
|
|
4405
4804
|
try {
|
|
4406
4805
|
const outcome = applySingleRange({
|
|
4407
4806
|
spec,
|
|
@@ -4413,7 +4812,7 @@ function createCore(ports = {}) {
|
|
|
4413
4812
|
countTokens,
|
|
4414
4813
|
preExistingCoverage
|
|
4415
4814
|
});
|
|
4416
|
-
blocksCreated
|
|
4815
|
+
blocksCreated += outcome.refolded ? outcome.refolded.length : 1;
|
|
4417
4816
|
tokensCompressed += outcome.tokens;
|
|
4418
4817
|
warnings.push(...outcome.warnings);
|
|
4419
4818
|
} catch (error) {
|
|
@@ -4435,7 +4834,13 @@ function createCore(ports = {}) {
|
|
|
4435
4834
|
}
|
|
4436
4835
|
return {
|
|
4437
4836
|
state,
|
|
4438
|
-
result: {
|
|
4837
|
+
result: {
|
|
4838
|
+
blocksCreated,
|
|
4839
|
+
tokensCompressed,
|
|
4840
|
+
errors,
|
|
4841
|
+
warnings,
|
|
4842
|
+
...notes.length > 0 ? { notes } : {}
|
|
4843
|
+
}
|
|
4439
4844
|
};
|
|
4440
4845
|
}
|
|
4441
4846
|
function processTurn(input) {
|
|
@@ -4619,7 +5024,11 @@ var hideCompressCallsNode = {
|
|
|
4619
5024
|
name: "hide-compress-calls",
|
|
4620
5025
|
run(io) {
|
|
4621
5026
|
const hidden = hideConsumedCompressCalls(io.state, io.messages);
|
|
4622
|
-
return {
|
|
5027
|
+
return {
|
|
5028
|
+
...io,
|
|
5029
|
+
messages: hidden.messages,
|
|
5030
|
+
state: { ...io.state, hiddenOrphanRefs: hidden.hiddenOrphanRefs }
|
|
5031
|
+
};
|
|
4623
5032
|
}
|
|
4624
5033
|
};
|
|
4625
5034
|
var recommendNode = {
|
|
@@ -4877,6 +5286,21 @@ function applySingleRange(input) {
|
|
|
4877
5286
|
}
|
|
4878
5287
|
}
|
|
4879
5288
|
if (!isBlockBoundary && filteredIds.length === 0 && consumedBlockIds.length > 0) {
|
|
5289
|
+
const decision = evaluateRefold(input.state, input.spec);
|
|
5290
|
+
if (decision.kind === "refold") {
|
|
5291
|
+
applyRefolds({
|
|
5292
|
+
spec: input.spec,
|
|
5293
|
+
state: input.state,
|
|
5294
|
+
runId: input.runId,
|
|
5295
|
+
config: input.config,
|
|
5296
|
+
blockIds: decision.blocks.map((block2) => block2.blockId)
|
|
5297
|
+
});
|
|
5298
|
+
return {
|
|
5299
|
+
tokens: 0,
|
|
5300
|
+
warnings,
|
|
5301
|
+
refolded: decision.blocks.map((block2) => block2.blockId)
|
|
5302
|
+
};
|
|
5303
|
+
}
|
|
4880
5304
|
const first = consumedBlockIds[0];
|
|
4881
5305
|
const last = consumedBlockIds[consumedBlockIds.length - 1];
|
|
4882
5306
|
throw new Error(
|
|
@@ -4957,7 +5381,15 @@ function applyPairBoundaryAdjustments(resolved, messages) {
|
|
|
4957
5381
|
}
|
|
4958
5382
|
function validateCompressionRange(input, directMessageIds, consumedBlockCount) {
|
|
4959
5383
|
const cfg = input.config.compress;
|
|
4960
|
-
|
|
5384
|
+
validateSummaryLength(input.spec, cfg);
|
|
5385
|
+
if (directMessageIds.length === 0 && consumedBlockCount === 0) {
|
|
5386
|
+
throw new Error(
|
|
5387
|
+
"Range contains no compressible messages \u2014 all are already covered by active blocks or protected."
|
|
5388
|
+
);
|
|
5389
|
+
}
|
|
5390
|
+
}
|
|
5391
|
+
function validateSummaryLength(spec, cfg) {
|
|
5392
|
+
const summary = spec.summary?.trim() ?? "";
|
|
4961
5393
|
if (summary.length === 0) {
|
|
4962
5394
|
throw new Error(
|
|
4963
5395
|
"Summary is empty \u2014 provide a meaningful summary of the compressed range."
|
|
@@ -4968,17 +5400,12 @@ function validateCompressionRange(input, directMessageIds, consumedBlockCount) {
|
|
|
4968
5400
|
`Summary too short (${summary.length} chars, min ${cfg.minSummaryLength}). The summary must capture the compressed range's key information.`
|
|
4969
5401
|
);
|
|
4970
5402
|
}
|
|
4971
|
-
const effectiveMax =
|
|
5403
|
+
const effectiveMax = spec.summaryMaxChars ?? cfg.maxSummaryLength;
|
|
4972
5404
|
if (effectiveMax > 0 && summary.length > effectiveMax) {
|
|
4973
5405
|
throw new Error(
|
|
4974
5406
|
`Summary too long (${summary.length} chars, max ${effectiveMax}). Strip noise \u2014 keep critical paths, decisions, errors, and code references. Or pass summaryMaxChars to increase the limit \u2014 don't lose critical info just to fit.`
|
|
4975
5407
|
);
|
|
4976
5408
|
}
|
|
4977
|
-
if (directMessageIds.length === 0 && consumedBlockCount === 0) {
|
|
4978
|
-
throw new Error(
|
|
4979
|
-
"Range contains no compressible messages \u2014 all are already covered by active blocks or protected."
|
|
4980
|
-
);
|
|
4981
|
-
}
|
|
4982
5409
|
}
|
|
4983
5410
|
function filterProtectedToolMessages(directMessageIds, messages, config) {
|
|
4984
5411
|
const protectedCallIds = /* @__PURE__ */ new Set();
|
|
@@ -5219,7 +5646,7 @@ function computeContextBreakdown(messages, total, growth, countTokens) {
|
|
|
5219
5646
|
const tokens = countMessageTokens(msg, count);
|
|
5220
5647
|
if (msg.text?.startsWith("[Compressed conversation section]")) {
|
|
5221
5648
|
summaries += tokens;
|
|
5222
|
-
} else if (msg
|
|
5649
|
+
} else if (isToolMessage(msg)) {
|
|
5223
5650
|
tool += tokens;
|
|
5224
5651
|
} else if (msg.role === "system") {
|
|
5225
5652
|
system += tokens;
|
|
@@ -5251,7 +5678,8 @@ function cloneState(state) {
|
|
|
5251
5678
|
nextRuleId: state.nextRuleId,
|
|
5252
5679
|
terminalStreak: state.terminalStreak,
|
|
5253
5680
|
nextBlockId: state.nextBlockId,
|
|
5254
|
-
nextRunId: state.nextRunId
|
|
5681
|
+
nextRunId: state.nextRunId,
|
|
5682
|
+
hiddenOrphanRefs: state.hiddenOrphanRefs ? [...state.hiddenOrphanRefs] : void 0
|
|
5255
5683
|
};
|
|
5256
5684
|
}
|
|
5257
5685
|
function scoreRelevance(block, terms) {
|
|
@@ -5521,147 +5949,6 @@ function defaultPackSources(opts) {
|
|
|
5521
5949
|
return sources;
|
|
5522
5950
|
}
|
|
5523
5951
|
|
|
5524
|
-
// src/decompress.ts
|
|
5525
|
-
function parseBlockIdArg(arg) {
|
|
5526
|
-
const normalized = arg.trim().toLowerCase();
|
|
5527
|
-
const refMatch = /^b0*(\d+)$/.exec(normalized);
|
|
5528
|
-
if (refMatch && refMatch[1] !== void 0) return `b${refMatch[1]}`;
|
|
5529
|
-
const numMatch = /^(\d+)$/.exec(normalized);
|
|
5530
|
-
if (numMatch && numMatch[1] !== void 0) return `b${numMatch[1]}`;
|
|
5531
|
-
return null;
|
|
5532
|
-
}
|
|
5533
|
-
function findBlocksOverlappingMessages(state, messageIds) {
|
|
5534
|
-
if (messageIds.size === 0) return [];
|
|
5535
|
-
const matched = [];
|
|
5536
|
-
for (const block of state.blocks) {
|
|
5537
|
-
if (!block.active) continue;
|
|
5538
|
-
if (block.effectiveMessageIds.some((id) => messageIds.has(id))) {
|
|
5539
|
-
matched.push(block);
|
|
5540
|
-
}
|
|
5541
|
-
}
|
|
5542
|
-
return matched.sort((a, b) => numericPart(a.blockId) - numericPart(b.blockId));
|
|
5543
|
-
}
|
|
5544
|
-
function findActiveAncestor(state, blockId) {
|
|
5545
|
-
const start = state.blocks.find((b) => b.blockId === blockId);
|
|
5546
|
-
if (!start) return null;
|
|
5547
|
-
const queue = [...start.directBlockIds];
|
|
5548
|
-
const visited = /* @__PURE__ */ new Set();
|
|
5549
|
-
while (queue.length > 0) {
|
|
5550
|
-
const currentId = queue.shift();
|
|
5551
|
-
if (visited.has(currentId)) continue;
|
|
5552
|
-
visited.add(currentId);
|
|
5553
|
-
const current = state.blocks.find((b) => b.blockId === currentId);
|
|
5554
|
-
if (!current) continue;
|
|
5555
|
-
if (current.active) return current.blockId;
|
|
5556
|
-
for (const ancestorId of current.directBlockIds) {
|
|
5557
|
-
if (!visited.has(ancestorId)) queue.push(ancestorId);
|
|
5558
|
-
}
|
|
5559
|
-
}
|
|
5560
|
-
return null;
|
|
5561
|
-
}
|
|
5562
|
-
function deactivateBlock(state, blockIds, options = {}) {
|
|
5563
|
-
const targets = new Set(blockIds);
|
|
5564
|
-
const updated = state.blocks.map((block) => {
|
|
5565
|
-
if (!targets.has(block.blockId) || !block.active) return block;
|
|
5566
|
-
return {
|
|
5567
|
-
...block,
|
|
5568
|
-
active: false,
|
|
5569
|
-
durationMs: block.durationMs,
|
|
5570
|
-
createdAt: block.createdAt
|
|
5571
|
-
};
|
|
5572
|
-
});
|
|
5573
|
-
let final = updated;
|
|
5574
|
-
if (options.deep) {
|
|
5575
|
-
const visited = /* @__PURE__ */ new Set();
|
|
5576
|
-
const queue = [];
|
|
5577
|
-
for (const id of blockIds) {
|
|
5578
|
-
const block = updated.find((b) => b.blockId === id);
|
|
5579
|
-
if (block) queue.push(...block.directBlockIds);
|
|
5580
|
-
}
|
|
5581
|
-
while (queue.length > 0) {
|
|
5582
|
-
const id = queue.shift();
|
|
5583
|
-
if (visited.has(id)) continue;
|
|
5584
|
-
visited.add(id);
|
|
5585
|
-
final = final.map((block) => {
|
|
5586
|
-
if (block.blockId !== id) return block;
|
|
5587
|
-
queue.push(...block.directBlockIds);
|
|
5588
|
-
return block.active ? { ...block, active: false } : block;
|
|
5589
|
-
});
|
|
5590
|
-
}
|
|
5591
|
-
}
|
|
5592
|
-
return { ...state, blocks: final };
|
|
5593
|
-
}
|
|
5594
|
-
function buildRestoredContentPreview(messages, beforeActiveMessageIds, state) {
|
|
5595
|
-
const restored = [];
|
|
5596
|
-
for (const message of messages) {
|
|
5597
|
-
if (!beforeActiveMessageIds.has(message.id)) continue;
|
|
5598
|
-
const stillCovered = state.blocks.some(
|
|
5599
|
-
(b) => b.active && b.effectiveMessageIds.includes(message.id)
|
|
5600
|
-
);
|
|
5601
|
-
if (!stillCovered) restored.push(message);
|
|
5602
|
-
}
|
|
5603
|
-
if (restored.length === 0) return { preview: "", restoredCount: 0 };
|
|
5604
|
-
const lines = [];
|
|
5605
|
-
let totalLength = 0;
|
|
5606
|
-
const MAX_PREVIEW = 2e3;
|
|
5607
|
-
const MAX_PER_MESSAGE = 200;
|
|
5608
|
-
for (const message of restored) {
|
|
5609
|
-
if (totalLength >= MAX_PREVIEW) break;
|
|
5610
|
-
const text = message.text ?? "";
|
|
5611
|
-
const truncated = text.length > MAX_PER_MESSAGE ? clampPrefix(text, MAX_PER_MESSAGE) + "..." : text;
|
|
5612
|
-
const label = message.toolName && message.contentType !== "text" ? `${message.toolName}: ${truncated}` : `[${message.role}] ${truncated}`;
|
|
5613
|
-
lines.push(label);
|
|
5614
|
-
totalLength += label.length + 1;
|
|
5615
|
-
}
|
|
5616
|
-
return { preview: lines.join("\n"), restoredCount: restored.length };
|
|
5617
|
-
}
|
|
5618
|
-
function collectBlockContent(state, block, messages, options = {}) {
|
|
5619
|
-
const full = options.full ?? false;
|
|
5620
|
-
const targetIds = new Set(block.effectiveMessageIds);
|
|
5621
|
-
if (full) {
|
|
5622
|
-
const msgs = messages.filter((m) => targetIds.has(m.id));
|
|
5623
|
-
if (msgs.length === 0) return { text: "", count: 0 };
|
|
5624
|
-
return { text: msgs.map(formatMessage).join("\n\n"), count: msgs.length };
|
|
5625
|
-
}
|
|
5626
|
-
const nestedChildren = [];
|
|
5627
|
-
const nestedCovered = /* @__PURE__ */ new Set();
|
|
5628
|
-
for (const childId of block.directBlockIds) {
|
|
5629
|
-
const child = state.blocks.find((b) => b.blockId === childId);
|
|
5630
|
-
if (!child?.active) continue;
|
|
5631
|
-
nestedChildren.push(child);
|
|
5632
|
-
for (const id of child.effectiveMessageIds) nestedCovered.add(id);
|
|
5633
|
-
}
|
|
5634
|
-
const parts = [];
|
|
5635
|
-
for (const child of nestedChildren) {
|
|
5636
|
-
const label = child.topic ? `${child.blockId}: ${child.topic}` : child.blockId;
|
|
5637
|
-
parts.push(`${SUMMARY_HEADER} \u2014 ${label}
|
|
5638
|
-
${child.summary}`);
|
|
5639
|
-
}
|
|
5640
|
-
let directCount = 0;
|
|
5641
|
-
for (const m of messages) {
|
|
5642
|
-
if (targetIds.has(m.id) && !nestedCovered.has(m.id)) {
|
|
5643
|
-
parts.push(formatMessage(m));
|
|
5644
|
-
directCount++;
|
|
5645
|
-
}
|
|
5646
|
-
}
|
|
5647
|
-
const count = directCount + nestedChildren.length;
|
|
5648
|
-
if (count === 0) return { text: "", count: 0 };
|
|
5649
|
-
return { text: parts.join("\n\n"), count };
|
|
5650
|
-
}
|
|
5651
|
-
function formatMessage(message) {
|
|
5652
|
-
const text = message.text ?? "";
|
|
5653
|
-
if (message.toolName && message.contentType !== "text") {
|
|
5654
|
-
return `[${message.role} \u2022 ${message.toolName}]
|
|
5655
|
-
${text}`;
|
|
5656
|
-
}
|
|
5657
|
-
return `[${message.role}]
|
|
5658
|
-
${text}`;
|
|
5659
|
-
}
|
|
5660
|
-
function numericPart(blockId) {
|
|
5661
|
-
const match = /^b(\d+)$/.exec(blockId);
|
|
5662
|
-
return match && match[1] !== void 0 ? Number(match[1]) : 0;
|
|
5663
|
-
}
|
|
5664
|
-
|
|
5665
5952
|
// src/report.ts
|
|
5666
5953
|
function formatTokens2(n) {
|
|
5667
5954
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -5686,14 +5973,16 @@ function tierLabel(block) {
|
|
|
5686
5973
|
}
|
|
5687
5974
|
function tierBreakdown(blocks, countTokens) {
|
|
5688
5975
|
const tierTokens = {};
|
|
5976
|
+
const tierCounts = {};
|
|
5689
5977
|
for (const block of blocks) {
|
|
5690
5978
|
tierTokens[block.tier] = (tierTokens[block.tier] ?? 0) + summaryTokensOf(block, countTokens);
|
|
5979
|
+
tierCounts[block.tier] = (tierCounts[block.tier] ?? 0) + 1;
|
|
5691
5980
|
}
|
|
5692
5981
|
const tiers = Object.keys(tierTokens).map(Number);
|
|
5693
5982
|
if (tiers.length <= 1) return null;
|
|
5694
5983
|
const parts = [];
|
|
5695
5984
|
for (const tier of [1, 2, 3]) {
|
|
5696
|
-
if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])}`);
|
|
5985
|
+
if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])} (${tierCounts[tier]} blocks)`);
|
|
5697
5986
|
}
|
|
5698
5987
|
return parts.join(" | ");
|
|
5699
5988
|
}
|
|
@@ -5719,8 +6008,9 @@ function collectVisible(messages, state, countTokens) {
|
|
|
5719
6008
|
const ref = refForRaw(state.messageRefs, message.id);
|
|
5720
6009
|
if (!ref) return;
|
|
5721
6010
|
const tokens = countMessageTokens(message, countTokens);
|
|
5722
|
-
const
|
|
5723
|
-
|
|
6011
|
+
const isTool = isToolMessage(message);
|
|
6012
|
+
const tool = isTool ? message.toolName ?? (message.toolCallId ? toolCallNames.get(message.toolCallId) : void 0) ?? "tool" : "text";
|
|
6013
|
+
if (tokens > 0) visible.push({ ref, tokens, tool, isTool, index });
|
|
5724
6014
|
});
|
|
5725
6015
|
return { visible, summaryTokens };
|
|
5726
6016
|
}
|
|
@@ -5763,8 +6053,8 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
|
|
|
5763
6053
|
toolTypeMap.set(message.tool, (toolTypeMap.get(message.tool) ?? 0) + message.tokens);
|
|
5764
6054
|
}
|
|
5765
6055
|
const topTool = [...toolTypeMap.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
5766
|
-
const totalTool = visible.filter((m) => m.
|
|
5767
|
-
const totalText = visible.filter((m) => m.
|
|
6056
|
+
const totalTool = visible.filter((m) => m.isTool).reduce((sum, m) => sum + m.tokens, 0);
|
|
6057
|
+
const totalText = visible.filter((m) => !m.isTool).reduce((sum, m) => sum + m.tokens, 0);
|
|
5768
6058
|
const total = summaryTokens + totalTool + totalText;
|
|
5769
6059
|
lines.push("CONTEXT BREAKDOWN");
|
|
5770
6060
|
lines.push(
|
|
@@ -5800,6 +6090,11 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
|
|
|
5800
6090
|
` ${block.blockId} (${tierLabel(block)}) ${formatTokens2(eff)}\u2192${formatTokens2(summaryTokensOf(block, countTokens))} ${block.effectiveMessageIds.length} msgs "${topic}"`
|
|
5801
6091
|
);
|
|
5802
6092
|
}
|
|
6093
|
+
if (blocks.length > limit) {
|
|
6094
|
+
lines.push(
|
|
6095
|
+
` ... and ${blocks.length - limit} more blocks not shown (scope:"compressed", limit:${blocks.length} for full list)`
|
|
6096
|
+
);
|
|
6097
|
+
}
|
|
5803
6098
|
}
|
|
5804
6099
|
lines.push("");
|
|
5805
6100
|
lines.push(
|
|
@@ -7482,7 +7777,11 @@ function build(text) {
|
|
|
7482
7777
|
}
|
|
7483
7778
|
function docFeatures(text) {
|
|
7484
7779
|
const hit = cache.get(text);
|
|
7485
|
-
if (hit)
|
|
7780
|
+
if (hit) {
|
|
7781
|
+
cache.delete(text);
|
|
7782
|
+
cache.set(text, hit);
|
|
7783
|
+
return hit;
|
|
7784
|
+
}
|
|
7486
7785
|
const f = build(text);
|
|
7487
7786
|
if (text.length > 0 && text.length <= capChars) {
|
|
7488
7787
|
while (cachedChars + text.length > capChars && cache.size > 0) {
|
|
@@ -7593,8 +7892,8 @@ var hybridAlgorithm = {
|
|
|
7593
7892
|
score(docs, query) {
|
|
7594
7893
|
const bm = bm25Algorithm.score(docs, query);
|
|
7595
7894
|
const fz = fuzzyAlgorithm.score(docs, query);
|
|
7596
|
-
const maxBm =
|
|
7597
|
-
const maxFz =
|
|
7895
|
+
const maxBm = bm.reduce((m, r) => r.score > m ? r.score : m, 1e-9);
|
|
7896
|
+
const maxFz = fz.reduce((m, r) => r.score > m ? r.score : m, 1e-9);
|
|
7598
7897
|
const bmMap = new Map(bm.map((r) => [r.ref, r.score / maxBm]));
|
|
7599
7898
|
const fzMap = new Map(fz.map((r) => [r.ref, r.score / maxFz]));
|
|
7600
7899
|
return docs.map((d) => ({
|
|
@@ -7941,6 +8240,7 @@ export {
|
|
|
7941
8240
|
TIER3_CONDENSE_RULES,
|
|
7942
8241
|
VERBOSITY_LEVELS,
|
|
7943
8242
|
VIABLE_RANGE_MIN_TOKENS,
|
|
8243
|
+
activeAncestorIds,
|
|
7944
8244
|
activeBlockSpans,
|
|
7945
8245
|
activeBlocks,
|
|
7946
8246
|
addRule,
|
|
@@ -7958,6 +8258,7 @@ export {
|
|
|
7958
8258
|
applySectionOverrides,
|
|
7959
8259
|
applySteeringToPrompt,
|
|
7960
8260
|
assignRefs,
|
|
8261
|
+
baseIdOf,
|
|
7961
8262
|
blockById,
|
|
7962
8263
|
blockDocs,
|
|
7963
8264
|
blockVisibleInRange,
|
|
@@ -8034,6 +8335,7 @@ export {
|
|
|
8034
8335
|
imageShrinksForRef,
|
|
8035
8336
|
indexToRef,
|
|
8036
8337
|
isAbsorbCandidate,
|
|
8338
|
+
isCovered,
|
|
8037
8339
|
isImageFullRestored,
|
|
8038
8340
|
isMessageLatestProtected,
|
|
8039
8341
|
isMessageProtected,
|
|
@@ -8041,6 +8343,7 @@ export {
|
|
|
8041
8343
|
isRetrievedMessage,
|
|
8042
8344
|
isStoredPlaceholderText,
|
|
8043
8345
|
isSummaryMessageId,
|
|
8346
|
+
isToolMessage,
|
|
8044
8347
|
isValidPackName,
|
|
8045
8348
|
jsonFoldPlugin,
|
|
8046
8349
|
leanPack,
|
|
@@ -8050,6 +8353,7 @@ export {
|
|
|
8050
8353
|
listSearchAlgorithms,
|
|
8051
8354
|
logSelectPlugin,
|
|
8052
8355
|
makeIO,
|
|
8356
|
+
markBlockRestoredInline,
|
|
8053
8357
|
matchSession,
|
|
8054
8358
|
matchToolPattern,
|
|
8055
8359
|
messageDocs,
|