memorix 1.9.6 → 1.9.7

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/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ All notable changes to this project will be documented in this file.
6
6
 
7
7
  No unreleased changes.
8
8
 
9
+ ## [1.9.7] - 2026-09-28
10
+
11
+ ### Fixed
12
+ - **SQLite FTS5 startup resilience** -- transient `SQLITE_BUSY` contention during
13
+ lexical-index initialization is retried before FTS5 is marked unavailable.
14
+ - **Large-store search fallback** -- runtime embedding failure, provider
15
+ disappearance, and vector-dimension mismatch now all fall back to the durable
16
+ SQLite lexical index instead of returning a silent empty result.
17
+ - **Release metadata** -- synchronized the npm, MCP Registry, and packaged
18
+ integration metadata for the 1.9.7 patch release.
19
+
9
20
  ## [1.9.6] - 2026-09-22
10
21
 
11
22
  ### Added
package/dist/cli/index.js CHANGED
@@ -3232,7 +3232,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3232
3232
 
3233
3233
  // src/cli/version.ts
3234
3234
  function getCliVersion() {
3235
- return true ? "1.9.6" : pkg.version;
3235
+ return true ? "1.9.7" : pkg.version;
3236
3236
  }
3237
3237
  var init_version = __esm({
3238
3238
  "src/cli/version.ts"() {
@@ -9349,6 +9349,74 @@ var init_launch_root = __esm({
9349
9349
  }
9350
9350
  });
9351
9351
 
9352
+ // src/store/sqlite-reliability.ts
9353
+ function isTransientSqliteError(error40) {
9354
+ const code = error40?.code;
9355
+ if (typeof code === "string") {
9356
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
9357
+ }
9358
+ return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
9359
+ }
9360
+ function isSqliteUnavailableError(error40) {
9361
+ return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
9362
+ }
9363
+ function degradedReadError(storeName) {
9364
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
9365
+ }
9366
+ function degradedWriteError(storeName) {
9367
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
9368
+ }
9369
+ function formatSqliteFailure(error40) {
9370
+ return error40 instanceof Error && error40.message ? error40.message : String(error40);
9371
+ }
9372
+ function delay(ms2) {
9373
+ return new Promise((resolve9) => setTimeout(resolve9, ms2));
9374
+ }
9375
+ async function retrySqliteBusy(operation, label) {
9376
+ for (let attempt = 0; ; attempt++) {
9377
+ try {
9378
+ return await operation();
9379
+ } catch (error40) {
9380
+ if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
9381
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
9382
+ console.error(
9383
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
9384
+ );
9385
+ await delay(waitMs);
9386
+ }
9387
+ }
9388
+ }
9389
+ async function initializeSqliteStore(factory, dataDir, label) {
9390
+ try {
9391
+ loadBetterSqlite3();
9392
+ } catch (error40) {
9393
+ throw new SqliteUnavailableError(error40);
9394
+ }
9395
+ return retrySqliteBusy(async () => {
9396
+ const store2 = factory();
9397
+ await store2.init(dataDir);
9398
+ return store2;
9399
+ }, label);
9400
+ }
9401
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
9402
+ var init_sqlite_reliability = __esm({
9403
+ "src/store/sqlite-reliability.ts"() {
9404
+ "use strict";
9405
+ init_esm_shims();
9406
+ init_sqlite_db();
9407
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
9408
+ SqliteUnavailableError = class extends Error {
9409
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
9410
+ constructor(cause) {
9411
+ super(
9412
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
9413
+ );
9414
+ this.name = "SqliteUnavailableError";
9415
+ }
9416
+ };
9417
+ }
9418
+ });
9419
+
9352
9420
  // src/store/sqlite-fts.ts
9353
9421
  function normalizeFtsToken(token) {
9354
9422
  return token.replace(/"/g, '""');
@@ -9362,22 +9430,27 @@ function buildFtsQuery(query) {
9362
9430
  function initializeObservationLexicalIndex(db2) {
9363
9431
  const known = ftsAvailability.get(db2);
9364
9432
  if (known !== void 0) return known;
9365
- try {
9366
- db2.exec(CREATE_OBSERVATION_FTS);
9367
- const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
9368
- const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
9369
- if (observationCount !== indexedCount) {
9370
- db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
9433
+ let lastError;
9434
+ for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
9435
+ try {
9436
+ db2.exec(CREATE_OBSERVATION_FTS);
9437
+ const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
9438
+ const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
9439
+ if (observationCount !== indexedCount) {
9440
+ db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
9441
+ }
9442
+ ftsAvailability.set(db2, true);
9443
+ return true;
9444
+ } catch (error40) {
9445
+ lastError = error40;
9446
+ if (!isTransientSqliteError(error40) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
9371
9447
  }
9372
- ftsAvailability.set(db2, true);
9373
- return true;
9374
- } catch (error40) {
9375
- ftsAvailability.set(db2, false);
9376
- console.warn(
9377
- `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error40 instanceof Error ? error40.message : String(error40)}`
9378
- );
9379
- return false;
9380
9448
  }
9449
+ ftsAvailability.set(db2, false);
9450
+ console.warn(
9451
+ `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
9452
+ );
9453
+ return false;
9381
9454
  }
9382
9455
  function isObservationLexicalIndexEnabled(db2) {
9383
9456
  return ftsAvailability.get(db2) === true;
@@ -9431,11 +9504,12 @@ function searchObservationLexically(db2, options) {
9431
9504
  score: Math.max(0, Number(row.lexical_score ?? 0))
9432
9505
  }));
9433
9506
  }
9434
- var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
9507
+ var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
9435
9508
  var init_sqlite_fts = __esm({
9436
9509
  "src/store/sqlite-fts.ts"() {
9437
9510
  "use strict";
9438
9511
  init_esm_shims();
9512
+ init_sqlite_reliability();
9439
9513
  ftsAvailability = /* @__PURE__ */ new WeakMap();
9440
9514
  FTS_TABLE = "observations_fts";
9441
9515
  CREATE_OBSERVATION_FTS = `
@@ -9469,6 +9543,7 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
9469
9543
  VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
9470
9544
  END;
9471
9545
  `;
9546
+ FTS_INIT_MAX_ATTEMPTS = 3;
9472
9547
  }
9473
9548
  });
9474
9549
 
@@ -10724,74 +10799,6 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
10724
10799
  }
10725
10800
  });
10726
10801
 
10727
- // src/store/sqlite-reliability.ts
10728
- function isTransientSqliteError(error40) {
10729
- const code = error40?.code;
10730
- if (typeof code === "string") {
10731
- return /^SQLITE_BUSY(?:_|$)/i.test(code);
10732
- }
10733
- return error40 instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error40.message);
10734
- }
10735
- function isSqliteUnavailableError(error40) {
10736
- return error40 instanceof SqliteUnavailableError || error40 instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error40.message);
10737
- }
10738
- function degradedReadError(storeName) {
10739
- return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
10740
- }
10741
- function degradedWriteError(storeName) {
10742
- return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
10743
- }
10744
- function formatSqliteFailure(error40) {
10745
- return error40 instanceof Error && error40.message ? error40.message : String(error40);
10746
- }
10747
- function delay(ms2) {
10748
- return new Promise((resolve9) => setTimeout(resolve9, ms2));
10749
- }
10750
- async function retrySqliteBusy(operation, label) {
10751
- for (let attempt = 0; ; attempt++) {
10752
- try {
10753
- return await operation();
10754
- } catch (error40) {
10755
- if (!isTransientSqliteError(error40) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error40;
10756
- const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
10757
- console.error(
10758
- `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
10759
- );
10760
- await delay(waitMs);
10761
- }
10762
- }
10763
- }
10764
- async function initializeSqliteStore(factory, dataDir, label) {
10765
- try {
10766
- loadBetterSqlite3();
10767
- } catch (error40) {
10768
- throw new SqliteUnavailableError(error40);
10769
- }
10770
- return retrySqliteBusy(async () => {
10771
- const store2 = factory();
10772
- await store2.init(dataDir);
10773
- return store2;
10774
- }, label);
10775
- }
10776
- var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
10777
- var init_sqlite_reliability = __esm({
10778
- "src/store/sqlite-reliability.ts"() {
10779
- "use strict";
10780
- init_esm_shims();
10781
- init_sqlite_db();
10782
- SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
10783
- SqliteUnavailableError = class extends Error {
10784
- code = "MEMORIX_SQLITE_UNAVAILABLE";
10785
- constructor(cause) {
10786
- super(
10787
- cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
10788
- );
10789
- this.name = "SqliteUnavailableError";
10790
- }
10791
- };
10792
- }
10793
- });
10794
-
10795
10802
  // src/store/file-lock.ts
10796
10803
  import { promises as fs2 } from "fs";
10797
10804
  import { randomUUID } from "crypto";
@@ -188224,6 +188231,7 @@ async function searchObservations(options) {
188224
188231
  };
188225
188232
  let queryVector = null;
188226
188233
  let embeddingProviderForSearch = null;
188234
+ let embeddingUnavailable = false;
188227
188235
  if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
188228
188236
  try {
188229
188237
  const provider2 = await getEmbeddingProvider();
@@ -188231,6 +188239,7 @@ async function searchObservations(options) {
188231
188239
  embeddingProviderForSearch = provider2;
188232
188240
  const activeVectorDimensions = getVectorDimensions();
188233
188241
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
188242
+ embeddingUnavailable = true;
188234
188243
  rememberSearchMode(
188235
188244
  modeKey,
188236
188245
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
@@ -188270,8 +188279,11 @@ async function searchObservations(options) {
188270
188279
  hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
188271
188280
  };
188272
188281
  }
188282
+ } else {
188283
+ embeddingUnavailable = true;
188273
188284
  }
188274
188285
  } catch (error40) {
188286
+ embeddingUnavailable = true;
188275
188287
  rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
188276
188288
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
188277
188289
  }
@@ -188279,7 +188291,7 @@ async function searchObservations(options) {
188279
188291
  mark("preSearch");
188280
188292
  let results;
188281
188293
  const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
188282
- const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
188294
+ const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
188283
188295
  let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
188284
188296
  const durableStore = getOptionalObservationStore();
188285
188297
  if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count2(database) === 0) {
@@ -209007,7 +209019,7 @@ var init_tool_profile = __esm({
209007
209019
  function getMcpServerInfo() {
209008
209020
  return {
209009
209021
  name: "memorix",
209010
- version: true ? "1.9.6" : "1.0.1"
209022
+ version: true ? "1.9.7" : "1.0.1"
209011
209023
  };
209012
209024
  }
209013
209025
  function createMcpDiscoverResult(capabilities, serverInfo) {
package/dist/index.js CHANGED
@@ -286,6 +286,74 @@ var init_launch_root = __esm({
286
286
  }
287
287
  });
288
288
 
289
+ // src/store/sqlite-reliability.ts
290
+ function isTransientSqliteError(error) {
291
+ const code = error?.code;
292
+ if (typeof code === "string") {
293
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
294
+ }
295
+ return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
296
+ }
297
+ function isSqliteUnavailableError(error) {
298
+ return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
299
+ }
300
+ function degradedReadError(storeName) {
301
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
302
+ }
303
+ function degradedWriteError(storeName) {
304
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
305
+ }
306
+ function formatSqliteFailure(error) {
307
+ return error instanceof Error && error.message ? error.message : String(error);
308
+ }
309
+ function delay(ms) {
310
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
311
+ }
312
+ async function retrySqliteBusy(operation, label) {
313
+ for (let attempt = 0; ; attempt++) {
314
+ try {
315
+ return await operation();
316
+ } catch (error) {
317
+ if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
318
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
319
+ console.error(
320
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
321
+ );
322
+ await delay(waitMs);
323
+ }
324
+ }
325
+ }
326
+ async function initializeSqliteStore(factory, dataDir, label) {
327
+ try {
328
+ loadBetterSqlite3();
329
+ } catch (error) {
330
+ throw new SqliteUnavailableError(error);
331
+ }
332
+ return retrySqliteBusy(async () => {
333
+ const store = factory();
334
+ await store.init(dataDir);
335
+ return store;
336
+ }, label);
337
+ }
338
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
339
+ var init_sqlite_reliability = __esm({
340
+ "src/store/sqlite-reliability.ts"() {
341
+ "use strict";
342
+ init_esm_shims();
343
+ init_sqlite_db();
344
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
345
+ SqliteUnavailableError = class extends Error {
346
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
347
+ constructor(cause) {
348
+ super(
349
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
350
+ );
351
+ this.name = "SqliteUnavailableError";
352
+ }
353
+ };
354
+ }
355
+ });
356
+
289
357
  // src/store/sqlite-fts.ts
290
358
  function normalizeFtsToken(token) {
291
359
  return token.replace(/"/g, '""');
@@ -299,22 +367,27 @@ function buildFtsQuery(query) {
299
367
  function initializeObservationLexicalIndex(db2) {
300
368
  const known = ftsAvailability.get(db2);
301
369
  if (known !== void 0) return known;
302
- try {
303
- db2.exec(CREATE_OBSERVATION_FTS);
304
- const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
305
- const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
306
- if (observationCount !== indexedCount) {
307
- db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
370
+ let lastError;
371
+ for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
372
+ try {
373
+ db2.exec(CREATE_OBSERVATION_FTS);
374
+ const observationCount = Number(db2.prepare("SELECT COUNT(*) AS count FROM observations").get()?.count ?? 0);
375
+ const indexedCount = Number(db2.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
376
+ if (observationCount !== indexedCount) {
377
+ db2.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
378
+ }
379
+ ftsAvailability.set(db2, true);
380
+ return true;
381
+ } catch (error) {
382
+ lastError = error;
383
+ if (!isTransientSqliteError(error) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
308
384
  }
309
- ftsAvailability.set(db2, true);
310
- return true;
311
- } catch (error) {
312
- ftsAvailability.set(db2, false);
313
- console.warn(
314
- `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error instanceof Error ? error.message : String(error)}`
315
- );
316
- return false;
317
385
  }
386
+ ftsAvailability.set(db2, false);
387
+ console.warn(
388
+ `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`
389
+ );
390
+ return false;
318
391
  }
319
392
  function isObservationLexicalIndexEnabled(db2) {
320
393
  return ftsAvailability.get(db2) === true;
@@ -368,11 +441,12 @@ function searchObservationLexically(db2, options) {
368
441
  score: Math.max(0, Number(row.lexical_score ?? 0))
369
442
  }));
370
443
  }
371
- var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS;
444
+ var ftsAvailability, FTS_TABLE, CREATE_OBSERVATION_FTS, FTS_INIT_MAX_ATTEMPTS;
372
445
  var init_sqlite_fts = __esm({
373
446
  "src/store/sqlite-fts.ts"() {
374
447
  "use strict";
375
448
  init_esm_shims();
449
+ init_sqlite_reliability();
376
450
  ftsAvailability = /* @__PURE__ */ new WeakMap();
377
451
  FTS_TABLE = "observations_fts";
378
452
  CREATE_OBSERVATION_FTS = `
@@ -406,6 +480,7 @@ CREATE TRIGGER IF NOT EXISTS observations_fts_au AFTER UPDATE ON observations BE
406
480
  VALUES (new.id, new.entityName, new.title, new.narrative, new.facts, new.filesModified, new.concepts, new.attachments);
407
481
  END;
408
482
  `;
483
+ FTS_INIT_MAX_ATTEMPTS = 3;
409
484
  }
410
485
  });
411
486
 
@@ -2557,74 +2632,6 @@ var init_types = __esm({
2557
2632
  }
2558
2633
  });
2559
2634
 
2560
- // src/store/sqlite-reliability.ts
2561
- function isTransientSqliteError(error) {
2562
- const code = error?.code;
2563
- if (typeof code === "string") {
2564
- return /^SQLITE_BUSY(?:_|$)/i.test(code);
2565
- }
2566
- return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
2567
- }
2568
- function isSqliteUnavailableError(error) {
2569
- return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
2570
- }
2571
- function degradedReadError(storeName) {
2572
- return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
2573
- }
2574
- function degradedWriteError(storeName) {
2575
- return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
2576
- }
2577
- function formatSqliteFailure(error) {
2578
- return error instanceof Error && error.message ? error.message : String(error);
2579
- }
2580
- function delay(ms) {
2581
- return new Promise((resolve5) => setTimeout(resolve5, ms));
2582
- }
2583
- async function retrySqliteBusy(operation, label) {
2584
- for (let attempt = 0; ; attempt++) {
2585
- try {
2586
- return await operation();
2587
- } catch (error) {
2588
- if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
2589
- const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
2590
- console.error(
2591
- `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
2592
- );
2593
- await delay(waitMs);
2594
- }
2595
- }
2596
- }
2597
- async function initializeSqliteStore(factory, dataDir, label) {
2598
- try {
2599
- loadBetterSqlite3();
2600
- } catch (error) {
2601
- throw new SqliteUnavailableError(error);
2602
- }
2603
- return retrySqliteBusy(async () => {
2604
- const store = factory();
2605
- await store.init(dataDir);
2606
- return store;
2607
- }, label);
2608
- }
2609
- var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
2610
- var init_sqlite_reliability = __esm({
2611
- "src/store/sqlite-reliability.ts"() {
2612
- "use strict";
2613
- init_esm_shims();
2614
- init_sqlite_db();
2615
- SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
2616
- SqliteUnavailableError = class extends Error {
2617
- code = "MEMORIX_SQLITE_UNAVAILABLE";
2618
- constructor(cause) {
2619
- super(
2620
- cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
2621
- );
2622
- this.name = "SqliteUnavailableError";
2623
- }
2624
- };
2625
- }
2626
- });
2627
-
2628
2635
  // src/store/mini-skill-store.ts
2629
2636
  var mini_skill_store_exports = {};
2630
2637
  __export(mini_skill_store_exports, {
@@ -9120,6 +9127,7 @@ async function searchObservations(options) {
9120
9127
  };
9121
9128
  let queryVector = null;
9122
9129
  let embeddingProviderForSearch = null;
9130
+ let embeddingUnavailable = false;
9123
9131
  if (quality !== "fast" && embeddingEnabled && hasQuery && tier !== "fast") {
9124
9132
  try {
9125
9133
  const provider2 = await getEmbeddingProvider();
@@ -9127,6 +9135,7 @@ async function searchObservations(options) {
9127
9135
  embeddingProviderForSearch = provider2;
9128
9136
  const activeVectorDimensions = getVectorDimensions();
9129
9137
  if (activeVectorDimensions !== null && provider2.dimensions !== activeVectorDimensions) {
9138
+ embeddingUnavailable = true;
9130
9139
  rememberSearchMode(
9131
9140
  modeKey,
9132
9141
  `fulltext (embedding dimension mismatch: provider ${provider2.dimensions}d vs index ${activeVectorDimensions}d)`
@@ -9166,8 +9175,11 @@ async function searchObservations(options) {
9166
9175
  hybridWeights: isCJKHeavy ? { text: 0.2, vector: 0.8 } : { text: 0.6, vector: 0.4 }
9167
9176
  };
9168
9177
  }
9178
+ } else {
9179
+ embeddingUnavailable = true;
9169
9180
  }
9170
9181
  } catch (error) {
9182
+ embeddingUnavailable = true;
9171
9183
  rememberSearchMode(modeKey, "fulltext (embedding unavailable)");
9172
9184
  console.error("[memorix] Embedding failed or timed out, falling back to fulltext search");
9173
9185
  }
@@ -9175,7 +9187,7 @@ async function searchObservations(options) {
9175
9187
  mark("preSearch");
9176
9188
  let results;
9177
9189
  const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
9178
- const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled);
9190
+ const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector && (quality === "fast" || tier === "fast" || !embeddingEnabled || embeddingUnavailable);
9179
9191
  let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch) : canUsePersistentLexical ? await searchPersistentLexically(options, projectIds, requestLimit) : null;
9180
9192
  const durableStore = getOptionalObservationStore();
9181
9193
  if (!persistent && queryVector && durableStore?.hasLexicalIndex?.() && count(database) === 0) {
@@ -160092,7 +160104,7 @@ var init_request_context = __esm({
160092
160104
  function getMcpServerInfo() {
160093
160105
  return {
160094
160106
  name: "memorix",
160095
- version: true ? "1.9.6" : "1.0.1"
160107
+ version: true ? "1.9.7" : "1.0.1"
160096
160108
  };
160097
160109
  }
160098
160110
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -176430,7 +176442,7 @@ var init_serve_shared = __esm({
176430
176442
 
176431
176443
  // src/cli/version.ts
176432
176444
  function getCliVersion() {
176433
- return true ? "1.9.6" : pkg.version;
176445
+ return true ? "1.9.7" : pkg.version;
176434
176446
  }
176435
176447
  var init_version3 = __esm({
176436
176448
  "src/cli/version.ts"() {