@animalabs/context-manager 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/adaptive/index.d.ts +6 -0
- package/dist/src/adaptive/index.d.ts.map +1 -1
- package/dist/src/adaptive/index.js +8 -0
- package/dist/src/adaptive/index.js.map +1 -1
- package/dist/src/adaptive/kv-cache-sim.d.ts +128 -0
- package/dist/src/adaptive/kv-cache-sim.d.ts.map +1 -0
- package/dist/src/adaptive/kv-cache-sim.js +0 -0
- package/dist/src/adaptive/kv-cache-sim.js.map +1 -0
- package/dist/src/adaptive/kv-control.d.ts +152 -0
- package/dist/src/adaptive/kv-control.d.ts.map +1 -0
- package/dist/src/adaptive/kv-control.js +368 -0
- package/dist/src/adaptive/kv-control.js.map +1 -0
- package/dist/src/adaptive/kv-replay.d.ts +76 -0
- package/dist/src/adaptive/kv-replay.d.ts.map +1 -0
- package/dist/src/adaptive/kv-replay.js +175 -0
- package/dist/src/adaptive/kv-replay.js.map +1 -0
- package/dist/src/adaptive/render-offsets.d.ts +61 -0
- package/dist/src/adaptive/render-offsets.d.ts.map +1 -0
- package/dist/src/adaptive/render-offsets.js +88 -0
- package/dist/src/adaptive/render-offsets.js.map +1 -0
- package/dist/src/adaptive/strategies/kv-stable.d.ts +48 -0
- package/dist/src/adaptive/strategies/kv-stable.d.ts.map +1 -0
- package/dist/src/adaptive/strategies/kv-stable.js +102 -0
- package/dist/src/adaptive/strategies/kv-stable.js.map +1 -0
- package/dist/src/adaptive/summary-tree.d.ts +98 -0
- package/dist/src/adaptive/summary-tree.d.ts.map +1 -0
- package/dist/src/adaptive/summary-tree.js +214 -0
- package/dist/src/adaptive/summary-tree.js.map +1 -0
- package/dist/src/context-log.d.ts.map +1 -1
- package/dist/src/context-log.js +7 -2
- package/dist/src/context-log.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/message-store.js +1 -1
- package/dist/src/message-store.js.map +1 -1
- package/dist/src/normalize-tool-messages.d.ts +33 -0
- package/dist/src/normalize-tool-messages.d.ts.map +1 -1
- package/dist/src/normalize-tool-messages.js +59 -0
- package/dist/src/normalize-tool-messages.js.map +1 -1
- package/dist/src/strategies/autobiographical.d.ts +101 -39
- package/dist/src/strategies/autobiographical.d.ts.map +1 -1
- package/dist/src/strategies/autobiographical.js +398 -222
- package/dist/src/strategies/autobiographical.js.map +1 -1
- package/dist/src/types/strategy.d.ts +40 -1
- package/dist/src/types/strategy.d.ts.map +1 -1
- package/dist/src/types/strategy.js +2 -0
- package/dist/src/types/strategy.js.map +1 -1
- package/dist/test/adaptive/kv-cache-sim.test.d.ts +16 -0
- package/dist/test/adaptive/kv-cache-sim.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-cache-sim.test.js +173 -0
- package/dist/test/adaptive/kv-cache-sim.test.js.map +1 -0
- package/dist/test/adaptive/kv-control.test.d.ts +14 -0
- package/dist/test/adaptive/kv-control.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-control.test.js +130 -0
- package/dist/test/adaptive/kv-control.test.js.map +1 -0
- package/dist/test/adaptive/kv-replay.test.d.ts +15 -0
- package/dist/test/adaptive/kv-replay.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-replay.test.js +112 -0
- package/dist/test/adaptive/kv-replay.test.js.map +1 -0
- package/dist/test/adaptive/kv-stable-strategy.test.d.ts +10 -0
- package/dist/test/adaptive/kv-stable-strategy.test.d.ts.map +1 -0
- package/dist/test/adaptive/kv-stable-strategy.test.js +89 -0
- package/dist/test/adaptive/kv-stable-strategy.test.js.map +1 -0
- package/dist/test/adaptive/render-offsets.test.d.ts +11 -0
- package/dist/test/adaptive/render-offsets.test.d.ts.map +1 -0
- package/dist/test/adaptive/render-offsets.test.js +85 -0
- package/dist/test/adaptive/render-offsets.test.js.map +1 -0
- package/dist/test/adaptive/summary-tree.test.d.ts +5 -0
- package/dist/test/adaptive/summary-tree.test.d.ts.map +1 -0
- package/dist/test/adaptive/summary-tree.test.js +72 -0
- package/dist/test/adaptive/summary-tree.test.js.map +1 -0
- package/dist/test/chunker-tool-boundary.test.d.ts +12 -0
- package/dist/test/chunker-tool-boundary.test.d.ts.map +1 -0
- package/dist/test/chunker-tool-boundary.test.js +89 -0
- package/dist/test/chunker-tool-boundary.test.js.map +1 -0
- package/dist/test/compression-shape-invariants.test.d.ts +22 -0
- package/dist/test/compression-shape-invariants.test.d.ts.map +1 -0
- package/dist/test/compression-shape-invariants.test.js +312 -0
- package/dist/test/compression-shape-invariants.test.js.map +1 -0
- package/dist/test/image-strip-render-stats.test.d.ts +2 -0
- package/dist/test/image-strip-render-stats.test.d.ts.map +1 -0
- package/dist/test/image-strip-render-stats.test.js +130 -0
- package/dist/test/image-strip-render-stats.test.js.map +1 -0
- package/dist/test/image-stripping.test.d.ts +13 -0
- package/dist/test/image-stripping.test.d.ts.map +1 -0
- package/dist/test/image-stripping.test.js +104 -0
- package/dist/test/image-stripping.test.js.map +1 -0
- package/dist/test/normalize-tool-messages.test.js +61 -1
- package/dist/test/normalize-tool-messages.test.js.map +1 -1
- package/dist/test/render-stats.test.d.ts +2 -0
- package/dist/test/render-stats.test.d.ts.map +1 -0
- package/dist/test/render-stats.test.js +91 -0
- package/dist/test/render-stats.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/adaptive/index.ts +48 -0
- package/src/adaptive/kv-cache-sim.ts +0 -0
- package/src/adaptive/kv-control.ts +497 -0
- package/src/adaptive/kv-replay.ts +238 -0
- package/src/adaptive/render-offsets.ts +116 -0
- package/src/adaptive/strategies/kv-stable.ts +123 -0
- package/src/adaptive/summary-tree.ts +252 -0
- package/src/context-log.ts +7 -2
- package/src/index.ts +1 -1
- package/src/message-store.ts +1 -1
- package/src/normalize-tool-messages.ts +61 -0
- package/src/strategies/autobiographical.ts +414 -271
- package/src/types/strategy.ts +40 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end: KvStableStrategy driven through the real Picker.run loop.
|
|
3
|
+
*
|
|
4
|
+
* Asserts the controller policy works as a drop-in FoldingStrategy: the picker
|
|
5
|
+
* walks to exactly the planned frontier, the result fits the budget, pins/locks
|
|
6
|
+
* are honored, and the same `planControlledFrontier` the replay harness measures
|
|
7
|
+
* is what the picker reaches.
|
|
8
|
+
*/
|
|
9
|
+
import { test } from 'node:test';
|
|
10
|
+
import assert from 'node:assert/strict';
|
|
11
|
+
import { Picker } from '../../src/adaptive/picker.js';
|
|
12
|
+
import { SummaryTree } from '../../src/adaptive/summary-tree.js';
|
|
13
|
+
import { renderLayout } from '../../src/adaptive/render-offsets.js';
|
|
14
|
+
import { planControlledFrontier } from '../../src/adaptive/kv-control.js';
|
|
15
|
+
import { KvStableStrategy } from '../../src/adaptive/strategies/kv-stable.js';
|
|
16
|
+
import { buildChronicleWithChain } from './harness.js';
|
|
17
|
+
const BUDGET = (totalBudget, slack = 0.1) => ({
|
|
18
|
+
totalBudget,
|
|
19
|
+
targetBudget: totalBudget * (1 - slack),
|
|
20
|
+
slack,
|
|
21
|
+
});
|
|
22
|
+
function inputsOf(ch) {
|
|
23
|
+
return {
|
|
24
|
+
chunks: ch.chunks, summaries: ch.summaries, recallPairTokens: ch.recallPairTokens,
|
|
25
|
+
headTokens: 0, tailTokens: 0, headChunkIds: new Set(), tailChunkIds: new Set(),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function sameMap(a, b) {
|
|
29
|
+
if (a.size !== b.size)
|
|
30
|
+
return false;
|
|
31
|
+
for (const [k, v] of a)
|
|
32
|
+
if (b.get(k) !== v)
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
test('kv-stable strategy: picker walks to exactly the planned frontier and fits budget', () => {
|
|
37
|
+
const ch = buildChronicleWithChain({
|
|
38
|
+
chunkCount: 48, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
39
|
+
});
|
|
40
|
+
const inputs = inputsOf(ch);
|
|
41
|
+
const tree = new SummaryTree(inputs);
|
|
42
|
+
const budget = BUDGET(15_000); // targetBudget 13_500
|
|
43
|
+
// Independent reference plan (same policy the strategy uses internally).
|
|
44
|
+
let now = 0;
|
|
45
|
+
for (const c of inputs.chunks)
|
|
46
|
+
if (c.sequence > now)
|
|
47
|
+
now = c.sequence;
|
|
48
|
+
const plan = planControlledFrontier(inputs, tree, {
|
|
49
|
+
previous: new Map(), foldAtTokens: budget.totalBudget, expandAtTokens: budget.targetBudget,
|
|
50
|
+
targetTokens: budget.targetBudget, windowTokens: budget.totalBudget, rawZone: new Set(), now, mergeThreshold: 6,
|
|
51
|
+
});
|
|
52
|
+
const strategy = new KvStableStrategy(inputs, {});
|
|
53
|
+
const result = new Picker(strategy).run(inputs, budget);
|
|
54
|
+
assert.ok(sameMap(result.finalResolutions, plan.resolutions), 'picker reaches the planned frontier');
|
|
55
|
+
assert.ok(result.finalTokens <= budget.totalBudget, `fits hard wall: ${result.finalTokens} ≤ 15000`);
|
|
56
|
+
assert.equal(renderLayout(inputs, tree, result.finalResolutions).totalTokens, result.finalTokens);
|
|
57
|
+
});
|
|
58
|
+
test('kv-stable strategy: converges to null and leaves a small budget untouched', () => {
|
|
59
|
+
const ch = buildChronicleWithChain({ chunkCount: 8, tokensPerChunk: 500, mergeThreshold: 6, recallPairTokens: 200 });
|
|
60
|
+
const inputs = inputsOf(ch); // 4k raw
|
|
61
|
+
const result = new Picker(new KvStableStrategy(inputs, {})).run(inputs, BUDGET(100_000));
|
|
62
|
+
for (const c of inputs.chunks) {
|
|
63
|
+
assert.equal(result.finalResolutions.get(c.id), 0, 'nothing folded under a generous budget');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
test('kv-stable strategy: pinned chunks stay raw, locked chunks keep their resolution', () => {
|
|
67
|
+
const ch = buildChronicleWithChain({ chunkCount: 48, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200 });
|
|
68
|
+
ch.chunks[2].pinned = true; // old → would otherwise fold
|
|
69
|
+
ch.chunks[5].lockedByAgent = true;
|
|
70
|
+
ch.chunks[5].currentResolution = 1; // frozen at L1
|
|
71
|
+
const inputs = inputsOf(ch);
|
|
72
|
+
const result = new Picker(new KvStableStrategy(inputs, {})).run(inputs, BUDGET(15_000));
|
|
73
|
+
assert.equal(result.finalResolutions.get(ch.chunks[2].id), 0, 'pinned chunk stays raw');
|
|
74
|
+
assert.equal(result.finalResolutions.get(ch.chunks[5].id), 1, 'locked chunk keeps its resolution');
|
|
75
|
+
});
|
|
76
|
+
test('kv-stable strategy: a tighter reach cap holds the deep prefix raw (shallower divergence)', () => {
|
|
77
|
+
const ch = buildChronicleWithChain({ chunkCount: 60, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200 });
|
|
78
|
+
const inputs = inputsOf(ch);
|
|
79
|
+
const budget = BUDGET(20_000);
|
|
80
|
+
const tight = new Picker(new KvStableStrategy(inputs, { reachTokens: 8_000 })).run(inputs, budget);
|
|
81
|
+
const wide = new Picker(new KvStableStrategy(inputs, { reachTokens: 60_000 })).run(inputs, budget);
|
|
82
|
+
// Deepest level among the oldest third of chunks: a tight reach can't fold
|
|
83
|
+
// that far back, so the deep prefix stays shallower (more raw) than with wide.
|
|
84
|
+
const ordered = [...inputs.chunks].sort((a, b) => a.sequence - b.sequence);
|
|
85
|
+
const oldThird = ordered.slice(0, 20);
|
|
86
|
+
const deepest = (r) => Math.max(...oldThird.map((c) => r.finalResolutions.get(c.id) ?? 0));
|
|
87
|
+
assert.ok(deepest(tight) <= deepest(wide), `tight reach keeps the deep prefix shallower (${deepest(tight)} ≤ ${deepest(wide)})`);
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=kv-stable-strategy.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-stable-strategy.test.js","sourceRoot":"","sources":["../../../test/adaptive/kv-stable-strategy.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for render-offsets — the rendered-unit layout + KV-cost accounting.
|
|
3
|
+
*
|
|
4
|
+
* Two things matter:
|
|
5
|
+
* 1. `renderLayout().totalTokens` must equal the picker's own computeTokens
|
|
6
|
+
* model (parity), so the KV term is computed over the real render.
|
|
7
|
+
* 2. KV cost must be position-aware: a change deep in the old prefix costs far
|
|
8
|
+
* more than a change near the tail — the whole reason for the term.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=render-offsets.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-offsets.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/render-offsets.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for render-offsets — the rendered-unit layout + KV-cost accounting.
|
|
3
|
+
*
|
|
4
|
+
* Two things matter:
|
|
5
|
+
* 1. `renderLayout().totalTokens` must equal the picker's own computeTokens
|
|
6
|
+
* model (parity), so the KV term is computed over the real render.
|
|
7
|
+
* 2. KV cost must be position-aware: a change deep in the old prefix costs far
|
|
8
|
+
* more than a change near the tail — the whole reason for the term.
|
|
9
|
+
*/
|
|
10
|
+
import { test } from 'node:test';
|
|
11
|
+
import assert from 'node:assert/strict';
|
|
12
|
+
import { Picker } from '../../src/adaptive/picker.js';
|
|
13
|
+
import { FlatProfileStrategy } from '../../src/adaptive/strategies/flat-profile.js';
|
|
14
|
+
import { SummaryTree } from '../../src/adaptive/summary-tree.js';
|
|
15
|
+
import { renderLayout, kvCost, earliestDivergenceIndex, } from '../../src/adaptive/render-offsets.js';
|
|
16
|
+
import { buildChronicleWithChain } from './harness.js';
|
|
17
|
+
const BUDGET = (totalBudget, slack = 0.1) => ({
|
|
18
|
+
totalBudget,
|
|
19
|
+
targetBudget: totalBudget * (1 - slack),
|
|
20
|
+
slack,
|
|
21
|
+
});
|
|
22
|
+
function inputsOf(ch) {
|
|
23
|
+
// Explicit empty head/tail. (computeHeadTail force-includes one tail chunk
|
|
24
|
+
// even at tailTokens:0, which would add the tail block to the recomputed
|
|
25
|
+
// suffix and muddy the position-aware assertion below.)
|
|
26
|
+
return {
|
|
27
|
+
chunks: ch.chunks,
|
|
28
|
+
summaries: ch.summaries,
|
|
29
|
+
recallPairTokens: ch.recallPairTokens,
|
|
30
|
+
headTokens: 0,
|
|
31
|
+
tailTokens: 0,
|
|
32
|
+
headChunkIds: new Set(),
|
|
33
|
+
tailChunkIds: new Set(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function frontier(ids, level) {
|
|
37
|
+
return new Map(ids.map((id) => [id, level]));
|
|
38
|
+
}
|
|
39
|
+
test('render-offsets: totalTokens matches the picker computeTokens model (parity)', () => {
|
|
40
|
+
const ch = buildChronicleWithChain({
|
|
41
|
+
chunkCount: 36, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
42
|
+
});
|
|
43
|
+
const inputs = inputsOf(ch);
|
|
44
|
+
const result = new Picker(new FlatProfileStrategy()).run(inputs, BUDGET(10_000));
|
|
45
|
+
assert.ok(result.applied.length > 0, 'expected the picker to fold something');
|
|
46
|
+
const tree = new SummaryTree(inputs);
|
|
47
|
+
const layout = renderLayout(inputs, tree, result.finalResolutions);
|
|
48
|
+
assert.equal(layout.totalTokens, result.finalTokens);
|
|
49
|
+
});
|
|
50
|
+
test('render-offsets: kvCost is zero for an unchanged frontier', () => {
|
|
51
|
+
const ch = buildChronicleWithChain({
|
|
52
|
+
chunkCount: 36, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
53
|
+
});
|
|
54
|
+
const inputs = inputsOf(ch);
|
|
55
|
+
const tree = new SummaryTree(inputs);
|
|
56
|
+
const layout = renderLayout(inputs, tree, new Map());
|
|
57
|
+
assert.equal(kvCost(layout, layout), 0);
|
|
58
|
+
});
|
|
59
|
+
test('render-offsets: deep-old change costs more than a near-tail change (position-aware)', () => {
|
|
60
|
+
const ch = buildChronicleWithChain({
|
|
61
|
+
chunkCount: 36, tokensPerChunk: 1000, mergeThreshold: 6, recallPairTokens: 200,
|
|
62
|
+
});
|
|
63
|
+
const inputs = inputsOf(ch);
|
|
64
|
+
const tree = new SummaryTree(inputs);
|
|
65
|
+
const allRaw = renderLayout(inputs, tree, new Map());
|
|
66
|
+
const foldOldest = renderLayout(inputs, tree, frontier(['c-0000', 'c-0001', 'c-0002', 'c-0003', 'c-0004', 'c-0005'], 1));
|
|
67
|
+
const foldNewest = renderLayout(inputs, tree, frontier(['c-0030', 'c-0031', 'c-0032', 'c-0033', 'c-0034', 'c-0035'], 1));
|
|
68
|
+
const costOld = kvCost(allRaw, foldOldest);
|
|
69
|
+
const costNew = kvCost(allRaw, foldNewest);
|
|
70
|
+
assert.ok(costNew < costOld, `near-tail (${costNew}) must cost less than deep-old (${costOld})`);
|
|
71
|
+
assert.equal(costNew, 200, 'near-tail fold recomputes only the new recall pair');
|
|
72
|
+
});
|
|
73
|
+
test('render-offsets: earliestDivergenceIndex + kvCost prefix semantics', () => {
|
|
74
|
+
const u = (kind, key, tokens, offset) => ({ kind, key, tokens, offset });
|
|
75
|
+
const a = [u('head', 'head', 10, 0), u('raw', 'x', 5, 10), u('raw', 'y', 5, 15)];
|
|
76
|
+
const same = [u('head', 'head', 10, 0), u('raw', 'x', 5, 10), u('raw', 'y', 5, 15)];
|
|
77
|
+
assert.equal(earliestDivergenceIndex(a, same), -1);
|
|
78
|
+
const prefix = [u('head', 'head', 10, 0), u('raw', 'x', 5, 10)]; // strict prefix of a
|
|
79
|
+
assert.equal(earliestDivergenceIndex(a, prefix), 2);
|
|
80
|
+
assert.equal(kvCost({ units: a, totalTokens: 20 }, { units: prefix, totalTokens: 15 }), 0);
|
|
81
|
+
const diff = [u('head', 'head', 10, 0), u('raw', 'z', 5, 10), u('raw', 'y', 5, 15)];
|
|
82
|
+
assert.equal(earliestDivergenceIndex(a, diff), 1);
|
|
83
|
+
assert.equal(kvCost({ units: a, totalTokens: 20 }, { units: diff, totalTokens: 20 }), 10); // 20 − offset@1
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=render-offsets.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-offsets.test.js","sourceRoot":"","sources":["../../../test/adaptive/render-offsets.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-tree.test.d.ts","sourceRoot":"","sources":["../../../test/adaptive/summary-tree.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for SummaryTree — the structural view the V2 best-fit DP walks.
|
|
3
|
+
*/
|
|
4
|
+
import { test } from 'node:test';
|
|
5
|
+
import assert from 'node:assert/strict';
|
|
6
|
+
import { SummaryTree, nodeTokens } from '../../src/adaptive/summary-tree.js';
|
|
7
|
+
import { MockChronicle, buildChronicleWithChain } from './harness.js';
|
|
8
|
+
function inputsOf(chronicle) {
|
|
9
|
+
const ht = chronicle.computeHeadTail({ headTokens: 0, tailTokens: 0 });
|
|
10
|
+
return {
|
|
11
|
+
chunks: chronicle.chunks,
|
|
12
|
+
summaries: chronicle.summaries,
|
|
13
|
+
recallPairTokens: chronicle.recallPairTokens,
|
|
14
|
+
...ht,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
test('summary-tree: two L1s, no L2 — roots are the L1 summaries', () => {
|
|
18
|
+
const ch = buildChronicleWithChain({
|
|
19
|
+
chunkCount: 12,
|
|
20
|
+
tokensPerChunk: 100,
|
|
21
|
+
mergeThreshold: 6,
|
|
22
|
+
recallPairTokens: 200,
|
|
23
|
+
});
|
|
24
|
+
const tree = new SummaryTree(inputsOf(ch));
|
|
25
|
+
assert.equal(tree.ancestorAt('c-0000', 1)?.id, 'L1-0');
|
|
26
|
+
assert.equal(tree.ancestorAt('c-0000', 2), null, 'no L2 produced');
|
|
27
|
+
assert.equal(tree.maxLevel('c-0000'), 1);
|
|
28
|
+
assert.deepEqual(tree.leavesUnder('L1-0'), [
|
|
29
|
+
'c-0000', 'c-0001', 'c-0002', 'c-0003', 'c-0004', 'c-0005',
|
|
30
|
+
]);
|
|
31
|
+
assert.equal(tree.recallTokens('L1-0'), 200);
|
|
32
|
+
const roots = tree.roots();
|
|
33
|
+
assert.deepEqual(roots.map((r) => (r.kind === 'summary' ? r.id : r.chunkId)), ['L1-0', 'L1-1']);
|
|
34
|
+
const l1 = tree.summary('L1-0');
|
|
35
|
+
const kids = tree.children(l1);
|
|
36
|
+
assert.equal(kids.length, 6);
|
|
37
|
+
assert.ok(kids.every((k) => k.kind === 'leaf'));
|
|
38
|
+
assert.equal(nodeTokens(l1), 200);
|
|
39
|
+
assert.equal(nodeTokens(kids[0]), 100);
|
|
40
|
+
});
|
|
41
|
+
test('summary-tree: full L1→L2 chain — single L2 root with summary children', () => {
|
|
42
|
+
const ch = buildChronicleWithChain({
|
|
43
|
+
chunkCount: 36,
|
|
44
|
+
tokensPerChunk: 100,
|
|
45
|
+
mergeThreshold: 6,
|
|
46
|
+
recallPairTokens: 200,
|
|
47
|
+
});
|
|
48
|
+
const tree = new SummaryTree(inputsOf(ch));
|
|
49
|
+
assert.equal(tree.ancestorAt('c-0000', 1)?.id, 'L1-0');
|
|
50
|
+
assert.equal(tree.ancestorAt('c-0000', 2)?.id, 'L2-0');
|
|
51
|
+
assert.equal(tree.maxLevel('c-0000'), 2);
|
|
52
|
+
const roots = tree.roots();
|
|
53
|
+
assert.equal(roots.length, 1);
|
|
54
|
+
assert.equal(roots[0].kind, 'summary');
|
|
55
|
+
assert.equal(roots[0].id, 'L2-0');
|
|
56
|
+
assert.equal(tree.leavesUnder('L2-0').length, 36);
|
|
57
|
+
const kids = tree.children(tree.summary('L2-0'));
|
|
58
|
+
assert.equal(kids.length, 6);
|
|
59
|
+
assert.ok(kids.every((k) => k.kind === 'summary'));
|
|
60
|
+
});
|
|
61
|
+
test('summary-tree: an uncovered leaf is its own root at level 0', () => {
|
|
62
|
+
const ch = new MockChronicle({ mergeThreshold: 6, recallPairTokens: 200 });
|
|
63
|
+
ch.addChunk({ id: 'a', rawTokens: 100 });
|
|
64
|
+
ch.addChunk({ id: 'b', rawTokens: 100 });
|
|
65
|
+
ch.produceL1(['a']); // only 'a' is covered
|
|
66
|
+
const tree = new SummaryTree(inputsOf(ch));
|
|
67
|
+
assert.equal(tree.maxLevel('b'), 0);
|
|
68
|
+
assert.equal(tree.ancestorAt('b', 1), null);
|
|
69
|
+
const rootKeys = tree.roots().map((r) => (r.kind === 'summary' ? r.id : r.chunkId));
|
|
70
|
+
assert.deepEqual(rootKeys, ['L1-0', 'b']); // ordered by source sequence
|
|
71
|
+
});
|
|
72
|
+
//# sourceMappingURL=summary-tree.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary-tree.test.js","sourceRoot":"","sources":["../../../test/adaptive/summary-tree.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunker invariant: a chunk must not close on a message containing a
|
|
3
|
+
* `tool_use` block, because the matching `tool_result` lives in the
|
|
4
|
+
* immediately-following user message and the Anthropic API rejects a
|
|
5
|
+
* compression request where a tool_use isn't followed by its result.
|
|
6
|
+
*
|
|
7
|
+
* Companion to `stripUnpairedToolBlocks` (the runtime safety net) — this
|
|
8
|
+
* verifies the upstream chunker behavior that avoids the situation in the
|
|
9
|
+
* first place.
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=chunker-tool-boundary.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker-tool-boundary.test.d.ts","sourceRoot":"","sources":["../../test/chunker-tool-boundary.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunker invariant: a chunk must not close on a message containing a
|
|
3
|
+
* `tool_use` block, because the matching `tool_result` lives in the
|
|
4
|
+
* immediately-following user message and the Anthropic API rejects a
|
|
5
|
+
* compression request where a tool_use isn't followed by its result.
|
|
6
|
+
*
|
|
7
|
+
* Companion to `stripUnpairedToolBlocks` (the runtime safety net) — this
|
|
8
|
+
* verifies the upstream chunker behavior that avoids the situation in the
|
|
9
|
+
* first place.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, before, after } from 'node:test';
|
|
12
|
+
import assert from 'node:assert';
|
|
13
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
14
|
+
import { ContextManager, AutobiographicalStrategy } from '../src/index.js';
|
|
15
|
+
const TEST_STORE_PATH = './test-chunker-tool-boundary';
|
|
16
|
+
function cleanup() {
|
|
17
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
18
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
describe('Chunker: tool cycle boundary', () => {
|
|
22
|
+
before(() => cleanup());
|
|
23
|
+
after(() => cleanup());
|
|
24
|
+
it("defers chunk closure when the last message contains a tool_use", async () => {
|
|
25
|
+
cleanup();
|
|
26
|
+
const strategy = new AutobiographicalStrategy({
|
|
27
|
+
// Very small chunk threshold so we hit close-conditions quickly.
|
|
28
|
+
targetChunkTokens: 50,
|
|
29
|
+
// Disable head/recent windows so all messages are eligible for chunking.
|
|
30
|
+
headWindowTokens: 0,
|
|
31
|
+
recentWindowTokens: 0,
|
|
32
|
+
});
|
|
33
|
+
const manager = await ContextManager.open({
|
|
34
|
+
path: TEST_STORE_PATH,
|
|
35
|
+
strategy,
|
|
36
|
+
});
|
|
37
|
+
// Build a sequence where the message that would land at the chunk
|
|
38
|
+
// boundary contains a tool_use. The matching tool_result is the next
|
|
39
|
+
// message. The chunker should defer closing to include both.
|
|
40
|
+
//
|
|
41
|
+
// Messages (~30 tokens each so we cross 50 tokens after the 2nd):
|
|
42
|
+
// 0: user text
|
|
43
|
+
// 1: agent text
|
|
44
|
+
// 2: user text
|
|
45
|
+
// 3: agent text + tool_use(id=A) <- would close here (4 msgs, >50 tok)
|
|
46
|
+
// 4: user tool_result(id=A) <- chunk extended to include this
|
|
47
|
+
// 5: agent text
|
|
48
|
+
// 6: user text
|
|
49
|
+
// ...
|
|
50
|
+
const filler = (n) => 'word '.repeat(n);
|
|
51
|
+
manager.addMessage('user', [{ type: 'text', text: filler(30) }]);
|
|
52
|
+
manager.addMessage('agent', [{ type: 'text', text: filler(30) }]);
|
|
53
|
+
manager.addMessage('user', [{ type: 'text', text: filler(30) }]);
|
|
54
|
+
manager.addMessage('agent', [
|
|
55
|
+
{ type: 'text', text: filler(20) },
|
|
56
|
+
{ type: 'tool_use', id: 'A', name: 'fn', input: {} },
|
|
57
|
+
]);
|
|
58
|
+
manager.addMessage('user', [
|
|
59
|
+
{ type: 'tool_result', toolUseId: 'A', content: 'res' },
|
|
60
|
+
]);
|
|
61
|
+
// Add more messages to ensure a second chunk forms (so the first
|
|
62
|
+
// chunk's boundary is unambiguous, not just "end of store").
|
|
63
|
+
for (let i = 0; i < 6; i++) {
|
|
64
|
+
manager.addMessage(i % 2 === 0 ? 'agent' : 'user', [
|
|
65
|
+
{ type: 'text', text: filler(30) },
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
// Force a chunker rebuild by reading state via the strategy.
|
|
69
|
+
// ContextManager runs rebuildChunks during compile() — easier to call
|
|
70
|
+
// the internal directly.
|
|
71
|
+
const s = strategy;
|
|
72
|
+
s.rebuildChunks(manager.messageStore);
|
|
73
|
+
assert.ok(s.chunks.length >= 1, 'should have produced at least one chunk');
|
|
74
|
+
// For every closed chunk, the last message must NOT contain a tool_use.
|
|
75
|
+
for (const chunk of s.chunks) {
|
|
76
|
+
const last = chunk.messages[chunk.messages.length - 1];
|
|
77
|
+
const hasToolUse = last.content.some((b) => b.type === 'tool_use');
|
|
78
|
+
assert.ok(!hasToolUse, `chunk ending on message ${last.id} contains a trailing tool_use — boundary not deferred`);
|
|
79
|
+
}
|
|
80
|
+
// Specifically: the first chunk should have included message 4 (the
|
|
81
|
+
// tool_result), not stopped at message 3.
|
|
82
|
+
const firstChunk = s.chunks[0];
|
|
83
|
+
const firstChunkLastContent = firstChunk.messages[firstChunk.messages.length - 1].content;
|
|
84
|
+
const lastBlockIsToolResult = firstChunkLastContent.some((b) => b.type === 'tool_result');
|
|
85
|
+
assert.ok(lastBlockIsToolResult || firstChunk.messages.length >= 5, 'first chunk should have been extended to include the tool_result');
|
|
86
|
+
await manager.close();
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=chunker-tool-boundary.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker-tool-boundary.test.js","sourceRoot":"","sources":["../../test/chunker-tool-boundary.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* End-to-end shape invariants for the compression pipeline.
|
|
3
|
+
*
|
|
4
|
+
* The whole compression-bug family (5/6/7/8/9) was about the *shape* of the
|
|
5
|
+
* request sent to the Anthropic API, not the content of summaries. This
|
|
6
|
+
* suite exercises the full pipeline (message store → chunker → L1 compress
|
|
7
|
+
* → L2/L3 merges) against a mock membrane that:
|
|
8
|
+
*
|
|
9
|
+
* 1. Validates every request against the API's structural rules
|
|
10
|
+
* (tool_use/tool_result adjacency, placement in user vs. assistant
|
|
11
|
+
* turns) and throws if any rule is violated.
|
|
12
|
+
* 2. Returns a deterministic summary text at a realistic ~5:1 compression
|
|
13
|
+
* ratio so summary IDs and merge cascades behave predictably.
|
|
14
|
+
*
|
|
15
|
+
* If any of the patched call sites — `compressChunkHierarchical`,
|
|
16
|
+
* `executeMerge`, the chunker's tool-cycle-respecting close, or the
|
|
17
|
+
* runtime `splitMixedToolMessages` / `stripUnpairedToolBlocks` passes —
|
|
18
|
+
* regresses, the mock will throw with the same error shape the live API
|
|
19
|
+
* would return, and the test fails.
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=compression-shape-invariants.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compression-shape-invariants.test.d.ts","sourceRoot":"","sources":["../../test/compression-shape-invariants.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"}
|