@claude-flow/memory 3.0.0-alpha.9 → 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.
Files changed (184) hide show
  1. package/README.md +308 -37
  2. package/dist/agent-memory-scope.d.ts.map +1 -1
  3. package/dist/agent-memory-scope.js +10 -2
  4. package/dist/agent-memory-scope.js.map +1 -1
  5. package/dist/agentdb-adapter-key-isolation.test.d.ts +2 -0
  6. package/dist/agentdb-adapter-key-isolation.test.d.ts.map +1 -0
  7. package/dist/agentdb-adapter-key-isolation.test.js +175 -0
  8. package/dist/agentdb-adapter-key-isolation.test.js.map +1 -0
  9. package/dist/agentdb-adapter-upsert.test.d.ts +2 -0
  10. package/dist/agentdb-adapter-upsert.test.d.ts.map +1 -0
  11. package/dist/agentdb-adapter-upsert.test.js +216 -0
  12. package/dist/agentdb-adapter-upsert.test.js.map +1 -0
  13. package/dist/agentdb-adapter.d.ts +70 -1
  14. package/dist/agentdb-adapter.d.ts.map +1 -1
  15. package/dist/agentdb-adapter.js +365 -32
  16. package/dist/agentdb-adapter.js.map +1 -1
  17. package/dist/agentdb-backend.d.ts +25 -0
  18. package/dist/agentdb-backend.d.ts.map +1 -1
  19. package/dist/agentdb-backend.js +113 -28
  20. package/dist/agentdb-backend.js.map +1 -1
  21. package/dist/agentdb-retrieval-guard.d.ts +76 -0
  22. package/dist/agentdb-retrieval-guard.d.ts.map +1 -0
  23. package/dist/agentdb-retrieval-guard.js +77 -0
  24. package/dist/agentdb-retrieval-guard.js.map +1 -0
  25. package/dist/agentdb-retrieval-guard.test.d.ts +12 -0
  26. package/dist/agentdb-retrieval-guard.test.d.ts.map +1 -0
  27. package/dist/agentdb-retrieval-guard.test.js +129 -0
  28. package/dist/agentdb-retrieval-guard.test.js.map +1 -0
  29. package/dist/auto-memory-bridge.d.ts +12 -1
  30. package/dist/auto-memory-bridge.d.ts.map +1 -1
  31. package/dist/auto-memory-bridge.js +101 -7
  32. package/dist/auto-memory-bridge.js.map +1 -1
  33. package/dist/auto-memory-bridge.test.js +121 -15
  34. package/dist/auto-memory-bridge.test.js.map +1 -1
  35. package/dist/auto-memory-index-3224.test.d.ts +2 -0
  36. package/dist/auto-memory-index-3224.test.d.ts.map +1 -0
  37. package/dist/auto-memory-index-3224.test.js +77 -0
  38. package/dist/auto-memory-index-3224.test.js.map +1 -0
  39. package/dist/consolidator-embedding-benchmark.test.d.ts +39 -0
  40. package/dist/consolidator-embedding-benchmark.test.d.ts.map +1 -0
  41. package/dist/consolidator-embedding-benchmark.test.js +366 -0
  42. package/dist/consolidator-embedding-benchmark.test.js.map +1 -0
  43. package/dist/consolidator.d.ts +131 -0
  44. package/dist/consolidator.d.ts.map +1 -0
  45. package/dist/consolidator.js +309 -0
  46. package/dist/consolidator.js.map +1 -0
  47. package/dist/consolidator.test.d.ts +17 -0
  48. package/dist/consolidator.test.d.ts.map +1 -0
  49. package/dist/consolidator.test.js +376 -0
  50. package/dist/consolidator.test.js.map +1 -0
  51. package/dist/controller-registry.d.ts +290 -0
  52. package/dist/controller-registry.d.ts.map +1 -0
  53. package/dist/controller-registry.js +1095 -0
  54. package/dist/controller-registry.js.map +1 -0
  55. package/dist/controller-registry.test.d.ts +14 -0
  56. package/dist/controller-registry.test.d.ts.map +1 -0
  57. package/dist/controller-registry.test.js +818 -0
  58. package/dist/controller-registry.test.js.map +1 -0
  59. package/dist/database-provider.d.ts +14 -2
  60. package/dist/database-provider.d.ts.map +1 -1
  61. package/dist/database-provider.js +63 -3
  62. package/dist/database-provider.js.map +1 -1
  63. package/dist/database-provider.test.js +56 -3
  64. package/dist/database-provider.test.js.map +1 -1
  65. package/dist/entity-tagger.d.ts +31 -0
  66. package/dist/entity-tagger.d.ts.map +1 -0
  67. package/dist/entity-tagger.js +80 -0
  68. package/dist/entity-tagger.js.map +1 -0
  69. package/dist/entity-tagger.test.d.ts +12 -0
  70. package/dist/entity-tagger.test.d.ts.map +1 -0
  71. package/dist/entity-tagger.test.js +70 -0
  72. package/dist/entity-tagger.test.js.map +1 -0
  73. package/dist/fts5.test.d.ts +13 -0
  74. package/dist/fts5.test.d.ts.map +1 -0
  75. package/dist/fts5.test.js +144 -0
  76. package/dist/fts5.test.js.map +1 -0
  77. package/dist/graceful-retrieval.test.d.ts +14 -0
  78. package/dist/graceful-retrieval.test.d.ts.map +1 -0
  79. package/dist/graceful-retrieval.test.js +251 -0
  80. package/dist/graceful-retrieval.test.js.map +1 -0
  81. package/dist/hnsw-index.d.ts +62 -0
  82. package/dist/hnsw-index.d.ts.map +1 -1
  83. package/dist/hnsw-index.js +461 -16
  84. package/dist/hnsw-index.js.map +1 -1
  85. package/dist/hnsw-persistence.test.d.ts +13 -0
  86. package/dist/hnsw-persistence.test.d.ts.map +1 -0
  87. package/dist/hnsw-persistence.test.js +208 -0
  88. package/dist/hnsw-persistence.test.js.map +1 -0
  89. package/dist/hnsw-quantization.test.d.ts +17 -0
  90. package/dist/hnsw-quantization.test.d.ts.map +1 -0
  91. package/dist/hnsw-quantization.test.js +140 -0
  92. package/dist/hnsw-quantization.test.js.map +1 -0
  93. package/dist/hybrid-backend.d.ts +46 -2
  94. package/dist/hybrid-backend.d.ts.map +1 -1
  95. package/dist/hybrid-backend.js +120 -21
  96. package/dist/hybrid-backend.js.map +1 -1
  97. package/dist/hybrid-backend.test.js +139 -5
  98. package/dist/hybrid-backend.test.js.map +1 -1
  99. package/dist/index.d.ts +164 -10
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +272 -30
  102. package/dist/index.js.map +1 -1
  103. package/dist/index.test.d.ts +10 -0
  104. package/dist/index.test.d.ts.map +1 -0
  105. package/dist/index.test.js +71 -0
  106. package/dist/index.test.js.map +1 -0
  107. package/dist/json-security.d.ts +12 -0
  108. package/dist/json-security.d.ts.map +1 -0
  109. package/dist/json-security.js +20 -0
  110. package/dist/json-security.js.map +1 -0
  111. package/dist/learning-bridge.d.ts +29 -0
  112. package/dist/learning-bridge.d.ts.map +1 -1
  113. package/dist/learning-bridge.js +71 -6
  114. package/dist/learning-bridge.js.map +1 -1
  115. package/dist/learning-bridge.test.js +181 -1
  116. package/dist/learning-bridge.test.js.map +1 -1
  117. package/dist/memory-key.d.ts +3 -0
  118. package/dist/memory-key.d.ts.map +1 -0
  119. package/dist/memory-key.js +5 -0
  120. package/dist/memory-key.js.map +1 -0
  121. package/dist/migration.d.ts +19 -4
  122. package/dist/migration.d.ts.map +1 -1
  123. package/dist/migration.js +99 -24
  124. package/dist/migration.js.map +1 -1
  125. package/dist/migration.test.d.ts +11 -0
  126. package/dist/migration.test.d.ts.map +1 -0
  127. package/dist/migration.test.js +141 -0
  128. package/dist/migration.test.js.map +1 -0
  129. package/dist/mmr-benchmark.test.d.ts +2 -0
  130. package/dist/mmr-benchmark.test.d.ts.map +1 -0
  131. package/dist/mmr-benchmark.test.js +286 -0
  132. package/dist/mmr-benchmark.test.js.map +1 -0
  133. package/dist/mmr-rerank-perf.test.d.ts +2 -0
  134. package/dist/mmr-rerank-perf.test.d.ts.map +1 -0
  135. package/dist/mmr-rerank-perf.test.js +191 -0
  136. package/dist/mmr-rerank-perf.test.js.map +1 -0
  137. package/dist/persistent-sona.d.ts +144 -0
  138. package/dist/persistent-sona.d.ts.map +1 -0
  139. package/dist/persistent-sona.js +332 -0
  140. package/dist/persistent-sona.js.map +1 -0
  141. package/dist/rvf-backend.d.ts +53 -0
  142. package/dist/rvf-backend.d.ts.map +1 -0
  143. package/dist/rvf-backend.js +643 -0
  144. package/dist/rvf-backend.js.map +1 -0
  145. package/dist/rvf-learning-store.d.ts +139 -0
  146. package/dist/rvf-learning-store.d.ts.map +1 -0
  147. package/dist/rvf-learning-store.js +295 -0
  148. package/dist/rvf-learning-store.js.map +1 -0
  149. package/dist/rvf-migration.d.ts +45 -0
  150. package/dist/rvf-migration.d.ts.map +1 -0
  151. package/dist/rvf-migration.js +254 -0
  152. package/dist/rvf-migration.js.map +1 -0
  153. package/dist/smart-retrieval-scores.test.d.ts +2 -0
  154. package/dist/smart-retrieval-scores.test.d.ts.map +1 -0
  155. package/dist/smart-retrieval-scores.test.js +75 -0
  156. package/dist/smart-retrieval-scores.test.js.map +1 -0
  157. package/dist/smart-retrieval.d.ts +141 -0
  158. package/dist/smart-retrieval.d.ts.map +1 -0
  159. package/dist/smart-retrieval.js +389 -0
  160. package/dist/smart-retrieval.js.map +1 -0
  161. package/dist/smart-retrieval.test.d.ts +2 -0
  162. package/dist/smart-retrieval.test.d.ts.map +1 -0
  163. package/dist/smart-retrieval.test.js +418 -0
  164. package/dist/smart-retrieval.test.js.map +1 -0
  165. package/dist/sqlite-backend.d.ts +10 -0
  166. package/dist/sqlite-backend.d.ts.map +1 -1
  167. package/dist/sqlite-backend.js +101 -5
  168. package/dist/sqlite-backend.js.map +1 -1
  169. package/dist/sqljs-backend.d.ts +9 -0
  170. package/dist/sqljs-backend.d.ts.map +1 -1
  171. package/dist/sqljs-backend.js +91 -3
  172. package/dist/sqljs-backend.js.map +1 -1
  173. package/dist/tiered-memory.d.ts +171 -0
  174. package/dist/tiered-memory.d.ts.map +1 -0
  175. package/dist/tiered-memory.js +350 -0
  176. package/dist/tiered-memory.js.map +1 -0
  177. package/dist/tiered-memory.test.d.ts +9 -0
  178. package/dist/tiered-memory.test.d.ts.map +1 -0
  179. package/dist/tiered-memory.test.js +220 -0
  180. package/dist/tiered-memory.test.js.map +1 -0
  181. package/dist/types.d.ts +17 -0
  182. package/dist/types.d.ts.map +1 -1
  183. package/dist/types.js.map +1 -1
  184. 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":"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"}
@@ -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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"}
@@ -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
  /**
@@ -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;IAwD/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;IAqEnD;;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;IAID,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,OAAO,CAAC,QAAQ;IAehB,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;AAiHD,eAAe,SAAS,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"}