@dzhechkov/memory 0.2.2 → 0.2.4

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.
@@ -21,13 +21,25 @@ export declare class JsonFileBackend implements MemoryBackend {
21
21
  constructor(options?: JsonFileBackendOptions);
22
22
  /** Create a backend and load any records already persisted at `filePath`. */
23
23
  static open(filePath: string): Promise<JsonFileBackend>;
24
+ /**
25
+ * Synchronous counterpart to {@link JsonFileBackend.open}. Because this backend
26
+ * is physically synchronous (in-memory map + sync `fs`), a hot sync path (e.g.
27
+ * a recommender) can read the store without an async ripple.
28
+ */
29
+ static openSync(filePath: string): JsonFileBackend;
24
30
  put(record: MemoryRecord): Promise<void>;
25
31
  query(query: MemoryQuery): Promise<MemoryRecord[]>;
32
+ /** Synchronous {@link JsonFileBackend.query} — same ranking, no Promise. */
33
+ querySync(query: MemoryQuery): MemoryRecord[];
26
34
  all(): Promise<MemoryRecord[]>;
35
+ /** Synchronous {@link JsonFileBackend.all}. */
36
+ allSync(): MemoryRecord[];
27
37
  count(): Promise<number>;
28
38
  /** Persist every record to `filePath`. No-op when no path is configured. */
29
39
  save(): Promise<void>;
30
40
  /** Load records from `filePath`. No-op when no path is set or the file is absent. */
31
41
  load(): Promise<void>;
42
+ /** Synchronous {@link JsonFileBackend.load}. */
43
+ loadSync(): void;
32
44
  }
33
45
  //# sourceMappingURL=json-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-backend.d.ts","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAuB7E,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,sFAAsF;AACtF,qBAAa,eAAgB,YAAW,aAAa;IACnD,QAAQ,CAAC,IAAI,eAAe;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAElC,OAAO,GAAE,sBAA2B;IAIhD,6EAA6E;WAChE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAM7D,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAkBlD,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,4EAA4E;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrB,qFAAqF;IACrF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAOtB"}
1
+ {"version":3,"file":"json-backend.d.ts","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAuB7E,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,sFAAsF;AACtF,qBAAa,eAAgB,YAAW,aAAa;IACnD,QAAQ,CAAC,IAAI,eAAe;IAE5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAElC,OAAO,GAAE,sBAA2B;IAIhD,6EAA6E;WAChE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAM7D;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe;IAMlD,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAKxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIlD,4EAA4E;IAC5E,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,EAAE;IAkB7C,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,+CAA+C;IAC/C,OAAO,IAAI,YAAY,EAAE;IAIzB,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,4EAA4E;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQrB,qFAAqF;IACrF,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAKrB,gDAAgD;IAChD,QAAQ,IAAI,IAAI;CAMjB"}
@@ -43,11 +43,25 @@ export class JsonFileBackend {
43
43
  await backend.load();
44
44
  return backend;
45
45
  }
46
+ /**
47
+ * Synchronous counterpart to {@link JsonFileBackend.open}. Because this backend
48
+ * is physically synchronous (in-memory map + sync `fs`), a hot sync path (e.g.
49
+ * a recommender) can read the store without an async ripple.
50
+ */
51
+ static openSync(filePath) {
52
+ const backend = new JsonFileBackend({ filePath });
53
+ backend.loadSync();
54
+ return backend;
55
+ }
46
56
  put(record) {
47
57
  this.records.set(record.id, record);
48
58
  return Promise.resolve();
49
59
  }
50
60
  query(query) {
61
+ return Promise.resolve(this.querySync(query));
62
+ }
63
+ /** Synchronous {@link JsonFileBackend.query} — same ranking, no Promise. */
64
+ querySync(query) {
51
65
  const limit = query.limit ?? DEFAULT_LIMIT;
52
66
  const terms = query.text !== undefined ? tokenize(query.text) : [];
53
67
  let candidates = [...this.records.values()];
@@ -59,10 +73,14 @@ export class JsonFileBackend {
59
73
  .sort((a, b) => b.relevance - a.relevance ||
60
74
  b.record.score - a.record.score ||
61
75
  b.record.timestamp.localeCompare(a.record.timestamp));
62
- return Promise.resolve(ranked.slice(0, limit).map((entry) => entry.record));
76
+ return ranked.slice(0, limit).map((entry) => entry.record);
63
77
  }
64
78
  all() {
65
- return Promise.resolve([...this.records.values()]);
79
+ return Promise.resolve(this.allSync());
80
+ }
81
+ /** Synchronous {@link JsonFileBackend.all}. */
82
+ allSync() {
83
+ return [...this.records.values()];
66
84
  }
67
85
  count() {
68
86
  return Promise.resolve(this.records.size);
@@ -77,12 +95,16 @@ export class JsonFileBackend {
77
95
  }
78
96
  /** Load records from `filePath`. No-op when no path is set or the file is absent. */
79
97
  load() {
98
+ this.loadSync();
99
+ return Promise.resolve();
100
+ }
101
+ /** Synchronous {@link JsonFileBackend.load}. */
102
+ loadSync() {
80
103
  if (this.filePath !== undefined && existsSync(this.filePath)) {
81
104
  const data = JSON.parse(readFileSync(this.filePath, 'utf-8'));
82
105
  for (const record of data)
83
106
  this.records.set(record.id, record);
84
107
  }
85
- return Promise.resolve();
86
108
  }
87
109
  }
88
110
  //# sourceMappingURL=json-backend.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-backend.js","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"json-backend.js","sourceRoot":"","sources":["../src/json-backend.ts"],"names":[],"mappings":"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"}
@@ -34,7 +34,15 @@ export declare class SqliteBackend implements MemoryBackend {
34
34
  static open(filePath: string): SqliteBackend;
35
35
  put(record: MemoryRecord): Promise<void>;
36
36
  query(query: MemoryQuery): Promise<MemoryRecord[]>;
37
+ /**
38
+ * Synchronous {@link SqliteBackend.query}. Same FTS5-ranked / keyword-fallback
39
+ * logic, no Promise — better-sqlite3 is physically synchronous, so a hot sync
40
+ * path (a recommender / `dz recall`) can query the store without an async ripple.
41
+ */
42
+ querySync(query: MemoryQuery): MemoryRecord[];
37
43
  all(): Promise<MemoryRecord[]>;
44
+ /** Synchronous {@link SqliteBackend.all}. */
45
+ allSync(): MemoryRecord[];
38
46
  count(): Promise<number>;
39
47
  /** Batch insert records within a transaction (for bulk loading). */
40
48
  putMany(records: readonly MemoryRecord[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-backend.d.ts","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAoF7E,iCAAiC;AACjC,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAM;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkB;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,EAAE,EAAE,GAAG;IAwBnB,4DAA4D;IAC5D,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa;IAW5C,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAgDlD,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,oEAAoE;IACpE,OAAO,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAiB/C,qCAAqC;IACrC,KAAK,IAAI,IAAI;CAGd"}
1
+ {"version":3,"file":"sqlite-backend.d.ts","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAoF7E,iCAAiC;AACjC,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAM;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkB;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,EAAE,EAAE,GAAG;IAwBnB,4DAA4D;IAC5D,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa;IAW5C,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIlD;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,EAAE;IAuD7C,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,6CAA6C;IAC7C,OAAO,IAAI,YAAY,EAAE;IAIzB,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,oEAAoE;IACpE,OAAO,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAiB/C,qCAAqC;IACrC,KAAK,IAAI,IAAI;CAGd"}
@@ -63,15 +63,15 @@ const FTS5_SQL = `
63
63
  INSERT INTO memory_fts(rowid, text, skill_id) VALUES (new.rowid, new.text, new.skill_id);
64
64
  END;
65
65
  `;
66
- /** FTS5 query — returns matching record ids ranked by relevance. */
66
+ /** FTS5 query — matching records with their relevance rank (lower = better). */
67
67
  const FTS5_SEARCH_SQL = `
68
- SELECT mr.* FROM memory_fts fts
68
+ SELECT mr.*, fts.rank AS _rank FROM memory_fts fts
69
69
  JOIN memory_records mr ON mr.rowid = fts.rowid
70
70
  WHERE memory_fts MATCH ?
71
71
  ORDER BY fts.rank
72
72
  `;
73
73
  const FTS5_SEARCH_SKILL_SQL = `
74
- SELECT mr.* FROM memory_fts fts
74
+ SELECT mr.*, fts.rank AS _rank FROM memory_fts fts
75
75
  JOIN memory_records mr ON mr.rowid = fts.rowid
76
76
  WHERE memory_fts MATCH ? AND mr.skill_id = ?
77
77
  ORDER BY fts.rank
@@ -137,6 +137,14 @@ export class SqliteBackend {
137
137
  return Promise.resolve();
138
138
  }
139
139
  query(query) {
140
+ return Promise.resolve(this.querySync(query));
141
+ }
142
+ /**
143
+ * Synchronous {@link SqliteBackend.query}. Same FTS5-ranked / keyword-fallback
144
+ * logic, no Promise — better-sqlite3 is physically synchronous, so a hot sync
145
+ * path (a recommender / `dz recall`) can query the store without an async ripple.
146
+ */
147
+ querySync(query) {
140
148
  const limit = query.limit ?? DEFAULT_LIMIT;
141
149
  // FTS5 path — use SQLite full-text search when available and text query provided
142
150
  if (this.hasFts5 && query.text !== undefined && query.text.trim().length > 0) {
@@ -151,10 +159,16 @@ export class SqliteBackend {
151
159
  else {
152
160
  rows = this.ftsSearchStmt.all(ftsQuery);
153
161
  }
154
- const records = rows.map(rowToRecord);
155
- // FTS5 rank is already applied; tiebreak by score DESC, timestamp DESC
156
- records.sort((a, b) => b.score - a.score || b.timestamp.localeCompare(a.timestamp));
157
- return Promise.resolve(records.slice(0, limit));
162
+ // Rank by FTS5 relevance FIRST (lower rank = better match), with score
163
+ // then timestamp as a true tiebreak between equally-relevant rows. Score
164
+ // must NOT be primary that would discard FTS5's relevance signal and
165
+ // diverge from the JSON backend (which also ranks relevance-primary).
166
+ const ranked = rows
167
+ .map((row) => ({ record: rowToRecord(row), rank: row._rank }))
168
+ .sort((a, b) => a.rank - b.rank ||
169
+ b.record.score - a.record.score ||
170
+ b.record.timestamp.localeCompare(a.record.timestamp));
171
+ return ranked.slice(0, limit).map((entry) => entry.record);
158
172
  }
159
173
  }
160
174
  catch {
@@ -176,10 +190,14 @@ export class SqliteBackend {
176
190
  .sort((a, b) => b.relevance - a.relevance ||
177
191
  b.record.score - a.record.score ||
178
192
  b.record.timestamp.localeCompare(a.record.timestamp));
179
- return Promise.resolve(ranked.slice(0, limit).map((entry) => entry.record));
193
+ return ranked.slice(0, limit).map((entry) => entry.record);
180
194
  }
181
195
  all() {
182
- return Promise.resolve(this.allStmt.all().map(rowToRecord));
196
+ return Promise.resolve(this.allSync());
197
+ }
198
+ /** Synchronous {@link SqliteBackend.all}. */
199
+ allSync() {
200
+ return this.allStmt.all().map(rowToRecord);
183
201
  }
184
202
  count() {
185
203
  return Promise.resolve(this.countStmt.get().cnt);
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-backend.js","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sqlite-backend.js","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/memory",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Harness memory layer - backend cascade, Reflexion feedback, and the host-memory bridge.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -58,12 +58,28 @@ export class JsonFileBackend implements MemoryBackend {
58
58
  return backend;
59
59
  }
60
60
 
61
+ /**
62
+ * Synchronous counterpart to {@link JsonFileBackend.open}. Because this backend
63
+ * is physically synchronous (in-memory map + sync `fs`), a hot sync path (e.g.
64
+ * a recommender) can read the store without an async ripple.
65
+ */
66
+ static openSync(filePath: string): JsonFileBackend {
67
+ const backend = new JsonFileBackend({ filePath });
68
+ backend.loadSync();
69
+ return backend;
70
+ }
71
+
61
72
  put(record: MemoryRecord): Promise<void> {
62
73
  this.records.set(record.id, record);
63
74
  return Promise.resolve();
64
75
  }
65
76
 
66
77
  query(query: MemoryQuery): Promise<MemoryRecord[]> {
78
+ return Promise.resolve(this.querySync(query));
79
+ }
80
+
81
+ /** Synchronous {@link JsonFileBackend.query} — same ranking, no Promise. */
82
+ querySync(query: MemoryQuery): MemoryRecord[] {
67
83
  const limit = query.limit ?? DEFAULT_LIMIT;
68
84
  const terms = query.text !== undefined ? tokenize(query.text) : [];
69
85
  let candidates = [...this.records.values()];
@@ -78,11 +94,16 @@ export class JsonFileBackend implements MemoryBackend {
78
94
  b.record.score - a.record.score ||
79
95
  b.record.timestamp.localeCompare(a.record.timestamp),
80
96
  );
81
- return Promise.resolve(ranked.slice(0, limit).map((entry) => entry.record));
97
+ return ranked.slice(0, limit).map((entry) => entry.record);
82
98
  }
83
99
 
84
100
  all(): Promise<MemoryRecord[]> {
85
- return Promise.resolve([...this.records.values()]);
101
+ return Promise.resolve(this.allSync());
102
+ }
103
+
104
+ /** Synchronous {@link JsonFileBackend.all}. */
105
+ allSync(): MemoryRecord[] {
106
+ return [...this.records.values()];
86
107
  }
87
108
 
88
109
  count(): Promise<number> {
@@ -100,10 +121,15 @@ export class JsonFileBackend implements MemoryBackend {
100
121
 
101
122
  /** Load records from `filePath`. No-op when no path is set or the file is absent. */
102
123
  load(): Promise<void> {
124
+ this.loadSync();
125
+ return Promise.resolve();
126
+ }
127
+
128
+ /** Synchronous {@link JsonFileBackend.load}. */
129
+ loadSync(): void {
103
130
  if (this.filePath !== undefined && existsSync(this.filePath)) {
104
131
  const data = JSON.parse(readFileSync(this.filePath, 'utf-8')) as MemoryRecord[];
105
132
  for (const record of data) this.records.set(record.id, record);
106
133
  }
107
- return Promise.resolve();
108
134
  }
109
135
  }
@@ -72,16 +72,16 @@ const FTS5_SQL = `
72
72
  END;
73
73
  `;
74
74
 
75
- /** FTS5 query — returns matching record ids ranked by relevance. */
75
+ /** FTS5 query — matching records with their relevance rank (lower = better). */
76
76
  const FTS5_SEARCH_SQL = `
77
- SELECT mr.* FROM memory_fts fts
77
+ SELECT mr.*, fts.rank AS _rank FROM memory_fts fts
78
78
  JOIN memory_records mr ON mr.rowid = fts.rowid
79
79
  WHERE memory_fts MATCH ?
80
80
  ORDER BY fts.rank
81
81
  `;
82
82
 
83
83
  const FTS5_SEARCH_SKILL_SQL = `
84
- SELECT mr.* FROM memory_fts fts
84
+ SELECT mr.*, fts.rank AS _rank FROM memory_fts fts
85
85
  JOIN memory_records mr ON mr.rowid = fts.rowid
86
86
  WHERE memory_fts MATCH ? AND mr.skill_id = ?
87
87
  ORDER BY fts.rank
@@ -170,6 +170,15 @@ export class SqliteBackend implements MemoryBackend {
170
170
  }
171
171
 
172
172
  query(query: MemoryQuery): Promise<MemoryRecord[]> {
173
+ return Promise.resolve(this.querySync(query));
174
+ }
175
+
176
+ /**
177
+ * Synchronous {@link SqliteBackend.query}. Same FTS5-ranked / keyword-fallback
178
+ * logic, no Promise — better-sqlite3 is physically synchronous, so a hot sync
179
+ * path (a recommender / `dz recall`) can query the store without an async ripple.
180
+ */
181
+ querySync(query: MemoryQuery): MemoryRecord[] {
173
182
  const limit = query.limit ?? DEFAULT_LIMIT;
174
183
 
175
184
  // FTS5 path — use SQLite full-text search when available and text query provided
@@ -184,12 +193,19 @@ export class SqliteBackend implements MemoryBackend {
184
193
  } else {
185
194
  rows = this.ftsSearchStmt!.all(ftsQuery);
186
195
  }
187
- const records = rows.map(rowToRecord);
188
- // FTS5 rank is already applied; tiebreak by score DESC, timestamp DESC
189
- records.sort(
190
- (a, b) => b.score - a.score || b.timestamp.localeCompare(a.timestamp),
191
- );
192
- return Promise.resolve(records.slice(0, limit));
196
+ // Rank by FTS5 relevance FIRST (lower rank = better match), with score
197
+ // then timestamp as a true tiebreak between equally-relevant rows. Score
198
+ // must NOT be primary — that would discard FTS5's relevance signal and
199
+ // diverge from the JSON backend (which also ranks relevance-primary).
200
+ const ranked = rows
201
+ .map((row) => ({ record: rowToRecord(row), rank: row._rank as number }))
202
+ .sort(
203
+ (a, b) =>
204
+ a.rank - b.rank ||
205
+ b.record.score - a.record.score ||
206
+ b.record.timestamp.localeCompare(a.record.timestamp),
207
+ );
208
+ return ranked.slice(0, limit).map((entry) => entry.record);
193
209
  }
194
210
  } catch {
195
211
  // FTS5 query failed (e.g., special chars) — fall through to keyword approach
@@ -214,11 +230,16 @@ export class SqliteBackend implements MemoryBackend {
214
230
  b.record.score - a.record.score ||
215
231
  b.record.timestamp.localeCompare(a.record.timestamp),
216
232
  );
217
- return Promise.resolve(ranked.slice(0, limit).map((entry) => entry.record));
233
+ return ranked.slice(0, limit).map((entry) => entry.record);
218
234
  }
219
235
 
220
236
  all(): Promise<MemoryRecord[]> {
221
- return Promise.resolve(this.allStmt.all().map(rowToRecord));
237
+ return Promise.resolve(this.allSync());
238
+ }
239
+
240
+ /** Synchronous {@link SqliteBackend.all}. */
241
+ allSync(): MemoryRecord[] {
242
+ return this.allStmt.all().map(rowToRecord);
222
243
  }
223
244
 
224
245
  count(): Promise<number> {