@claude-flow/memory 3.0.0-alpha.8 → 3.0.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/README.md +308 -37
- package/dist/agent-memory-scope.d.ts.map +1 -1
- package/dist/agent-memory-scope.js +10 -2
- package/dist/agent-memory-scope.js.map +1 -1
- package/dist/agentdb-adapter-key-isolation.test.d.ts +2 -0
- package/dist/agentdb-adapter-key-isolation.test.d.ts.map +1 -0
- package/dist/agentdb-adapter-key-isolation.test.js +175 -0
- package/dist/agentdb-adapter-key-isolation.test.js.map +1 -0
- package/dist/agentdb-adapter-upsert.test.d.ts +2 -0
- package/dist/agentdb-adapter-upsert.test.d.ts.map +1 -0
- package/dist/agentdb-adapter-upsert.test.js +216 -0
- package/dist/agentdb-adapter-upsert.test.js.map +1 -0
- package/dist/agentdb-adapter.d.ts +70 -1
- package/dist/agentdb-adapter.d.ts.map +1 -1
- package/dist/agentdb-adapter.js +365 -32
- package/dist/agentdb-adapter.js.map +1 -1
- package/dist/agentdb-backend.d.ts +25 -0
- package/dist/agentdb-backend.d.ts.map +1 -1
- package/dist/agentdb-backend.js +113 -28
- package/dist/agentdb-backend.js.map +1 -1
- package/dist/agentdb-retrieval-guard.d.ts +76 -0
- package/dist/agentdb-retrieval-guard.d.ts.map +1 -0
- package/dist/agentdb-retrieval-guard.js +77 -0
- package/dist/agentdb-retrieval-guard.js.map +1 -0
- package/dist/agentdb-retrieval-guard.test.d.ts +12 -0
- package/dist/agentdb-retrieval-guard.test.d.ts.map +1 -0
- package/dist/agentdb-retrieval-guard.test.js +129 -0
- package/dist/agentdb-retrieval-guard.test.js.map +1 -0
- package/dist/auto-memory-bridge.d.ts +12 -1
- package/dist/auto-memory-bridge.d.ts.map +1 -1
- package/dist/auto-memory-bridge.js +101 -7
- package/dist/auto-memory-bridge.js.map +1 -1
- package/dist/auto-memory-bridge.test.js +121 -15
- package/dist/auto-memory-bridge.test.js.map +1 -1
- package/dist/auto-memory-index-3224.test.d.ts +2 -0
- package/dist/auto-memory-index-3224.test.d.ts.map +1 -0
- package/dist/auto-memory-index-3224.test.js +77 -0
- package/dist/auto-memory-index-3224.test.js.map +1 -0
- package/dist/consolidator-embedding-benchmark.test.d.ts +39 -0
- package/dist/consolidator-embedding-benchmark.test.d.ts.map +1 -0
- package/dist/consolidator-embedding-benchmark.test.js +366 -0
- package/dist/consolidator-embedding-benchmark.test.js.map +1 -0
- package/dist/consolidator.d.ts +131 -0
- package/dist/consolidator.d.ts.map +1 -0
- package/dist/consolidator.js +309 -0
- package/dist/consolidator.js.map +1 -0
- package/dist/consolidator.test.d.ts +17 -0
- package/dist/consolidator.test.d.ts.map +1 -0
- package/dist/consolidator.test.js +376 -0
- package/dist/consolidator.test.js.map +1 -0
- package/dist/controller-registry.d.ts +290 -0
- package/dist/controller-registry.d.ts.map +1 -0
- package/dist/controller-registry.js +1095 -0
- package/dist/controller-registry.js.map +1 -0
- package/dist/controller-registry.test.d.ts +14 -0
- package/dist/controller-registry.test.d.ts.map +1 -0
- package/dist/controller-registry.test.js +818 -0
- package/dist/controller-registry.test.js.map +1 -0
- package/dist/database-provider.d.ts +14 -2
- package/dist/database-provider.d.ts.map +1 -1
- package/dist/database-provider.js +63 -3
- package/dist/database-provider.js.map +1 -1
- package/dist/database-provider.test.js +56 -3
- package/dist/database-provider.test.js.map +1 -1
- package/dist/entity-tagger.d.ts +31 -0
- package/dist/entity-tagger.d.ts.map +1 -0
- package/dist/entity-tagger.js +80 -0
- package/dist/entity-tagger.js.map +1 -0
- package/dist/entity-tagger.test.d.ts +12 -0
- package/dist/entity-tagger.test.d.ts.map +1 -0
- package/dist/entity-tagger.test.js +70 -0
- package/dist/entity-tagger.test.js.map +1 -0
- package/dist/fts5.test.d.ts +13 -0
- package/dist/fts5.test.d.ts.map +1 -0
- package/dist/fts5.test.js +144 -0
- package/dist/fts5.test.js.map +1 -0
- package/dist/graceful-retrieval.test.d.ts +14 -0
- package/dist/graceful-retrieval.test.d.ts.map +1 -0
- package/dist/graceful-retrieval.test.js +251 -0
- package/dist/graceful-retrieval.test.js.map +1 -0
- package/dist/hnsw-index.d.ts +62 -0
- package/dist/hnsw-index.d.ts.map +1 -1
- package/dist/hnsw-index.js +461 -16
- package/dist/hnsw-index.js.map +1 -1
- package/dist/hnsw-persistence.test.d.ts +13 -0
- package/dist/hnsw-persistence.test.d.ts.map +1 -0
- package/dist/hnsw-persistence.test.js +208 -0
- package/dist/hnsw-persistence.test.js.map +1 -0
- package/dist/hnsw-quantization.test.d.ts +17 -0
- package/dist/hnsw-quantization.test.d.ts.map +1 -0
- package/dist/hnsw-quantization.test.js +140 -0
- package/dist/hnsw-quantization.test.js.map +1 -0
- package/dist/hybrid-backend.d.ts +46 -2
- package/dist/hybrid-backend.d.ts.map +1 -1
- package/dist/hybrid-backend.js +120 -21
- package/dist/hybrid-backend.js.map +1 -1
- package/dist/hybrid-backend.test.js +139 -5
- package/dist/hybrid-backend.test.js.map +1 -1
- package/dist/index.d.ts +164 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +272 -30
- package/dist/index.js.map +1 -1
- package/dist/index.test.d.ts +10 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +71 -0
- package/dist/index.test.js.map +1 -0
- package/dist/json-security.d.ts +12 -0
- package/dist/json-security.d.ts.map +1 -0
- package/dist/json-security.js +20 -0
- package/dist/json-security.js.map +1 -0
- package/dist/learning-bridge.d.ts +29 -0
- package/dist/learning-bridge.d.ts.map +1 -1
- package/dist/learning-bridge.js +71 -6
- package/dist/learning-bridge.js.map +1 -1
- package/dist/learning-bridge.test.js +181 -1
- package/dist/learning-bridge.test.js.map +1 -1
- package/dist/memory-key.d.ts +3 -0
- package/dist/memory-key.d.ts.map +1 -0
- package/dist/memory-key.js +5 -0
- package/dist/memory-key.js.map +1 -0
- package/dist/migration.d.ts +19 -4
- package/dist/migration.d.ts.map +1 -1
- package/dist/migration.js +99 -24
- package/dist/migration.js.map +1 -1
- package/dist/migration.test.d.ts +11 -0
- package/dist/migration.test.d.ts.map +1 -0
- package/dist/migration.test.js +141 -0
- package/dist/migration.test.js.map +1 -0
- package/dist/mmr-benchmark.test.d.ts +2 -0
- package/dist/mmr-benchmark.test.d.ts.map +1 -0
- package/dist/mmr-benchmark.test.js +286 -0
- package/dist/mmr-benchmark.test.js.map +1 -0
- package/dist/mmr-rerank-perf.test.d.ts +2 -0
- package/dist/mmr-rerank-perf.test.d.ts.map +1 -0
- package/dist/mmr-rerank-perf.test.js +191 -0
- package/dist/mmr-rerank-perf.test.js.map +1 -0
- package/dist/persistent-sona.d.ts +144 -0
- package/dist/persistent-sona.d.ts.map +1 -0
- package/dist/persistent-sona.js +332 -0
- package/dist/persistent-sona.js.map +1 -0
- package/dist/rvf-backend.d.ts +53 -0
- package/dist/rvf-backend.d.ts.map +1 -0
- package/dist/rvf-backend.js +643 -0
- package/dist/rvf-backend.js.map +1 -0
- package/dist/rvf-learning-store.d.ts +139 -0
- package/dist/rvf-learning-store.d.ts.map +1 -0
- package/dist/rvf-learning-store.js +295 -0
- package/dist/rvf-learning-store.js.map +1 -0
- package/dist/rvf-migration.d.ts +45 -0
- package/dist/rvf-migration.d.ts.map +1 -0
- package/dist/rvf-migration.js +254 -0
- package/dist/rvf-migration.js.map +1 -0
- package/dist/smart-retrieval-scores.test.d.ts +2 -0
- package/dist/smart-retrieval-scores.test.d.ts.map +1 -0
- package/dist/smart-retrieval-scores.test.js +75 -0
- package/dist/smart-retrieval-scores.test.js.map +1 -0
- package/dist/smart-retrieval.d.ts +141 -0
- package/dist/smart-retrieval.d.ts.map +1 -0
- package/dist/smart-retrieval.js +389 -0
- package/dist/smart-retrieval.js.map +1 -0
- package/dist/smart-retrieval.test.d.ts +2 -0
- package/dist/smart-retrieval.test.d.ts.map +1 -0
- package/dist/smart-retrieval.test.js +418 -0
- package/dist/smart-retrieval.test.js.map +1 -0
- package/dist/sqlite-backend.d.ts +10 -0
- package/dist/sqlite-backend.d.ts.map +1 -1
- package/dist/sqlite-backend.js +101 -5
- package/dist/sqlite-backend.js.map +1 -1
- package/dist/sqljs-backend.d.ts +9 -0
- package/dist/sqljs-backend.d.ts.map +1 -1
- package/dist/sqljs-backend.js +91 -3
- package/dist/sqljs-backend.js.map +1 -1
- package/dist/tiered-memory.d.ts +171 -0
- package/dist/tiered-memory.d.ts.map +1 -0
- package/dist/tiered-memory.js +350 -0
- package/dist/tiered-memory.js.map +1 -0
- package/dist/tiered-memory.test.d.ts +9 -0
- package/dist/tiered-memory.test.d.ts.map +1 -0
- package/dist/tiered-memory.test.js +220 -0
- package/dist/tiered-memory.test.js.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +10 -8
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the regex entity tagger (ADR-147 P2, ruvnet/ruflo#2317).
|
|
3
|
+
*
|
|
4
|
+
* Goal of the tagger: a conservative third signal for hybridSearch.
|
|
5
|
+
* False negatives are fine (dense + sparse cover the rest); false
|
|
6
|
+
* positives would dilute the RRF score by adding noise rows. These
|
|
7
|
+
* tests pin the conservatism — generic prose returns nothing, and
|
|
8
|
+
* concrete structured tokens (email, URL, path, quote, proper-noun
|
|
9
|
+
* bigram) round-trip cleanly.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect } from 'vitest';
|
|
12
|
+
import { extractEntities } from './entity-tagger.js';
|
|
13
|
+
describe('extractEntities', () => {
|
|
14
|
+
it('returns empty for empty / nullish input', () => {
|
|
15
|
+
expect(extractEntities('')).toEqual([]);
|
|
16
|
+
expect(extractEntities(undefined)).toEqual([]);
|
|
17
|
+
});
|
|
18
|
+
it('returns empty for plain prose with no entities', () => {
|
|
19
|
+
expect(extractEntities('how do i debug this very slow query')).toEqual([]);
|
|
20
|
+
});
|
|
21
|
+
it('extracts email addresses', () => {
|
|
22
|
+
const got = extractEntities('contact alice@example.com or bob+ops@team.io');
|
|
23
|
+
expect(got).toContain('alice@example.com');
|
|
24
|
+
expect(got).toContain('bob+ops@team.io');
|
|
25
|
+
});
|
|
26
|
+
it('extracts URLs', () => {
|
|
27
|
+
const got = extractEntities('see https://example.com/path?x=1 and http://internal');
|
|
28
|
+
expect(got).toContain('https://example.com/path?x=1');
|
|
29
|
+
expect(got).toContain('http://internal');
|
|
30
|
+
});
|
|
31
|
+
it('extracts POSIX file paths', () => {
|
|
32
|
+
const got = extractEntities('open src/foo/bar.ts and ./scripts/build.mjs');
|
|
33
|
+
expect(got).toContain('src/foo/bar.ts');
|
|
34
|
+
expect(got).toContain('./scripts/build.mjs');
|
|
35
|
+
});
|
|
36
|
+
it('extracts Windows file paths', () => {
|
|
37
|
+
const got = extractEntities(String.raw `look at C:\Users\ruv\repo\file.ts`);
|
|
38
|
+
expect(got.some((e) => e.includes(String.raw `C:\Users\ruv\repo\file.ts`))).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
it('does not pick up "and/or" style false positives', () => {
|
|
41
|
+
expect(extractEntities('proceed and/or stop')).not.toContain('and/or');
|
|
42
|
+
});
|
|
43
|
+
it('extracts quoted phrases (both quote styles)', () => {
|
|
44
|
+
const got = extractEntities(`open "config alpha" then 'session beta'`);
|
|
45
|
+
expect(got).toContain('config alpha');
|
|
46
|
+
expect(got).toContain('session beta');
|
|
47
|
+
});
|
|
48
|
+
it('extracts proper-noun 2-grams', () => {
|
|
49
|
+
const got = extractEntities('hand off to Alice Smith and notify Acme Corp');
|
|
50
|
+
expect(got).toContain('Alice Smith');
|
|
51
|
+
expect(got).toContain('Acme Corp');
|
|
52
|
+
});
|
|
53
|
+
it('does not extract single capitalized words (too noisy)', () => {
|
|
54
|
+
expect(extractEntities('Reset the database now')).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
it('deduplicates repeats while preserving first-seen order', () => {
|
|
57
|
+
const got = extractEntities('Alice Smith filed Alice Smith again at alice@x.io. Alice Smith.');
|
|
58
|
+
// 'Alice Smith' should appear exactly once even though the source
|
|
59
|
+
// mentions it three times.
|
|
60
|
+
expect(got.filter((e) => e === 'Alice Smith').length).toBe(1);
|
|
61
|
+
// Both entity types are present.
|
|
62
|
+
expect(got).toContain('Alice Smith');
|
|
63
|
+
expect(got).toContain('alice@x.io');
|
|
64
|
+
});
|
|
65
|
+
it('drops trimmed-to-shorter-than-2 fragments', () => {
|
|
66
|
+
// Quoted single-char phrase ("a") should be filtered by the min-length guard.
|
|
67
|
+
expect(extractEntities(`prefer "a" over "b"`)).toEqual([]);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=entity-tagger.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-tagger.test.js","sourceRoot":"","sources":["../src/entity-tagger.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,CAAC,eAAe,CAAC,SAA8B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,eAAe,CAAC,qCAAqC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,GAAG,GAAG,eAAe,CAAC,8CAA8C,CAAC,CAAC;QAC5E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,eAAe,EAAE,GAAG,EAAE;QACvB,MAAM,GAAG,GAAG,eAAe,CAAC,sDAAsD,CAAC,CAAC;QACpF,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,8BAA8B,CAAC,CAAC;QACtD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,eAAe,CAAC,6CAA6C,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,GAAG,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,CAAA,mCAAmC,CAAC,CAAC;QAC3E,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAA,2BAA2B,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,GAAG,GAAG,eAAe,CAAC,yCAAyC,CAAC,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,GAAG,GAAG,eAAe,CAAC,8CAA8C,CAAC,CAAC;QAC5E,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,eAAe,CAAC,wBAAwB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,GAAG,GAAG,eAAe,CACzB,iEAAiE,CAClE,CAAC;QACF,kEAAkE;QAClE,2BAA2B;QAC3B,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC9D,iCAAiC;QACjC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,8EAA8E;QAC9E,MAAM,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 — FTS5 keyword index (ADR-125)
|
|
3
|
+
*
|
|
4
|
+
* Verifies the FTS5 wiring on the two SQL backends:
|
|
5
|
+
* - SqlJsBackend (sql.js — WASM, ships FTS5 since 1.10)
|
|
6
|
+
* - SQLiteBackend (better-sqlite3 — bundles FTS5 since 11.x)
|
|
7
|
+
*
|
|
8
|
+
* The SQLiteBackend test is conditional: better-sqlite3 is an optional
|
|
9
|
+
* dependency. If it isn't installable in the current env, that test is
|
|
10
|
+
* skipped.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=fts5.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fts5.test.d.ts","sourceRoot":"","sources":["../src/fts5.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 — FTS5 keyword index (ADR-125)
|
|
3
|
+
*
|
|
4
|
+
* Verifies the FTS5 wiring on the two SQL backends:
|
|
5
|
+
* - SqlJsBackend (sql.js — WASM, ships FTS5 since 1.10)
|
|
6
|
+
* - SQLiteBackend (better-sqlite3 — bundles FTS5 since 11.x)
|
|
7
|
+
*
|
|
8
|
+
* The SQLiteBackend test is conditional: better-sqlite3 is an optional
|
|
9
|
+
* dependency. If it isn't installable in the current env, that test is
|
|
10
|
+
* skipped.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import { existsSync } from 'node:fs';
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
15
|
+
import { SqlJsBackend } from './sqljs-backend.js';
|
|
16
|
+
import { createDefaultEntry } from './types.js';
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
/**
|
|
19
|
+
* Locate the local sql-wasm.wasm so vitest doesn't try to fetch it over HTTP.
|
|
20
|
+
* Returns null if sql.js isn't installed in this env (treated as a skip
|
|
21
|
+
* signal by the suite).
|
|
22
|
+
*/
|
|
23
|
+
function locateSqlWasm() {
|
|
24
|
+
try {
|
|
25
|
+
const sqljsPkg = require.resolve('sql.js/package.json');
|
|
26
|
+
const wasmPath = sqljsPkg.replace(/package\.json$/, 'dist/sql-wasm.wasm');
|
|
27
|
+
return existsSync(wasmPath) ? wasmPath : null;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const WASM_PATH = locateSqlWasm();
|
|
34
|
+
async function newSqlJs() {
|
|
35
|
+
if (!WASM_PATH)
|
|
36
|
+
throw new Error('sql.js/dist/sql-wasm.wasm not found');
|
|
37
|
+
const backend = new SqlJsBackend({ databasePath: ':memory:', wasmPath: WASM_PATH });
|
|
38
|
+
await backend.initialize();
|
|
39
|
+
return backend;
|
|
40
|
+
}
|
|
41
|
+
describe('Phase 5 — SqlJsBackend FTS5', () => {
|
|
42
|
+
it('searchKeyword returns matching entries for an FTS phrase', async () => {
|
|
43
|
+
const backend = await newSqlJs();
|
|
44
|
+
const phrases = [
|
|
45
|
+
'authentication patterns for OAuth flow',
|
|
46
|
+
'database migrations and schema changes',
|
|
47
|
+
'rate limiting and throttling middleware',
|
|
48
|
+
'caching strategies for distributed systems',
|
|
49
|
+
'jwt token validation security',
|
|
50
|
+
];
|
|
51
|
+
for (let i = 0; i < 20; i++) {
|
|
52
|
+
const phrase = phrases[i % phrases.length];
|
|
53
|
+
const entry = createDefaultEntry({ key: `k-${i}`, content: `${phrase} ${i}` });
|
|
54
|
+
await backend.store(entry);
|
|
55
|
+
}
|
|
56
|
+
const results = await backend.searchKeyword('authentication patterns', 10);
|
|
57
|
+
expect(results.length).toBeGreaterThan(0);
|
|
58
|
+
for (const r of results) {
|
|
59
|
+
const c = r.entry.content.toLowerCase();
|
|
60
|
+
// At least one of the two query tokens should appear
|
|
61
|
+
expect(c.includes('authentication') || c.includes('patterns')).toBe(true);
|
|
62
|
+
}
|
|
63
|
+
await backend.shutdown();
|
|
64
|
+
});
|
|
65
|
+
it('searchKeyword does not return a deleted entry', async () => {
|
|
66
|
+
const backend = await newSqlJs();
|
|
67
|
+
const entry = createDefaultEntry({ key: 'doomed', content: 'unique-marker-phrase' });
|
|
68
|
+
await backend.store(entry);
|
|
69
|
+
let results = await backend.searchKeyword('unique-marker-phrase', 5);
|
|
70
|
+
expect(results.length).toBe(1);
|
|
71
|
+
await backend.delete(entry.id);
|
|
72
|
+
results = await backend.searchKeyword('unique-marker-phrase', 5);
|
|
73
|
+
expect(results.length).toBe(0);
|
|
74
|
+
await backend.shutdown();
|
|
75
|
+
});
|
|
76
|
+
it('searchKeyword reflects content updates', async () => {
|
|
77
|
+
const backend = await newSqlJs();
|
|
78
|
+
const entry = createDefaultEntry({ key: 'updatable', content: 'original phrase about apples' });
|
|
79
|
+
await backend.store(entry);
|
|
80
|
+
let results = await backend.searchKeyword('apples', 5);
|
|
81
|
+
expect(results.length).toBe(1);
|
|
82
|
+
await backend.update(entry.id, { content: 'rewritten phrase about bananas' });
|
|
83
|
+
results = await backend.searchKeyword('apples', 5);
|
|
84
|
+
expect(results.length).toBe(0);
|
|
85
|
+
results = await backend.searchKeyword('bananas', 5);
|
|
86
|
+
expect(results.length).toBe(1);
|
|
87
|
+
await backend.shutdown();
|
|
88
|
+
});
|
|
89
|
+
it('searchKeyword returns [] for an empty query', async () => {
|
|
90
|
+
const backend = await newSqlJs();
|
|
91
|
+
const results = await backend.searchKeyword(' ', 5);
|
|
92
|
+
expect(results).toEqual([]);
|
|
93
|
+
await backend.shutdown();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
// Conditionally run the SQLiteBackend tests — better-sqlite3 is an optional
|
|
97
|
+
// dependency, so probe for it first.
|
|
98
|
+
async function probeBetterSqlite3() {
|
|
99
|
+
try {
|
|
100
|
+
await import('better-sqlite3');
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
describe('Phase 5 — SQLiteBackend FTS5 (better-sqlite3)', async () => {
|
|
108
|
+
const hasBetterSqlite3 = await probeBetterSqlite3();
|
|
109
|
+
const maybeIt = hasBetterSqlite3 ? it : it.skip;
|
|
110
|
+
maybeIt('searchKeyword returns matching entries with FTS5 ranking', async () => {
|
|
111
|
+
const { SQLiteBackend } = await import('./sqlite-backend.js');
|
|
112
|
+
const backend = new SQLiteBackend({ databasePath: ':memory:' });
|
|
113
|
+
await backend.initialize();
|
|
114
|
+
for (let i = 0; i < 20; i++) {
|
|
115
|
+
const entry = createDefaultEntry({
|
|
116
|
+
key: `k-${i}`,
|
|
117
|
+
content: i % 2 === 0
|
|
118
|
+
? `authentication patterns OAuth ${i}`
|
|
119
|
+
: `database indexing strategies ${i}`,
|
|
120
|
+
});
|
|
121
|
+
await backend.store(entry);
|
|
122
|
+
}
|
|
123
|
+
const results = await backend.searchKeyword('authentication', 10);
|
|
124
|
+
expect(results.length).toBeGreaterThan(0);
|
|
125
|
+
for (const r of results) {
|
|
126
|
+
expect(r.entry.content.toLowerCase()).toContain('authentication');
|
|
127
|
+
}
|
|
128
|
+
await backend.shutdown();
|
|
129
|
+
});
|
|
130
|
+
maybeIt('searchKeyword stops finding entries after delete', async () => {
|
|
131
|
+
const { SQLiteBackend } = await import('./sqlite-backend.js');
|
|
132
|
+
const backend = new SQLiteBackend({ databasePath: ':memory:' });
|
|
133
|
+
await backend.initialize();
|
|
134
|
+
const entry = createDefaultEntry({ key: 'doomed', content: 'unique-marker' });
|
|
135
|
+
await backend.store(entry);
|
|
136
|
+
let results = await backend.searchKeyword('unique-marker', 5);
|
|
137
|
+
expect(results.length).toBe(1);
|
|
138
|
+
await backend.delete(entry.id);
|
|
139
|
+
results = await backend.searchKeyword('unique-marker', 5);
|
|
140
|
+
expect(results.length).toBe(0);
|
|
141
|
+
await backend.shutdown();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=fts5.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fts5.test.js","sourceRoot":"","sources":["../src/fts5.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 — Graceful retrieval degradation (ADR-125)
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* - `MemoryService.semanticSearch('foo')` with NO embedder configured falls
|
|
6
|
+
* back to FTS / keyword search results without throwing AND emits
|
|
7
|
+
* `health:embedder = { status: 'degraded' }` (Acceptance Criterion #5).
|
|
8
|
+
* - Same fallback when the embedder is configured but throws on call.
|
|
9
|
+
* - `hybridSearch` controller fuses dense + sparse arms via RRF + MMR and
|
|
10
|
+
* returns non-empty results with a mean score at least as high as either
|
|
11
|
+
* pure arm.
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=graceful-retrieval.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graceful-retrieval.test.d.ts","sourceRoot":"","sources":["../src/graceful-retrieval.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 5 — Graceful retrieval degradation (ADR-125)
|
|
3
|
+
*
|
|
4
|
+
* Verifies:
|
|
5
|
+
* - `MemoryService.semanticSearch('foo')` with NO embedder configured falls
|
|
6
|
+
* back to FTS / keyword search results without throwing AND emits
|
|
7
|
+
* `health:embedder = { status: 'degraded' }` (Acceptance Criterion #5).
|
|
8
|
+
* - Same fallback when the embedder is configured but throws on call.
|
|
9
|
+
* - `hybridSearch` controller fuses dense + sparse arms via RRF + MMR and
|
|
10
|
+
* returns non-empty results with a mean score at least as high as either
|
|
11
|
+
* pure arm.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, it, expect } from 'vitest';
|
|
14
|
+
import { MemoryService } from './index.js';
|
|
15
|
+
import { ControllerRegistry } from './controller-registry.js';
|
|
16
|
+
import { createDefaultEntry } from './types.js';
|
|
17
|
+
async function newSvc(opts = {}) {
|
|
18
|
+
const svc = new MemoryService({
|
|
19
|
+
dimensions: 8,
|
|
20
|
+
persistenceEnabled: false,
|
|
21
|
+
snapshotInterval: 0,
|
|
22
|
+
...opts,
|
|
23
|
+
});
|
|
24
|
+
await svc.initialize();
|
|
25
|
+
return svc;
|
|
26
|
+
}
|
|
27
|
+
function randomVec(dim, seed) {
|
|
28
|
+
const out = new Float32Array(dim);
|
|
29
|
+
let s = seed | 0 || 1;
|
|
30
|
+
for (let i = 0; i < dim; i++) {
|
|
31
|
+
s ^= s << 13;
|
|
32
|
+
s ^= s >>> 17;
|
|
33
|
+
s ^= s << 5;
|
|
34
|
+
out[i] = (s | 0) / 2 ** 31;
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
describe('Phase 5 — semanticSearch falls back without embedder', () => {
|
|
39
|
+
it('Acceptance Criterion #5 — no embedder → keyword results, no throw', async () => {
|
|
40
|
+
const svc = await newSvc(); // no embeddingGenerator
|
|
41
|
+
for (let i = 0; i < 10; i++) {
|
|
42
|
+
const entry = createDefaultEntry({
|
|
43
|
+
key: `k-${i}`,
|
|
44
|
+
content: i % 2 === 0
|
|
45
|
+
? `authentication patterns for OAuth ${i}`
|
|
46
|
+
: `database migration schema ${i}`,
|
|
47
|
+
});
|
|
48
|
+
await svc.store(entry);
|
|
49
|
+
}
|
|
50
|
+
let degraded = null;
|
|
51
|
+
svc.getAdapter().on('health:embedder', (ev) => { degraded = ev; });
|
|
52
|
+
const results = await svc.semanticSearch('authentication', 5);
|
|
53
|
+
expect(Array.isArray(results)).toBe(true);
|
|
54
|
+
expect(results.length).toBeGreaterThan(0);
|
|
55
|
+
// Every result should match the keyword
|
|
56
|
+
for (const r of results) {
|
|
57
|
+
expect(r.entry.content.toLowerCase()).toContain('authentication');
|
|
58
|
+
}
|
|
59
|
+
expect(degraded).not.toBeNull();
|
|
60
|
+
expect(degraded.status).toBe('degraded');
|
|
61
|
+
await svc.close();
|
|
62
|
+
});
|
|
63
|
+
it('embedder that throws → degraded event + keyword fallback', async () => {
|
|
64
|
+
// Build a service where the embedder ALWAYS throws on read paths only.
|
|
65
|
+
// We bypass the throw on write by pre-populating an embedding on each
|
|
66
|
+
// stored entry — this mirrors real-world failure modes where indexing
|
|
67
|
+
// happened with a healthy embedder but the embedder later goes down.
|
|
68
|
+
const svc = await newSvc({
|
|
69
|
+
embeddingGenerator: async () => {
|
|
70
|
+
throw new Error('embedding service unavailable');
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
for (let i = 0; i < 5; i++) {
|
|
74
|
+
const entry = createDefaultEntry({
|
|
75
|
+
key: `t-${i}`,
|
|
76
|
+
content: `token validation ${i}`,
|
|
77
|
+
});
|
|
78
|
+
entry.embedding = randomVec(8, i + 1); // pre-set so store() doesn't call embedder
|
|
79
|
+
await svc.store(entry);
|
|
80
|
+
}
|
|
81
|
+
let degraded = null;
|
|
82
|
+
svc.getAdapter().on('health:embedder', (ev) => { degraded = ev; });
|
|
83
|
+
const results = await svc.semanticSearch('token validation', 5);
|
|
84
|
+
expect(results.length).toBeGreaterThan(0);
|
|
85
|
+
expect(degraded?.status).toBe('degraded');
|
|
86
|
+
expect(degraded?.reason).toContain('embedding service unavailable');
|
|
87
|
+
await svc.close();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('Phase 5 — hybridSearch controller (RRF + MMR)', () => {
|
|
91
|
+
it('fuses dense + sparse arms into a non-empty result', async () => {
|
|
92
|
+
// Use a deterministic embedder so the dense arm is reproducible
|
|
93
|
+
const dim = 8;
|
|
94
|
+
const svc = new MemoryService({
|
|
95
|
+
dimensions: dim,
|
|
96
|
+
persistenceEnabled: false,
|
|
97
|
+
snapshotInterval: 0,
|
|
98
|
+
embeddingGenerator: async (text) => randomVec(dim, hashStr(text)),
|
|
99
|
+
});
|
|
100
|
+
await svc.initialize();
|
|
101
|
+
// 50 entries: 25 about "authentication", 25 about "indexing"
|
|
102
|
+
for (let i = 0; i < 25; i++) {
|
|
103
|
+
const entry = createDefaultEntry({
|
|
104
|
+
key: `auth-${i}`,
|
|
105
|
+
content: `authentication and authorization patterns ${i}`,
|
|
106
|
+
});
|
|
107
|
+
entry.embedding = randomVec(dim, hashStr(entry.content));
|
|
108
|
+
await svc.store(entry);
|
|
109
|
+
}
|
|
110
|
+
for (let i = 0; i < 25; i++) {
|
|
111
|
+
const entry = createDefaultEntry({
|
|
112
|
+
key: `idx-${i}`,
|
|
113
|
+
content: `database indexing strategies ${i}`,
|
|
114
|
+
});
|
|
115
|
+
entry.embedding = randomVec(dim, hashStr(entry.content));
|
|
116
|
+
await svc.store(entry);
|
|
117
|
+
}
|
|
118
|
+
const registry = new ControllerRegistry();
|
|
119
|
+
await registry.initialize({ memoryService: svc });
|
|
120
|
+
const hybrid = registry.get('hybridSearch');
|
|
121
|
+
expect(hybrid).toBeTruthy();
|
|
122
|
+
expect(hybrid.source).toBe('hybrid-rrf-mmr');
|
|
123
|
+
const results = await hybrid.search('authentication', { limit: 10 });
|
|
124
|
+
expect(results.length).toBeGreaterThan(0);
|
|
125
|
+
// Mean RRF-fused score should be > 0
|
|
126
|
+
const mean = results.reduce((acc, r) => acc + r.score, 0) / results.length;
|
|
127
|
+
expect(mean).toBeGreaterThan(0);
|
|
128
|
+
// Verify hybrid surfaces relevant entries from BOTH arms by checking
|
|
129
|
+
// that >=1 result mentions "authentication" (keyword arm signal).
|
|
130
|
+
const authMatches = results.filter((r) => r.entry.content.toLowerCase().includes('authentication'));
|
|
131
|
+
expect(authMatches.length).toBeGreaterThan(0);
|
|
132
|
+
// Hybrid should return up to `limit` entries (10 here, given 50 stored).
|
|
133
|
+
expect(results.length).toBeLessThanOrEqual(10);
|
|
134
|
+
// ADR-147: every result carries provenance — at least one signal name.
|
|
135
|
+
for (const r of results) {
|
|
136
|
+
expect(Array.isArray(r.signals)).toBe(true);
|
|
137
|
+
expect(r.signals.length).toBeGreaterThan(0);
|
|
138
|
+
for (const s of r.signals) {
|
|
139
|
+
expect(['vector', 'bm25', 'entity']).toContain(s);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
await registry.shutdown();
|
|
143
|
+
await svc.close();
|
|
144
|
+
});
|
|
145
|
+
it('ADR-147 — entity arm surfaces proper-noun matches above vector noise', async () => {
|
|
146
|
+
// Store 30 generic "authentication" entries (no Alice) + a single
|
|
147
|
+
// entry mentioning "Alice Smith" — the proper-noun bigram extractEntities
|
|
148
|
+
// should pick up. The entity arm should boost the Alice entry above
|
|
149
|
+
// the 30 generic ones for a query that names Alice explicitly.
|
|
150
|
+
const dim = 8;
|
|
151
|
+
const svc = new MemoryService({
|
|
152
|
+
dimensions: dim,
|
|
153
|
+
persistenceEnabled: false,
|
|
154
|
+
snapshotInterval: 0,
|
|
155
|
+
embeddingGenerator: async (text) => randomVec(dim, hashStr(text)),
|
|
156
|
+
});
|
|
157
|
+
await svc.initialize();
|
|
158
|
+
for (let i = 0; i < 30; i++) {
|
|
159
|
+
const entry = createDefaultEntry({
|
|
160
|
+
key: `generic-${i}`,
|
|
161
|
+
content: `authentication and authorization patterns ${i}`,
|
|
162
|
+
});
|
|
163
|
+
entry.embedding = randomVec(dim, hashStr(entry.content));
|
|
164
|
+
await svc.store(entry);
|
|
165
|
+
}
|
|
166
|
+
const aliceEntry = createDefaultEntry({
|
|
167
|
+
key: 'alice-needle',
|
|
168
|
+
content: 'Alice Smith authored these authentication flows',
|
|
169
|
+
});
|
|
170
|
+
aliceEntry.embedding = randomVec(dim, hashStr(aliceEntry.content));
|
|
171
|
+
await svc.store(aliceEntry);
|
|
172
|
+
const registry = new ControllerRegistry();
|
|
173
|
+
await registry.initialize({ memoryService: svc });
|
|
174
|
+
const hybrid = registry.get('hybridSearch');
|
|
175
|
+
const results = await hybrid.search('Alice Smith authentication', { limit: 10 });
|
|
176
|
+
// The Alice entry should appear in the top-10 fused results.
|
|
177
|
+
const alice = results.find((r) => r.entry.key === 'alice-needle');
|
|
178
|
+
expect(alice).toBeTruthy();
|
|
179
|
+
// …and should carry the 'entity' provenance signal (it's the only entry
|
|
180
|
+
// that matches the "Alice Smith" proper-noun bigram).
|
|
181
|
+
expect(alice.signals).toContain('entity');
|
|
182
|
+
await registry.shutdown();
|
|
183
|
+
await svc.close();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
describe('Dream Cycle 2026-08-18 — hybridSearch explicit opt-in without memoryService', () => {
|
|
187
|
+
it('default behavior is unchanged: backend-only + no explicit override → disabled', async () => {
|
|
188
|
+
const svc = await newSvc({
|
|
189
|
+
embeddingGenerator: async (text) => randomVec(8, hashStr(text)),
|
|
190
|
+
});
|
|
191
|
+
const registry = new ControllerRegistry();
|
|
192
|
+
await registry.initialize({ backend: svc.getAdapter() });
|
|
193
|
+
expect(registry.isEnabled('hybridSearch')).toBe(false);
|
|
194
|
+
expect(registry.get('hybridSearch')).toBeNull();
|
|
195
|
+
await registry.shutdown();
|
|
196
|
+
await svc.close();
|
|
197
|
+
});
|
|
198
|
+
it('explicit `controllers: { hybridSearch: true }` + backend (no memoryService) now constructs a working controller', async () => {
|
|
199
|
+
const svc = await newSvc({
|
|
200
|
+
embeddingGenerator: async (text) => randomVec(8, hashStr(text)),
|
|
201
|
+
});
|
|
202
|
+
for (let i = 0; i < 20; i++) {
|
|
203
|
+
const entry = createDefaultEntry({
|
|
204
|
+
key: `note-${i}`,
|
|
205
|
+
content: `release process and deployment checklist ${i}`,
|
|
206
|
+
});
|
|
207
|
+
entry.embedding = randomVec(8, hashStr(entry.content));
|
|
208
|
+
await svc.store(entry);
|
|
209
|
+
}
|
|
210
|
+
const registry = new ControllerRegistry();
|
|
211
|
+
// Only `backend` is supplied (the AgentDBAdapter directly) — no
|
|
212
|
+
// `memoryService`. Before the 2026-08-18 fix this silently returned
|
|
213
|
+
// null despite the explicit `true` override.
|
|
214
|
+
await registry.initialize({
|
|
215
|
+
backend: svc.getAdapter(),
|
|
216
|
+
controllers: { hybridSearch: true },
|
|
217
|
+
});
|
|
218
|
+
expect(registry.isEnabled('hybridSearch')).toBe(true);
|
|
219
|
+
const hybrid = registry.get('hybridSearch');
|
|
220
|
+
expect(hybrid).toBeTruthy();
|
|
221
|
+
expect(hybrid.source).toBe('hybrid-rrf-mmr');
|
|
222
|
+
const results = await hybrid.search('deployment checklist', { limit: 5 });
|
|
223
|
+
expect(results.length).toBeGreaterThan(0);
|
|
224
|
+
for (const r of results) {
|
|
225
|
+
expect(Array.isArray(r.signals)).toBe(true);
|
|
226
|
+
expect(r.signals.length).toBeGreaterThan(0);
|
|
227
|
+
}
|
|
228
|
+
await registry.shutdown();
|
|
229
|
+
await svc.close();
|
|
230
|
+
});
|
|
231
|
+
it('explicit override with an incompatible backend (no semanticSearch/searchKeyword) still degrades to null, not a throw', async () => {
|
|
232
|
+
const registry = new ControllerRegistry();
|
|
233
|
+
const incompatibleBackend = {
|
|
234
|
+
initialize: async () => { },
|
|
235
|
+
shutdown: async () => { },
|
|
236
|
+
};
|
|
237
|
+
await registry.initialize({
|
|
238
|
+
backend: incompatibleBackend,
|
|
239
|
+
controllers: { hybridSearch: true },
|
|
240
|
+
});
|
|
241
|
+
expect(registry.get('hybridSearch')).toBeNull();
|
|
242
|
+
await registry.shutdown();
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
function hashStr(s) {
|
|
246
|
+
let h = 0;
|
|
247
|
+
for (let i = 0; i < s.length; i++)
|
|
248
|
+
h = (h * 31 + s.charCodeAt(i)) | 0;
|
|
249
|
+
return Math.abs(h) + 1;
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=graceful-retrieval.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graceful-retrieval.test.js","sourceRoot":"","sources":["../src/graceful-retrieval.test.ts"],"names":[],"mappings":"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"}
|
package/dist/hnsw-index.d.ts
CHANGED
|
@@ -75,6 +75,57 @@ export declare class HNSWIndex extends EventEmitter {
|
|
|
75
75
|
* Get the number of vectors in the index
|
|
76
76
|
*/
|
|
77
77
|
get size(): number;
|
|
78
|
+
/**
|
|
79
|
+
* Read-only view of the resolved HNSW config (dimensions, M, etc.).
|
|
80
|
+
* Used by {@link MemoryConsolidator.compactHnsw} to rebuild the index with
|
|
81
|
+
* matching parameters.
|
|
82
|
+
*/
|
|
83
|
+
getConfig(): Readonly<HNSWConfig>;
|
|
84
|
+
/**
|
|
85
|
+
* Magic header for serialized HNSW snapshots.
|
|
86
|
+
* Format: "HNSW" + version byte (0x01).
|
|
87
|
+
*/
|
|
88
|
+
static readonly SERIALIZATION_MAGIC: Buffer<ArrayBuffer>;
|
|
89
|
+
/**
|
|
90
|
+
* Serialize the index to a binary buffer.
|
|
91
|
+
*
|
|
92
|
+
* Layout (all integers big-endian):
|
|
93
|
+
* - 5 bytes: magic header "HNSW" + version (0x01)
|
|
94
|
+
* - 4 bytes: dimensions (uint32)
|
|
95
|
+
* - 4 bytes: M (uint32)
|
|
96
|
+
* - 4 bytes: efConstruction (uint32)
|
|
97
|
+
* - 4 bytes: metric length (uint32) + metric utf-8 bytes
|
|
98
|
+
* - 4 bytes: maxLevel (uint32)
|
|
99
|
+
* - 4 bytes: entryPoint length (uint32) + entryPoint utf-8 bytes (0 = null)
|
|
100
|
+
* - 4 bytes: node count (uint32)
|
|
101
|
+
* - per node:
|
|
102
|
+
* - 4 bytes id length + id utf-8 bytes
|
|
103
|
+
* - 4 bytes level
|
|
104
|
+
* - 4 bytes vector length (in floats) + vector bytes (Float32, little-endian native)
|
|
105
|
+
* - 1 byte hasNormalized (0/1)
|
|
106
|
+
* - if hasNormalized: 4 bytes normalized length + normalized bytes
|
|
107
|
+
* - 4 bytes connection-level count
|
|
108
|
+
* - per level:
|
|
109
|
+
* - 4 bytes layer index
|
|
110
|
+
* - 4 bytes neighbor count
|
|
111
|
+
* - per neighbor: 4 bytes id length + id utf-8 bytes
|
|
112
|
+
*/
|
|
113
|
+
serialize(): Buffer;
|
|
114
|
+
/**
|
|
115
|
+
* Deserialize an HNSW index from a buffer produced by {@link serialize}.
|
|
116
|
+
*
|
|
117
|
+
* Throws on magic-header mismatch, version mismatch, or truncated input.
|
|
118
|
+
*/
|
|
119
|
+
static deserialize(buf: Buffer): HNSWIndex;
|
|
120
|
+
private encodeLengthPrefixedString;
|
|
121
|
+
private encodeFloat32Array;
|
|
122
|
+
/**
|
|
123
|
+
* Remove a point from the index — alias for {@link removePoint}.
|
|
124
|
+
*
|
|
125
|
+
* Added by ADR-125 Phase 4 so {@link MemoryConsolidator} has a stable
|
|
126
|
+
* synchronous-shaped API to call. Internally delegates to {@link removePoint}.
|
|
127
|
+
*/
|
|
128
|
+
remove(id: string): Promise<boolean>;
|
|
78
129
|
private mergeConfig;
|
|
79
130
|
private getRandomLevel;
|
|
80
131
|
private insertNode;
|
|
@@ -87,6 +138,17 @@ export declare class HNSWIndex extends EventEmitter {
|
|
|
87
138
|
private searchLayerOptimized;
|
|
88
139
|
private selectNeighbors;
|
|
89
140
|
private pruneConnections;
|
|
141
|
+
/**
|
|
142
|
+
* True when this index uses product quantization with trained codebooks.
|
|
143
|
+
* Stored vectors are then PQ centroid indices, not embedding coordinates —
|
|
144
|
+
* generic metric distances (cosine/euclidean/dot/manhattan) computed
|
|
145
|
+
* directly on those indices are meaningless (index 5 vs index 200 has no
|
|
146
|
+
* relation to embedding-space proximity). Falls back to false during the
|
|
147
|
+
* pre-training bootstrap window (see Quantizer.productQuantize()), where
|
|
148
|
+
* stored vectors are still sub-vector-mean floats and generic distance,
|
|
149
|
+
* while imprecise, is not nonsensical — matching prior behavior exactly.
|
|
150
|
+
*/
|
|
151
|
+
private isProductQuantized;
|
|
90
152
|
private distance;
|
|
91
153
|
private cosineDistance;
|
|
92
154
|
/**
|
package/dist/hnsw-index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hnsw-index.d.ts","sourceRoot":"","sources":["../src/hnsw-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAEL,UAAU,EACV,SAAS,EAMV,MAAM,YAAY,CAAC;AAwLpB;;;;;;;GAOG;AACH,qBAAa,SAAU,SAAQ,YAAY;IACzC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,KAAK,CAYX;IAGF,OAAO,CAAC,SAAS,CAA0B;gBAE/B,MAAM,GAAE,OAAO,CAAC,UAAU,CAAM;IAU5C;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"hnsw-index.d.ts","sourceRoot":"","sources":["../src/hnsw-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAEL,UAAU,EACV,SAAS,EAMV,MAAM,YAAY,CAAC;AAwLpB;;;;;;;GAOG;AACH,qBAAa,SAAU,SAAQ,YAAY;IACzC,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,KAAK,CAYX;IAGF,OAAO,CAAC,SAAS,CAA0B;gBAE/B,MAAM,GAAE,OAAO,CAAC,UAAU,CAAM;IAU5C;;OAEG;IACG,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA2D/D;;OAEG;IACG,MAAM,CACV,KAAK,EAAE,YAAY,EACnB,CAAC,EAAE,MAAM,EACT,EAAE,CAAC,EAAE,MAAM,GACV,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAsEnD;;OAEG;IACG,iBAAiB,CACrB,KAAK,EAAE,YAAY,EACnB,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,EAC/B,EAAE,CAAC,EAAE,MAAM,GACV,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAUnD;;OAEG;IACG,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA6C/C;;OAEG;IACG,OAAO,CACX,OAAO,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC;IAmBhB;;OAEG;IACH,QAAQ,IAAI,SAAS;IAqBrB;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;OAEG;IACH,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,SAAS,IAAI,QAAQ,CAAC,UAAU,CAAC;IAMjC;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,mBAAmB,sBAA+C;IAElF;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,SAAS,IAAI,MAAM;IAsDnB;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAmF1C,OAAO,CAAC,0BAA0B;IAQlC,OAAO,CAAC,kBAAkB;IAO1B;;;;;OAKG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM1C,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,cAAc;YAQR,UAAU;YAkEV,WAAW;IA2DzB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAgE5B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,gBAAgB;IA6BxB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,QAAQ;IAuChB,OAAO,CAAC,cAAc;IAetB;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;OAEG;IACH,OAAO,CAAC,eAAe;IAkBvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,iBAAiB;IASzB,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,iBAAiB;CAO1B;AAqXD,eAAe,SAAS,CAAC"}
|