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.
Files changed (84) hide show
  1. package/LICENSE +6 -2
  2. package/README.md +1 -1
  3. package/dist/extensions/dashboard-server/dashboard-client-core.js +201 -0
  4. package/dist/extensions/dashboard-server/dashboard-client-game.js +241 -0
  5. package/dist/extensions/dashboard-server/dashboard-client-repos.js +212 -0
  6. package/dist/extensions/dashboard-server/dashboard-client.js +19 -0
  7. package/dist/extensions/dashboard-server/html.js +2 -621
  8. package/dist/extensions/dashboard-server/routes-core.js +62 -0
  9. package/dist/extensions/dashboard-server/routes-game.js +323 -0
  10. package/dist/extensions/dashboard-server/routes-repo.js +170 -0
  11. package/dist/extensions/dashboard-server/routes-sessions.js +159 -0
  12. package/dist/extensions/dashboard-server/routes.js +10 -0
  13. package/dist/extensions/dashboard-server/server.js +26 -623
  14. package/dist/extensions/mega-commands.js +4 -3
  15. package/dist/extensions/mega-events/agent-handlers.js +2 -1
  16. package/dist/extensions/mega-events/compact-handlers.js +26 -0
  17. package/dist/extensions/mega-events/session-handlers.js +2 -1
  18. package/dist/extensions/mega-pipeline/compact.js +3 -2
  19. package/dist/extensions/mega-runtime/state.js +7 -7
  20. package/dist/src/dedup/raptor/multilevel.js +172 -0
  21. package/dist/src/dedup/raptor/multilevel.test.js +203 -0
  22. package/dist/src/dedup/raptor/promote.test.js +5 -5
  23. package/dist/src/dedup/raptor/retrieval.js +1 -1
  24. package/dist/src/dedup/sprint12.test.js +7 -7
  25. package/dist/src/dedup-engine.test.js +29 -29
  26. package/dist/src/e2e.test.js +38 -38
  27. package/dist/src/engine.js +3 -3
  28. package/dist/src/engine.test.js +6 -6
  29. package/dist/src/importance.js +197 -0
  30. package/dist/src/importance.test.js +372 -0
  31. package/dist/src/ratio.bench.test.js +18 -18
  32. package/dist/src/recall.js +6 -5
  33. package/dist/src/recall.test.js +85 -27
  34. package/dist/src/sprint14.test.js +2 -2
  35. package/dist/src/store/migrate.test.js +5 -5
  36. package/dist/src/store/sprint10.test.js +5 -5
  37. package/dist/src/store/sqlite/global-index.js +5 -174
  38. package/dist/src/store/sqlite/global-sessions.js +190 -0
  39. package/dist/src/vector-read.js +168 -0
  40. package/dist/src/vector-search.js +191 -0
  41. package/dist/src/vectorStore.js +10 -297
  42. package/dist/src/vectorStore.test.js +32 -32
  43. package/extensions/dashboard-server/dashboard-client-core.ts +202 -0
  44. package/extensions/dashboard-server/dashboard-client-game.ts +242 -0
  45. package/extensions/dashboard-server/dashboard-client-repos.ts +213 -0
  46. package/extensions/dashboard-server/dashboard-client.ts +21 -0
  47. package/extensions/dashboard-server/html.ts +2 -621
  48. package/extensions/dashboard-server/routes-core.ts +113 -0
  49. package/extensions/dashboard-server/routes-game.ts +386 -0
  50. package/extensions/dashboard-server/routes-repo.ts +212 -0
  51. package/extensions/dashboard-server/routes-sessions.ts +195 -0
  52. package/extensions/dashboard-server/routes.ts +13 -0
  53. package/extensions/dashboard-server/server.ts +37 -700
  54. package/extensions/mega-commands.ts +4 -3
  55. package/extensions/mega-events/agent-handlers.ts +2 -1
  56. package/extensions/mega-events/compact-handlers.ts +28 -0
  57. package/extensions/mega-events/session-handlers.ts +2 -1
  58. package/extensions/mega-pipeline/compact.ts +3 -2
  59. package/extensions/mega-runtime/state.ts +7 -7
  60. package/extensions/openclaw-mega-compact.ts +2 -2
  61. package/package.json +2 -2
  62. package/src/dedup/raptor/multilevel.test.ts +278 -0
  63. package/src/dedup/raptor/multilevel.ts +246 -0
  64. package/src/dedup/raptor/promote.test.ts +5 -5
  65. package/src/dedup/raptor/retrieval.ts +1 -1
  66. package/src/dedup/sprint12.test.ts +7 -7
  67. package/src/dedup-engine.test.ts +30 -30
  68. package/src/e2e.test.ts +38 -38
  69. package/src/engine.test.ts +6 -6
  70. package/src/engine.ts +3 -3
  71. package/src/importance.test.ts +538 -0
  72. package/src/importance.ts +312 -0
  73. package/src/ratio.bench.test.ts +18 -18
  74. package/src/recall.test.ts +101 -29
  75. package/src/recall.ts +9 -9
  76. package/src/sprint14.test.ts +2 -2
  77. package/src/store/migrate.test.ts +5 -5
  78. package/src/store/sprint10.test.ts +5 -5
  79. package/src/store/sqlite/global-index.ts +18 -290
  80. package/src/store/sqlite/global-sessions.ts +291 -0
  81. package/src/vector-read.ts +237 -0
  82. package/src/vector-search.ts +231 -0
  83. package/src/vectorStore.test.ts +32 -32
  84. 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.search()
127
- const hits = s.search(SESS, "server.ts memory leak JWT auth", 5);
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.search("sess_multi_a", "database connection pooling", 5);
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.list(SESS).length, 1, "only one checkpoint stored");
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.list(SESS).length, 2, "both checkpoints stored");
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.list(SESS).length, 1, "only one checkpoint after semantic dedup");
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.search(SESS, "parser tokenization source code files", 4);
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.semDedup(SESS, 0.85);
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.list(SESS);
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.search(SESS, "cache parsed ast nodes frontend virtualized list", 10);
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.semDedup(SESS, 0.85);
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.markInjected(SESS, cpId);
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.wasInjected(SESS, cpId), true, "wasInjected returns true for injected checkpoint");
373
- assert.equal(s.wasInjected(SESS, "chkpt_999"), false, "wasInjected returns false for non-injected");
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.topSimilar(SESS, 3);
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.list(SESS);
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.topSimilar(SESS, 2);
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.topSimilar("sess_empty", 5), []);
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.topSimilar("sess_one", 5), []);
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.list(SESS)[0];
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.list(SESS);
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.stats(SESS);
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.markInjected(SESS, "chkpt_001");
556
- const st2 = s.stats(SESS);
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.stats("sess_nothing");
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.list(sess.id);
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.search("sess_concurrent_a", "authentication JWT", 5);
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.search("sess_concurrent_b", "database queries indexes", 5);
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.search("sess_concurrent_c", "canvas rendering bug", 5);
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.list("sess_empty_msgs").length, 0);
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.search(SESS, "module feature fix review", 3);
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.search(SESS, "中文 translation 本地化", 3);
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.list("sess_all_preserved").length, 0, "no checkpoints stored");
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.list(SESS).length, 2, "both checkpoints stored when L0 is off");
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.list(SESS).length, 2, "both checkpoints stored (mark only)");
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.list(SESS);
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.list(SESS).length, 2, "both stored");
834
+ assert.equal(vectorList(s,SESS).length, 2, "both stored");
835
835
  });
836
836
 
837
837
  // ---------------------------------------------------------------------------
@@ -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.search(SESS, "bug src/server.ts", 5).length, 1);
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.search(SESS, "parser", 10).length, 1);
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.search(SESS, "x", 5).length, 0);
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.markInjected(SESS, first.hits[0].checkpoint.checkpointId);
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.search("sess_extr", "auth refactor", 5);
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.search(input.sessionId, input.query, input.limit ?? 3);
219
- const newHits = input.skipInjected === false ? hits : hits.filter((h) => !store.wasInjected(input.sessionId, h.checkpoint.checkpointId));
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