acp-kernel 0.0.89 → 0.0.90-pr.422.272
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 +521 -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,10 @@ 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) {
|
|
298
|
+
if (typeof message.id === "string") presentBases.add(baseIdOf(message.id));
|
|
299
|
+
}
|
|
284
300
|
const deactivated = [];
|
|
285
301
|
const result = {
|
|
286
302
|
blocks: state.blocks.map((block) => ({
|
|
@@ -330,7 +346,7 @@ function syncBlocks(messages, state) {
|
|
|
330
346
|
continue;
|
|
331
347
|
}
|
|
332
348
|
block.active = true;
|
|
333
|
-
const stillPresent = block.effectiveMessageIds.some((id) =>
|
|
349
|
+
const stillPresent = block.effectiveMessageIds.some((id) => presentBases.has(baseIdOf(id))) || presentIds.has(summaryMessageId(block.blockId));
|
|
334
350
|
if (!stillPresent) {
|
|
335
351
|
block.active = false;
|
|
336
352
|
deactivated.push(block.blockId);
|
|
@@ -1248,7 +1264,12 @@ function hasMediaPayload(msg) {
|
|
|
1248
1264
|
if (m.rawOpenaiContent != null) return true;
|
|
1249
1265
|
if (Array.isArray(m.rawOpenaiContentParts) && m.rawOpenaiContentParts.length > 0)
|
|
1250
1266
|
return true;
|
|
1251
|
-
|
|
1267
|
+
const ab = m.rawAnthropicBlock;
|
|
1268
|
+
if (isObjWith(ab, "type", "image")) return true;
|
|
1269
|
+
if (isObjWith(ab, "type", "tool_result")) {
|
|
1270
|
+
const content = ab.content;
|
|
1271
|
+
if (Array.isArray(content)) return content.some((p) => !isObjWith(p, "type", "text"));
|
|
1272
|
+
}
|
|
1252
1273
|
const item = m.rawResponsesItem;
|
|
1253
1274
|
if (isObjWith(item, "type", "input_image")) return true;
|
|
1254
1275
|
if (item && typeof item === "object") {
|
|
@@ -1612,8 +1633,10 @@ function resolveBoundaries(input) {
|
|
|
1612
1633
|
if (endAnchor.snapped) snappedBoundaries.push(endAnchor.snapped);
|
|
1613
1634
|
let startIndex = startAnchor.index;
|
|
1614
1635
|
let endIndex = endAnchor.index;
|
|
1636
|
+
let reversedNote;
|
|
1615
1637
|
if (startIndex > endIndex) {
|
|
1616
1638
|
[startIndex, endIndex] = [endIndex, startIndex];
|
|
1639
|
+
reversedNote = `note: refs were given reversed (${start.raw}\u2192${end.raw}), normalized to ${end.raw}..${start.raw}`;
|
|
1617
1640
|
}
|
|
1618
1641
|
const messageIds = [];
|
|
1619
1642
|
for (let index = startIndex; index <= endIndex; index++) {
|
|
@@ -1640,7 +1663,8 @@ function resolveBoundaries(input) {
|
|
|
1640
1663
|
nestedBlockIds,
|
|
1641
1664
|
boundaryKind,
|
|
1642
1665
|
protectedGaps,
|
|
1643
|
-
snappedBoundaries
|
|
1666
|
+
snappedBoundaries,
|
|
1667
|
+
reversedNote
|
|
1644
1668
|
};
|
|
1645
1669
|
}
|
|
1646
1670
|
function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
@@ -1665,6 +1689,27 @@ function resolveAnchorIndex(boundary, state, indexByMessageId, endpoint) {
|
|
|
1665
1689
|
snapped: `${label}="${boundary.raw}" refers to a message already compressed into an active block \u2014 anchored to the active block covering it instead.`
|
|
1666
1690
|
};
|
|
1667
1691
|
}
|
|
1692
|
+
const paddedRef = formatPaddedRef(boundary.numericId);
|
|
1693
|
+
if (state.hiddenOrphanRefs?.includes(paddedRef)) {
|
|
1694
|
+
const neighbor = snapToNeighborVisible(
|
|
1695
|
+
state,
|
|
1696
|
+
indexByMessageId,
|
|
1697
|
+
boundary.numericId,
|
|
1698
|
+
endpoint
|
|
1699
|
+
);
|
|
1700
|
+
if (neighbor !== null) {
|
|
1701
|
+
const dir = endpoint === "start" ? "the next visible message after it" : "the previous visible message before it";
|
|
1702
|
+
return {
|
|
1703
|
+
index: neighbor,
|
|
1704
|
+
snapped: `${label}="${boundary.raw}" is a hidden orphan compress call (no matching block) \u2014 snapped to ${dir} instead.`
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
throw new BoundaryNotFoundError(
|
|
1708
|
+
"consumed",
|
|
1709
|
+
endpoint,
|
|
1710
|
+
`${label}="${boundary.raw}" is a hidden orphan compress call with no adjacent visible message to anchor to \u2014 run acp_status for current refs.`
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1668
1713
|
throw new BoundaryNotFoundError(
|
|
1669
1714
|
"consumed",
|
|
1670
1715
|
endpoint,
|
|
@@ -1741,6 +1786,26 @@ function inheritedContentIds(state, block) {
|
|
|
1741
1786
|
}
|
|
1742
1787
|
return ids;
|
|
1743
1788
|
}
|
|
1789
|
+
function snapToNeighborVisible(state, indexByMessageId, refNumber, endpoint) {
|
|
1790
|
+
let bestIndex = null;
|
|
1791
|
+
let bestRef = null;
|
|
1792
|
+
for (const [rawId, index] of indexByMessageId) {
|
|
1793
|
+
const refText = state.messageRefs.byRaw[rawId];
|
|
1794
|
+
if (!refText || refText === BLOCKED_REF) continue;
|
|
1795
|
+
const ref = refToIndex(refText);
|
|
1796
|
+
if (ref === null) continue;
|
|
1797
|
+
if (endpoint === "start") {
|
|
1798
|
+
if (ref > refNumber && (bestRef === null || ref < bestRef)) {
|
|
1799
|
+
bestRef = ref;
|
|
1800
|
+
bestIndex = index;
|
|
1801
|
+
}
|
|
1802
|
+
} else if (ref < refNumber && (bestRef === null || ref > bestRef)) {
|
|
1803
|
+
bestRef = ref;
|
|
1804
|
+
bestIndex = index;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
return bestIndex;
|
|
1808
|
+
}
|
|
1744
1809
|
function formatPaddedRef(index) {
|
|
1745
1810
|
return `m${String(index).padStart(5, "0")}`;
|
|
1746
1811
|
}
|
|
@@ -1771,6 +1836,214 @@ function earliestIndexOfIds(ids, indexByMessageId) {
|
|
|
1771
1836
|
return earliest;
|
|
1772
1837
|
}
|
|
1773
1838
|
|
|
1839
|
+
// src/block-map.ts
|
|
1840
|
+
function refNum(ref) {
|
|
1841
|
+
const m = ref.match(/\d+/);
|
|
1842
|
+
return m ? parseInt(m[0], 10) : 0;
|
|
1843
|
+
}
|
|
1844
|
+
var M_REF = /^m\d+$/;
|
|
1845
|
+
function resolveBlockSpan(block, byRaw) {
|
|
1846
|
+
if (block.startRef && block.endRef && M_REF.test(block.startRef) && M_REF.test(block.endRef)) {
|
|
1847
|
+
return { startRef: block.startRef, endRef: block.endRef };
|
|
1848
|
+
}
|
|
1849
|
+
const refs = block.effectiveMessageIds.map((id) => byRaw[id]).filter((r) => typeof r === "string" && r !== "BLOCKED");
|
|
1850
|
+
if (refs.length === 0) return null;
|
|
1851
|
+
const sorted = [...refs].sort((a, b) => refNum(a) - refNum(b));
|
|
1852
|
+
return { startRef: sorted[0], endRef: sorted[sorted.length - 1] };
|
|
1853
|
+
}
|
|
1854
|
+
function activeBlockSpans(state) {
|
|
1855
|
+
const spans = [];
|
|
1856
|
+
for (const block of state.blocks) {
|
|
1857
|
+
if (!block.active) continue;
|
|
1858
|
+
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
1859
|
+
if (!span) continue;
|
|
1860
|
+
spans.push({ blockId: block.blockId, tier: block.tier, ...span });
|
|
1861
|
+
}
|
|
1862
|
+
return spans;
|
|
1863
|
+
}
|
|
1864
|
+
function formatCreatedBlocks(state, newBlocks) {
|
|
1865
|
+
const parts = [];
|
|
1866
|
+
for (const block of newBlocks) {
|
|
1867
|
+
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
1868
|
+
parts.push(
|
|
1869
|
+
span ? `${block.blockId}=${span.startRef}\u2013${span.endRef}` : block.blockId
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
return parts.length > 0 ? `blocks: ${parts.join(", ")}` : "";
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// src/decompress.ts
|
|
1876
|
+
function parseBlockIdArg(arg) {
|
|
1877
|
+
const normalized = arg.trim().toLowerCase();
|
|
1878
|
+
const refMatch = /^b0*(\d+)$/.exec(normalized);
|
|
1879
|
+
if (refMatch && refMatch[1] !== void 0) return `b${refMatch[1]}`;
|
|
1880
|
+
const numMatch = /^(\d+)$/.exec(normalized);
|
|
1881
|
+
if (numMatch && numMatch[1] !== void 0) return `b${numMatch[1]}`;
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
function findBlocksOverlappingMessages(state, messageIds) {
|
|
1885
|
+
if (messageIds.size === 0) return [];
|
|
1886
|
+
const matched = [];
|
|
1887
|
+
for (const block of state.blocks) {
|
|
1888
|
+
if (!block.active) continue;
|
|
1889
|
+
if (block.effectiveMessageIds.some((id) => messageIds.has(id))) {
|
|
1890
|
+
matched.push(block);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return matched.sort((a, b) => numericPart(a.blockId) - numericPart(b.blockId));
|
|
1894
|
+
}
|
|
1895
|
+
function findActiveAncestor(state, blockId) {
|
|
1896
|
+
const start = state.blocks.find((b) => b.blockId === blockId);
|
|
1897
|
+
if (!start) return null;
|
|
1898
|
+
const queue = [...start.directBlockIds];
|
|
1899
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1900
|
+
while (queue.length > 0) {
|
|
1901
|
+
const currentId = queue.shift();
|
|
1902
|
+
if (visited.has(currentId)) continue;
|
|
1903
|
+
visited.add(currentId);
|
|
1904
|
+
const current = state.blocks.find((b) => b.blockId === currentId);
|
|
1905
|
+
if (!current) continue;
|
|
1906
|
+
if (current.active) return current.blockId;
|
|
1907
|
+
for (const ancestorId of current.directBlockIds) {
|
|
1908
|
+
if (!visited.has(ancestorId)) queue.push(ancestorId);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
return null;
|
|
1912
|
+
}
|
|
1913
|
+
function deactivateBlock(state, blockIds, options = {}) {
|
|
1914
|
+
const targets = new Set(blockIds);
|
|
1915
|
+
const updated = state.blocks.map((block) => {
|
|
1916
|
+
if (!targets.has(block.blockId) || !block.active) return block;
|
|
1917
|
+
return {
|
|
1918
|
+
...block,
|
|
1919
|
+
active: false,
|
|
1920
|
+
durationMs: block.durationMs,
|
|
1921
|
+
createdAt: block.createdAt
|
|
1922
|
+
};
|
|
1923
|
+
});
|
|
1924
|
+
let final = updated;
|
|
1925
|
+
if (options.deep) {
|
|
1926
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1927
|
+
const queue = [];
|
|
1928
|
+
for (const id of blockIds) {
|
|
1929
|
+
const block = updated.find((b) => b.blockId === id);
|
|
1930
|
+
if (block) queue.push(...block.directBlockIds);
|
|
1931
|
+
}
|
|
1932
|
+
while (queue.length > 0) {
|
|
1933
|
+
const id = queue.shift();
|
|
1934
|
+
if (visited.has(id)) continue;
|
|
1935
|
+
visited.add(id);
|
|
1936
|
+
final = final.map((block) => {
|
|
1937
|
+
if (block.blockId !== id) return block;
|
|
1938
|
+
queue.push(...block.directBlockIds);
|
|
1939
|
+
return block.active ? { ...block, active: false } : block;
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
return { ...state, blocks: final };
|
|
1944
|
+
}
|
|
1945
|
+
function buildRestoredContentPreview(messages, beforeActiveMessageIds, state) {
|
|
1946
|
+
const restored = [];
|
|
1947
|
+
for (const message of messages) {
|
|
1948
|
+
if (!beforeActiveMessageIds.has(message.id)) continue;
|
|
1949
|
+
const stillCovered = state.blocks.some(
|
|
1950
|
+
(b) => b.active && b.effectiveMessageIds.includes(message.id)
|
|
1951
|
+
);
|
|
1952
|
+
if (!stillCovered) restored.push(message);
|
|
1953
|
+
}
|
|
1954
|
+
if (restored.length === 0) return { preview: "", restoredCount: 0 };
|
|
1955
|
+
const lines = [];
|
|
1956
|
+
let totalLength = 0;
|
|
1957
|
+
const MAX_PREVIEW = 2e3;
|
|
1958
|
+
const MAX_PER_MESSAGE = 200;
|
|
1959
|
+
for (const message of restored) {
|
|
1960
|
+
if (totalLength >= MAX_PREVIEW) break;
|
|
1961
|
+
const text = message.text ?? "";
|
|
1962
|
+
const truncated = text.length > MAX_PER_MESSAGE ? clampPrefix(text, MAX_PER_MESSAGE) + "..." : text;
|
|
1963
|
+
const label = message.toolName && message.contentType !== "text" ? `${message.toolName}: ${truncated}` : `[${message.role}] ${truncated}`;
|
|
1964
|
+
lines.push(label);
|
|
1965
|
+
totalLength += label.length + 1;
|
|
1966
|
+
}
|
|
1967
|
+
return { preview: lines.join("\n"), restoredCount: restored.length };
|
|
1968
|
+
}
|
|
1969
|
+
function collectBlockContent(state, block, messages, options = {}) {
|
|
1970
|
+
const full = options.full ?? false;
|
|
1971
|
+
const targetIds = new Set(block.effectiveMessageIds);
|
|
1972
|
+
if (full) {
|
|
1973
|
+
const msgs = messages.filter((m) => targetIds.has(m.id));
|
|
1974
|
+
if (msgs.length === 0) return { text: "", count: 0 };
|
|
1975
|
+
return { text: msgs.map(formatMessage).join("\n\n"), count: msgs.length };
|
|
1976
|
+
}
|
|
1977
|
+
const nestedChildren = [];
|
|
1978
|
+
const nestedCovered = /* @__PURE__ */ new Set();
|
|
1979
|
+
for (const childId of block.directBlockIds) {
|
|
1980
|
+
const child = state.blocks.find((b) => b.blockId === childId);
|
|
1981
|
+
if (!child?.active) continue;
|
|
1982
|
+
nestedChildren.push(child);
|
|
1983
|
+
for (const id of child.effectiveMessageIds) nestedCovered.add(id);
|
|
1984
|
+
}
|
|
1985
|
+
const parts = [];
|
|
1986
|
+
for (const child of nestedChildren) {
|
|
1987
|
+
const label = child.topic ? `${child.blockId}: ${child.topic}` : child.blockId;
|
|
1988
|
+
parts.push(`${SUMMARY_HEADER} \u2014 ${label}
|
|
1989
|
+
${child.summary}`);
|
|
1990
|
+
}
|
|
1991
|
+
let directCount = 0;
|
|
1992
|
+
for (const m of messages) {
|
|
1993
|
+
if (targetIds.has(m.id) && !nestedCovered.has(m.id)) {
|
|
1994
|
+
parts.push(formatMessage(m));
|
|
1995
|
+
directCount++;
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
const count = directCount + nestedChildren.length;
|
|
1999
|
+
if (count === 0) return { text: "", count: 0 };
|
|
2000
|
+
return { text: parts.join("\n\n"), count };
|
|
2001
|
+
}
|
|
2002
|
+
function formatMessage(message) {
|
|
2003
|
+
const text = message.text ?? "";
|
|
2004
|
+
if (message.toolName && message.contentType !== "text") {
|
|
2005
|
+
return `[${message.role} \u2022 ${message.toolName}]
|
|
2006
|
+
${text}`;
|
|
2007
|
+
}
|
|
2008
|
+
return `[${message.role}]
|
|
2009
|
+
${text}`;
|
|
2010
|
+
}
|
|
2011
|
+
function numericPart(blockId) {
|
|
2012
|
+
const match = /^b(\d+)$/.exec(blockId);
|
|
2013
|
+
return match && match[1] !== void 0 ? Number(match[1]) : 0;
|
|
2014
|
+
}
|
|
2015
|
+
function markBlockRestoredInline(state, blockId) {
|
|
2016
|
+
const existing = state.blocks.find((b) => b.blockId === blockId);
|
|
2017
|
+
if (!existing) return { state, result: null };
|
|
2018
|
+
const blocks = state.blocks.map(
|
|
2019
|
+
(block) => block.blockId === blockId ? { ...block, restoredInline: true } : block
|
|
2020
|
+
);
|
|
2021
|
+
const span = resolveBlockSpan(existing, state.messageRefs.byRaw);
|
|
2022
|
+
const result = {
|
|
2023
|
+
restored: true,
|
|
2024
|
+
blockId,
|
|
2025
|
+
...span ? { restoredStartRef: span.startRef, restoredEndRef: span.endRef } : {}
|
|
2026
|
+
};
|
|
2027
|
+
return { state: { ...state, blocks }, result };
|
|
2028
|
+
}
|
|
2029
|
+
function activeAncestorIds(state, blockId) {
|
|
2030
|
+
const out = [];
|
|
2031
|
+
const visited = /* @__PURE__ */ new Set([blockId]);
|
|
2032
|
+
let frontier = [blockId];
|
|
2033
|
+
while (frontier.length > 0) {
|
|
2034
|
+
const next = [];
|
|
2035
|
+
for (const block of state.blocks) {
|
|
2036
|
+
if (visited.has(block.blockId)) continue;
|
|
2037
|
+
if (!block.directBlockIds.some((id) => frontier.includes(id))) continue;
|
|
2038
|
+
visited.add(block.blockId);
|
|
2039
|
+
if (block.active) out.push(block.blockId);
|
|
2040
|
+
next.push(block.blockId);
|
|
2041
|
+
}
|
|
2042
|
+
frontier = next;
|
|
2043
|
+
}
|
|
2044
|
+
return out;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
1774
2047
|
// src/truncate-tools.ts
|
|
1775
2048
|
var TRUNCATION_MARKER = "[truncated for context space]";
|
|
1776
2049
|
var DEFAULTS = {
|
|
@@ -1955,14 +2228,26 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
1955
2228
|
}
|
|
1956
2229
|
}
|
|
1957
2230
|
let hidden = 0;
|
|
2231
|
+
const hiddenOrphanRefs = [];
|
|
2232
|
+
const rememberHiddenRef = (message) => {
|
|
2233
|
+
if (message.toolCallId && allBlockCallIds.has(message.toolCallId)) {
|
|
2234
|
+
return;
|
|
2235
|
+
}
|
|
2236
|
+
const ref = state.messageRefs.byRaw[message.id];
|
|
2237
|
+
if (ref && ref !== BLOCKED_REF && !hiddenOrphanRefs.includes(ref)) {
|
|
2238
|
+
hiddenOrphanRefs.push(ref);
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
1958
2241
|
const result = [];
|
|
1959
2242
|
for (const message of messages) {
|
|
1960
2243
|
if (message.toolName === "compress" && message.contentType === "tool-call" && (!message.toolCallId || !keepCallIds.has(message.toolCallId))) {
|
|
1961
2244
|
hidden++;
|
|
2245
|
+
rememberHiddenRef(message);
|
|
1962
2246
|
continue;
|
|
1963
2247
|
}
|
|
1964
2248
|
if (message.contentType === "tool-result" && message.toolCallId && hiddenCallIds.has(message.toolCallId)) {
|
|
1965
2249
|
hidden++;
|
|
2250
|
+
rememberHiddenRef(message);
|
|
1966
2251
|
continue;
|
|
1967
2252
|
}
|
|
1968
2253
|
if (message.toolName === "compress" && message.contentType === "tool-call" && message.toolCallId && keepCallIds.has(message.toolCallId)) {
|
|
@@ -1982,7 +2267,7 @@ function hideConsumedCompressCalls(state, messages) {
|
|
|
1982
2267
|
}
|
|
1983
2268
|
result.push(message);
|
|
1984
2269
|
}
|
|
1985
|
-
return { messages: result, hidden };
|
|
2270
|
+
return { messages: result, hidden, hiddenOrphanRefs };
|
|
1986
2271
|
}
|
|
1987
2272
|
|
|
1988
2273
|
// src/absorb.ts
|
|
@@ -3632,42 +3917,6 @@ function applyMessageFilters(messages, config) {
|
|
|
3632
3917
|
return { messages: working, ...tally };
|
|
3633
3918
|
}
|
|
3634
3919
|
|
|
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
3920
|
// src/render-refs.ts
|
|
3672
3921
|
function formatTokens(tokens) {
|
|
3673
3922
|
if (tokens < 1e3) return String(tokens);
|
|
@@ -3740,6 +3989,11 @@ function createRenderRefsNode(strategy) {
|
|
|
3740
3989
|
}
|
|
3741
3990
|
var renderRefsNode = createRenderRefsNode("all");
|
|
3742
3991
|
|
|
3992
|
+
// src/message-kind.ts
|
|
3993
|
+
function isToolMessage(message) {
|
|
3994
|
+
return message.contentType === "tool-call" || message.contentType === "tool-result";
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3743
3997
|
// src/tool-pairs.ts
|
|
3744
3998
|
function adjustBoundariesForToolPairs(startIndex, endIndex, messages, maxScan = 20) {
|
|
3745
3999
|
const callIdsInRange = /* @__PURE__ */ new Set();
|
|
@@ -3944,9 +4198,6 @@ function computeIntegrityWithdrawals(messages, foldedIds) {
|
|
|
3944
4198
|
function estimateTextTokens(text) {
|
|
3945
4199
|
return Math.ceil(text.length / 4);
|
|
3946
4200
|
}
|
|
3947
|
-
function isToolMessage(message) {
|
|
3948
|
-
return message.contentType === "tool-call" || message.contentType === "tool-result";
|
|
3949
|
-
}
|
|
3950
4201
|
function isSyntheticOrPruned(message, state) {
|
|
3951
4202
|
if (message.text?.startsWith("[Compressed conversation section]")) return true;
|
|
3952
4203
|
for (const block of state.blocks) {
|
|
@@ -4266,6 +4517,79 @@ function tierActionHint(config, state) {
|
|
|
4266
4517
|
}
|
|
4267
4518
|
return "";
|
|
4268
4519
|
}
|
|
4520
|
+
function requestedRefoldSpan(state, spec) {
|
|
4521
|
+
const nums = [];
|
|
4522
|
+
for (const ref of [spec.startRef, spec.endRef]) {
|
|
4523
|
+
const parsed = parseBoundary(ref);
|
|
4524
|
+
if (!parsed) return null;
|
|
4525
|
+
if (parsed.kind === "message") {
|
|
4526
|
+
nums.push(parsed.numericId);
|
|
4527
|
+
continue;
|
|
4528
|
+
}
|
|
4529
|
+
const block = blockById(state, `b${parsed.numericId}`);
|
|
4530
|
+
if (!block) return null;
|
|
4531
|
+
const span = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
4532
|
+
if (!span) return null;
|
|
4533
|
+
const lo = parseBoundary(span.startRef);
|
|
4534
|
+
const hi = parseBoundary(span.endRef);
|
|
4535
|
+
if (!lo || !hi) return null;
|
|
4536
|
+
nums.push(lo.numericId, hi.numericId);
|
|
4537
|
+
}
|
|
4538
|
+
return { lo: Math.min(...nums), hi: Math.max(...nums) };
|
|
4539
|
+
}
|
|
4540
|
+
function evaluateRefold(state, spec) {
|
|
4541
|
+
const span = requestedRefoldSpan(state, spec);
|
|
4542
|
+
if (!span) return { kind: "blocked", reasons: [] };
|
|
4543
|
+
const reasons = [];
|
|
4544
|
+
const blocks = [];
|
|
4545
|
+
for (const block of state.blocks) {
|
|
4546
|
+
if (!block.active) continue;
|
|
4547
|
+
const resolved = resolveBlockSpan(block, state.messageRefs.byRaw);
|
|
4548
|
+
if (!resolved) {
|
|
4549
|
+
reasons.push(`partially covered ${block.blockId}`);
|
|
4550
|
+
continue;
|
|
4551
|
+
}
|
|
4552
|
+
const ownLo = parseBoundary(resolved.startRef)?.numericId;
|
|
4553
|
+
const ownHi = parseBoundary(resolved.endRef)?.numericId;
|
|
4554
|
+
if (ownLo === void 0 || ownHi === void 0) continue;
|
|
4555
|
+
if (ownHi < span.lo || ownLo > span.hi) continue;
|
|
4556
|
+
if (!block.restoredInline) {
|
|
4557
|
+
reasons.push(`blocked by ${block.blockId} (not restored)`);
|
|
4558
|
+
continue;
|
|
4559
|
+
}
|
|
4560
|
+
if (ownLo < span.lo || ownHi > span.hi) {
|
|
4561
|
+
reasons.push(`partially covered ${block.blockId}`);
|
|
4562
|
+
continue;
|
|
4563
|
+
}
|
|
4564
|
+
const blockers = activeAncestorIds(state, block.blockId).filter(
|
|
4565
|
+
(ancestorId) => !blockById(state, ancestorId)?.restoredInline
|
|
4566
|
+
);
|
|
4567
|
+
if (blockers.length > 0) {
|
|
4568
|
+
for (const id of blockers)
|
|
4569
|
+
reasons.push(`blocked by ${id} (not restored)`);
|
|
4570
|
+
continue;
|
|
4571
|
+
}
|
|
4572
|
+
blocks.push(block);
|
|
4573
|
+
}
|
|
4574
|
+
const uniqueReasons = [...new Set(reasons)];
|
|
4575
|
+
if (uniqueReasons.length > 0)
|
|
4576
|
+
return { kind: "blocked", reasons: uniqueReasons };
|
|
4577
|
+
if (blocks.length > 0) return { kind: "refold", blocks };
|
|
4578
|
+
return { kind: "blocked", reasons: [] };
|
|
4579
|
+
}
|
|
4580
|
+
function applyRefolds(input) {
|
|
4581
|
+
validateSummaryLength(input.spec, input.config.compress);
|
|
4582
|
+
const targets = new Set(input.blockIds);
|
|
4583
|
+
input.state.blocks = input.state.blocks.map(
|
|
4584
|
+
(block) => targets.has(block.blockId) ? {
|
|
4585
|
+
...block,
|
|
4586
|
+
summary: input.spec.summary,
|
|
4587
|
+
topic: input.spec.topic ?? block.topic,
|
|
4588
|
+
runId: input.runId,
|
|
4589
|
+
restoredInline: false
|
|
4590
|
+
} : block
|
|
4591
|
+
);
|
|
4592
|
+
}
|
|
4269
4593
|
function createCore(ports = {}) {
|
|
4270
4594
|
const countTokens = ports.countTokens ?? defaultCountTokens;
|
|
4271
4595
|
function applyCompression(input) {
|
|
@@ -4275,6 +4599,7 @@ function createCore(ports = {}) {
|
|
|
4275
4599
|
let tokensCompressed = 0;
|
|
4276
4600
|
const errors = [];
|
|
4277
4601
|
const warnings = [];
|
|
4602
|
+
const notes = [];
|
|
4278
4603
|
const protectedMessageIds = input.protectedMessageIds ?? computeProtectedRefs(
|
|
4279
4604
|
input.messages,
|
|
4280
4605
|
input.state,
|
|
@@ -4325,6 +4650,18 @@ function createCore(ports = {}) {
|
|
|
4325
4650
|
if (resolution.status === "ok") resolvableCount++;
|
|
4326
4651
|
else if (resolution.status === "unknown") unknownCount++;
|
|
4327
4652
|
}
|
|
4653
|
+
const refoldDecisions = /* @__PURE__ */ new Map();
|
|
4654
|
+
const isRefoldCandidate = (resolution) => resolution.status === "consumed" || resolution.status === "ok" && resolution.resolved.boundaryKind !== "block" && resolution.resolved.messageIds.every(
|
|
4655
|
+
(id) => preExistingCoverage.has(id)
|
|
4656
|
+
);
|
|
4657
|
+
for (const [spec, resolution] of classifications) {
|
|
4658
|
+
if (isRefoldCandidate(resolution)) {
|
|
4659
|
+
refoldDecisions.set(spec, evaluateRefold(state, spec));
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
const allRefold = input.ranges.length > 0 && unknownCount === 0 && [...classifications.entries()].every(
|
|
4663
|
+
([spec, resolution]) => isRefoldCandidate(resolution) && refoldDecisions.get(spec)?.kind === "refold"
|
|
4664
|
+
);
|
|
4328
4665
|
const rangeSpans = [];
|
|
4329
4666
|
for (const [spec, resolution] of classifications) {
|
|
4330
4667
|
if (resolution.status !== "ok") continue;
|
|
@@ -4363,7 +4700,7 @@ function createCore(ports = {}) {
|
|
|
4363
4700
|
totalRangeChars += msg?.text?.length ?? 0;
|
|
4364
4701
|
}
|
|
4365
4702
|
}
|
|
4366
|
-
if (!hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
4703
|
+
if (!allRefold && !hasBlockBoundaryRange && totalRangeChars < input.config.compress.minCompressRange) {
|
|
4367
4704
|
const diagnostics = refGateDiagnostics(
|
|
4368
4705
|
state,
|
|
4369
4706
|
input.ranges.length,
|
|
@@ -4372,10 +4709,50 @@ function createCore(ports = {}) {
|
|
|
4372
4709
|
const firstConsumed = consumedRanges[0];
|
|
4373
4710
|
const covering = firstConsumed ? coveringBlockIds(state, firstConsumed) : [];
|
|
4374
4711
|
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)`;
|
|
4712
|
+
const refoldReasons = [
|
|
4713
|
+
...new Set(
|
|
4714
|
+
consumedRanges.flatMap((spec) => {
|
|
4715
|
+
const decision = refoldDecisions.get(spec);
|
|
4716
|
+
return decision?.kind === "blocked" ? decision.reasons : [];
|
|
4717
|
+
})
|
|
4718
|
+
)
|
|
4719
|
+
];
|
|
4720
|
+
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` : "";
|
|
4721
|
+
const refoldDetail = refoldSuffix(refoldReasons);
|
|
4722
|
+
const okBlockedReasons = [
|
|
4723
|
+
...new Set(
|
|
4724
|
+
[...classifications.entries()].flatMap(([spec, resolution]) => {
|
|
4725
|
+
if (skipSpecs.has(spec) || resolution.status !== "ok") return [];
|
|
4726
|
+
const decision = refoldDecisions.get(spec);
|
|
4727
|
+
return decision?.kind === "blocked" ? decision.reasons : [];
|
|
4728
|
+
})
|
|
4729
|
+
)
|
|
4730
|
+
];
|
|
4375
4731
|
const danglingRefs = consumedRanges.flatMap(
|
|
4376
4732
|
(spec) => danglingMessageRefs(state, input.messages, spec)
|
|
4377
4733
|
);
|
|
4378
|
-
|
|
4734
|
+
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;
|
|
4735
|
+
if (gateMessage === null) {
|
|
4736
|
+
return {
|
|
4737
|
+
state: input.state,
|
|
4738
|
+
result: {
|
|
4739
|
+
blocksCreated: 0,
|
|
4740
|
+
tokensCompressed: 0,
|
|
4741
|
+
errors: [...classificationErrors],
|
|
4742
|
+
warnings: []
|
|
4743
|
+
}
|
|
4744
|
+
};
|
|
4745
|
+
}
|
|
4746
|
+
const reversalNotes = [];
|
|
4747
|
+
for (const [spec, resolution] of classifications) {
|
|
4748
|
+
if (resolution.status === "ok" && !skipSpecs.has(spec)) {
|
|
4749
|
+
const note = resolution.resolved.reversedNote;
|
|
4750
|
+
if (note) reversalNotes.push(note);
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
if (reversalNotes.length > 0) {
|
|
4754
|
+
gateMessage += ` ${reversalNotes.join(" ")}`;
|
|
4755
|
+
}
|
|
4379
4756
|
return {
|
|
4380
4757
|
state: input.state,
|
|
4381
4758
|
result: {
|
|
@@ -4392,8 +4769,30 @@ function createCore(ports = {}) {
|
|
|
4392
4769
|
const resolution = classifications.get(spec);
|
|
4393
4770
|
if (resolution === void 0) continue;
|
|
4394
4771
|
if (resolution.status === "consumed") {
|
|
4772
|
+
const decision = refoldDecisions.get(spec);
|
|
4773
|
+
if (decision?.kind === "refold") {
|
|
4774
|
+
try {
|
|
4775
|
+
applyRefolds({
|
|
4776
|
+
spec,
|
|
4777
|
+
state,
|
|
4778
|
+
runId,
|
|
4779
|
+
config: input.config,
|
|
4780
|
+
blockIds: decision.blocks.map((block) => block.blockId)
|
|
4781
|
+
});
|
|
4782
|
+
blocksCreated += decision.blocks.length;
|
|
4783
|
+
} catch (error) {
|
|
4784
|
+
errors.push(
|
|
4785
|
+
rangeError(
|
|
4786
|
+
spec,
|
|
4787
|
+
error instanceof Error ? error.message : String(error)
|
|
4788
|
+
)
|
|
4789
|
+
);
|
|
4790
|
+
}
|
|
4791
|
+
continue;
|
|
4792
|
+
}
|
|
4793
|
+
const reasons = decision?.kind === "blocked" ? decision.reasons : [];
|
|
4395
4794
|
warnings.push(
|
|
4396
|
-
`Skipped range (${spec.startRef}..${spec.endRef}) \u2014 already compressed (messages consumed by existing block(s)); nothing to compress.`
|
|
4795
|
+
`Skipped range (${spec.startRef}..${spec.endRef}) \u2014 already compressed${reasons.length > 0 ? `: ${reasons.join("; ")}` : " (messages consumed by existing block(s))"}; nothing to compress.`
|
|
4397
4796
|
);
|
|
4398
4797
|
continue;
|
|
4399
4798
|
}
|
|
@@ -4402,6 +4801,8 @@ function createCore(ports = {}) {
|
|
|
4402
4801
|
continue;
|
|
4403
4802
|
}
|
|
4404
4803
|
warnings.push(...resolution.resolved.snappedBoundaries);
|
|
4804
|
+
const note = resolution.resolved.reversedNote;
|
|
4805
|
+
if (note) notes.push(note);
|
|
4405
4806
|
try {
|
|
4406
4807
|
const outcome = applySingleRange({
|
|
4407
4808
|
spec,
|
|
@@ -4413,7 +4814,7 @@ function createCore(ports = {}) {
|
|
|
4413
4814
|
countTokens,
|
|
4414
4815
|
preExistingCoverage
|
|
4415
4816
|
});
|
|
4416
|
-
blocksCreated
|
|
4817
|
+
blocksCreated += outcome.refolded ? outcome.refolded.length : 1;
|
|
4417
4818
|
tokensCompressed += outcome.tokens;
|
|
4418
4819
|
warnings.push(...outcome.warnings);
|
|
4419
4820
|
} catch (error) {
|
|
@@ -4435,7 +4836,13 @@ function createCore(ports = {}) {
|
|
|
4435
4836
|
}
|
|
4436
4837
|
return {
|
|
4437
4838
|
state,
|
|
4438
|
-
result: {
|
|
4839
|
+
result: {
|
|
4840
|
+
blocksCreated,
|
|
4841
|
+
tokensCompressed,
|
|
4842
|
+
errors,
|
|
4843
|
+
warnings,
|
|
4844
|
+
...notes.length > 0 ? { notes } : {}
|
|
4845
|
+
}
|
|
4439
4846
|
};
|
|
4440
4847
|
}
|
|
4441
4848
|
function processTurn(input) {
|
|
@@ -4619,7 +5026,11 @@ var hideCompressCallsNode = {
|
|
|
4619
5026
|
name: "hide-compress-calls",
|
|
4620
5027
|
run(io) {
|
|
4621
5028
|
const hidden = hideConsumedCompressCalls(io.state, io.messages);
|
|
4622
|
-
return {
|
|
5029
|
+
return {
|
|
5030
|
+
...io,
|
|
5031
|
+
messages: hidden.messages,
|
|
5032
|
+
state: { ...io.state, hiddenOrphanRefs: hidden.hiddenOrphanRefs }
|
|
5033
|
+
};
|
|
4623
5034
|
}
|
|
4624
5035
|
};
|
|
4625
5036
|
var recommendNode = {
|
|
@@ -4877,6 +5288,21 @@ function applySingleRange(input) {
|
|
|
4877
5288
|
}
|
|
4878
5289
|
}
|
|
4879
5290
|
if (!isBlockBoundary && filteredIds.length === 0 && consumedBlockIds.length > 0) {
|
|
5291
|
+
const decision = evaluateRefold(input.state, input.spec);
|
|
5292
|
+
if (decision.kind === "refold") {
|
|
5293
|
+
applyRefolds({
|
|
5294
|
+
spec: input.spec,
|
|
5295
|
+
state: input.state,
|
|
5296
|
+
runId: input.runId,
|
|
5297
|
+
config: input.config,
|
|
5298
|
+
blockIds: decision.blocks.map((block2) => block2.blockId)
|
|
5299
|
+
});
|
|
5300
|
+
return {
|
|
5301
|
+
tokens: 0,
|
|
5302
|
+
warnings,
|
|
5303
|
+
refolded: decision.blocks.map((block2) => block2.blockId)
|
|
5304
|
+
};
|
|
5305
|
+
}
|
|
4880
5306
|
const first = consumedBlockIds[0];
|
|
4881
5307
|
const last = consumedBlockIds[consumedBlockIds.length - 1];
|
|
4882
5308
|
throw new Error(
|
|
@@ -4957,7 +5383,15 @@ function applyPairBoundaryAdjustments(resolved, messages) {
|
|
|
4957
5383
|
}
|
|
4958
5384
|
function validateCompressionRange(input, directMessageIds, consumedBlockCount) {
|
|
4959
5385
|
const cfg = input.config.compress;
|
|
4960
|
-
|
|
5386
|
+
validateSummaryLength(input.spec, cfg);
|
|
5387
|
+
if (directMessageIds.length === 0 && consumedBlockCount === 0) {
|
|
5388
|
+
throw new Error(
|
|
5389
|
+
"Range contains no compressible messages \u2014 all are already covered by active blocks or protected."
|
|
5390
|
+
);
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
function validateSummaryLength(spec, cfg) {
|
|
5394
|
+
const summary = spec.summary?.trim() ?? "";
|
|
4961
5395
|
if (summary.length === 0) {
|
|
4962
5396
|
throw new Error(
|
|
4963
5397
|
"Summary is empty \u2014 provide a meaningful summary of the compressed range."
|
|
@@ -4968,17 +5402,12 @@ function validateCompressionRange(input, directMessageIds, consumedBlockCount) {
|
|
|
4968
5402
|
`Summary too short (${summary.length} chars, min ${cfg.minSummaryLength}). The summary must capture the compressed range's key information.`
|
|
4969
5403
|
);
|
|
4970
5404
|
}
|
|
4971
|
-
const effectiveMax =
|
|
5405
|
+
const effectiveMax = spec.summaryMaxChars ?? cfg.maxSummaryLength;
|
|
4972
5406
|
if (effectiveMax > 0 && summary.length > effectiveMax) {
|
|
4973
5407
|
throw new Error(
|
|
4974
5408
|
`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
5409
|
);
|
|
4976
5410
|
}
|
|
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
5411
|
}
|
|
4983
5412
|
function filterProtectedToolMessages(directMessageIds, messages, config) {
|
|
4984
5413
|
const protectedCallIds = /* @__PURE__ */ new Set();
|
|
@@ -5219,7 +5648,7 @@ function computeContextBreakdown(messages, total, growth, countTokens) {
|
|
|
5219
5648
|
const tokens = countMessageTokens(msg, count);
|
|
5220
5649
|
if (msg.text?.startsWith("[Compressed conversation section]")) {
|
|
5221
5650
|
summaries += tokens;
|
|
5222
|
-
} else if (msg
|
|
5651
|
+
} else if (isToolMessage(msg)) {
|
|
5223
5652
|
tool += tokens;
|
|
5224
5653
|
} else if (msg.role === "system") {
|
|
5225
5654
|
system += tokens;
|
|
@@ -5251,7 +5680,8 @@ function cloneState(state) {
|
|
|
5251
5680
|
nextRuleId: state.nextRuleId,
|
|
5252
5681
|
terminalStreak: state.terminalStreak,
|
|
5253
5682
|
nextBlockId: state.nextBlockId,
|
|
5254
|
-
nextRunId: state.nextRunId
|
|
5683
|
+
nextRunId: state.nextRunId,
|
|
5684
|
+
hiddenOrphanRefs: state.hiddenOrphanRefs ? [...state.hiddenOrphanRefs] : void 0
|
|
5255
5685
|
};
|
|
5256
5686
|
}
|
|
5257
5687
|
function scoreRelevance(block, terms) {
|
|
@@ -5521,147 +5951,6 @@ function defaultPackSources(opts) {
|
|
|
5521
5951
|
return sources;
|
|
5522
5952
|
}
|
|
5523
5953
|
|
|
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
5954
|
// src/report.ts
|
|
5666
5955
|
function formatTokens2(n) {
|
|
5667
5956
|
if (!Number.isFinite(n) || n <= 0) return "0";
|
|
@@ -5686,14 +5975,16 @@ function tierLabel(block) {
|
|
|
5686
5975
|
}
|
|
5687
5976
|
function tierBreakdown(blocks, countTokens) {
|
|
5688
5977
|
const tierTokens = {};
|
|
5978
|
+
const tierCounts = {};
|
|
5689
5979
|
for (const block of blocks) {
|
|
5690
5980
|
tierTokens[block.tier] = (tierTokens[block.tier] ?? 0) + summaryTokensOf(block, countTokens);
|
|
5981
|
+
tierCounts[block.tier] = (tierCounts[block.tier] ?? 0) + 1;
|
|
5691
5982
|
}
|
|
5692
5983
|
const tiers = Object.keys(tierTokens).map(Number);
|
|
5693
5984
|
if (tiers.length <= 1) return null;
|
|
5694
5985
|
const parts = [];
|
|
5695
5986
|
for (const tier of [1, 2, 3]) {
|
|
5696
|
-
if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])}`);
|
|
5987
|
+
if (tierTokens[tier]) parts.push(`T${tier}: ${formatTokens2(tierTokens[tier])} (${tierCounts[tier]} blocks)`);
|
|
5697
5988
|
}
|
|
5698
5989
|
return parts.join(" | ");
|
|
5699
5990
|
}
|
|
@@ -5719,8 +6010,9 @@ function collectVisible(messages, state, countTokens) {
|
|
|
5719
6010
|
const ref = refForRaw(state.messageRefs, message.id);
|
|
5720
6011
|
if (!ref) return;
|
|
5721
6012
|
const tokens = countMessageTokens(message, countTokens);
|
|
5722
|
-
const
|
|
5723
|
-
|
|
6013
|
+
const isTool = isToolMessage(message);
|
|
6014
|
+
const tool = isTool ? message.toolName ?? (message.toolCallId ? toolCallNames.get(message.toolCallId) : void 0) ?? "tool" : "text";
|
|
6015
|
+
if (tokens > 0) visible.push({ ref, tokens, tool, isTool, index });
|
|
5724
6016
|
});
|
|
5725
6017
|
return { visible, summaryTokens };
|
|
5726
6018
|
}
|
|
@@ -5763,8 +6055,8 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
|
|
|
5763
6055
|
toolTypeMap.set(message.tool, (toolTypeMap.get(message.tool) ?? 0) + message.tokens);
|
|
5764
6056
|
}
|
|
5765
6057
|
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.
|
|
6058
|
+
const totalTool = visible.filter((m) => m.isTool).reduce((sum, m) => sum + m.tokens, 0);
|
|
6059
|
+
const totalText = visible.filter((m) => !m.isTool).reduce((sum, m) => sum + m.tokens, 0);
|
|
5768
6060
|
const total = summaryTokens + totalTool + totalText;
|
|
5769
6061
|
lines.push("CONTEXT BREAKDOWN");
|
|
5770
6062
|
lines.push(
|
|
@@ -5800,6 +6092,11 @@ function renderOverview(visible, summaryTokens, blocks, state, countTokens, limi
|
|
|
5800
6092
|
` ${block.blockId} (${tierLabel(block)}) ${formatTokens2(eff)}\u2192${formatTokens2(summaryTokensOf(block, countTokens))} ${block.effectiveMessageIds.length} msgs "${topic}"`
|
|
5801
6093
|
);
|
|
5802
6094
|
}
|
|
6095
|
+
if (blocks.length > limit) {
|
|
6096
|
+
lines.push(
|
|
6097
|
+
` ... and ${blocks.length - limit} more blocks not shown (scope:"compressed", limit:${blocks.length} for full list)`
|
|
6098
|
+
);
|
|
6099
|
+
}
|
|
5803
6100
|
}
|
|
5804
6101
|
lines.push("");
|
|
5805
6102
|
lines.push(
|
|
@@ -7482,7 +7779,11 @@ function build(text) {
|
|
|
7482
7779
|
}
|
|
7483
7780
|
function docFeatures(text) {
|
|
7484
7781
|
const hit = cache.get(text);
|
|
7485
|
-
if (hit)
|
|
7782
|
+
if (hit) {
|
|
7783
|
+
cache.delete(text);
|
|
7784
|
+
cache.set(text, hit);
|
|
7785
|
+
return hit;
|
|
7786
|
+
}
|
|
7486
7787
|
const f = build(text);
|
|
7487
7788
|
if (text.length > 0 && text.length <= capChars) {
|
|
7488
7789
|
while (cachedChars + text.length > capChars && cache.size > 0) {
|
|
@@ -7593,8 +7894,8 @@ var hybridAlgorithm = {
|
|
|
7593
7894
|
score(docs, query) {
|
|
7594
7895
|
const bm = bm25Algorithm.score(docs, query);
|
|
7595
7896
|
const fz = fuzzyAlgorithm.score(docs, query);
|
|
7596
|
-
const maxBm =
|
|
7597
|
-
const maxFz =
|
|
7897
|
+
const maxBm = bm.reduce((m, r) => r.score > m ? r.score : m, 1e-9);
|
|
7898
|
+
const maxFz = fz.reduce((m, r) => r.score > m ? r.score : m, 1e-9);
|
|
7598
7899
|
const bmMap = new Map(bm.map((r) => [r.ref, r.score / maxBm]));
|
|
7599
7900
|
const fzMap = new Map(fz.map((r) => [r.ref, r.score / maxFz]));
|
|
7600
7901
|
return docs.map((d) => ({
|
|
@@ -7941,6 +8242,7 @@ export {
|
|
|
7941
8242
|
TIER3_CONDENSE_RULES,
|
|
7942
8243
|
VERBOSITY_LEVELS,
|
|
7943
8244
|
VIABLE_RANGE_MIN_TOKENS,
|
|
8245
|
+
activeAncestorIds,
|
|
7944
8246
|
activeBlockSpans,
|
|
7945
8247
|
activeBlocks,
|
|
7946
8248
|
addRule,
|
|
@@ -7958,6 +8260,7 @@ export {
|
|
|
7958
8260
|
applySectionOverrides,
|
|
7959
8261
|
applySteeringToPrompt,
|
|
7960
8262
|
assignRefs,
|
|
8263
|
+
baseIdOf,
|
|
7961
8264
|
blockById,
|
|
7962
8265
|
blockDocs,
|
|
7963
8266
|
blockVisibleInRange,
|
|
@@ -8034,6 +8337,7 @@ export {
|
|
|
8034
8337
|
imageShrinksForRef,
|
|
8035
8338
|
indexToRef,
|
|
8036
8339
|
isAbsorbCandidate,
|
|
8340
|
+
isCovered,
|
|
8037
8341
|
isImageFullRestored,
|
|
8038
8342
|
isMessageLatestProtected,
|
|
8039
8343
|
isMessageProtected,
|
|
@@ -8041,6 +8345,7 @@ export {
|
|
|
8041
8345
|
isRetrievedMessage,
|
|
8042
8346
|
isStoredPlaceholderText,
|
|
8043
8347
|
isSummaryMessageId,
|
|
8348
|
+
isToolMessage,
|
|
8044
8349
|
isValidPackName,
|
|
8045
8350
|
jsonFoldPlugin,
|
|
8046
8351
|
leanPack,
|
|
@@ -8050,6 +8355,7 @@ export {
|
|
|
8050
8355
|
listSearchAlgorithms,
|
|
8051
8356
|
logSelectPlugin,
|
|
8052
8357
|
makeIO,
|
|
8358
|
+
markBlockRestoredInline,
|
|
8053
8359
|
matchSession,
|
|
8054
8360
|
matchToolPattern,
|
|
8055
8361
|
messageDocs,
|