@animalabs/context-manager 0.3.0 → 0.4.0
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/context-manager.d.ts +69 -2
- package/dist/src/context-manager.d.ts.map +1 -1
- package/dist/src/context-manager.js +128 -10
- package/dist/src/context-manager.js.map +1 -1
- package/dist/src/strategies/autobiographical.d.ts +205 -3
- package/dist/src/strategies/autobiographical.d.ts.map +1 -1
- package/dist/src/strategies/autobiographical.js +767 -59
- package/dist/src/strategies/autobiographical.js.map +1 -1
- package/dist/src/types/index.d.ts +2 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/strategy.d.ts +208 -0
- package/dist/src/types/strategy.d.ts.map +1 -1
- package/dist/src/types/strategy.js +21 -0
- package/dist/src/types/strategy.js.map +1 -1
- package/dist/test/non-blocking-compile.test.d.ts +22 -0
- package/dist/test/non-blocking-compile.test.d.ts.map +1 -0
- package/dist/test/non-blocking-compile.test.js +114 -0
- package/dist/test/non-blocking-compile.test.js.map +1 -0
- package/dist/test/pins.test.d.ts +10 -0
- package/dist/test/pins.test.d.ts.map +1 -0
- package/dist/test/pins.test.js +164 -0
- package/dist/test/pins.test.js.map +1 -0
- package/dist/test/recall-positioning.test.d.ts +16 -0
- package/dist/test/recall-positioning.test.d.ts.map +1 -0
- package/dist/test/recall-positioning.test.js +181 -0
- package/dist/test/recall-positioning.test.js.map +1 -0
- package/dist/test/search.test.d.ts +10 -0
- package/dist/test/search.test.d.ts.map +1 -0
- package/dist/test/search.test.js +141 -0
- package/dist/test/search.test.js.map +1 -0
- package/dist/test/speculation-cap.test.d.ts +17 -0
- package/dist/test/speculation-cap.test.d.ts.map +1 -0
- package/dist/test/speculation-cap.test.js +157 -0
- package/dist/test/speculation-cap.test.js.map +1 -0
- package/dist/test/strategy-persistence.test.d.ts +15 -0
- package/dist/test/strategy-persistence.test.d.ts.map +1 -0
- package/dist/test/strategy-persistence.test.js +244 -0
- package/dist/test/strategy-persistence.test.js.map +1 -0
- package/dist/test/tool-pruning.test.d.ts +10 -0
- package/dist/test/tool-pruning.test.d.ts.map +1 -0
- package/dist/test/tool-pruning.test.js +140 -0
- package/dist/test/tool-pruning.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/context-manager.ts +142 -10
- package/src/strategies/autobiographical.ts +815 -61
- package/src/types/index.ts +14 -1
- package/src/types/strategy.ts +223 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `compile()`'s non-blocking contract + the uncompressed-chunk
|
|
3
|
+
* fallback that prevents silent message loss.
|
|
4
|
+
*
|
|
5
|
+
* Background: commit `3e42e98` dropped the `await readiness.pendingWork`
|
|
6
|
+
* inside `compile()` to keep turn latency low (compression can take
|
|
7
|
+
* 30+s; awaiting it per-turn was the wrong tradeoff). The side effect
|
|
8
|
+
* was that messages caught in a queued-but-not-yet-compressed chunk
|
|
9
|
+
* would vanish from rendered context in hierarchical mode — no summary
|
|
10
|
+
* existed yet for the chunk, and `selectHierarchical` only emitted raw
|
|
11
|
+
* messages from `recentStart` onward.
|
|
12
|
+
*
|
|
13
|
+
* The fix (these tests pin in place): `selectHierarchical` now emits
|
|
14
|
+
* raw messages for any uncompressed chunk that overlaps the middle
|
|
15
|
+
* region between the head and recent windows, mirroring
|
|
16
|
+
* `selectLegacy`'s "Uncompressed: emit raw" behavior. Once compression
|
|
17
|
+
* finishes and the chunk transitions to `compressed: true`, the raw
|
|
18
|
+
* emission stops (the chunk is now covered by an L1 summary), so
|
|
19
|
+
* there's no double-counting across the transition.
|
|
20
|
+
*/
|
|
21
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
22
|
+
import assert from 'node:assert';
|
|
23
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
24
|
+
import { ContextManager, AutobiographicalStrategy } from '../src/index.js';
|
|
25
|
+
const TEST_STORE_PATH = './test-non-blocking-compile';
|
|
26
|
+
function cleanup() {
|
|
27
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
28
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function textBlock(text) {
|
|
32
|
+
return [{ type: 'text', text }];
|
|
33
|
+
}
|
|
34
|
+
function compiledHas(compiled, fragment) {
|
|
35
|
+
for (const m of compiled.messages) {
|
|
36
|
+
for (const block of m.content) {
|
|
37
|
+
if (block.type === 'text' && block.text.includes(fragment))
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
describe('AutobiographicalStrategy — non-blocking compile + uncompressed-chunk fallback', () => {
|
|
44
|
+
before(cleanup);
|
|
45
|
+
after(cleanup);
|
|
46
|
+
beforeEach(cleanup);
|
|
47
|
+
it('emits raw messages for uncompressed chunks in the middle region (no silent drop)', async () => {
|
|
48
|
+
// Configure for a small middle region:
|
|
49
|
+
// - No head window (so all messages compete for visibility via the
|
|
50
|
+
// summary path and the recent window).
|
|
51
|
+
// - Small recentWindowTokens so older messages fall out of recent.
|
|
52
|
+
// - Small targetChunkTokens so a single old message ends up in
|
|
53
|
+
// its own chunk.
|
|
54
|
+
// - Hierarchical mode so `selectHierarchical` is the codepath
|
|
55
|
+
// under test (selectLegacy already had this fallback).
|
|
56
|
+
const strategy = new AutobiographicalStrategy({
|
|
57
|
+
headWindowTokens: 0,
|
|
58
|
+
recentWindowTokens: 12,
|
|
59
|
+
targetChunkTokens: 4,
|
|
60
|
+
hierarchical: true,
|
|
61
|
+
});
|
|
62
|
+
const manager = await ContextManager.open({
|
|
63
|
+
path: TEST_STORE_PATH,
|
|
64
|
+
strategy,
|
|
65
|
+
});
|
|
66
|
+
// Three "old" messages whose chunks will be queued but un-ticked,
|
|
67
|
+
// plus a "latest" big enough to dominate the small recent window.
|
|
68
|
+
manager.addMessage('user', textBlock('ANCIENT_MARKER_1'));
|
|
69
|
+
manager.addMessage('user', textBlock('ANCIENT_MARKER_2'));
|
|
70
|
+
manager.addMessage('user', textBlock('ANCIENT_MARKER_3'));
|
|
71
|
+
manager.addMessage('user', textBlock('LATEST ' + 'X'.repeat(80)));
|
|
72
|
+
// Critically: we do NOT call manager.tick(). The chunks for the
|
|
73
|
+
// ancient messages exist in the strategy's compression queue but
|
|
74
|
+
// never run through executeMerge/compressChunk, so they stay
|
|
75
|
+
// `chunk.compressed === false`. This is the exact state where the
|
|
76
|
+
// bug used to silently drop messages.
|
|
77
|
+
const compiled = await manager.compile({
|
|
78
|
+
maxTokens: 100_000,
|
|
79
|
+
reserveForResponse: 0,
|
|
80
|
+
});
|
|
81
|
+
assert.ok(compiledHas(compiled, 'ANCIENT_MARKER_1'), 'ANCIENT_MARKER_1 must appear in compiled output (uncompressed-chunk fallback)');
|
|
82
|
+
assert.ok(compiledHas(compiled, 'ANCIENT_MARKER_2'), 'ANCIENT_MARKER_2 must appear in compiled output');
|
|
83
|
+
assert.ok(compiledHas(compiled, 'ANCIENT_MARKER_3'), 'ANCIENT_MARKER_3 must appear in compiled output');
|
|
84
|
+
// The recent message must of course still be there.
|
|
85
|
+
assert.ok(compiledHas(compiled, 'LATEST'), 'LATEST message must appear in compiled output');
|
|
86
|
+
manager.close();
|
|
87
|
+
});
|
|
88
|
+
it('compile() returns promptly without awaiting pending compression', async () => {
|
|
89
|
+
// Sanity check on the freshness contract change: compile() must
|
|
90
|
+
// not block on `pendingCompression`. We can't easily inject a slow
|
|
91
|
+
// mock LLM here, but we *can* assert that compile() completes
|
|
92
|
+
// before any tick would have time to finish.
|
|
93
|
+
const strategy = new AutobiographicalStrategy({
|
|
94
|
+
headWindowTokens: 0,
|
|
95
|
+
recentWindowTokens: 12,
|
|
96
|
+
targetChunkTokens: 4,
|
|
97
|
+
hierarchical: true,
|
|
98
|
+
});
|
|
99
|
+
const manager = await ContextManager.open({
|
|
100
|
+
path: TEST_STORE_PATH,
|
|
101
|
+
strategy,
|
|
102
|
+
});
|
|
103
|
+
manager.addMessage('user', textBlock('older'));
|
|
104
|
+
manager.addMessage('user', textBlock('latest ' + 'Y'.repeat(80)));
|
|
105
|
+
const t0 = Date.now();
|
|
106
|
+
await manager.compile({ maxTokens: 100_000, reserveForResponse: 0 });
|
|
107
|
+
const elapsed = Date.now() - t0;
|
|
108
|
+
// No LLM, no real awaiting — compile should be effectively instant.
|
|
109
|
+
// 500ms is wildly generous; the actual time is sub-10ms in practice.
|
|
110
|
+
assert.ok(elapsed < 500, `compile() should return promptly; took ${elapsed}ms`);
|
|
111
|
+
manager.close();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
//# sourceMappingURL=non-blocking-compile.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"non-blocking-compile.test.js","sourceRoot":"","sources":["../../test/non-blocking-compile.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for pins / documents / protected ranges (audit gap #3).
|
|
3
|
+
*
|
|
4
|
+
* Pinned messages are excluded from compression and render raw at their
|
|
5
|
+
* original chronological position. Marks survive process restart and follow
|
|
6
|
+
* Chronicle branches (state is persisted as a snapshot under
|
|
7
|
+
* `${ns}/autobio:pins`).
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=pins.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pins.test.d.ts","sourceRoot":"","sources":["../../test/pins.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for pins / documents / protected ranges (audit gap #3).
|
|
3
|
+
*
|
|
4
|
+
* Pinned messages are excluded from compression and render raw at their
|
|
5
|
+
* original chronological position. Marks survive process restart and follow
|
|
6
|
+
* Chronicle branches (state is persisted as a snapshot under
|
|
7
|
+
* `${ns}/autobio:pins`).
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
10
|
+
import assert from 'node:assert';
|
|
11
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
12
|
+
import { ContextManager, AutobiographicalStrategy } from '../src/index.js';
|
|
13
|
+
const TEST_STORE_PATH = './test-pins';
|
|
14
|
+
function cleanup() {
|
|
15
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
16
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function textBlock(text) {
|
|
20
|
+
return [{ type: 'text', text }];
|
|
21
|
+
}
|
|
22
|
+
class TestableStrategy extends AutobiographicalStrategy {
|
|
23
|
+
seedL1Against(content, firstMsgId, lastMsgId) {
|
|
24
|
+
const entry = {
|
|
25
|
+
id: `L1-${this.nextSummaryIdCounter()}`,
|
|
26
|
+
level: 1,
|
|
27
|
+
content,
|
|
28
|
+
tokens: Math.ceil(content.length / 4),
|
|
29
|
+
sourceLevel: 0,
|
|
30
|
+
sourceIds: [firstMsgId, lastMsgId],
|
|
31
|
+
sourceRange: { first: firstMsgId, last: lastMsgId },
|
|
32
|
+
created: Date.now(),
|
|
33
|
+
};
|
|
34
|
+
this.pushSummary(entry);
|
|
35
|
+
return entry;
|
|
36
|
+
}
|
|
37
|
+
// Expose the protected getter for assertions
|
|
38
|
+
exposeCompressibleIds(store) {
|
|
39
|
+
return this.getCompressibleMessages(store).map((m) => m.id);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
describe('AutobiographicalStrategy — pins / documents (gap #3)', () => {
|
|
43
|
+
before(cleanup);
|
|
44
|
+
after(cleanup);
|
|
45
|
+
beforeEach(cleanup);
|
|
46
|
+
it('pinned messages are excluded from the compressible set', async () => {
|
|
47
|
+
const strategy = new TestableStrategy({ headWindowTokens: 0, recentWindowTokens: 5 });
|
|
48
|
+
const manager = await ContextManager.open({
|
|
49
|
+
path: TEST_STORE_PATH,
|
|
50
|
+
strategy,
|
|
51
|
+
});
|
|
52
|
+
const m1 = manager.addMessage('user', textBlock('one'));
|
|
53
|
+
const m2 = manager.addMessage('user', textBlock('two'));
|
|
54
|
+
const m3 = manager.addMessage('user', textBlock('three'));
|
|
55
|
+
manager.addMessage('user', textBlock('latest ' + 'X'.repeat(50)));
|
|
56
|
+
// Without any pins, all old messages are compressible.
|
|
57
|
+
const beforePin = strategy.exposeCompressibleIds(manager.messageStore.createView());
|
|
58
|
+
assert.ok(beforePin.includes(m2), 'm2 should be compressible before pin');
|
|
59
|
+
manager.pinRange(m2, m2);
|
|
60
|
+
const afterPin = strategy.exposeCompressibleIds(manager.messageStore.createView());
|
|
61
|
+
assert.ok(!afterPin.includes(m2), 'm2 must NOT be compressible after pin');
|
|
62
|
+
assert.ok(afterPin.includes(m1), 'm1 still compressible');
|
|
63
|
+
assert.ok(afterPin.includes(m3), 'm3 still compressible');
|
|
64
|
+
manager.close();
|
|
65
|
+
});
|
|
66
|
+
it('renders pinned messages raw at their chronological position', async () => {
|
|
67
|
+
const strategy = new TestableStrategy({ headWindowTokens: 0, recentWindowTokens: 5 });
|
|
68
|
+
const manager = await ContextManager.open({
|
|
69
|
+
path: TEST_STORE_PATH,
|
|
70
|
+
strategy,
|
|
71
|
+
});
|
|
72
|
+
const m1 = manager.addMessage('user', textBlock('alpha'));
|
|
73
|
+
const m2 = manager.addMessage('user', textBlock('PINNED-DOCUMENT-CONTENT'));
|
|
74
|
+
const m3 = manager.addMessage('user', textBlock('gamma'));
|
|
75
|
+
manager.addMessage('user', textBlock('latest ' + 'X'.repeat(50)));
|
|
76
|
+
// Seed summaries that cover m1 and m3 (NOT m2 — m2 is pinned).
|
|
77
|
+
strategy.seedL1Against('summary about alpha', m1, m1);
|
|
78
|
+
strategy.seedL1Against('summary about gamma', m3, m3);
|
|
79
|
+
manager.markDocument(m2, { name: 'big-doc' });
|
|
80
|
+
const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 0 });
|
|
81
|
+
const allTexts = compiled.messages.map(m => m.content.filter(c => c.type === 'text').map(c => c.text).join('|'));
|
|
82
|
+
// The pinned message's full content should be rendered raw, not via a summary.
|
|
83
|
+
const pinIdx = allTexts.findIndex(t => t.includes('PINNED-DOCUMENT-CONTENT'));
|
|
84
|
+
assert.ok(pinIdx >= 0, 'pinned message text should be visible verbatim in output');
|
|
85
|
+
// The two summaries should also be present.
|
|
86
|
+
const alphaIdx = allTexts.findIndex(t => t.includes('summary about alpha'));
|
|
87
|
+
const gammaIdx = allTexts.findIndex(t => t.includes('summary about gamma'));
|
|
88
|
+
assert.ok(alphaIdx >= 0, 'alpha summary should appear');
|
|
89
|
+
assert.ok(gammaIdx >= 0, 'gamma summary should appear');
|
|
90
|
+
// Chronological order: alpha summary < pinned m2 < gamma summary
|
|
91
|
+
assert.ok(alphaIdx < pinIdx, `alpha summary should come before pinned m2 (alpha=${alphaIdx} pin=${pinIdx})`);
|
|
92
|
+
assert.ok(pinIdx < gammaIdx, `pinned m2 should come before gamma summary (pin=${pinIdx} gamma=${gammaIdx})`);
|
|
93
|
+
manager.close();
|
|
94
|
+
});
|
|
95
|
+
it('persists pins across close/reopen', async () => {
|
|
96
|
+
let pinId;
|
|
97
|
+
{
|
|
98
|
+
const strategy = new TestableStrategy({ headWindowTokens: 0, recentWindowTokens: 5 });
|
|
99
|
+
const manager = await ContextManager.open({
|
|
100
|
+
path: TEST_STORE_PATH,
|
|
101
|
+
strategy,
|
|
102
|
+
});
|
|
103
|
+
const m1 = manager.addMessage('user', textBlock('survive me'));
|
|
104
|
+
manager.addMessage('user', textBlock('latest'));
|
|
105
|
+
pinId = manager.pinRange(m1, m1, { name: 'first-test-pin' });
|
|
106
|
+
assert.equal(manager.listPins().length, 1);
|
|
107
|
+
manager.sync();
|
|
108
|
+
manager.close();
|
|
109
|
+
}
|
|
110
|
+
{
|
|
111
|
+
const strategy = new TestableStrategy({ headWindowTokens: 0, recentWindowTokens: 5 });
|
|
112
|
+
const manager = await ContextManager.open({
|
|
113
|
+
path: TEST_STORE_PATH,
|
|
114
|
+
strategy,
|
|
115
|
+
});
|
|
116
|
+
const pins = manager.listPins();
|
|
117
|
+
assert.equal(pins.length, 1, 'pin should reload from chronicle');
|
|
118
|
+
assert.equal(pins[0].id, pinId);
|
|
119
|
+
assert.equal(pins[0].name, 'first-test-pin');
|
|
120
|
+
manager.close();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
it('unpin removes the protection and persists', async () => {
|
|
124
|
+
const strategy = new TestableStrategy({ headWindowTokens: 0, recentWindowTokens: 5 });
|
|
125
|
+
const manager = await ContextManager.open({
|
|
126
|
+
path: TEST_STORE_PATH,
|
|
127
|
+
strategy,
|
|
128
|
+
});
|
|
129
|
+
const m1 = manager.addMessage('user', textBlock('toggle me'));
|
|
130
|
+
manager.addMessage('user', textBlock('latest'));
|
|
131
|
+
const pinId = manager.pinRange(m1, m1);
|
|
132
|
+
assert.equal(manager.listPins().length, 1);
|
|
133
|
+
const removed = manager.unpin(pinId);
|
|
134
|
+
assert.equal(removed, true);
|
|
135
|
+
assert.equal(manager.listPins().length, 0);
|
|
136
|
+
// Re-unpinning is a no-op (returns false)
|
|
137
|
+
assert.equal(manager.unpin(pinId), false);
|
|
138
|
+
manager.close();
|
|
139
|
+
});
|
|
140
|
+
it('multiple pins isolate per Chronicle branch (via fork)', async () => {
|
|
141
|
+
const strategy = new TestableStrategy({ headWindowTokens: 0, recentWindowTokens: 5 });
|
|
142
|
+
const manager = await ContextManager.open({
|
|
143
|
+
path: TEST_STORE_PATH,
|
|
144
|
+
strategy,
|
|
145
|
+
});
|
|
146
|
+
const m1 = manager.addMessage('user', textBlock('shared'));
|
|
147
|
+
manager.addMessage('user', textBlock('latest'));
|
|
148
|
+
const sharedPin = manager.pinRange(m1, m1, { name: 'shared' });
|
|
149
|
+
const mainBranch = manager.currentBranch().name;
|
|
150
|
+
await manager.fork('experimental');
|
|
151
|
+
// Adding a pin only on the experimental branch
|
|
152
|
+
const m2 = manager.addMessage('user', textBlock('only on fork'));
|
|
153
|
+
manager.addMessage('user', textBlock('latest 2'));
|
|
154
|
+
const forkPin = manager.pinRange(m2, m2, { name: 'fork-only' });
|
|
155
|
+
assert.equal(manager.listPins().length, 2, 'fork should have shared + fork-only pin');
|
|
156
|
+
await manager.switchBranch(mainBranch);
|
|
157
|
+
const mainPins = manager.listPins();
|
|
158
|
+
assert.equal(mainPins.length, 1, 'main should only have the shared pin');
|
|
159
|
+
assert.equal(mainPins[0].id, sharedPin);
|
|
160
|
+
assert.ok(!mainPins.some(p => p.id === forkPin), 'main must NOT see the fork-only pin');
|
|
161
|
+
manager.close();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=pins.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pins.test.js","sourceRoot":"","sources":["../../test/pins.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for per-region recall pair emission (audit gap #2).
|
|
3
|
+
*
|
|
4
|
+
* Pre-fix, AutobiographicalStrategy concatenated ALL selected summaries with
|
|
5
|
+
* `\n\n---\n\n` separators into ONE Q/A pair placed between the head window
|
|
6
|
+
* and the recent window. Per the spec audit, this is the structural
|
|
7
|
+
* equivalent of Lena's dual-recall corruption pattern from Hermes: the agent
|
|
8
|
+
* sees memories as a wall of summaries from another speaker, with no
|
|
9
|
+
* positional or temporal coherence per individual summary.
|
|
10
|
+
*
|
|
11
|
+
* Post-fix, each selected summary emits as its own Q/A recall pair, sorted
|
|
12
|
+
* chronologically by source-range position. The legacy combined-pair shape
|
|
13
|
+
* is still available via `positionedRecallPairs: false`.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=recall-positioning.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-positioning.test.d.ts","sourceRoot":"","sources":["../../test/recall-positioning.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for per-region recall pair emission (audit gap #2).
|
|
3
|
+
*
|
|
4
|
+
* Pre-fix, AutobiographicalStrategy concatenated ALL selected summaries with
|
|
5
|
+
* `\n\n---\n\n` separators into ONE Q/A pair placed between the head window
|
|
6
|
+
* and the recent window. Per the spec audit, this is the structural
|
|
7
|
+
* equivalent of Lena's dual-recall corruption pattern from Hermes: the agent
|
|
8
|
+
* sees memories as a wall of summaries from another speaker, with no
|
|
9
|
+
* positional or temporal coherence per individual summary.
|
|
10
|
+
*
|
|
11
|
+
* Post-fix, each selected summary emits as its own Q/A recall pair, sorted
|
|
12
|
+
* chronologically by source-range position. The legacy combined-pair shape
|
|
13
|
+
* is still available via `positionedRecallPairs: false`.
|
|
14
|
+
*/
|
|
15
|
+
import { describe, it, before, after, beforeEach } from 'node:test';
|
|
16
|
+
import assert from 'node:assert';
|
|
17
|
+
import { rmSync, existsSync } from 'node:fs';
|
|
18
|
+
import { ContextManager, AutobiographicalStrategy } from '../src/index.js';
|
|
19
|
+
const TEST_STORE_PATH = './test-recall-positioning';
|
|
20
|
+
function cleanup() {
|
|
21
|
+
if (existsSync(TEST_STORE_PATH)) {
|
|
22
|
+
rmSync(TEST_STORE_PATH, { recursive: true, force: true });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function textBlock(text) {
|
|
26
|
+
return [{ type: 'text', text }];
|
|
27
|
+
}
|
|
28
|
+
class TestableStrategy extends AutobiographicalStrategy {
|
|
29
|
+
/** Seed an L1 against a specific source-message id. */
|
|
30
|
+
seedL1Against(content, firstMsgId, lastMsgId) {
|
|
31
|
+
const entry = {
|
|
32
|
+
id: `L1-${this.nextSummaryIdCounter()}`,
|
|
33
|
+
level: 1,
|
|
34
|
+
content,
|
|
35
|
+
tokens: Math.ceil(content.length / 4),
|
|
36
|
+
sourceLevel: 0,
|
|
37
|
+
sourceIds: [firstMsgId, lastMsgId],
|
|
38
|
+
sourceRange: { first: firstMsgId, last: lastMsgId },
|
|
39
|
+
created: Date.now(),
|
|
40
|
+
};
|
|
41
|
+
this.pushSummary(entry);
|
|
42
|
+
return entry;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function recallEntries(messages) {
|
|
46
|
+
const pairs = [];
|
|
47
|
+
for (let i = 0; i < messages.length - 1; i++) {
|
|
48
|
+
const a = messages[i];
|
|
49
|
+
const b = messages[i + 1];
|
|
50
|
+
if (a.participant === 'Context Manager') {
|
|
51
|
+
const q = a.content.map(c => (c.type === 'text' ? c.text : '')).join('');
|
|
52
|
+
const ans = b.content.map(c => (c.type === 'text' ? c.text : '')).join('');
|
|
53
|
+
pairs.push({ q, a: ans });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return pairs;
|
|
57
|
+
}
|
|
58
|
+
describe('AutobiographicalStrategy — per-region recall pairs (gap #2)', () => {
|
|
59
|
+
before(cleanup);
|
|
60
|
+
after(cleanup);
|
|
61
|
+
beforeEach(cleanup);
|
|
62
|
+
it('emits one Q/A pair per selected summary, in chronological order', async () => {
|
|
63
|
+
// recentWindowTokens deliberately small so the three "old" messages we
|
|
64
|
+
// seed summaries against fall OUTSIDE the recent window — otherwise
|
|
65
|
+
// anti-redundancy (correctly) hides summaries about messages that are
|
|
66
|
+
// already shown verbatim in the recent window.
|
|
67
|
+
const strategy = new TestableStrategy({
|
|
68
|
+
headWindowTokens: 0,
|
|
69
|
+
recentWindowTokens: 8,
|
|
70
|
+
});
|
|
71
|
+
const manager = await ContextManager.open({
|
|
72
|
+
path: TEST_STORE_PATH,
|
|
73
|
+
strategy,
|
|
74
|
+
});
|
|
75
|
+
// Three "old" messages whose summaries we'll test, plus one message
|
|
76
|
+
// big enough to dominate the recent window so the older three fall out.
|
|
77
|
+
const m1 = manager.addMessage('user', textBlock('first'));
|
|
78
|
+
const m2 = manager.addMessage('user', textBlock('second'));
|
|
79
|
+
const m3 = manager.addMessage('user', textBlock('third'));
|
|
80
|
+
manager.addMessage('user', textBlock('latest message ' + 'X'.repeat(50)));
|
|
81
|
+
// Seed three summaries. Seed in the *opposite* order from chronological
|
|
82
|
+
// so we can verify the strategy sorts them by source position rather
|
|
83
|
+
// than by creation order.
|
|
84
|
+
strategy.seedL1Against('summary about THIRD', m3, m3);
|
|
85
|
+
strategy.seedL1Against('summary about FIRST', m1, m1);
|
|
86
|
+
strategy.seedL1Against('summary about SECOND', m2, m2);
|
|
87
|
+
const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 0 });
|
|
88
|
+
const pairs = recallEntries(compiled.messages);
|
|
89
|
+
assert.equal(pairs.length, 3, 'should emit 3 distinct recall pairs');
|
|
90
|
+
// Order should follow source position (m1 < m2 < m3), not seed order.
|
|
91
|
+
assert.match(pairs[0].a, /FIRST/, 'first pair must cover m1');
|
|
92
|
+
assert.match(pairs[1].a, /SECOND/, 'second pair must cover m2');
|
|
93
|
+
assert.match(pairs[2].a, /THIRD/, 'third pair must cover m3');
|
|
94
|
+
// No --- separators (the legacy concat-marker should be gone).
|
|
95
|
+
for (const p of pairs) {
|
|
96
|
+
assert.ok(!p.a.includes('---'), `pair ${p.q} unexpectedly contains '---' separator`);
|
|
97
|
+
}
|
|
98
|
+
manager.close();
|
|
99
|
+
});
|
|
100
|
+
it('renders the recall header template with summary id by default', async () => {
|
|
101
|
+
const strategy = new TestableStrategy({
|
|
102
|
+
headWindowTokens: 0,
|
|
103
|
+
recentWindowTokens: 8,
|
|
104
|
+
});
|
|
105
|
+
const manager = await ContextManager.open({
|
|
106
|
+
path: TEST_STORE_PATH,
|
|
107
|
+
strategy,
|
|
108
|
+
});
|
|
109
|
+
const m1 = manager.addMessage('user', textBlock('foo'));
|
|
110
|
+
manager.addMessage('user', textBlock('latest ' + 'X'.repeat(50)));
|
|
111
|
+
const seeded = strategy.seedL1Against('content', m1, m1);
|
|
112
|
+
const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 0 });
|
|
113
|
+
const pairs = recallEntries(compiled.messages);
|
|
114
|
+
assert.equal(pairs.length, 1);
|
|
115
|
+
assert.equal(pairs[0].q, `[Recall ${seeded.id}]`, 'default template should be [Recall {id}]');
|
|
116
|
+
});
|
|
117
|
+
it('honors a custom recall header template with all substitutions', async () => {
|
|
118
|
+
const strategy = new TestableStrategy({
|
|
119
|
+
headWindowTokens: 0,
|
|
120
|
+
recentWindowTokens: 8,
|
|
121
|
+
recallHeaderTemplate: 'memory {id} (L{level}) covering {first}..{last}',
|
|
122
|
+
});
|
|
123
|
+
const manager = await ContextManager.open({
|
|
124
|
+
path: TEST_STORE_PATH,
|
|
125
|
+
strategy,
|
|
126
|
+
});
|
|
127
|
+
const m1 = manager.addMessage('user', textBlock('foo'));
|
|
128
|
+
const m2 = manager.addMessage('user', textBlock('bar'));
|
|
129
|
+
manager.addMessage('user', textBlock('latest ' + 'X'.repeat(50)));
|
|
130
|
+
const seeded = strategy.seedL1Against('content', m1, m2);
|
|
131
|
+
const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 0 });
|
|
132
|
+
const pairs = recallEntries(compiled.messages);
|
|
133
|
+
assert.equal(pairs[0].q, `memory ${seeded.id} (L1) covering ${m1}..${m2}`);
|
|
134
|
+
});
|
|
135
|
+
it('reverts to combined single-pair shape when positionedRecallPairs is false', async () => {
|
|
136
|
+
const strategy = new TestableStrategy({
|
|
137
|
+
headWindowTokens: 0,
|
|
138
|
+
recentWindowTokens: 8,
|
|
139
|
+
positionedRecallPairs: false,
|
|
140
|
+
});
|
|
141
|
+
const manager = await ContextManager.open({
|
|
142
|
+
path: TEST_STORE_PATH,
|
|
143
|
+
strategy,
|
|
144
|
+
});
|
|
145
|
+
const m1 = manager.addMessage('user', textBlock('a'));
|
|
146
|
+
const m2 = manager.addMessage('user', textBlock('b'));
|
|
147
|
+
const m3 = manager.addMessage('user', textBlock('c'));
|
|
148
|
+
manager.addMessage('user', textBlock('latest ' + 'X'.repeat(50)));
|
|
149
|
+
strategy.seedL1Against('A', m1, m1);
|
|
150
|
+
strategy.seedL1Against('B', m2, m2);
|
|
151
|
+
strategy.seedL1Against('C', m3, m3);
|
|
152
|
+
const compiled = await manager.compile({ maxTokens: 100_000, reserveForResponse: 0 });
|
|
153
|
+
const pairs = recallEntries(compiled.messages);
|
|
154
|
+
assert.equal(pairs.length, 1, 'legacy mode should emit a single combined pair');
|
|
155
|
+
assert.equal(pairs[0].q, 'What do you remember from earlier?');
|
|
156
|
+
assert.match(pairs[0].a, /A\n\n---\n\nB\n\n---\n\nC/);
|
|
157
|
+
});
|
|
158
|
+
it('stops emitting pairs when overall budget would be exceeded', async () => {
|
|
159
|
+
const strategy = new TestableStrategy({
|
|
160
|
+
headWindowTokens: 0,
|
|
161
|
+
recentWindowTokens: 8,
|
|
162
|
+
});
|
|
163
|
+
const manager = await ContextManager.open({
|
|
164
|
+
path: TEST_STORE_PATH,
|
|
165
|
+
strategy,
|
|
166
|
+
});
|
|
167
|
+
const m1 = manager.addMessage('user', textBlock('a'));
|
|
168
|
+
const m2 = manager.addMessage('user', textBlock('b'));
|
|
169
|
+
const m3 = manager.addMessage('user', textBlock('c'));
|
|
170
|
+
manager.addMessage('user', textBlock('latest ' + 'X'.repeat(50)));
|
|
171
|
+
// Each summary is ~400 chars (~100 tokens). Three pairs ≈ 300+ tokens of recall.
|
|
172
|
+
const big = 'X'.repeat(400);
|
|
173
|
+
strategy.seedL1Against(big + ' first', m1, m1);
|
|
174
|
+
strategy.seedL1Against(big + ' second', m2, m2);
|
|
175
|
+
strategy.seedL1Against(big + ' third', m3, m3);
|
|
176
|
+
const compiled = await manager.compile({ maxTokens: 250, reserveForResponse: 0 });
|
|
177
|
+
const pairs = recallEntries(compiled.messages);
|
|
178
|
+
assert.ok(pairs.length > 0 && pairs.length < 3, `expected partial recall emission under tight budget, got ${pairs.length}`);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=recall-positioning.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recall-positioning.test.js","sourceRoot":"","sources":["../../test/recall-positioning.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the search API (audit gap #7).
|
|
3
|
+
*
|
|
4
|
+
* Substring + regex search over summary content, plus single-summary
|
|
5
|
+
* lookup by id. Exposed through ContextManager passthrough so callers
|
|
6
|
+
* (e.g. agent-framework MCPL host) can build `memory_search` /
|
|
7
|
+
* `memory_read` agent tools on top.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=search.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.test.d.ts","sourceRoot":"","sources":["../../test/search.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|