pi-mega-compact 0.8.21 → 0.8.23
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/LICENSE +6 -2
- package/README.md +1 -1
- package/dist/extensions/dashboard-server/dashboard-client-core.js +201 -0
- package/dist/extensions/dashboard-server/dashboard-client-game.js +241 -0
- package/dist/extensions/dashboard-server/dashboard-client-repos.js +212 -0
- package/dist/extensions/dashboard-server/dashboard-client.js +19 -0
- package/dist/extensions/dashboard-server/html.js +2 -621
- package/dist/extensions/dashboard-server/routes-core.js +62 -0
- package/dist/extensions/dashboard-server/routes-game.js +323 -0
- package/dist/extensions/dashboard-server/routes-repo.js +170 -0
- package/dist/extensions/dashboard-server/routes-sessions.js +159 -0
- package/dist/extensions/dashboard-server/routes.js +10 -0
- package/dist/extensions/dashboard-server/server.js +26 -623
- package/dist/extensions/mega-commands.js +4 -3
- package/dist/extensions/mega-events/agent-handlers.js +2 -1
- package/dist/extensions/mega-events/compact-handlers.js +26 -0
- package/dist/extensions/mega-events/session-handlers.js +2 -1
- package/dist/extensions/mega-pipeline/compact.js +3 -2
- package/dist/extensions/mega-runtime/state.js +7 -7
- package/dist/src/dedup/raptor/multilevel.js +172 -0
- package/dist/src/dedup/raptor/multilevel.test.js +203 -0
- package/dist/src/dedup/raptor/promote.test.js +5 -5
- package/dist/src/dedup/raptor/retrieval.js +1 -1
- package/dist/src/dedup/sprint12.test.js +7 -7
- package/dist/src/dedup-engine.test.js +29 -29
- package/dist/src/e2e.test.js +38 -38
- package/dist/src/engine.js +3 -3
- package/dist/src/engine.test.js +6 -6
- package/dist/src/importance.js +197 -0
- package/dist/src/importance.test.js +372 -0
- package/dist/src/ratio.bench.test.js +18 -18
- package/dist/src/recall.js +6 -5
- package/dist/src/recall.test.js +85 -27
- package/dist/src/sprint14.test.js +2 -2
- package/dist/src/store/migrate.test.js +5 -5
- package/dist/src/store/sprint10.test.js +5 -5
- package/dist/src/store/sqlite/global-index.js +5 -174
- package/dist/src/store/sqlite/global-sessions.js +190 -0
- package/dist/src/vector-read.js +168 -0
- package/dist/src/vector-search.js +191 -0
- package/dist/src/vectorStore.js +10 -297
- package/dist/src/vectorStore.test.js +32 -32
- package/extensions/dashboard-server/dashboard-client-core.ts +202 -0
- package/extensions/dashboard-server/dashboard-client-game.ts +242 -0
- package/extensions/dashboard-server/dashboard-client-repos.ts +213 -0
- package/extensions/dashboard-server/dashboard-client.ts +21 -0
- package/extensions/dashboard-server/html.ts +2 -621
- package/extensions/dashboard-server/routes-core.ts +113 -0
- package/extensions/dashboard-server/routes-game.ts +386 -0
- package/extensions/dashboard-server/routes-repo.ts +212 -0
- package/extensions/dashboard-server/routes-sessions.ts +195 -0
- package/extensions/dashboard-server/routes.ts +13 -0
- package/extensions/dashboard-server/server.ts +37 -700
- package/extensions/mega-commands.ts +4 -3
- package/extensions/mega-events/agent-handlers.ts +2 -1
- package/extensions/mega-events/compact-handlers.ts +28 -0
- package/extensions/mega-events/session-handlers.ts +2 -1
- package/extensions/mega-pipeline/compact.ts +3 -2
- package/extensions/mega-runtime/state.ts +7 -7
- package/extensions/openclaw-mega-compact.ts +2 -2
- package/package.json +2 -2
- package/src/dedup/raptor/multilevel.test.ts +278 -0
- package/src/dedup/raptor/multilevel.ts +246 -0
- package/src/dedup/raptor/promote.test.ts +5 -5
- package/src/dedup/raptor/retrieval.ts +1 -1
- package/src/dedup/sprint12.test.ts +7 -7
- package/src/dedup-engine.test.ts +30 -30
- package/src/e2e.test.ts +38 -38
- package/src/engine.test.ts +6 -6
- package/src/engine.ts +3 -3
- package/src/importance.test.ts +538 -0
- package/src/importance.ts +312 -0
- package/src/ratio.bench.test.ts +18 -18
- package/src/recall.test.ts +101 -29
- package/src/recall.ts +9 -9
- package/src/sprint14.test.ts +2 -2
- package/src/store/migrate.test.ts +5 -5
- package/src/store/sprint10.test.ts +5 -5
- package/src/store/sqlite/global-index.ts +18 -290
- package/src/store/sqlite/global-sessions.ts +291 -0
- package/src/vector-read.ts +237 -0
- package/src/vector-search.ts +231 -0
- package/src/vectorStore.test.ts +32 -32
- package/src/vectorStore.ts +29 -356
package/src/e2e.test.ts
CHANGED
|
@@ -10,7 +10,7 @@ import assert from "node:assert/strict";
|
|
|
10
10
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
11
11
|
import { tmpdir } from "node:os";
|
|
12
12
|
import { join } from "node:path";
|
|
13
|
-
import { VectorStore, computeRegionHash } from "./vectorStore.js";
|
|
13
|
+
import { VectorStore, computeRegionHash, vectorList, vectorSemDedup, vectorWasInjected, vectorMarkInjected, vectorTopSimilar, vectorStats, vectorSearch } from "./vectorStore.js";
|
|
14
14
|
import { compactSession, recall, supersededCount } from "./engine.js";
|
|
15
15
|
import { loadDedupConfig, type DedupConfigShape } from "./config/dedup.js";
|
|
16
16
|
import { defaultEmbedder } from "./embedder.js";
|
|
@@ -123,8 +123,8 @@ test("1. Full compaction pipeline: SUPERSEDE → COLLAPSE → CLUSTER", () => {
|
|
|
123
123
|
assert.ok(r.checkpointId, "checkpointId should be set");
|
|
124
124
|
assert.match(r.checkpointId!, /^chkpt_001$/);
|
|
125
125
|
|
|
126
|
-
// Verify: checkpoint is searchable via store
|
|
127
|
-
const hits = s
|
|
126
|
+
// Verify: checkpoint is searchable via vectorSearch(store, )
|
|
127
|
+
const hits = vectorSearch(s, SESS, "server.ts memory leak JWT auth", 5);
|
|
128
128
|
assert.ok(hits.length > 0, "checkpoint should be searchable");
|
|
129
129
|
assert.equal(hits[0].checkpoint.checkpointId, r.checkpointId);
|
|
130
130
|
});
|
|
@@ -164,7 +164,7 @@ test("2. Multi-session dedup: identical content in same session detected by L0",
|
|
|
164
164
|
`dedup reason should be L0 (regionHash or contentHash), got ${rB.dedupReason}`);
|
|
165
165
|
|
|
166
166
|
// Search should still return the checkpoint
|
|
167
|
-
const hits = s
|
|
167
|
+
const hits = vectorSearch(s, "sess_multi_a", "database connection pooling", 5);
|
|
168
168
|
assert.ok(hits.length > 0, "search returns the deduped result");
|
|
169
169
|
assert.equal(hits[0].checkpoint.checkpointId, rA.checkpointId);
|
|
170
170
|
});
|
|
@@ -198,7 +198,7 @@ test("3. Near-duplicate detection: L1 MinHash/LSH catches one-word edits that L0
|
|
|
198
198
|
// L1 should catch the near-dup
|
|
199
199
|
assert.equal(r2.deduped, true, "L1 should catch the near-duplicate");
|
|
200
200
|
assert.equal(r2.reason, "l1MinHash", "dedup reason should be l1MinHash");
|
|
201
|
-
assert.equal(s
|
|
201
|
+
assert.equal(vectorList(s,SESS).length, 1, "only one checkpoint stored");
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
test("3b. Negative: L1 does NOT collapse genuinely different content", () => {
|
|
@@ -220,7 +220,7 @@ test("3b. Negative: L1 does NOT collapse genuinely different content", () => {
|
|
|
220
220
|
});
|
|
221
221
|
|
|
222
222
|
assert.equal(r2.deduped, false, "distinct content should not be deduped");
|
|
223
|
-
assert.equal(s
|
|
223
|
+
assert.equal(vectorList(s,SESS).length, 2, "both checkpoints stored");
|
|
224
224
|
});
|
|
225
225
|
|
|
226
226
|
// ---------------------------------------------------------------------------
|
|
@@ -254,7 +254,7 @@ test("4. Semantic dedup: L2 cosine catches highly similar content that L0/L1 mis
|
|
|
254
254
|
assert.equal(r2.deduped, true, "L2 should catch the high-similarity paraphrase");
|
|
255
255
|
assert.ok(r2.reason === "contentSimilarity" || r2.reason === "l1MinHash",
|
|
256
256
|
`dedup reason should be L2 contentSimilarity or L1 l1MinHash, got ${r2.reason}`);
|
|
257
|
-
assert.equal(s
|
|
257
|
+
assert.equal(vectorList(s,SESS).length, 1, "only one checkpoint after semantic dedup");
|
|
258
258
|
});
|
|
259
259
|
|
|
260
260
|
test("4b. MMR diversifies search results", () => {
|
|
@@ -280,7 +280,7 @@ test("4b. MMR diversifies search results", () => {
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
// Search for parser-related content — should return relevant results
|
|
283
|
-
const hits = s
|
|
283
|
+
const hits = vectorSearch(s, SESS, "parser tokenization source code files", 4);
|
|
284
284
|
assert.ok(hits.length >= 1, "should return at least one result");
|
|
285
285
|
|
|
286
286
|
// The top hit should be the parser checkpoint
|
|
@@ -311,23 +311,23 @@ test("5. SemDeDup offline cleanup: redundant rows marked removed, not deleted",
|
|
|
311
311
|
]);
|
|
312
312
|
|
|
313
313
|
// Run SemDeDup with threshold that catches the near-identical pair
|
|
314
|
-
const removed = s
|
|
314
|
+
const removed = vectorSemDedup(s,SESS, 0.85);
|
|
315
315
|
assert.equal(removed, 1, "one redundant row should be marked removed");
|
|
316
316
|
|
|
317
317
|
// Verify: the lower-tokenEstimate row is the one removed
|
|
318
|
-
const all = s
|
|
318
|
+
const all = vectorList(s,SESS);
|
|
319
319
|
const dropped = all.find((c) => c.dedupStatus === "removed");
|
|
320
320
|
assert.ok(dropped, "a row should have dedup_status='removed'");
|
|
321
321
|
assert.equal(dropped!.checkpointId, "chkpt_001", "lower tokenEstimate row removed");
|
|
322
322
|
assert.equal(dropped!.dedupStatus, "removed");
|
|
323
323
|
|
|
324
324
|
// Verify: search excludes removed rows (3 total, 1 removed → 2 active)
|
|
325
|
-
const hits = s
|
|
325
|
+
const hits = vectorSearch(s, SESS, "cache parsed ast nodes frontend virtualized list", 10);
|
|
326
326
|
assert.equal(hits.length, 2, "search excludes the removed row, returns 2 active");
|
|
327
327
|
assert.ok(hits.every((h) => h.checkpoint.dedupStatus !== "removed"), "no removed rows in search");
|
|
328
328
|
|
|
329
329
|
// Verify: idempotent re-run removes nothing new
|
|
330
|
-
const secondRun = s
|
|
330
|
+
const secondRun = vectorSemDedup(s,SESS, 0.85);
|
|
331
331
|
assert.equal(secondRun, 0, "idempotent re-run removes nothing new");
|
|
332
332
|
|
|
333
333
|
closeStore(dir);
|
|
@@ -359,7 +359,7 @@ test("6. Recall + inline with dedup sentinel: injected checkpoints are skipped",
|
|
|
359
359
|
|
|
360
360
|
// Mark it injected
|
|
361
361
|
const cpId = first.hits[0].checkpoint.checkpointId;
|
|
362
|
-
s
|
|
362
|
+
vectorMarkInjected(s,SESS, cpId);
|
|
363
363
|
|
|
364
364
|
// Recall again — should skip the injected checkpoint
|
|
365
365
|
const second = recall({ sessionId: SESS, query: "vectorStore dedup cascade", limit: 5, skipInjected: true }, s);
|
|
@@ -369,8 +369,8 @@ test("6. Recall + inline with dedup sentinel: injected checkpoints are skipped",
|
|
|
369
369
|
assert.equal(second.hits.length, 1, "hits still surface without skipInjected");
|
|
370
370
|
|
|
371
371
|
// Verify wasInjected
|
|
372
|
-
assert.equal(s
|
|
373
|
-
assert.equal(s
|
|
372
|
+
assert.equal(vectorWasInjected(s,SESS, cpId), true, "wasInjected returns true for injected checkpoint");
|
|
373
|
+
assert.equal(vectorWasInjected(s,SESS, "chkpt_999"), false, "wasInjected returns false for non-injected");
|
|
374
374
|
});
|
|
375
375
|
|
|
376
376
|
// ---------------------------------------------------------------------------
|
|
@@ -400,12 +400,12 @@ test("7. topSimilar: returns n most cosine-similar checkpoints", () => {
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
// Call topSimilar(3)
|
|
403
|
-
const hits = s
|
|
403
|
+
const hits = vectorTopSimilar(s,SESS, 3);
|
|
404
404
|
assert.ok(hits.length <= 3, "should respect n limit");
|
|
405
405
|
assert.ok(hits.length > 0, "should return results");
|
|
406
406
|
|
|
407
407
|
// Verify self-exclusion: the most recent checkpoint should not be in results
|
|
408
|
-
const all = s
|
|
408
|
+
const all = vectorList(s,SESS);
|
|
409
409
|
const ordered = [...all].sort((a, b) => a.checkpointId.localeCompare(b.checkpointId));
|
|
410
410
|
const current = ordered[ordered.length - 1];
|
|
411
411
|
assert.ok(!hits.some((h) => h.checkpoint.checkpointId === current.checkpointId), "current checkpoint excluded");
|
|
@@ -438,15 +438,15 @@ test("7b. topSimilar respects n limit strictly", () => {
|
|
|
438
438
|
});
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
const hits = s
|
|
441
|
+
const hits = vectorTopSimilar(s,SESS, 2);
|
|
442
442
|
assert.equal(hits.length, 2, "n limit respected");
|
|
443
443
|
});
|
|
444
444
|
|
|
445
445
|
test("7c. topSimilar returns empty for sessions with 0 or 1 checkpoints", () => {
|
|
446
446
|
const s = store();
|
|
447
|
-
assert.deepEqual(s
|
|
447
|
+
assert.deepEqual(vectorTopSimilar(s,"sess_empty", 5), []);
|
|
448
448
|
s.add({ sessionId: "sess_one", summary: "solo", regionText: "only checkpoint here", timestamp: 1 });
|
|
449
|
-
assert.deepEqual(s
|
|
449
|
+
assert.deepEqual(vectorTopSimilar(s,"sess_one", 5), []);
|
|
450
450
|
});
|
|
451
451
|
|
|
452
452
|
// ---------------------------------------------------------------------------
|
|
@@ -476,7 +476,7 @@ test("8. Compression round-trip: decompression produces original content", () =>
|
|
|
476
476
|
});
|
|
477
477
|
|
|
478
478
|
// Retrieve and verify decompression
|
|
479
|
-
const cp = s
|
|
479
|
+
const cp = vectorList(s,SESS)[0];
|
|
480
480
|
assert.ok(cp.compressedOriginal instanceof Buffer, "compressedOriginal is a Buffer");
|
|
481
481
|
|
|
482
482
|
const restored = decompressSmart(cp.compressedOriginal as Buffer).toString("utf-8");
|
|
@@ -499,7 +499,7 @@ test("8b. Compression round-trip with different content sizes", () => {
|
|
|
499
499
|
const large = "detailed region text with lots of context about the codebase. ".repeat(200);
|
|
500
500
|
s.add({ sessionId: SESS, summary: "large", regionText: large, timestamp: 3 });
|
|
501
501
|
|
|
502
|
-
const checkpoints = s
|
|
502
|
+
const checkpoints = vectorList(s,SESS);
|
|
503
503
|
assert.equal(checkpoints.length, 3);
|
|
504
504
|
|
|
505
505
|
// Verify each round-trips correctly
|
|
@@ -543,7 +543,7 @@ test("9. Store stats and metrics reflect actual state after compactions", () =>
|
|
|
543
543
|
assert.equal(r3.deduped, false);
|
|
544
544
|
|
|
545
545
|
// Get stats
|
|
546
|
-
const st = s
|
|
546
|
+
const st = vectorStats(s,SESS);
|
|
547
547
|
assert.equal(st.checkpointCount, 3, "three checkpoints stored");
|
|
548
548
|
assert.ok(st.totalTokenEstimate > 0, "totalTokenEstimate should be positive");
|
|
549
549
|
assert.equal(st.lastCheckpointId, "chkpt_003", "last checkpoint id correct");
|
|
@@ -552,15 +552,15 @@ test("9. Store stats and metrics reflect actual state after compactions", () =>
|
|
|
552
552
|
assert.equal(st.dedupHitRate, 0, "dedupHitRate is 0 with no injections");
|
|
553
553
|
|
|
554
554
|
// Mark one injected and re-check
|
|
555
|
-
s
|
|
556
|
-
const st2 = s
|
|
555
|
+
vectorMarkInjected(s,SESS, "chkpt_001");
|
|
556
|
+
const st2 = vectorStats(s,SESS);
|
|
557
557
|
assert.equal(st2.injectedCount, 1, "one injection tracked");
|
|
558
558
|
assert.ok(Math.abs(st2.dedupHitRate - 1 / 3) < 1e-9, "dedupHitRate = injected/checkpoints");
|
|
559
559
|
});
|
|
560
560
|
|
|
561
561
|
test("9b. Stats on empty session returns zeros", () => {
|
|
562
562
|
const s = store();
|
|
563
|
-
const st = s
|
|
563
|
+
const st = vectorStats(s,"sess_nothing");
|
|
564
564
|
assert.equal(st.checkpointCount, 0);
|
|
565
565
|
assert.equal(st.totalTokenEstimate, 0);
|
|
566
566
|
assert.equal(st.lastCheckpointId, undefined);
|
|
@@ -605,20 +605,20 @@ test("10. Concurrent sessions: no cross-contamination", () => {
|
|
|
605
605
|
|
|
606
606
|
// Verify: each session has exactly one checkpoint
|
|
607
607
|
sessions.forEach((sess) => {
|
|
608
|
-
const cps = s
|
|
608
|
+
const cps = vectorList(s,sess.id);
|
|
609
609
|
assert.equal(cps.length, 1, `${sess.id} should have 1 checkpoint`);
|
|
610
610
|
});
|
|
611
611
|
|
|
612
612
|
// Verify: search in one session doesn't return another session's checkpoints
|
|
613
|
-
const hitsA = s
|
|
613
|
+
const hitsA = vectorSearch(s, "sess_concurrent_a", "authentication JWT", 5);
|
|
614
614
|
assert.ok(hitsA.every((h) => h.checkpoint.sessionId === "sess_concurrent_a"),
|
|
615
615
|
"search in A returns only A's checkpoints");
|
|
616
616
|
|
|
617
|
-
const hitsB = s
|
|
617
|
+
const hitsB = vectorSearch(s, "sess_concurrent_b", "database queries indexes", 5);
|
|
618
618
|
assert.ok(hitsB.every((h) => h.checkpoint.sessionId === "sess_concurrent_b"),
|
|
619
619
|
"search in B returns only B's checkpoints");
|
|
620
620
|
|
|
621
|
-
const hitsC = s
|
|
621
|
+
const hitsC = vectorSearch(s, "sess_concurrent_c", "canvas rendering bug", 5);
|
|
622
622
|
assert.ok(hitsC.every((h) => h.checkpoint.sessionId === "sess_concurrent_c"),
|
|
623
623
|
"search in C returns only C's checkpoints");
|
|
624
624
|
});
|
|
@@ -633,7 +633,7 @@ test("11a. Empty session (no messages) — compactSession should skip", () => {
|
|
|
633
633
|
assert.equal(r.skipped, true, "empty session should be skipped");
|
|
634
634
|
assert.equal(r.summary, "");
|
|
635
635
|
assert.equal(r.regionHash, "");
|
|
636
|
-
assert.equal(s
|
|
636
|
+
assert.equal(vectorList(s,"sess_empty_msgs").length, 0);
|
|
637
637
|
});
|
|
638
638
|
|
|
639
639
|
test("11b. Single message session — should skip or handle gracefully", () => {
|
|
@@ -671,7 +671,7 @@ test("11c. Very large region text — should still compact and store", () => {
|
|
|
671
671
|
assert.ok(r.summary.length > 0, "summary produced");
|
|
672
672
|
|
|
673
673
|
// Verify it's searchable
|
|
674
|
-
const hits = s
|
|
674
|
+
const hits = vectorSearch(s, SESS, "module feature fix review", 3);
|
|
675
675
|
assert.ok(hits.length > 0, "large checkpoint is searchable");
|
|
676
676
|
});
|
|
677
677
|
|
|
@@ -691,7 +691,7 @@ test("11d. Unicode and emoji content — should normalize and hash correctly", (
|
|
|
691
691
|
assert.ok(r.regionHash.length > 0, "regionHash computed for unicode content");
|
|
692
692
|
|
|
693
693
|
// Verify the checkpoint exists and is searchable
|
|
694
|
-
const hits = s
|
|
694
|
+
const hits = vectorSearch(s, SESS, "中文 translation 本地化", 3);
|
|
695
695
|
assert.ok(hits.length > 0, "unicode checkpoint is searchable");
|
|
696
696
|
|
|
697
697
|
// Verify regionHash is deterministic for the same unicode content
|
|
@@ -717,7 +717,7 @@ test("11e. All messages in preserve-recent window — nothing to compact", () =>
|
|
|
717
717
|
timestamp: 1,
|
|
718
718
|
}, s);
|
|
719
719
|
assert.equal(r.skipped, true, "should skip when keepFrom=0 (nothing to compact)");
|
|
720
|
-
assert.equal(s
|
|
720
|
+
assert.equal(vectorList(s,"sess_all_preserved").length, 0, "no checkpoints stored");
|
|
721
721
|
});
|
|
722
722
|
|
|
723
723
|
// ---------------------------------------------------------------------------
|
|
@@ -749,7 +749,7 @@ test("12a. L0_ENABLED=false — L0 does not collapse exact dups", () => {
|
|
|
749
749
|
assert.equal(r1.deduped, false);
|
|
750
750
|
// With L0 off (and L1/L2 off too), near-dup should NOT be collapsed
|
|
751
751
|
assert.equal(r2.deduped, false, "L0 disabled → near-dup not collapsed");
|
|
752
|
-
assert.equal(s
|
|
752
|
+
assert.equal(vectorList(s,SESS).length, 2, "both checkpoints stored when L0 is off");
|
|
753
753
|
});
|
|
754
754
|
|
|
755
755
|
test("12b. MARK_ONLY_L1=true — L1 records but does not collapse", () => {
|
|
@@ -773,10 +773,10 @@ test("12b. MARK_ONLY_L1=true — L1 records but does not collapse", () => {
|
|
|
773
773
|
assert.equal(r1.deduped, false);
|
|
774
774
|
// MARK_ONLY_L1 → L1 detects the near-dup but does NOT collapse
|
|
775
775
|
assert.equal(r2.deduped, false, "MARK_ONLY_L1 → not collapsed");
|
|
776
|
-
assert.equal(s
|
|
776
|
+
assert.equal(vectorList(s,SESS).length, 2, "both checkpoints stored (mark only)");
|
|
777
777
|
|
|
778
778
|
// Both should be active (not removed)
|
|
779
|
-
const all = s
|
|
779
|
+
const all = vectorList(s,SESS);
|
|
780
780
|
assert.ok(all.every((c) => c.dedupStatus === "active"), "both rows active under MARK_ONLY");
|
|
781
781
|
});
|
|
782
782
|
|
|
@@ -831,7 +831,7 @@ test("12d. All tiers disabled — nothing deduped, everything stored", () => {
|
|
|
831
831
|
|
|
832
832
|
assert.equal(r1.deduped, false);
|
|
833
833
|
assert.equal(r2.deduped, false, "all tiers off → nothing deduped");
|
|
834
|
-
assert.equal(s
|
|
834
|
+
assert.equal(vectorList(s,SESS).length, 2, "both stored");
|
|
835
835
|
});
|
|
836
836
|
|
|
837
837
|
// ---------------------------------------------------------------------------
|
package/src/engine.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import { VectorStore } from "./vectorStore.js";
|
|
6
|
+
import { VectorStore, vectorMarkInjected, vectorSearch } from "./vectorStore.js";
|
|
7
7
|
import { compactSession, recall, mergeSummary, supersededCount } from "./engine.js";
|
|
8
8
|
import type { EngineMessage } from "./types.js";
|
|
9
9
|
|
|
@@ -40,7 +40,7 @@ test("compactSession supersedes then persists a checkpoint", () => {
|
|
|
40
40
|
assert.equal(supersededCount(messages.slice(0, 4)), 1);
|
|
41
41
|
assert.equal(r.compactedFrom, 4);
|
|
42
42
|
// The persisted checkpoint is searchable.
|
|
43
|
-
assert.equal(s
|
|
43
|
+
assert.equal(vectorSearch(s, SESS, "bug src/server.ts", 5).length, 1);
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test("compactSession is idempotent on identical region (dedup sentinel)", () => {
|
|
@@ -56,14 +56,14 @@ test("compactSession is idempotent on identical region (dedup sentinel)", () =>
|
|
|
56
56
|
assert.equal(r1.deduped, false);
|
|
57
57
|
assert.equal(r2.deduped, true);
|
|
58
58
|
assert.equal(r1.checkpointId, r2.checkpointId);
|
|
59
|
-
assert.equal(s
|
|
59
|
+
assert.equal(vectorSearch(s, SESS, "parser", 10).length, 1);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
test("compactSession skipped when slice is empty", () => {
|
|
63
63
|
const s = store();
|
|
64
64
|
const r = compactSession({ sessionId: SESS, messages: [msg("user", "only tail")], keepFrom: 0 }, s);
|
|
65
65
|
assert.equal(r.skipped, true);
|
|
66
|
-
assert.equal(s
|
|
66
|
+
assert.equal(vectorSearch(s, SESS, "x", 5).length, 0);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
test("recall drops already-injected checkpoints", () => {
|
|
@@ -71,7 +71,7 @@ test("recall drops already-injected checkpoints", () => {
|
|
|
71
71
|
compactSession({ sessionId: SESS, messages: [msg("user", "investigated src/vectorStore.ts"), msg("assistant", "ok", "Edit")], keepFrom: 2, timestamp: 1 }, s);
|
|
72
72
|
const first = recall({ sessionId: SESS, query: "vectorStore", limit: 5, skipInjected: true }, s);
|
|
73
73
|
assert.equal(first.newHits.length, 1);
|
|
74
|
-
s
|
|
74
|
+
vectorMarkInjected(s,SESS, first.hits[0].checkpoint.checkpointId);
|
|
75
75
|
const second = recall({ sessionId: SESS, query: "vectorStore", limit: 5, skipInjected: true }, s);
|
|
76
76
|
assert.equal(second.newHits.length, 0);
|
|
77
77
|
// Without the skip flag, both hits still surface.
|
|
@@ -111,7 +111,7 @@ test("compactSession with useExtractive produces topicSummary on checkpoint", ()
|
|
|
111
111
|
assert.equal(r.skipped, false);
|
|
112
112
|
assert.ok(r.checkpointId, "checkpoint created");
|
|
113
113
|
// The checkpoint should have topicSummary populated
|
|
114
|
-
const hits = s
|
|
114
|
+
const hits = vectorSearch(s, "sess_extr", "auth refactor", 5);
|
|
115
115
|
assert.ok(hits.length > 0);
|
|
116
116
|
// topicSummary should be present on the stored checkpoint (via extractive path)
|
|
117
117
|
assert.ok(hits[0].checkpoint.topicSummary, "topicSummary should be populated when useExtractive is true");
|
package/src/engine.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { findSuperseded, supersede } from "./supersede.js";
|
|
|
16
16
|
import { summarizeMessages, mergeCompactSummaries, formatCompactSummary } from "./compact.js";
|
|
17
17
|
import { extractiveSummarize } from "./extractive.js";
|
|
18
18
|
import { estimateSessionTokens, estimateBlockTokens } from "./tokens.js";
|
|
19
|
-
import { computeRegionHash, VectorStore, type SearchHit } from "./vectorStore.js";
|
|
19
|
+
import { computeRegionHash, vectorWasInjected, vectorSearch, VectorStore, type SearchHit } from "./vectorStore.js";
|
|
20
20
|
import type { EngineMessage } from "./types.js";
|
|
21
21
|
|
|
22
22
|
export interface CompactInput {
|
|
@@ -215,8 +215,8 @@ export interface RecallResult {
|
|
|
215
215
|
* inject (Sprint 4 wires injection); this module only does the deduped search.
|
|
216
216
|
*/
|
|
217
217
|
export function recall(input: RecallInput, store: VectorStore = getDefaultStore()): RecallResult {
|
|
218
|
-
const hits = store
|
|
219
|
-
const newHits = input.skipInjected === false ? hits : hits.filter((h) => !store
|
|
218
|
+
const hits = vectorSearch(store, input.sessionId, input.query, input.limit ?? 3);
|
|
219
|
+
const newHits = input.skipInjected === false ? hits : hits.filter((h) => !vectorWasInjected(store, input.sessionId, h.checkpoint.checkpointId));
|
|
220
220
|
return { hits, newHits };
|
|
221
221
|
}
|
|
222
222
|
|