memorix 1.9.2 → 1.9.3

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/dist/index.js CHANGED
@@ -452,70 +452,79 @@ function getDatabase(dataDir) {
452
452
  loadBetterSqlite3();
453
453
  fs.mkdirSync(dataDir, { recursive: true });
454
454
  const dbPath = path3.join(dataDir, "memorix.db");
455
- const db2 = createDatabase(dbPath);
456
- db2.pragma("journal_mode = WAL");
457
- db2.pragma("busy_timeout = 5000");
458
- db2.pragma("foreign_keys = ON");
459
- db2.exec(CREATE_OBSERVATIONS_TABLE);
460
- db2.exec(CREATE_MINI_SKILLS_TABLE);
461
- db2.exec(CREATE_SESSIONS_TABLE);
462
- db2.exec(CREATE_META_TABLE);
463
- db2.exec(CREATE_TEAM_AGENTS_TABLE);
464
- db2.exec(CREATE_TEAM_TASKS_TABLE);
465
- db2.exec(CREATE_TEAM_TASK_DEPS_TABLE);
466
- db2.exec(CREATE_TEAM_MESSAGES_TABLE);
467
- db2.exec(CREATE_TEAM_LOCKS_TABLE);
468
- db2.exec(CREATE_TEAM_ROLES_TABLE);
469
- db2.exec(CREATE_GRAPH_ENTITIES_TABLE);
470
- db2.exec(CREATE_GRAPH_RELATIONS_TABLE);
471
- db2.exec(CREATE_CHAT_TRANSCRIPT_TABLE);
472
- db2.exec(CREATE_CODE_FILES_TABLE);
473
- db2.exec(CREATE_CODE_SYMBOLS_TABLE);
474
- db2.exec(CREATE_CODE_EDGES_TABLE);
475
- db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
476
- db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
477
- db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
478
- db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
455
+ let db2;
479
456
  try {
480
- db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
481
- } catch {
482
- }
483
- try {
484
- db2.exec(`ALTER TABLE mini_skills ADD COLUMN updatedAt TEXT`);
485
- } catch {
486
- }
487
- try {
488
- db2.exec(`ALTER TABLE observations ADD COLUMN createdByAgentId TEXT`);
489
- } catch {
490
- }
491
- try {
492
- db2.exec(`ALTER TABLE observations ADD COLUMN writeGeneration INTEGER DEFAULT 0`);
493
- } catch {
494
- }
495
- try {
496
- db2.exec(`ALTER TABLE team_tasks ADD COLUMN required_role TEXT`);
497
- } catch {
498
- }
499
- try {
500
- db2.exec(`ALTER TABLE team_tasks ADD COLUMN preferred_role TEXT`);
501
- } catch {
502
- }
503
- try {
504
- db2.exec(`ALTER TABLE team_messages ADD COLUMN to_role TEXT`);
505
- } catch {
506
- }
507
- try {
508
- db2.exec(`ALTER TABLE team_messages ADD COLUMN handoff_status TEXT`);
509
- } catch {
457
+ db2 = createDatabase(dbPath);
458
+ db2.pragma("busy_timeout = 5000");
459
+ db2.pragma("journal_mode = WAL");
460
+ db2.pragma("foreign_keys = ON");
461
+ db2.exec(CREATE_OBSERVATIONS_TABLE);
462
+ db2.exec(CREATE_MINI_SKILLS_TABLE);
463
+ db2.exec(CREATE_SESSIONS_TABLE);
464
+ db2.exec(CREATE_META_TABLE);
465
+ db2.exec(CREATE_TEAM_AGENTS_TABLE);
466
+ db2.exec(CREATE_TEAM_TASKS_TABLE);
467
+ db2.exec(CREATE_TEAM_TASK_DEPS_TABLE);
468
+ db2.exec(CREATE_TEAM_MESSAGES_TABLE);
469
+ db2.exec(CREATE_TEAM_LOCKS_TABLE);
470
+ db2.exec(CREATE_TEAM_ROLES_TABLE);
471
+ db2.exec(CREATE_GRAPH_ENTITIES_TABLE);
472
+ db2.exec(CREATE_GRAPH_RELATIONS_TABLE);
473
+ db2.exec(CREATE_CHAT_TRANSCRIPT_TABLE);
474
+ db2.exec(CREATE_CODE_FILES_TABLE);
475
+ db2.exec(CREATE_CODE_SYMBOLS_TABLE);
476
+ db2.exec(CREATE_CODE_EDGES_TABLE);
477
+ db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
478
+ db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
479
+ db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
480
+ db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
481
+ try {
482
+ db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
483
+ } catch {
484
+ }
485
+ try {
486
+ db2.exec(`ALTER TABLE mini_skills ADD COLUMN updatedAt TEXT`);
487
+ } catch {
488
+ }
489
+ try {
490
+ db2.exec(`ALTER TABLE observations ADD COLUMN createdByAgentId TEXT`);
491
+ } catch {
492
+ }
493
+ try {
494
+ db2.exec(`ALTER TABLE observations ADD COLUMN writeGeneration INTEGER DEFAULT 0`);
495
+ } catch {
496
+ }
497
+ try {
498
+ db2.exec(`ALTER TABLE team_tasks ADD COLUMN required_role TEXT`);
499
+ } catch {
500
+ }
501
+ try {
502
+ db2.exec(`ALTER TABLE team_tasks ADD COLUMN preferred_role TEXT`);
503
+ } catch {
504
+ }
505
+ try {
506
+ db2.exec(`ALTER TABLE team_messages ADD COLUMN to_role TEXT`);
507
+ } catch {
508
+ }
509
+ try {
510
+ db2.exec(`ALTER TABLE team_messages ADD COLUMN handoff_status TEXT`);
511
+ } catch {
512
+ }
513
+ applySchemaMigrations(db2);
514
+ db2.exec(CREATE_INDEXES);
515
+ initializeObservationLexicalIndex(db2);
516
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
517
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
518
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
519
+ _dbCache.set(normalized, db2);
520
+ return db2;
521
+ } catch (error) {
522
+ try {
523
+ db2?.close();
524
+ } catch {
525
+ }
526
+ throw error;
510
527
  }
511
- applySchemaMigrations(db2);
512
- db2.exec(CREATE_INDEXES);
513
- initializeObservationLexicalIndex(db2);
514
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
515
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
516
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
517
- _dbCache.set(normalized, db2);
518
- return db2;
519
528
  }
520
529
  function closeAllDatabases() {
521
530
  for (const [key, db2] of _dbCache) {
@@ -2329,6 +2338,74 @@ var init_types = __esm({
2329
2338
  }
2330
2339
  });
2331
2340
 
2341
+ // src/store/sqlite-reliability.ts
2342
+ function isTransientSqliteError(error) {
2343
+ const code = error?.code;
2344
+ if (typeof code === "string") {
2345
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
2346
+ }
2347
+ return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
2348
+ }
2349
+ function isSqliteUnavailableError(error) {
2350
+ return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
2351
+ }
2352
+ function degradedReadError(storeName) {
2353
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
2354
+ }
2355
+ function degradedWriteError(storeName) {
2356
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
2357
+ }
2358
+ function formatSqliteFailure(error) {
2359
+ return error instanceof Error && error.message ? error.message : String(error);
2360
+ }
2361
+ function delay(ms) {
2362
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
2363
+ }
2364
+ async function retrySqliteBusy(operation, label) {
2365
+ for (let attempt = 0; ; attempt++) {
2366
+ try {
2367
+ return await operation();
2368
+ } catch (error) {
2369
+ if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
2370
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
2371
+ console.error(
2372
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
2373
+ );
2374
+ await delay(waitMs);
2375
+ }
2376
+ }
2377
+ }
2378
+ async function initializeSqliteStore(factory, dataDir, label) {
2379
+ try {
2380
+ loadBetterSqlite3();
2381
+ } catch (error) {
2382
+ throw new SqliteUnavailableError(error);
2383
+ }
2384
+ return retrySqliteBusy(async () => {
2385
+ const store = factory();
2386
+ await store.init(dataDir);
2387
+ return store;
2388
+ }, label);
2389
+ }
2390
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
2391
+ var init_sqlite_reliability = __esm({
2392
+ "src/store/sqlite-reliability.ts"() {
2393
+ "use strict";
2394
+ init_esm_shims();
2395
+ init_sqlite_db();
2396
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
2397
+ SqliteUnavailableError = class extends Error {
2398
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
2399
+ constructor(cause) {
2400
+ super(
2401
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
2402
+ );
2403
+ this.name = "SqliteUnavailableError";
2404
+ }
2405
+ };
2406
+ }
2407
+ });
2408
+
2332
2409
  // src/store/mini-skill-store.ts
2333
2410
  var mini_skill_store_exports = {};
2334
2411
  __export(mini_skill_store_exports, {
@@ -2400,16 +2477,18 @@ async function initMiniSkillStore(dataDir) {
2400
2477
  if (_store && _storeDataDir === dataDir) return _store;
2401
2478
  _store = null;
2402
2479
  _storeDataDir = null;
2480
+ let degradedReason = "SQLite runtime unavailable";
2403
2481
  try {
2404
- const store2 = new MiniSkillSqliteStore();
2405
- await store2.init(dataDir);
2482
+ const store2 = await initializeSqliteStore(() => new MiniSkillSqliteStore(), dataDir, "Mini-skill store");
2406
2483
  _store = store2;
2407
2484
  _storeDataDir = dataDir;
2408
2485
  return store2;
2409
2486
  } catch (err) {
2410
- console.error(`[memorix] MiniSkillSqliteStore unavailable, running in degraded read-only mode: ${err instanceof Error ? err.message : err}`);
2487
+ if (!isSqliteUnavailableError(err)) throw err;
2488
+ degradedReason = err instanceof Error ? err.message : String(err);
2489
+ console.error(`[memorix] MiniSkillSqliteStore unavailable, running in degraded read-only mode: ${degradedReason}`);
2411
2490
  }
2412
- const store = new MiniSkillGracefulDegrade();
2491
+ const store = new MiniSkillGracefulDegrade(degradedReason);
2413
2492
  await store.init(dataDir);
2414
2493
  _store = store;
2415
2494
  _storeDataDir = dataDir;
@@ -2421,6 +2500,7 @@ var init_mini_skill_store = __esm({
2421
2500
  "use strict";
2422
2501
  init_esm_shims();
2423
2502
  init_sqlite_db();
2503
+ init_sqlite_reliability();
2424
2504
  MiniSkillSqliteStore = class {
2425
2505
  db = null;
2426
2506
  dataDir = "";
@@ -2529,7 +2609,7 @@ var init_mini_skill_store = __esm({
2529
2609
  * SQLite serializes write transactions, so concurrent calls are safely sequenced.
2530
2610
  */
2531
2611
  async atomicInsertWithId(skillWithoutId) {
2532
- const result = this.db.transaction(() => {
2612
+ return retrySqliteBusy(async () => this.db.transaction(() => {
2533
2613
  const row = this.stmtGetMeta.get("mini_skills_next_id");
2534
2614
  const nextId2 = row ? parseInt(row.value, 10) : 1;
2535
2615
  const skill2 = { ...skillWithoutId, id: nextId2 };
@@ -2538,8 +2618,7 @@ var init_mini_skill_store = __esm({
2538
2618
  this.stmtBumpGeneration.run();
2539
2619
  this.knownGeneration = this.readGeneration();
2540
2620
  return skill2;
2541
- })();
2542
- return result;
2621
+ })(), "Mini-skill promotion");
2543
2622
  }
2544
2623
  // ── Freshness ────────────────────────────────────────────────────
2545
2624
  async ensureFresh() {
@@ -2558,10 +2637,17 @@ var init_mini_skill_store = __esm({
2558
2637
  }
2559
2638
  };
2560
2639
  MiniSkillGracefulDegrade = class {
2640
+ constructor(reason = "SQLite runtime unavailable") {
2641
+ this.reason = reason;
2642
+ }
2643
+ reason;
2561
2644
  warned = false;
2645
+ unavailableRead() {
2646
+ return degradedReadError("mini-skills");
2647
+ }
2562
2648
  warn() {
2563
2649
  if (!this.warned) {
2564
- console.error("[memorix] MiniSkillStore: SQLite unavailable \u2014 mini-skills are disabled (read-only empty). Install better-sqlite3 for full functionality.");
2650
+ console.error("[memorix] MiniSkillStore: SQLite unavailable \u2014 mini-skills cannot be read or written. Install a supported SQLite runtime.");
2565
2651
  this.warned = true;
2566
2652
  }
2567
2653
  }
@@ -2569,28 +2655,33 @@ var init_mini_skill_store = __esm({
2569
2655
  this.warn();
2570
2656
  }
2571
2657
  async loadAll() {
2572
- return [];
2658
+ throw this.unavailableRead();
2573
2659
  }
2574
2660
  async loadByProject(_projectId) {
2575
- return [];
2661
+ throw this.unavailableRead();
2576
2662
  }
2577
2663
  async loadIdCounter() {
2578
- return 1;
2664
+ throw this.unavailableRead();
2579
2665
  }
2580
2666
  async insert(_skill) {
2581
2667
  this.warn();
2668
+ throw degradedWriteError("mini-skills");
2582
2669
  }
2583
2670
  async update(_skill) {
2584
2671
  this.warn();
2672
+ throw degradedWriteError("mini-skills");
2585
2673
  }
2586
2674
  async remove(_id) {
2587
2675
  this.warn();
2676
+ throw degradedWriteError("mini-skills");
2588
2677
  }
2589
2678
  async saveIdCounter(_nextId) {
2679
+ this.warn();
2680
+ throw degradedWriteError("mini-skills");
2590
2681
  }
2591
2682
  async atomicInsertWithId(skillWithoutId) {
2592
2683
  this.warn();
2593
- return { ...skillWithoutId, id: 0 };
2684
+ throw degradedWriteError("mini-skills");
2594
2685
  }
2595
2686
  async ensureFresh() {
2596
2687
  return false;
@@ -2601,6 +2692,9 @@ var init_mini_skill_store = __esm({
2601
2692
  getBackendName() {
2602
2693
  return "degraded";
2603
2694
  }
2695
+ getBackendReason() {
2696
+ return this.reason;
2697
+ }
2604
2698
  };
2605
2699
  _store = null;
2606
2700
  _storeDataDir = null;
@@ -4855,10 +4949,10 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
4855
4949
  );
4856
4950
  }
4857
4951
  if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
4858
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
4952
+ const delay3 = BASE_DELAY_MS * Math.pow(2, attempt);
4859
4953
  const retryAfter = response.headers.get("retry-after");
4860
4954
  const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
4861
- const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
4955
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay3;
4862
4956
  console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
4863
4957
  await waitForRetry(waitMs, options.signal);
4864
4958
  return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
@@ -6890,6 +6984,7 @@ var init_sqlite_store = __esm({
6890
6984
  "use strict";
6891
6985
  init_esm_shims();
6892
6986
  init_sqlite_db();
6987
+ init_sqlite_reliability();
6893
6988
  init_sqlite_fts();
6894
6989
  SqliteBackend = class {
6895
6990
  db = null;
@@ -7167,7 +7262,9 @@ var init_sqlite_store = __esm({
7167
7262
  // ── Compound atomic operation ────────────────────────────────────
7168
7263
  async atomic(fn) {
7169
7264
  const run = this._atomicQueue.catch(() => void 0).then(async () => {
7170
- this.db.prepare("BEGIN IMMEDIATE").run();
7265
+ await retrySqliteBusy(async () => {
7266
+ this.db.prepare("BEGIN IMMEDIATE").run();
7267
+ }, "Observation atomic write");
7171
7268
  try {
7172
7269
  const tx = {
7173
7270
  loadAll: async () => this.rawLoadAll(),
@@ -7266,15 +7363,15 @@ function resetObservationStore() {
7266
7363
  async function createObservationStore(dataDir) {
7267
7364
  try {
7268
7365
  const { SqliteBackend: SqliteBackend2 } = await Promise.resolve().then(() => (init_sqlite_store(), sqlite_store_exports));
7269
- const store2 = new SqliteBackend2();
7270
- await store2.init(dataDir);
7271
- return store2;
7366
+ return await initializeSqliteStore(() => new SqliteBackend2(), dataDir, "Observation store");
7272
7367
  } catch (err) {
7273
- console.error(`[memorix] SQLite backend unavailable \u2014 degraded mode (read-only): ${err instanceof Error ? err.message : err}`);
7368
+ if (!isSqliteUnavailableError(err)) throw err;
7369
+ const reason = formatSqliteFailure(err);
7370
+ console.error(`[memorix] SQLite module unavailable \u2014 degraded mode (reads/writes disabled): ${reason}`);
7371
+ const degraded = new DegradedBackend(reason);
7372
+ await degraded.init(dataDir);
7373
+ return degraded;
7274
7374
  }
7275
- const store = new DegradedBackend();
7276
- await store.init(dataDir);
7277
- return store;
7278
7375
  }
7279
7376
  async function initObservationStore(dataDir) {
7280
7377
  if (_store2 && _storeDataDir2 === dataDir) {
@@ -7298,36 +7395,44 @@ var init_obs_store = __esm({
7298
7395
  "src/store/obs-store.ts"() {
7299
7396
  "use strict";
7300
7397
  init_esm_shims();
7398
+ init_sqlite_reliability();
7301
7399
  _store2 = null;
7302
7400
  _storeDataDir2 = null;
7303
7401
  DegradedBackend = class {
7402
+ constructor(reason = "SQLite runtime unavailable") {
7403
+ this.reason = reason;
7404
+ }
7405
+ reason;
7304
7406
  dataDir = "";
7407
+ unavailableRead() {
7408
+ return degradedReadError("observations");
7409
+ }
7305
7410
  async init(dataDir) {
7306
7411
  this.dataDir = dataDir;
7307
7412
  }
7308
7413
  async loadAll() {
7309
- return [];
7414
+ throw this.unavailableRead();
7310
7415
  }
7311
7416
  async loadByProject(_projectId, _options) {
7312
- return [];
7417
+ throw this.unavailableRead();
7313
7418
  }
7314
7419
  async countByProject(_projectId, _options) {
7315
- return 0;
7420
+ throw this.unavailableRead();
7316
7421
  }
7317
7422
  async getById(_id) {
7318
- return void 0;
7423
+ throw this.unavailableRead();
7319
7424
  }
7320
7425
  async loadIdCounter() {
7321
- return 1;
7426
+ throw this.unavailableRead();
7322
7427
  }
7323
7428
  async countAll() {
7324
- return 0;
7429
+ throw this.unavailableRead();
7325
7430
  }
7326
7431
  async listProjectIds() {
7327
- return [];
7432
+ throw this.unavailableRead();
7328
7433
  }
7329
7434
  async searchLexical(_options) {
7330
- return [];
7435
+ throw this.unavailableRead();
7331
7436
  }
7332
7437
  hasLexicalIndex() {
7333
7438
  return false;
@@ -7336,25 +7441,25 @@ var init_obs_store = __esm({
7336
7441
  return false;
7337
7442
  }
7338
7443
  async insert(_obs) {
7339
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7444
+ throw degradedWriteError("observations");
7340
7445
  }
7341
7446
  async update(_obs) {
7342
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7447
+ throw degradedWriteError("observations");
7343
7448
  }
7344
7449
  async remove(_id) {
7345
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7450
+ throw degradedWriteError("observations");
7346
7451
  }
7347
7452
  async bulkReplace(_obs) {
7348
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7453
+ throw degradedWriteError("observations");
7349
7454
  }
7350
7455
  async bulkRemoveByIds(_ids) {
7351
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7456
+ throw degradedWriteError("observations");
7352
7457
  }
7353
7458
  async saveIdCounter(_nextId) {
7354
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7459
+ throw degradedWriteError("observations");
7355
7460
  }
7356
7461
  async atomic(_fn) {
7357
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7462
+ throw degradedWriteError("observations");
7358
7463
  }
7359
7464
  async ensureFresh() {
7360
7465
  return false;
@@ -7367,6 +7472,9 @@ var init_obs_store = __esm({
7367
7472
  getBackendName() {
7368
7473
  return "degraded";
7369
7474
  }
7475
+ getBackendReason() {
7476
+ return this.reason;
7477
+ }
7370
7478
  };
7371
7479
  }
7372
7480
  });
@@ -56963,10 +57071,10 @@ var require_retry3 = __commonJS({
56963
57071
  if (!await shouldRetryFn(err)) {
56964
57072
  return { shouldRetry: false, config: err.config };
56965
57073
  }
56966
- const delay2 = getNextRetryDelay(config);
57074
+ const delay3 = getNextRetryDelay(config);
56967
57075
  err.config.retryConfig.currentRetryAttempt += 1;
56968
- const backoff = config.retryBackoff ? config.retryBackoff(err, delay2) : new Promise((resolve5) => {
56969
- setTimeout(resolve5, delay2);
57076
+ const backoff = config.retryBackoff ? config.retryBackoff(err, delay3) : new Promise((resolve5) => {
57077
+ setTimeout(resolve5, delay3);
56970
57078
  });
56971
57079
  if (config.onRetryAttempt) {
56972
57080
  await config.onRetryAttempt(err);
@@ -98752,7 +98860,7 @@ async function retryBackoff(fn, strategy) {
98752
98860
  retryInterval = initialInterval * Math.pow(x2, exponent) + Math.random() * 1e3;
98753
98861
  }
98754
98862
  const d = Math.min(retryInterval, maxInterval);
98755
- await delay(d);
98863
+ await delay2(d);
98756
98864
  x2++;
98757
98865
  }
98758
98866
  }
@@ -98773,8 +98881,8 @@ function retryIntervalFromResponse(res) {
98773
98881
  }
98774
98882
  return 0;
98775
98883
  }
98776
- async function delay(delay2) {
98777
- return new Promise((resolve5) => setTimeout(resolve5, delay2));
98884
+ async function delay2(delay3) {
98885
+ return new Promise((resolve5) => setTimeout(resolve5, delay3));
98778
98886
  }
98779
98887
  var defaultBackoff, PermanentError, TemporaryError, codeRangeRE2;
98780
98888
  var init_retries = __esm({
@@ -154230,16 +154338,18 @@ async function initSessionStore(dataDir) {
154230
154338
  if (_store3 && _storeDataDir3 === dataDir) return _store3;
154231
154339
  _store3 = null;
154232
154340
  _storeDataDir3 = null;
154341
+ let degradedReason = "SQLite runtime unavailable";
154233
154342
  try {
154234
- const store2 = new SessionSqliteStore();
154235
- await store2.init(dataDir);
154343
+ const store2 = await initializeSqliteStore(() => new SessionSqliteStore(), dataDir, "Session store");
154236
154344
  _store3 = store2;
154237
154345
  _storeDataDir3 = dataDir;
154238
154346
  return store2;
154239
154347
  } catch (err) {
154240
- console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${err instanceof Error ? err.message : err}`);
154348
+ if (!isSqliteUnavailableError(err)) throw err;
154349
+ degradedReason = formatSqliteFailure(err);
154350
+ console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${degradedReason}`);
154241
154351
  }
154242
- const store = new SessionGracefulDegrade();
154352
+ const store = new SessionGracefulDegrade(degradedReason);
154243
154353
  await store.init(dataDir);
154244
154354
  _store3 = store;
154245
154355
  _storeDataDir3 = dataDir;
@@ -154251,6 +154361,7 @@ var init_session_store = __esm({
154251
154361
  "use strict";
154252
154362
  init_esm_shims();
154253
154363
  init_sqlite_db();
154364
+ init_sqlite_reliability();
154254
154365
  SessionSqliteStore = class {
154255
154366
  db = null;
154256
154367
  dataDir = "";
@@ -154339,7 +154450,7 @@ var init_session_store = __esm({
154339
154450
  }
154340
154451
  const stmtComplete = this._stmtCompleteActive;
154341
154452
  const stmtIns = this.stmtInsert;
154342
- const result = this.db.transaction(() => {
154453
+ return retrySqliteBusy(async () => this.db.transaction(() => {
154343
154454
  let completedCount = 0;
154344
154455
  for (const pid of projectIds) {
154345
154456
  const info = stmtComplete.run({ pid, now: now3 });
@@ -154347,18 +154458,24 @@ var init_session_store = __esm({
154347
154458
  }
154348
154459
  stmtIns.run(sessionToRow(newSession));
154349
154460
  return completedCount;
154350
- })();
154351
- return result;
154461
+ })(), "Session rollover");
154352
154462
  }
154353
154463
  getBackendName() {
154354
154464
  return "sqlite";
154355
154465
  }
154356
154466
  };
154357
154467
  SessionGracefulDegrade = class {
154468
+ constructor(reason = "SQLite runtime unavailable") {
154469
+ this.reason = reason;
154470
+ }
154471
+ reason;
154358
154472
  warned = false;
154473
+ unavailableRead() {
154474
+ return degradedReadError("sessions");
154475
+ }
154359
154476
  warn() {
154360
154477
  if (!this.warned) {
154361
- console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions are disabled (read-only empty). Install better-sqlite3 for full functionality.");
154478
+ console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions cannot be read or written. Install a supported SQLite runtime.");
154362
154479
  this.warned = true;
154363
154480
  }
154364
154481
  }
@@ -154366,33 +154483,39 @@ var init_session_store = __esm({
154366
154483
  this.warn();
154367
154484
  }
154368
154485
  async loadAll() {
154369
- return [];
154486
+ throw this.unavailableRead();
154370
154487
  }
154371
154488
  async getById(_id) {
154372
- return void 0;
154489
+ throw this.unavailableRead();
154373
154490
  }
154374
154491
  async loadByProject(_projectId) {
154375
- return [];
154492
+ throw this.unavailableRead();
154376
154493
  }
154377
154494
  async loadActive(_projectId) {
154378
- return [];
154495
+ throw this.unavailableRead();
154379
154496
  }
154380
154497
  async insert(_session) {
154381
154498
  this.warn();
154499
+ throw degradedWriteError("sessions");
154382
154500
  }
154383
154501
  async update(_session) {
154384
154502
  this.warn();
154503
+ throw degradedWriteError("sessions");
154385
154504
  }
154386
154505
  async bulkUpdate(_sessions) {
154387
154506
  this.warn();
154507
+ throw degradedWriteError("sessions");
154388
154508
  }
154389
154509
  async atomicRolloverInsert(_newSession, _projectIds, _now) {
154390
154510
  this.warn();
154391
- return 0;
154511
+ throw degradedWriteError("sessions");
154392
154512
  }
154393
154513
  getBackendName() {
154394
154514
  return "degraded";
154395
154515
  }
154516
+ getBackendReason() {
154517
+ return this.reason;
154518
+ }
154396
154519
  };
154397
154520
  _store3 = null;
154398
154521
  _storeDataDir3 = null;
@@ -159648,7 +159771,7 @@ var init_request_context = __esm({
159648
159771
  function getMcpServerInfo() {
159649
159772
  return {
159650
159773
  name: "memorix",
159651
- version: true ? "1.9.2" : "1.0.1"
159774
+ version: true ? "1.9.3" : "1.0.1"
159652
159775
  };
159653
159776
  }
159654
159777
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -175357,7 +175480,7 @@ var init_serve_shared = __esm({
175357
175480
 
175358
175481
  // src/cli/version.ts
175359
175482
  function getCliVersion() {
175360
- return true ? "1.9.2" : pkg.version;
175483
+ return true ? "1.9.3" : pkg.version;
175361
175484
  }
175362
175485
  var init_version3 = __esm({
175363
175486
  "src/cli/version.ts"() {