memorix 1.9.1 → 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.
Files changed (46) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +17 -4
  3. package/README.zh-CN.md +8 -4
  4. package/dist/cli/index.js +448 -243
  5. package/dist/index.js +302 -165
  6. package/dist/maintenance-runner.js +194 -98
  7. package/dist/media-audio-runner.js +196 -100
  8. package/dist/media-video-runner.js +194 -98
  9. package/dist/memcode-runtime/CHANGELOG.md +22 -0
  10. package/dist/sdk.js +301 -164
  11. package/dist/vector-backfill-runner.js +194 -98
  12. package/docs/AGENT_OPERATOR_PLAYBOOK.md +15 -0
  13. package/docs/ARCHITECTURE.md +23 -0
  14. package/docs/DEVELOPMENT.md +1 -1
  15. package/docs/INTEGRATIONS.md +4 -0
  16. package/docs/PERFORMANCE.md +50 -0
  17. package/docs/README.md +1 -1
  18. package/docs/SETUP.md +5 -0
  19. package/package.json +1 -2
  20. package/plugins/antigravity/memorix/plugin.json +1 -1
  21. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  22. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  23. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  24. package/plugins/copilot/memorix/plugin.json +1 -1
  25. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  26. package/plugins/hermes/memorix/plugin.yaml +1 -1
  27. package/plugins/omp/memorix/package.json +1 -1
  28. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  29. package/plugins/pi/memorix/package.json +1 -1
  30. package/server.json +2 -2
  31. package/src/cli/commands/doctor.ts +78 -17
  32. package/src/cli/commands/memory.ts +2 -1
  33. package/src/cli/default-mode.ts +15 -0
  34. package/src/cli/index.ts +10 -1
  35. package/src/codegraph/lite-provider.ts +10 -1
  36. package/src/dashboard/maintenance.ts +11 -2
  37. package/src/memory/consolidation.ts +15 -6
  38. package/src/store/mini-skill-store.ts +31 -22
  39. package/src/store/obs-store.ts +46 -30
  40. package/src/store/session-store.ts +35 -21
  41. package/src/store/sqlite-db.ts +16 -7
  42. package/src/store/sqlite-reliability.ts +87 -0
  43. package/src/store/sqlite-store.ts +4 -1
  44. package/assets/star-history-dark.svg +0 -30
  45. package/assets/star-history-light.svg +0 -30
  46. package/assets/star-history.json +0 -712
package/dist/sdk.js CHANGED
@@ -468,70 +468,79 @@ function getDatabase(dataDir) {
468
468
  loadBetterSqlite3();
469
469
  fs.mkdirSync(dataDir, { recursive: true });
470
470
  const dbPath = path3.join(dataDir, "memorix.db");
471
- const db2 = createDatabase(dbPath);
472
- db2.pragma("journal_mode = WAL");
473
- db2.pragma("busy_timeout = 5000");
474
- db2.pragma("foreign_keys = ON");
475
- db2.exec(CREATE_OBSERVATIONS_TABLE);
476
- db2.exec(CREATE_MINI_SKILLS_TABLE);
477
- db2.exec(CREATE_SESSIONS_TABLE);
478
- db2.exec(CREATE_META_TABLE);
479
- db2.exec(CREATE_TEAM_AGENTS_TABLE);
480
- db2.exec(CREATE_TEAM_TASKS_TABLE);
481
- db2.exec(CREATE_TEAM_TASK_DEPS_TABLE);
482
- db2.exec(CREATE_TEAM_MESSAGES_TABLE);
483
- db2.exec(CREATE_TEAM_LOCKS_TABLE);
484
- db2.exec(CREATE_TEAM_ROLES_TABLE);
485
- db2.exec(CREATE_GRAPH_ENTITIES_TABLE);
486
- db2.exec(CREATE_GRAPH_RELATIONS_TABLE);
487
- db2.exec(CREATE_CHAT_TRANSCRIPT_TABLE);
488
- db2.exec(CREATE_CODE_FILES_TABLE);
489
- db2.exec(CREATE_CODE_SYMBOLS_TABLE);
490
- db2.exec(CREATE_CODE_EDGES_TABLE);
491
- db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
492
- db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
493
- db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
494
- db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
471
+ let db2;
495
472
  try {
496
- db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
497
- } catch {
498
- }
499
- try {
500
- db2.exec(`ALTER TABLE mini_skills ADD COLUMN updatedAt TEXT`);
501
- } catch {
502
- }
503
- try {
504
- db2.exec(`ALTER TABLE observations ADD COLUMN createdByAgentId TEXT`);
505
- } catch {
506
- }
507
- try {
508
- db2.exec(`ALTER TABLE observations ADD COLUMN writeGeneration INTEGER DEFAULT 0`);
509
- } catch {
510
- }
511
- try {
512
- db2.exec(`ALTER TABLE team_tasks ADD COLUMN required_role TEXT`);
513
- } catch {
514
- }
515
- try {
516
- db2.exec(`ALTER TABLE team_tasks ADD COLUMN preferred_role TEXT`);
517
- } catch {
518
- }
519
- try {
520
- db2.exec(`ALTER TABLE team_messages ADD COLUMN to_role TEXT`);
521
- } catch {
522
- }
523
- try {
524
- db2.exec(`ALTER TABLE team_messages ADD COLUMN handoff_status TEXT`);
525
- } catch {
473
+ db2 = createDatabase(dbPath);
474
+ db2.pragma("busy_timeout = 5000");
475
+ db2.pragma("journal_mode = WAL");
476
+ db2.pragma("foreign_keys = ON");
477
+ db2.exec(CREATE_OBSERVATIONS_TABLE);
478
+ db2.exec(CREATE_MINI_SKILLS_TABLE);
479
+ db2.exec(CREATE_SESSIONS_TABLE);
480
+ db2.exec(CREATE_META_TABLE);
481
+ db2.exec(CREATE_TEAM_AGENTS_TABLE);
482
+ db2.exec(CREATE_TEAM_TASKS_TABLE);
483
+ db2.exec(CREATE_TEAM_TASK_DEPS_TABLE);
484
+ db2.exec(CREATE_TEAM_MESSAGES_TABLE);
485
+ db2.exec(CREATE_TEAM_LOCKS_TABLE);
486
+ db2.exec(CREATE_TEAM_ROLES_TABLE);
487
+ db2.exec(CREATE_GRAPH_ENTITIES_TABLE);
488
+ db2.exec(CREATE_GRAPH_RELATIONS_TABLE);
489
+ db2.exec(CREATE_CHAT_TRANSCRIPT_TABLE);
490
+ db2.exec(CREATE_CODE_FILES_TABLE);
491
+ db2.exec(CREATE_CODE_SYMBOLS_TABLE);
492
+ db2.exec(CREATE_CODE_EDGES_TABLE);
493
+ db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
494
+ db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
495
+ db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
496
+ db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
497
+ try {
498
+ db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
499
+ } catch {
500
+ }
501
+ try {
502
+ db2.exec(`ALTER TABLE mini_skills ADD COLUMN updatedAt TEXT`);
503
+ } catch {
504
+ }
505
+ try {
506
+ db2.exec(`ALTER TABLE observations ADD COLUMN createdByAgentId TEXT`);
507
+ } catch {
508
+ }
509
+ try {
510
+ db2.exec(`ALTER TABLE observations ADD COLUMN writeGeneration INTEGER DEFAULT 0`);
511
+ } catch {
512
+ }
513
+ try {
514
+ db2.exec(`ALTER TABLE team_tasks ADD COLUMN required_role TEXT`);
515
+ } catch {
516
+ }
517
+ try {
518
+ db2.exec(`ALTER TABLE team_tasks ADD COLUMN preferred_role TEXT`);
519
+ } catch {
520
+ }
521
+ try {
522
+ db2.exec(`ALTER TABLE team_messages ADD COLUMN to_role TEXT`);
523
+ } catch {
524
+ }
525
+ try {
526
+ db2.exec(`ALTER TABLE team_messages ADD COLUMN handoff_status TEXT`);
527
+ } catch {
528
+ }
529
+ applySchemaMigrations(db2);
530
+ db2.exec(CREATE_INDEXES);
531
+ initializeObservationLexicalIndex(db2);
532
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
533
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
534
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
535
+ _dbCache.set(normalized, db2);
536
+ return db2;
537
+ } catch (error) {
538
+ try {
539
+ db2?.close();
540
+ } catch {
541
+ }
542
+ throw error;
526
543
  }
527
- applySchemaMigrations(db2);
528
- db2.exec(CREATE_INDEXES);
529
- initializeObservationLexicalIndex(db2);
530
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
531
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
532
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
533
- _dbCache.set(normalized, db2);
534
- return db2;
535
544
  }
536
545
  function closeDatabase(dataDir) {
537
546
  const normalized = path3.resolve(dataDir);
@@ -2369,6 +2378,74 @@ var init_types = __esm({
2369
2378
  }
2370
2379
  });
2371
2380
 
2381
+ // src/store/sqlite-reliability.ts
2382
+ function isTransientSqliteError(error) {
2383
+ const code = error?.code;
2384
+ if (typeof code === "string") {
2385
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
2386
+ }
2387
+ return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
2388
+ }
2389
+ function isSqliteUnavailableError(error) {
2390
+ return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
2391
+ }
2392
+ function degradedReadError(storeName) {
2393
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
2394
+ }
2395
+ function degradedWriteError(storeName) {
2396
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
2397
+ }
2398
+ function formatSqliteFailure(error) {
2399
+ return error instanceof Error && error.message ? error.message : String(error);
2400
+ }
2401
+ function delay(ms) {
2402
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
2403
+ }
2404
+ async function retrySqliteBusy(operation, label) {
2405
+ for (let attempt = 0; ; attempt++) {
2406
+ try {
2407
+ return await operation();
2408
+ } catch (error) {
2409
+ if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
2410
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
2411
+ console.error(
2412
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
2413
+ );
2414
+ await delay(waitMs);
2415
+ }
2416
+ }
2417
+ }
2418
+ async function initializeSqliteStore(factory, dataDir, label) {
2419
+ try {
2420
+ loadBetterSqlite3();
2421
+ } catch (error) {
2422
+ throw new SqliteUnavailableError(error);
2423
+ }
2424
+ return retrySqliteBusy(async () => {
2425
+ const store = factory();
2426
+ await store.init(dataDir);
2427
+ return store;
2428
+ }, label);
2429
+ }
2430
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
2431
+ var init_sqlite_reliability = __esm({
2432
+ "src/store/sqlite-reliability.ts"() {
2433
+ "use strict";
2434
+ init_esm_shims();
2435
+ init_sqlite_db();
2436
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
2437
+ SqliteUnavailableError = class extends Error {
2438
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
2439
+ constructor(cause) {
2440
+ super(
2441
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
2442
+ );
2443
+ this.name = "SqliteUnavailableError";
2444
+ }
2445
+ };
2446
+ }
2447
+ });
2448
+
2372
2449
  // src/store/mini-skill-store.ts
2373
2450
  var mini_skill_store_exports = {};
2374
2451
  __export(mini_skill_store_exports, {
@@ -2440,16 +2517,18 @@ async function initMiniSkillStore(dataDir) {
2440
2517
  if (_store && _storeDataDir === dataDir) return _store;
2441
2518
  _store = null;
2442
2519
  _storeDataDir = null;
2520
+ let degradedReason = "SQLite runtime unavailable";
2443
2521
  try {
2444
- const store2 = new MiniSkillSqliteStore();
2445
- await store2.init(dataDir);
2522
+ const store2 = await initializeSqliteStore(() => new MiniSkillSqliteStore(), dataDir, "Mini-skill store");
2446
2523
  _store = store2;
2447
2524
  _storeDataDir = dataDir;
2448
2525
  return store2;
2449
2526
  } catch (err) {
2450
- console.error(`[memorix] MiniSkillSqliteStore unavailable, running in degraded read-only mode: ${err instanceof Error ? err.message : err}`);
2527
+ if (!isSqliteUnavailableError(err)) throw err;
2528
+ degradedReason = err instanceof Error ? err.message : String(err);
2529
+ console.error(`[memorix] MiniSkillSqliteStore unavailable, running in degraded read-only mode: ${degradedReason}`);
2451
2530
  }
2452
- const store = new MiniSkillGracefulDegrade();
2531
+ const store = new MiniSkillGracefulDegrade(degradedReason);
2453
2532
  await store.init(dataDir);
2454
2533
  _store = store;
2455
2534
  _storeDataDir = dataDir;
@@ -2461,6 +2540,7 @@ var init_mini_skill_store = __esm({
2461
2540
  "use strict";
2462
2541
  init_esm_shims();
2463
2542
  init_sqlite_db();
2543
+ init_sqlite_reliability();
2464
2544
  MiniSkillSqliteStore = class {
2465
2545
  db = null;
2466
2546
  dataDir = "";
@@ -2569,7 +2649,7 @@ var init_mini_skill_store = __esm({
2569
2649
  * SQLite serializes write transactions, so concurrent calls are safely sequenced.
2570
2650
  */
2571
2651
  async atomicInsertWithId(skillWithoutId) {
2572
- const result = this.db.transaction(() => {
2652
+ return retrySqliteBusy(async () => this.db.transaction(() => {
2573
2653
  const row = this.stmtGetMeta.get("mini_skills_next_id");
2574
2654
  const nextId2 = row ? parseInt(row.value, 10) : 1;
2575
2655
  const skill2 = { ...skillWithoutId, id: nextId2 };
@@ -2578,8 +2658,7 @@ var init_mini_skill_store = __esm({
2578
2658
  this.stmtBumpGeneration.run();
2579
2659
  this.knownGeneration = this.readGeneration();
2580
2660
  return skill2;
2581
- })();
2582
- return result;
2661
+ })(), "Mini-skill promotion");
2583
2662
  }
2584
2663
  // ── Freshness ────────────────────────────────────────────────────
2585
2664
  async ensureFresh() {
@@ -2598,10 +2677,17 @@ var init_mini_skill_store = __esm({
2598
2677
  }
2599
2678
  };
2600
2679
  MiniSkillGracefulDegrade = class {
2680
+ constructor(reason = "SQLite runtime unavailable") {
2681
+ this.reason = reason;
2682
+ }
2683
+ reason;
2601
2684
  warned = false;
2685
+ unavailableRead() {
2686
+ return degradedReadError("mini-skills");
2687
+ }
2602
2688
  warn() {
2603
2689
  if (!this.warned) {
2604
- console.error("[memorix] MiniSkillStore: SQLite unavailable \u2014 mini-skills are disabled (read-only empty). Install better-sqlite3 for full functionality.");
2690
+ console.error("[memorix] MiniSkillStore: SQLite unavailable \u2014 mini-skills cannot be read or written. Install a supported SQLite runtime.");
2605
2691
  this.warned = true;
2606
2692
  }
2607
2693
  }
@@ -2609,28 +2695,33 @@ var init_mini_skill_store = __esm({
2609
2695
  this.warn();
2610
2696
  }
2611
2697
  async loadAll() {
2612
- return [];
2698
+ throw this.unavailableRead();
2613
2699
  }
2614
2700
  async loadByProject(_projectId) {
2615
- return [];
2701
+ throw this.unavailableRead();
2616
2702
  }
2617
2703
  async loadIdCounter() {
2618
- return 1;
2704
+ throw this.unavailableRead();
2619
2705
  }
2620
2706
  async insert(_skill) {
2621
2707
  this.warn();
2708
+ throw degradedWriteError("mini-skills");
2622
2709
  }
2623
2710
  async update(_skill) {
2624
2711
  this.warn();
2712
+ throw degradedWriteError("mini-skills");
2625
2713
  }
2626
2714
  async remove(_id) {
2627
2715
  this.warn();
2716
+ throw degradedWriteError("mini-skills");
2628
2717
  }
2629
2718
  async saveIdCounter(_nextId) {
2719
+ this.warn();
2720
+ throw degradedWriteError("mini-skills");
2630
2721
  }
2631
2722
  async atomicInsertWithId(skillWithoutId) {
2632
2723
  this.warn();
2633
- return { ...skillWithoutId, id: 0 };
2724
+ throw degradedWriteError("mini-skills");
2634
2725
  }
2635
2726
  async ensureFresh() {
2636
2727
  return false;
@@ -2641,6 +2732,9 @@ var init_mini_skill_store = __esm({
2641
2732
  getBackendName() {
2642
2733
  return "degraded";
2643
2734
  }
2735
+ getBackendReason() {
2736
+ return this.reason;
2737
+ }
2644
2738
  };
2645
2739
  _store = null;
2646
2740
  _storeDataDir = null;
@@ -4843,10 +4937,10 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
4843
4937
  );
4844
4938
  }
4845
4939
  if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
4846
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
4940
+ const delay3 = BASE_DELAY_MS * Math.pow(2, attempt);
4847
4941
  const retryAfter = response.headers.get("retry-after");
4848
4942
  const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
4849
- const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
4943
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay3;
4850
4944
  console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
4851
4945
  await waitForRetry(waitMs, options.signal);
4852
4946
  return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
@@ -6878,6 +6972,7 @@ var init_sqlite_store = __esm({
6878
6972
  "use strict";
6879
6973
  init_esm_shims();
6880
6974
  init_sqlite_db();
6975
+ init_sqlite_reliability();
6881
6976
  init_sqlite_fts();
6882
6977
  SqliteBackend = class {
6883
6978
  db = null;
@@ -7155,7 +7250,9 @@ var init_sqlite_store = __esm({
7155
7250
  // ── Compound atomic operation ────────────────────────────────────
7156
7251
  async atomic(fn) {
7157
7252
  const run = this._atomicQueue.catch(() => void 0).then(async () => {
7158
- this.db.prepare("BEGIN IMMEDIATE").run();
7253
+ await retrySqliteBusy(async () => {
7254
+ this.db.prepare("BEGIN IMMEDIATE").run();
7255
+ }, "Observation atomic write");
7159
7256
  try {
7160
7257
  const tx = {
7161
7258
  loadAll: async () => this.rawLoadAll(),
@@ -7254,15 +7351,15 @@ function resetObservationStore() {
7254
7351
  async function createObservationStore(dataDir) {
7255
7352
  try {
7256
7353
  const { SqliteBackend: SqliteBackend2 } = await Promise.resolve().then(() => (init_sqlite_store(), sqlite_store_exports));
7257
- const store2 = new SqliteBackend2();
7258
- await store2.init(dataDir);
7259
- return store2;
7354
+ return await initializeSqliteStore(() => new SqliteBackend2(), dataDir, "Observation store");
7260
7355
  } catch (err) {
7261
- console.error(`[memorix] SQLite backend unavailable \u2014 degraded mode (read-only): ${err instanceof Error ? err.message : err}`);
7356
+ if (!isSqliteUnavailableError(err)) throw err;
7357
+ const reason = formatSqliteFailure(err);
7358
+ console.error(`[memorix] SQLite module unavailable \u2014 degraded mode (reads/writes disabled): ${reason}`);
7359
+ const degraded = new DegradedBackend(reason);
7360
+ await degraded.init(dataDir);
7361
+ return degraded;
7262
7362
  }
7263
- const store = new DegradedBackend();
7264
- await store.init(dataDir);
7265
- return store;
7266
7363
  }
7267
7364
  async function initObservationStore(dataDir) {
7268
7365
  if (_store2 && _storeDataDir2 === dataDir) {
@@ -7286,36 +7383,44 @@ var init_obs_store = __esm({
7286
7383
  "src/store/obs-store.ts"() {
7287
7384
  "use strict";
7288
7385
  init_esm_shims();
7386
+ init_sqlite_reliability();
7289
7387
  _store2 = null;
7290
7388
  _storeDataDir2 = null;
7291
7389
  DegradedBackend = class {
7390
+ constructor(reason = "SQLite runtime unavailable") {
7391
+ this.reason = reason;
7392
+ }
7393
+ reason;
7292
7394
  dataDir = "";
7395
+ unavailableRead() {
7396
+ return degradedReadError("observations");
7397
+ }
7293
7398
  async init(dataDir) {
7294
7399
  this.dataDir = dataDir;
7295
7400
  }
7296
7401
  async loadAll() {
7297
- return [];
7402
+ throw this.unavailableRead();
7298
7403
  }
7299
7404
  async loadByProject(_projectId, _options) {
7300
- return [];
7405
+ throw this.unavailableRead();
7301
7406
  }
7302
7407
  async countByProject(_projectId, _options) {
7303
- return 0;
7408
+ throw this.unavailableRead();
7304
7409
  }
7305
7410
  async getById(_id) {
7306
- return void 0;
7411
+ throw this.unavailableRead();
7307
7412
  }
7308
7413
  async loadIdCounter() {
7309
- return 1;
7414
+ throw this.unavailableRead();
7310
7415
  }
7311
7416
  async countAll() {
7312
- return 0;
7417
+ throw this.unavailableRead();
7313
7418
  }
7314
7419
  async listProjectIds() {
7315
- return [];
7420
+ throw this.unavailableRead();
7316
7421
  }
7317
7422
  async searchLexical(_options) {
7318
- return [];
7423
+ throw this.unavailableRead();
7319
7424
  }
7320
7425
  hasLexicalIndex() {
7321
7426
  return false;
@@ -7324,25 +7429,25 @@ var init_obs_store = __esm({
7324
7429
  return false;
7325
7430
  }
7326
7431
  async insert(_obs) {
7327
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7432
+ throw degradedWriteError("observations");
7328
7433
  }
7329
7434
  async update(_obs) {
7330
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7435
+ throw degradedWriteError("observations");
7331
7436
  }
7332
7437
  async remove(_id) {
7333
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7438
+ throw degradedWriteError("observations");
7334
7439
  }
7335
7440
  async bulkReplace(_obs) {
7336
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7441
+ throw degradedWriteError("observations");
7337
7442
  }
7338
7443
  async bulkRemoveByIds(_ids) {
7339
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7444
+ throw degradedWriteError("observations");
7340
7445
  }
7341
7446
  async saveIdCounter(_nextId) {
7342
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7447
+ throw degradedWriteError("observations");
7343
7448
  }
7344
7449
  async atomic(_fn) {
7345
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
7450
+ throw degradedWriteError("observations");
7346
7451
  }
7347
7452
  async ensureFresh() {
7348
7453
  return false;
@@ -7355,6 +7460,9 @@ var init_obs_store = __esm({
7355
7460
  getBackendName() {
7356
7461
  return "degraded";
7357
7462
  }
7463
+ getBackendReason() {
7464
+ return this.reason;
7465
+ }
7358
7466
  };
7359
7467
  }
7360
7468
  });
@@ -10928,7 +11036,8 @@ function clampCursor(afterId) {
10928
11036
  }
10929
11037
  async function loadConsolidationPage(projectId, options) {
10930
11038
  const limit2 = clampBatchSize(options.limit);
10931
- const page = await getObservationStore().loadByProject(projectId, {
11039
+ const store = options.store ?? getObservationStore();
11040
+ const page = await store.loadByProject(projectId, {
10932
11041
  status: "active",
10933
11042
  afterId: clampCursor(options.afterId),
10934
11043
  limit: limit2 + 1
@@ -10988,7 +11097,7 @@ async function findConsolidationCandidates(_projectDir, projectId, opts) {
10988
11097
  return findClusters(page.observations, threshold);
10989
11098
  }
10990
11099
  async function executeConsolidation(_projectDir, projectId, opts) {
10991
- const store = getObservationStore();
11100
+ const store = opts?.store ?? getObservationStore();
10992
11101
  const page = await loadConsolidationPage(projectId, opts ?? {});
10993
11102
  const clusters = findClusters(page.observations, opts?.threshold ?? DEFAULT_SIMILARITY_THRESHOLD);
10994
11103
  if (clusters.length === 0) {
@@ -57008,10 +57117,10 @@ var require_retry3 = __commonJS({
57008
57117
  if (!await shouldRetryFn(err)) {
57009
57118
  return { shouldRetry: false, config: err.config };
57010
57119
  }
57011
- const delay2 = getNextRetryDelay(config);
57120
+ const delay3 = getNextRetryDelay(config);
57012
57121
  err.config.retryConfig.currentRetryAttempt += 1;
57013
- const backoff = config.retryBackoff ? config.retryBackoff(err, delay2) : new Promise((resolve5) => {
57014
- setTimeout(resolve5, delay2);
57122
+ const backoff = config.retryBackoff ? config.retryBackoff(err, delay3) : new Promise((resolve5) => {
57123
+ setTimeout(resolve5, delay3);
57015
57124
  });
57016
57125
  if (config.onRetryAttempt) {
57017
57126
  await config.onRetryAttempt(err);
@@ -98797,7 +98906,7 @@ async function retryBackoff(fn, strategy) {
98797
98906
  retryInterval = initialInterval * Math.pow(x2, exponent) + Math.random() * 1e3;
98798
98907
  }
98799
98908
  const d = Math.min(retryInterval, maxInterval);
98800
- await delay(d);
98909
+ await delay2(d);
98801
98910
  x2++;
98802
98911
  }
98803
98912
  }
@@ -98818,8 +98927,8 @@ function retryIntervalFromResponse(res) {
98818
98927
  }
98819
98928
  return 0;
98820
98929
  }
98821
- async function delay(delay2) {
98822
- return new Promise((resolve5) => setTimeout(resolve5, delay2));
98930
+ async function delay2(delay3) {
98931
+ return new Promise((resolve5) => setTimeout(resolve5, delay3));
98823
98932
  }
98824
98933
  var defaultBackoff, PermanentError, TemporaryError, codeRangeRE2;
98825
98934
  var init_retries = __esm({
@@ -148928,7 +149037,7 @@ __export(lite_provider_exports, {
148928
149037
  refreshProjectLite: () => refreshProjectLite
148929
149038
  });
148930
149039
  import { createHash as createHash10 } from "crypto";
148931
- import { readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
149040
+ import { existsSync as existsSync12, readdirSync as readdirSync2, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
148932
149041
  import { join as join4, relative as relative2 } from "path";
148933
149042
  function hashText2(text3) {
148934
149043
  return createHash10("sha256").update(text3).digest("hex");
@@ -148945,6 +149054,9 @@ function resolveMaxFileBytes(value) {
148945
149054
  if (!Number.isFinite(value)) return DEFAULT_CODEGRAPH_MAX_FILE_BYTES;
148946
149055
  return Math.max(1, Math.floor(value));
148947
149056
  }
149057
+ function isPythonVirtualenvDir(dir) {
149058
+ return existsSync12(join4(dir, "pyvenv.cfg"));
149059
+ }
148948
149060
  function walk(root, exclude, maxFiles) {
148949
149061
  const out = [];
148950
149062
  const visit = (dir) => {
@@ -148962,6 +149074,7 @@ function walk(root, exclude, maxFiles) {
148962
149074
  if (entry.isDirectory()) {
148963
149075
  if (entry.name === ".git" || entry.name === ".worktrees" || entry.name === ".tmp" || entry.name === "node_modules") continue;
148964
149076
  if (rel === ".claude/worktrees" || rel.startsWith(".claude/worktrees/")) continue;
149077
+ if (isPythonVirtualenvDir(abs)) continue;
148965
149078
  visit(abs);
148966
149079
  if (out.length >= maxFiles) return;
148967
149080
  continue;
@@ -152858,7 +152971,7 @@ __export(vector_backfill_runner_exports, {
152858
152971
  import { spawn as spawn4 } from "child_process";
152859
152972
  import {
152860
152973
  closeSync,
152861
- existsSync as existsSync12,
152974
+ existsSync as existsSync13,
152862
152975
  mkdirSync as mkdirSync2,
152863
152976
  openSync,
152864
152977
  readFileSync as readFileSync9,
@@ -152986,7 +153099,7 @@ function releaseVectorBackfillLock(lockPath = process.env.MEMORIX_VECTOR_BACKFIL
152986
153099
  }
152987
153100
  function launchDetachedVectorBackfill(request, options = {}) {
152988
153101
  const runnerPath = options.runnerPath ?? resolveVectorBackfillRunnerPath();
152989
- const exists = options.exists ?? existsSync12;
153102
+ const exists = options.exists ?? existsSync13;
152990
153103
  if (!exists(runnerPath)) return false;
152991
153104
  const lock = acquireVectorBackfillLock(request.dataDir, request.projectId, options.lockPath);
152992
153105
  if (!lock) return false;
@@ -154271,16 +154384,18 @@ async function initSessionStore(dataDir) {
154271
154384
  if (_store3 && _storeDataDir3 === dataDir) return _store3;
154272
154385
  _store3 = null;
154273
154386
  _storeDataDir3 = null;
154387
+ let degradedReason = "SQLite runtime unavailable";
154274
154388
  try {
154275
- const store2 = new SessionSqliteStore();
154276
- await store2.init(dataDir);
154389
+ const store2 = await initializeSqliteStore(() => new SessionSqliteStore(), dataDir, "Session store");
154277
154390
  _store3 = store2;
154278
154391
  _storeDataDir3 = dataDir;
154279
154392
  return store2;
154280
154393
  } catch (err) {
154281
- console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${err instanceof Error ? err.message : err}`);
154394
+ if (!isSqliteUnavailableError(err)) throw err;
154395
+ degradedReason = formatSqliteFailure(err);
154396
+ console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${degradedReason}`);
154282
154397
  }
154283
- const store = new SessionGracefulDegrade();
154398
+ const store = new SessionGracefulDegrade(degradedReason);
154284
154399
  await store.init(dataDir);
154285
154400
  _store3 = store;
154286
154401
  _storeDataDir3 = dataDir;
@@ -154292,6 +154407,7 @@ var init_session_store = __esm({
154292
154407
  "use strict";
154293
154408
  init_esm_shims();
154294
154409
  init_sqlite_db();
154410
+ init_sqlite_reliability();
154295
154411
  SessionSqliteStore = class {
154296
154412
  db = null;
154297
154413
  dataDir = "";
@@ -154380,7 +154496,7 @@ var init_session_store = __esm({
154380
154496
  }
154381
154497
  const stmtComplete = this._stmtCompleteActive;
154382
154498
  const stmtIns = this.stmtInsert;
154383
- const result = this.db.transaction(() => {
154499
+ return retrySqliteBusy(async () => this.db.transaction(() => {
154384
154500
  let completedCount = 0;
154385
154501
  for (const pid of projectIds) {
154386
154502
  const info = stmtComplete.run({ pid, now: now3 });
@@ -154388,18 +154504,24 @@ var init_session_store = __esm({
154388
154504
  }
154389
154505
  stmtIns.run(sessionToRow(newSession));
154390
154506
  return completedCount;
154391
- })();
154392
- return result;
154507
+ })(), "Session rollover");
154393
154508
  }
154394
154509
  getBackendName() {
154395
154510
  return "sqlite";
154396
154511
  }
154397
154512
  };
154398
154513
  SessionGracefulDegrade = class {
154514
+ constructor(reason = "SQLite runtime unavailable") {
154515
+ this.reason = reason;
154516
+ }
154517
+ reason;
154399
154518
  warned = false;
154519
+ unavailableRead() {
154520
+ return degradedReadError("sessions");
154521
+ }
154400
154522
  warn() {
154401
154523
  if (!this.warned) {
154402
- console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions are disabled (read-only empty). Install better-sqlite3 for full functionality.");
154524
+ console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions cannot be read or written. Install a supported SQLite runtime.");
154403
154525
  this.warned = true;
154404
154526
  }
154405
154527
  }
@@ -154407,33 +154529,39 @@ var init_session_store = __esm({
154407
154529
  this.warn();
154408
154530
  }
154409
154531
  async loadAll() {
154410
- return [];
154532
+ throw this.unavailableRead();
154411
154533
  }
154412
154534
  async getById(_id) {
154413
- return void 0;
154535
+ throw this.unavailableRead();
154414
154536
  }
154415
154537
  async loadByProject(_projectId) {
154416
- return [];
154538
+ throw this.unavailableRead();
154417
154539
  }
154418
154540
  async loadActive(_projectId) {
154419
- return [];
154541
+ throw this.unavailableRead();
154420
154542
  }
154421
154543
  async insert(_session) {
154422
154544
  this.warn();
154545
+ throw degradedWriteError("sessions");
154423
154546
  }
154424
154547
  async update(_session) {
154425
154548
  this.warn();
154549
+ throw degradedWriteError("sessions");
154426
154550
  }
154427
154551
  async bulkUpdate(_sessions) {
154428
154552
  this.warn();
154553
+ throw degradedWriteError("sessions");
154429
154554
  }
154430
154555
  async atomicRolloverInsert(_newSession, _projectIds, _now) {
154431
154556
  this.warn();
154432
- return 0;
154557
+ throw degradedWriteError("sessions");
154433
154558
  }
154434
154559
  getBackendName() {
154435
154560
  return "degraded";
154436
154561
  }
154562
+ getBackendReason() {
154563
+ return this.reason;
154564
+ }
154437
154565
  };
154438
154566
  _store3 = null;
154439
154567
  _storeDataDir3 = null;
@@ -155664,7 +155792,7 @@ __export(behavior_exports, {
155664
155792
  getBehaviorConfig: () => getBehaviorConfig,
155665
155793
  resetBehaviorConfigCache: () => resetBehaviorConfigCache
155666
155794
  });
155667
- import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
155795
+ import { existsSync as existsSync16, readFileSync as readFileSync11 } from "fs";
155668
155796
  import { homedir as homedir22 } from "os";
155669
155797
  function isSessionInject(value) {
155670
155798
  return value === "full" || value === "minimal" || value === "silent";
@@ -155675,7 +155803,7 @@ function isFormationMode(value) {
155675
155803
  function legacyBehavior(homeDir) {
155676
155804
  const configPath = getLegacyConfigJsonPath(homeDir);
155677
155805
  try {
155678
- if (!existsSync15(configPath)) return {};
155806
+ if (!existsSync16(configPath)) return {};
155679
155807
  const parsed = JSON.parse(readFileSync11(configPath, "utf-8"));
155680
155808
  return parsed.behavior && typeof parsed.behavior === "object" && !Array.isArray(parsed.behavior) ? parsed.behavior : {};
155681
155809
  } catch {
@@ -157029,11 +157157,11 @@ __export(current_facts_exports, {
157029
157157
  formatGitFact: () => formatGitFact
157030
157158
  });
157031
157159
  import { execFileSync } from "child_process";
157032
- import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
157160
+ import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
157033
157161
  import path29 from "path";
157034
157162
  function readTextIfExists(filePath) {
157035
157163
  try {
157036
- if (!existsSync16(filePath)) return null;
157164
+ if (!existsSync17(filePath)) return null;
157037
157165
  return readFileSync12(filePath, "utf8");
157038
157166
  } catch {
157039
157167
  return null;
@@ -157324,7 +157452,7 @@ __export(external_provider_exports, {
157324
157452
  runExternalCodeGraphLifecycle: () => runExternalCodeGraphLifecycle
157325
157453
  });
157326
157454
  import { spawn as spawn5 } from "child_process";
157327
- import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
157455
+ import { existsSync as existsSync18, readFileSync as readFileSync13 } from "fs";
157328
157456
  import { delimiter, dirname as dirname2, extname, isAbsolute as isAbsolute3, join as join20, relative as relative4, resolve as resolve3, sep } from "path";
157329
157457
  function boundedTimeout(value) {
157330
157458
  if (!Number.isFinite(value)) return DEFAULT_EXTERNAL_CODEGRAPH_TIMEOUT_MS;
@@ -157361,13 +157489,13 @@ function resolveCommandCandidates(command) {
157361
157489
  }
157362
157490
  function resolveLaunch(command) {
157363
157491
  for (const candidate of resolveCommandCandidates(command)) {
157364
- if (!existsSync17(candidate)) continue;
157492
+ if (!existsSync18(candidate)) continue;
157365
157493
  const extension3 = extname(candidate).toLowerCase();
157366
157494
  if (extension3 === ".cmd") {
157367
157495
  const bundleRoot = resolve3(dirname2(candidate), "..");
157368
157496
  const node = join20(bundleRoot, "node.exe");
157369
157497
  const entry = join20(bundleRoot, "lib", "dist", "bin", "codegraph.js");
157370
- if (existsSync17(node) && existsSync17(entry)) {
157498
+ if (existsSync18(node) && existsSync18(entry)) {
157371
157499
  return { executable: node, prefixArgs: ["--liftoff-only", entry] };
157372
157500
  }
157373
157501
  try {
@@ -157376,7 +157504,7 @@ function resolveLaunch(command) {
157376
157504
  if (match2?.[1]) {
157377
157505
  const base = dirname2(candidate).endsWith(sep) ? dirname2(candidate) : dirname2(candidate) + sep;
157378
157506
  const entry2 = resolve3(match2[1].replace(/%(?:~)?dp0%?/gi, base));
157379
- if (existsSync17(entry2)) return { executable: process.execPath, prefixArgs: [entry2] };
157507
+ if (existsSync18(entry2)) return { executable: process.execPath, prefixArgs: [entry2] };
157380
157508
  }
157381
157509
  } catch {
157382
157510
  }
@@ -157524,7 +157652,7 @@ async function inspectExternalCodeGraph(input) {
157524
157652
  const health2 = { state: "disabled", reason: "External CodeGraph is disabled by configuration." };
157525
157653
  return { health: health2, quality: providerQuality({ mode, health: health2 }) };
157526
157654
  }
157527
- if (!existsSync17(join20(input.projectRoot, ".codegraph"))) {
157655
+ if (!existsSync18(join20(input.projectRoot, ".codegraph"))) {
157528
157656
  const health2 = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
157529
157657
  return { health: health2, quality: providerQuality({ mode, health: health2 }) };
157530
157658
  }
@@ -157560,7 +157688,7 @@ async function runExternalCodeGraphLifecycle(input) {
157560
157688
  };
157561
157689
  }
157562
157690
  const indexPath = join20(input.projectRoot, ".codegraph");
157563
- if (input.action === "sync" && !existsSync17(indexPath)) {
157691
+ if (input.action === "sync" && !existsSync18(indexPath)) {
157564
157692
  const health = { state: "not-detected", reason: "No local .codegraph index is present for this project." };
157565
157693
  return {
157566
157694
  action: input.action,
@@ -157570,7 +157698,7 @@ async function runExternalCodeGraphLifecycle(input) {
157570
157698
  quality: providerQuality({ mode, health })
157571
157699
  };
157572
157700
  }
157573
- if (input.action === "init" && existsSync17(indexPath)) {
157701
+ if (input.action === "init" && existsSync18(indexPath)) {
157574
157702
  const inspected2 = await inspectExternalCodeGraph(input);
157575
157703
  return {
157576
157704
  action: input.action,
@@ -158631,7 +158759,7 @@ __export(auto_context_exports, {
158631
158759
  formatAutoProjectContextSummary: () => formatAutoProjectContextSummary,
158632
158760
  formatLegacyAutoProjectContextPrompt: () => formatLegacyAutoProjectContextPrompt
158633
158761
  });
158634
- import { existsSync as existsSync18, statSync as statSync5 } from "fs";
158762
+ import { existsSync as existsSync19, statSync as statSync5 } from "fs";
158635
158763
  import path30 from "path";
158636
158764
  function activeProjectObservations(observations2, projectId) {
158637
158765
  return observations2.filter((obs) => obs.projectId === projectId && (obs.status ?? "active") === "active");
@@ -158977,7 +159105,7 @@ function existingLensCandidates(rootPath, lens) {
158977
159105
  for (const candidate of lensPathCandidates(lens)) {
158978
159106
  const absolute = path30.join(rootPath, candidate);
158979
159107
  try {
158980
- if (!existsSync18(absolute)) continue;
159108
+ if (!existsSync19(absolute)) continue;
158981
159109
  const stat5 = statSync5(absolute);
158982
159110
  if (stat5.isFile()) out.push(candidate);
158983
159111
  if (stat5.isDirectory()) out.push(candidate.replace(/\\/g, "/"));
@@ -159511,7 +159639,7 @@ var engine_exports2 = {};
159511
159639
  __export(engine_exports2, {
159512
159640
  SkillsEngine: () => SkillsEngine
159513
159641
  });
159514
- import { existsSync as existsSync19, readFileSync as readFileSync14, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
159642
+ import { existsSync as existsSync20, readFileSync as readFileSync14, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
159515
159643
  import { join as join21 } from "path";
159516
159644
  import { homedir as homedir23 } from "os";
159517
159645
  var SKILLS_DIRS, SKILL_WORTHY_TYPES, MIN_OBS_FOR_SKILL, MIN_SCORE_FOR_SKILL, LOW_SIGNAL_TITLE_PATTERNS, LOW_SIGNAL_ENTITY_PATTERNS, COMMAND_TRACE_PATTERNS2, SkillsEngine;
@@ -159592,7 +159720,7 @@ var init_engine3 = __esm({
159592
159720
  paths.push(join21(home, dir));
159593
159721
  }
159594
159722
  for (const skillsRoot of paths) {
159595
- if (!existsSync19(skillsRoot)) continue;
159723
+ if (!existsSync20(skillsRoot)) continue;
159596
159724
  try {
159597
159725
  const entries = readdirSync4(skillsRoot, { withFileTypes: true });
159598
159726
  for (const entry of entries) {
@@ -159600,7 +159728,7 @@ var init_engine3 = __esm({
159600
159728
  const name = entry.name;
159601
159729
  if (seen.has(name)) continue;
159602
159730
  const skillMd = join21(skillsRoot, name, "SKILL.md");
159603
- if (!existsSync19(skillMd)) continue;
159731
+ if (!existsSync20(skillMd)) continue;
159604
159732
  try {
159605
159733
  const content = readFileSync14(skillMd, "utf-8");
159606
159734
  const description = this.parseDescription(content);
@@ -161401,7 +161529,8 @@ async function executeDeduplicate(context, payload, token) {
161401
161529
  async function previewConsolidate(context) {
161402
161530
  const clusters = await findConsolidationCandidates(
161403
161531
  context.projectRoot ?? context.dataDir,
161404
- context.projectId
161532
+ context.projectId,
161533
+ { store: context.store }
161405
161534
  );
161406
161535
  const payload = { clusterIds: normalizeClusters(clusters.map((cluster) => cluster.ids)) };
161407
161536
  return {
@@ -161424,11 +161553,19 @@ async function executeConsolidate(context, payload, token) {
161424
161553
  const clusterIds = record80.clusterIds.map((ids, index) => payloadIds(ids, `clusterIds[${index}]`));
161425
161554
  const normalized = { clusterIds: normalizeClusters(clusterIds) };
161426
161555
  verify("consolidate", context.projectId, normalized, token);
161427
- const current = await findConsolidationCandidates(context.projectRoot ?? context.dataDir, context.projectId);
161556
+ const current = await findConsolidationCandidates(
161557
+ context.projectRoot ?? context.dataDir,
161558
+ context.projectId,
161559
+ { store: context.store }
161560
+ );
161428
161561
  if (JSON.stringify(normalized.clusterIds) !== JSON.stringify(normalizeClusters(current.map((cluster) => cluster.ids)))) {
161429
161562
  throw new DashboardMaintenanceError("Memory changed after the preview. Refresh before consolidating.", 409);
161430
161563
  }
161431
- const result = await executeConsolidation(context.projectRoot ?? context.dataDir, context.projectId);
161564
+ const result = await executeConsolidation(
161565
+ context.projectRoot ?? context.dataDir,
161566
+ context.projectId,
161567
+ { store: context.store }
161568
+ );
161432
161569
  return { action: "consolidate", projectId: context.projectId, ...result };
161433
161570
  }
161434
161571
  async function retentionCandidates(context) {
@@ -162585,7 +162722,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
162585
162722
  }
162586
162723
  case "/config": {
162587
162724
  const os6 = await import("os");
162588
- const { existsSync: existsSync22 } = await import("fs");
162725
+ const { existsSync: existsSync23 } = await import("fs");
162589
162726
  const { join: join25 } = await import("path");
162590
162727
  const configProjectRoot = effectiveProjectRoot;
162591
162728
  const resolved = getResolvedConfig({ projectRoot: configProjectRoot });
@@ -162613,7 +162750,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
162613
162750
  if (fpath === null) {
162614
162751
  files[key] = { exists: false, path: "unavailable", unavailable: true };
162615
162752
  } else {
162616
- files[key] = { exists: existsSync22(fpath), path: fpath };
162753
+ files[key] = { exists: existsSync23(fpath), path: fpath };
162617
162754
  }
162618
162755
  }
162619
162756
  } catch {
@@ -163423,7 +163560,7 @@ var init_task_graph = __esm({
163423
163560
 
163424
163561
  // src/orchestrate/context-collector.ts
163425
163562
  import { execFileSync as execFileSync2 } from "child_process";
163426
- import { readFileSync as readFileSync15, existsSync as existsSync20 } from "fs";
163563
+ import { readFileSync as readFileSync15, existsSync as existsSync21 } from "fs";
163427
163564
  import { join as join22 } from "path";
163428
163565
  function normalizeEntryLimit(value, fallback) {
163429
163566
  if (!Number.isSafeInteger(value) || value < 1) return fallback;
@@ -163500,7 +163637,7 @@ function collectFileTree(projectDir2, maxEntries) {
163500
163637
  function collectDependencies(projectDir2) {
163501
163638
  try {
163502
163639
  const pkgPath = join22(projectDir2, "package.json");
163503
- if (!existsSync20(pkgPath)) return "";
163640
+ if (!existsSync21(pkgPath)) return "";
163504
163641
  const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
163505
163642
  const parts = [];
163506
163643
  if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) {
@@ -166001,11 +166138,11 @@ __export(hooks_path_exports, {
166001
166138
  resolveGitDir: () => resolveGitDir,
166002
166139
  resolveHooksDir: () => resolveHooksDir
166003
166140
  });
166004
- import { existsSync as existsSync21, readFileSync as readFileSync16, statSync as statSync6, mkdirSync as mkdirSync6 } from "fs";
166141
+ import { existsSync as existsSync22, readFileSync as readFileSync16, statSync as statSync6, mkdirSync as mkdirSync6 } from "fs";
166005
166142
  import path35 from "path";
166006
166143
  function resolveGitDir(projectRoot) {
166007
166144
  const dotGit = path35.join(projectRoot, ".git");
166008
- if (!existsSync21(dotGit)) return null;
166145
+ if (!existsSync22(dotGit)) return null;
166009
166146
  const stat5 = statSync6(dotGit);
166010
166147
  if (stat5.isDirectory()) {
166011
166148
  return dotGit;
@@ -166017,7 +166154,7 @@ function resolveGitDir(projectRoot) {
166017
166154
  if (match2) {
166018
166155
  const gitdir = match2[1].trim();
166019
166156
  const resolved = path35.isAbsolute(gitdir) ? gitdir : path35.resolve(projectRoot, gitdir);
166020
- if (existsSync21(resolved)) {
166157
+ if (existsSync22(resolved)) {
166021
166158
  return resolved;
166022
166159
  }
166023
166160
  }
@@ -168233,7 +168370,7 @@ var RulesSyncer = class {
168233
168370
 
168234
168371
  // src/workspace/engine.ts
168235
168372
  init_esm_shims();
168236
- import { readFileSync as readFileSync10, readdirSync as readdirSync3, existsSync as existsSync14, cpSync, mkdirSync as mkdirSync4 } from "fs";
168373
+ import { readFileSync as readFileSync10, readdirSync as readdirSync3, existsSync as existsSync15, cpSync, mkdirSync as mkdirSync4 } from "fs";
168237
168374
  import { join as join19 } from "path";
168238
168375
  import { homedir as homedir21 } from "os";
168239
168376
 
@@ -169185,7 +169322,7 @@ function sanitize(input) {
169185
169322
 
169186
169323
  // src/workspace/applier.ts
169187
169324
  init_esm_shims();
169188
- import { existsSync as existsSync13, mkdirSync as mkdirSync3, copyFileSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, renameSync } from "fs";
169325
+ import { existsSync as existsSync14, mkdirSync as mkdirSync3, copyFileSync, writeFileSync as writeFileSync3, unlinkSync as unlinkSync2, renameSync } from "fs";
169189
169326
  import { dirname } from "path";
169190
169327
  var WorkspaceSyncApplier = class {
169191
169328
  /**
@@ -169211,7 +169348,7 @@ var WorkspaceSyncApplier = class {
169211
169348
  for (const file of files) {
169212
169349
  try {
169213
169350
  const dir = dirname(file.filePath);
169214
- if (!existsSync13(dir)) {
169351
+ if (!existsSync14(dir)) {
169215
169352
  mkdirSync3(dir, { recursive: true });
169216
169353
  }
169217
169354
  } catch (err) {
@@ -169220,7 +169357,7 @@ var WorkspaceSyncApplier = class {
169220
169357
  }
169221
169358
  }
169222
169359
  for (const file of files) {
169223
- if (existsSync13(file.filePath)) {
169360
+ if (existsSync14(file.filePath)) {
169224
169361
  try {
169225
169362
  const backupPath = file.filePath + `.backup-${Date.now()}`;
169226
169363
  copyFileSync(file.filePath, backupPath);
@@ -169271,7 +169408,7 @@ var WorkspaceSyncApplier = class {
169271
169408
  cleanBackups(backups) {
169272
169409
  for (const backup of backups) {
169273
169410
  try {
169274
- if (existsSync13(backup.backupPath)) {
169411
+ if (existsSync14(backup.backupPath)) {
169275
169412
  unlinkSync2(backup.backupPath);
169276
169413
  }
169277
169414
  } catch {
@@ -169336,7 +169473,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
169336
169473
  }
169337
169474
  const merged = /* @__PURE__ */ new Map();
169338
169475
  for (const path36 of pathsToCheck) {
169339
- if (existsSync14(path36)) {
169476
+ if (existsSync15(path36)) {
169340
169477
  try {
169341
169478
  const content = readFileSync10(path36, "utf-8");
169342
169479
  const servers = adapter.parse(content);
@@ -169390,7 +169527,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
169390
169527
  const adapter = this.adapters.get(target);
169391
169528
  const configPath = adapter.getConfigPath(this.projectRoot);
169392
169529
  let configContent;
169393
- if (target === "antigravity" && existsSync14(configPath)) {
169530
+ if (target === "antigravity" && existsSync15(configPath)) {
169394
169531
  try {
169395
169532
  const existing = JSON.parse(readFileSync10(configPath, "utf-8"));
169396
169533
  const generated = JSON.parse(adapter.generate(result.mcpServers.scanned));
@@ -169466,13 +169603,13 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
169466
169603
  join19(home, dir)
169467
169604
  ];
169468
169605
  for (const skillsRoot of paths) {
169469
- if (!existsSync14(skillsRoot)) continue;
169606
+ if (!existsSync15(skillsRoot)) continue;
169470
169607
  try {
169471
169608
  const entries = readdirSync3(skillsRoot, { withFileTypes: true });
169472
169609
  for (const entry of entries) {
169473
169610
  if (!entry.isDirectory()) continue;
169474
169611
  const skillMd = join19(skillsRoot, entry.name, "SKILL.md");
169475
- if (!existsSync14(skillMd)) continue;
169612
+ if (!existsSync15(skillMd)) continue;
169476
169613
  let description = "";
169477
169614
  try {
169478
169615
  const content = readFileSync10(skillMd, "utf-8");
@@ -169521,7 +169658,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
169521
169658
  for (const skill2 of skills) {
169522
169659
  if (skill2.sourceAgent === target) continue;
169523
169660
  const dest = join19(targetDir, skill2.name);
169524
- if (existsSync14(dest)) {
169661
+ if (existsSync15(dest)) {
169525
169662
  skipped.push(`${skill2.name} (already exists in ${target})`);
169526
169663
  continue;
169527
169664
  }
@@ -169537,7 +169674,7 @@ var WorkspaceSyncEngine = class _WorkspaceSyncEngine {
169537
169674
  scanWorkflows() {
169538
169675
  const workflows = [];
169539
169676
  const wfDir = join19(this.projectRoot, ".windsurf", "workflows");
169540
- if (!existsSync14(wfDir)) return workflows;
169677
+ if (!existsSync15(wfDir)) return workflows;
169541
169678
  try {
169542
169679
  const files = readdirSync3(wfDir).filter((f3) => f3.endsWith(".md"));
169543
169680
  for (const file of files) {
@@ -169882,7 +170019,7 @@ var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
169882
170019
  function getMcpServerInfo() {
169883
170020
  return {
169884
170021
  name: "memorix",
169885
- version: true ? "1.9.1" : "1.0.1"
170022
+ version: true ? "1.9.3" : "1.0.1"
169886
170023
  };
169887
170024
  }
169888
170025
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -174499,10 +174636,10 @@ Preview: ${analysis.description.slice(0, 300)}${analysis.description.length > 30
174499
174636
  const { ensureHooksDir: ensureHooksDir2 } = await Promise.resolve().then(() => (init_hooks_path(), hooks_path_exports));
174500
174637
  const resolved = ensureHooksDir2(project.rootPath);
174501
174638
  if (resolved) {
174502
- const { existsSync: existsSync22, readFileSync: readFileSync17, writeFileSync: writeFileSync5, chmodSync } = await import("fs");
174639
+ const { existsSync: existsSync23, readFileSync: readFileSync17, writeFileSync: writeFileSync5, chmodSync } = await import("fs");
174503
174640
  const { hookPath } = resolved;
174504
174641
  const HOOK_MARKER = "# [memorix-git-hook]";
174505
- const needsInstall = !existsSync22(hookPath) || !readFileSync17(hookPath, "utf-8").includes(HOOK_MARKER);
174642
+ const needsInstall = !existsSync23(hookPath) || !readFileSync17(hookPath, "utf-8").includes(HOOK_MARKER);
174506
174643
  if (needsInstall) {
174507
174644
  const hookScript = `#!/bin/sh
174508
174645
  ${HOOK_MARKER}
@@ -174511,7 +174648,7 @@ if command -v memorix >/dev/null 2>&1; then
174511
174648
  memorix ingest commit --auto >/dev/null 2>&1 &
174512
174649
  fi
174513
174650
  `;
174514
- if (existsSync22(hookPath)) {
174651
+ if (existsSync23(hookPath)) {
174515
174652
  const existing = readFileSync17(hookPath, "utf-8");
174516
174653
  writeFileSync5(hookPath, existing.trimEnd() + `
174517
174654