memorix 1.9.2 → 1.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/dist/cli/index.js +358 -146
  5. package/dist/index.js +281 -125
  6. package/dist/maintenance-runner.js +219 -97
  7. package/dist/media-audio-runner.js +219 -97
  8. package/dist/media-video-runner.js +219 -97
  9. package/dist/memcode-runtime/CHANGELOG.md +27 -0
  10. package/dist/sdk.js +280 -124
  11. package/dist/vector-backfill-runner.js +219 -97
  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/PERFORMANCE.md +50 -0
  16. package/docs/README.md +1 -1
  17. package/package.json +1 -1
  18. package/plugins/antigravity/memorix/plugin.json +1 -1
  19. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  20. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  21. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  22. package/plugins/copilot/memorix/plugin.json +1 -1
  23. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  24. package/plugins/hermes/memorix/plugin.yaml +1 -1
  25. package/plugins/omp/memorix/package.json +1 -1
  26. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  27. package/plugins/pi/memorix/package.json +1 -1
  28. package/server.json +2 -2
  29. package/src/cli/commands/doctor.ts +78 -17
  30. package/src/cli/commands/media.ts +2 -0
  31. package/src/media/minimax.ts +26 -2
  32. package/src/media/video-jobs.ts +9 -1
  33. package/src/server.ts +3 -1
  34. package/src/store/mini-skill-store.ts +31 -22
  35. package/src/store/obs-store.ts +46 -30
  36. package/src/store/session-store.ts +35 -21
  37. package/src/store/sqlite-db.ts +16 -7
  38. package/src/store/sqlite-reliability.ts +87 -0
  39. package/src/store/sqlite-store.ts +4 -1
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
  });
@@ -57009,10 +57117,10 @@ var require_retry3 = __commonJS({
57009
57117
  if (!await shouldRetryFn(err)) {
57010
57118
  return { shouldRetry: false, config: err.config };
57011
57119
  }
57012
- const delay2 = getNextRetryDelay(config);
57120
+ const delay3 = getNextRetryDelay(config);
57013
57121
  err.config.retryConfig.currentRetryAttempt += 1;
57014
- const backoff = config.retryBackoff ? config.retryBackoff(err, delay2) : new Promise((resolve5) => {
57015
- setTimeout(resolve5, delay2);
57122
+ const backoff = config.retryBackoff ? config.retryBackoff(err, delay3) : new Promise((resolve5) => {
57123
+ setTimeout(resolve5, delay3);
57016
57124
  });
57017
57125
  if (config.onRetryAttempt) {
57018
57126
  await config.onRetryAttempt(err);
@@ -98798,7 +98906,7 @@ async function retryBackoff(fn, strategy) {
98798
98906
  retryInterval = initialInterval * Math.pow(x2, exponent) + Math.random() * 1e3;
98799
98907
  }
98800
98908
  const d = Math.min(retryInterval, maxInterval);
98801
- await delay(d);
98909
+ await delay2(d);
98802
98910
  x2++;
98803
98911
  }
98804
98912
  }
@@ -98819,8 +98927,8 @@ function retryIntervalFromResponse(res) {
98819
98927
  }
98820
98928
  return 0;
98821
98929
  }
98822
- async function delay(delay2) {
98823
- return new Promise((resolve5) => setTimeout(resolve5, delay2));
98930
+ async function delay2(delay3) {
98931
+ return new Promise((resolve5) => setTimeout(resolve5, delay3));
98824
98932
  }
98825
98933
  var defaultBackoff, PermanentError, TemporaryError, codeRangeRE2;
98826
98934
  var init_retries = __esm({
@@ -145126,7 +145234,20 @@ function normalizeMiniMaxVideoRequest(input) {
145126
145234
  if (ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1") {
145127
145235
  throw new Error("MiniMax video ratio must be adaptive, 16:9, 9:16, or 1:1");
145128
145236
  }
145129
- return { region, model, prompt, resolution, duration, ratio };
145237
+ let firstFrameImageUrl;
145238
+ if (input.firstFrameImageUrl !== void 0) {
145239
+ firstFrameImageUrl = input.firstFrameImageUrl.trim();
145240
+ let parsed;
145241
+ try {
145242
+ parsed = new URL(firstFrameImageUrl);
145243
+ } catch {
145244
+ throw new Error("MiniMax first-frame image must be a public HTTPS URL");
145245
+ }
145246
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || sanitizeCredentials(firstFrameImageUrl) !== firstFrameImageUrl) {
145247
+ throw new Error("MiniMax first-frame image must be an HTTPS URL without credentials");
145248
+ }
145249
+ }
145250
+ return { region, model, prompt, resolution, duration, ratio, ...firstFrameImageUrl ? { firstFrameImageUrl } : {} };
145130
145251
  }
145131
145252
  function videoRequestConfig(input) {
145132
145253
  const normalized = normalizeMiniMaxVideoRequest(input);
@@ -145203,7 +145324,14 @@ async function createMiniMaxVideoTask(input, dependencies = {}) {
145203
145324
  headers: videoHeaders(config.apiKey),
145204
145325
  body: JSON.stringify({
145205
145326
  model: config.model,
145206
- content: [{ type: "text", text: config.prompt }],
145327
+ content: [
145328
+ { type: "text", text: config.prompt },
145329
+ ...config.firstFrameImageUrl ? [{
145330
+ type: "image_url",
145331
+ image_url: { url: config.firstFrameImageUrl },
145332
+ role: "first_frame"
145333
+ }] : []
145334
+ ],
145207
145335
  resolution: config.resolution,
145208
145336
  duration: config.duration,
145209
145337
  ratio: config.ratio
@@ -145451,10 +145579,19 @@ function parseStoredRequest(value) {
145451
145579
  const duration = value.duration;
145452
145580
  const ratio = value.ratio;
145453
145581
  const maxBytes = value.maxBytes;
145454
- if (provider2 !== "minimax" && provider2 !== "minimax-cn" || region !== "global" && region !== "cn" || model !== "MiniMax-H3" || typeof prompt !== "string" || resolution !== "2K" || duration !== 5 && duration !== 10 || ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1" || typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 1) {
145582
+ if (provider2 !== "minimax" && provider2 !== "minimax-cn" || region !== "global" && region !== "cn" || model !== "MiniMax-H3" || typeof prompt !== "string" || value.firstFrameImageUrl !== void 0 && typeof value.firstFrameImageUrl !== "string" || resolution !== "2K" || duration !== 5 && duration !== 10 || ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1" || typeof maxBytes !== "number" || !Number.isSafeInteger(maxBytes) || maxBytes < 1) {
145455
145583
  throw new Error("Media video job has an invalid request payload");
145456
145584
  }
145457
- return { provider: provider2, region, model, prompt, resolution, duration, ratio, maxBytes };
145585
+ const normalized = normalizeMiniMaxVideoRequest({
145586
+ region,
145587
+ model,
145588
+ prompt,
145589
+ resolution,
145590
+ duration,
145591
+ ratio,
145592
+ firstFrameImageUrl: value.firstFrameImageUrl
145593
+ });
145594
+ return { provider: provider2, ...normalized, maxBytes };
145458
145595
  }
145459
145596
  function mediaJobIdFromMaintenance(job) {
145460
145597
  const mediaJobId = job.payload.mediaJobId;
@@ -145466,6 +145603,7 @@ function mediaJobIdFromMaintenance(job) {
145466
145603
  function toVideoRequest(request) {
145467
145604
  return {
145468
145605
  prompt: request.prompt,
145606
+ firstFrameImageUrl: request.firstFrameImageUrl,
145469
145607
  model: request.model,
145470
145608
  region: request.region,
145471
145609
  resolution: request.resolution,
@@ -145557,6 +145695,7 @@ function queueMiniMaxVideoGeneration(input) {
145557
145695
  region: normalized.region,
145558
145696
  model: normalized.model,
145559
145697
  prompt: normalized.prompt,
145698
+ ...normalized.firstFrameImageUrl ? { firstFrameImageUrl: normalized.firstFrameImageUrl } : {},
145560
145699
  resolution: normalized.resolution,
145561
145700
  duration: normalized.duration,
145562
145701
  ratio: normalized.ratio,
@@ -154276,16 +154415,18 @@ async function initSessionStore(dataDir) {
154276
154415
  if (_store3 && _storeDataDir3 === dataDir) return _store3;
154277
154416
  _store3 = null;
154278
154417
  _storeDataDir3 = null;
154418
+ let degradedReason = "SQLite runtime unavailable";
154279
154419
  try {
154280
- const store2 = new SessionSqliteStore();
154281
- await store2.init(dataDir);
154420
+ const store2 = await initializeSqliteStore(() => new SessionSqliteStore(), dataDir, "Session store");
154282
154421
  _store3 = store2;
154283
154422
  _storeDataDir3 = dataDir;
154284
154423
  return store2;
154285
154424
  } catch (err) {
154286
- console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${err instanceof Error ? err.message : err}`);
154425
+ if (!isSqliteUnavailableError(err)) throw err;
154426
+ degradedReason = formatSqliteFailure(err);
154427
+ console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${degradedReason}`);
154287
154428
  }
154288
- const store = new SessionGracefulDegrade();
154429
+ const store = new SessionGracefulDegrade(degradedReason);
154289
154430
  await store.init(dataDir);
154290
154431
  _store3 = store;
154291
154432
  _storeDataDir3 = dataDir;
@@ -154297,6 +154438,7 @@ var init_session_store = __esm({
154297
154438
  "use strict";
154298
154439
  init_esm_shims();
154299
154440
  init_sqlite_db();
154441
+ init_sqlite_reliability();
154300
154442
  SessionSqliteStore = class {
154301
154443
  db = null;
154302
154444
  dataDir = "";
@@ -154385,7 +154527,7 @@ var init_session_store = __esm({
154385
154527
  }
154386
154528
  const stmtComplete = this._stmtCompleteActive;
154387
154529
  const stmtIns = this.stmtInsert;
154388
- const result = this.db.transaction(() => {
154530
+ return retrySqliteBusy(async () => this.db.transaction(() => {
154389
154531
  let completedCount = 0;
154390
154532
  for (const pid of projectIds) {
154391
154533
  const info = stmtComplete.run({ pid, now: now3 });
@@ -154393,18 +154535,24 @@ var init_session_store = __esm({
154393
154535
  }
154394
154536
  stmtIns.run(sessionToRow(newSession));
154395
154537
  return completedCount;
154396
- })();
154397
- return result;
154538
+ })(), "Session rollover");
154398
154539
  }
154399
154540
  getBackendName() {
154400
154541
  return "sqlite";
154401
154542
  }
154402
154543
  };
154403
154544
  SessionGracefulDegrade = class {
154545
+ constructor(reason = "SQLite runtime unavailable") {
154546
+ this.reason = reason;
154547
+ }
154548
+ reason;
154404
154549
  warned = false;
154550
+ unavailableRead() {
154551
+ return degradedReadError("sessions");
154552
+ }
154405
154553
  warn() {
154406
154554
  if (!this.warned) {
154407
- console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions are disabled (read-only empty). Install better-sqlite3 for full functionality.");
154555
+ console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions cannot be read or written. Install a supported SQLite runtime.");
154408
154556
  this.warned = true;
154409
154557
  }
154410
154558
  }
@@ -154412,33 +154560,39 @@ var init_session_store = __esm({
154412
154560
  this.warn();
154413
154561
  }
154414
154562
  async loadAll() {
154415
- return [];
154563
+ throw this.unavailableRead();
154416
154564
  }
154417
154565
  async getById(_id) {
154418
- return void 0;
154566
+ throw this.unavailableRead();
154419
154567
  }
154420
154568
  async loadByProject(_projectId) {
154421
- return [];
154569
+ throw this.unavailableRead();
154422
154570
  }
154423
154571
  async loadActive(_projectId) {
154424
- return [];
154572
+ throw this.unavailableRead();
154425
154573
  }
154426
154574
  async insert(_session) {
154427
154575
  this.warn();
154576
+ throw degradedWriteError("sessions");
154428
154577
  }
154429
154578
  async update(_session) {
154430
154579
  this.warn();
154580
+ throw degradedWriteError("sessions");
154431
154581
  }
154432
154582
  async bulkUpdate(_sessions) {
154433
154583
  this.warn();
154584
+ throw degradedWriteError("sessions");
154434
154585
  }
154435
154586
  async atomicRolloverInsert(_newSession, _projectIds, _now) {
154436
154587
  this.warn();
154437
- return 0;
154588
+ throw degradedWriteError("sessions");
154438
154589
  }
154439
154590
  getBackendName() {
154440
154591
  return "degraded";
154441
154592
  }
154593
+ getBackendReason() {
154594
+ return this.reason;
154595
+ }
154442
154596
  };
154443
154597
  _store3 = null;
154444
154598
  _storeDataDir3 = null;
@@ -169896,7 +170050,7 @@ var MCP_SERVER_INFO_META_KEY = "io.modelcontextprotocol/serverInfo";
169896
170050
  function getMcpServerInfo() {
169897
170051
  return {
169898
170052
  name: "memorix",
169899
- version: true ? "1.9.2" : "1.0.1"
170053
+ version: true ? "1.9.4" : "1.0.1"
169900
170054
  };
169901
170055
  }
169902
170056
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -174202,6 +174356,7 @@ ${lines.join("\n")}` }] };
174202
174356
  } : {},
174203
174357
  ...mediaGenerationEnabled ? {
174204
174358
  image: z627.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
174359
+ firstFrameImageUrl: z627.string().optional().describe("Public HTTPS first-frame image URL for MiniMax video generation; stored with the queued job."),
174205
174360
  region: z627.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
174206
174361
  n: z627.number().int().min(1).max(4).optional().describe("Image output count."),
174207
174362
  ratio: z627.enum(["adaptive", "1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Image or video aspect ratio."),
@@ -174211,7 +174366,7 @@ ${lines.join("\n")}` }] };
174211
174366
  } : {}
174212
174367
  }
174213
174368
  },
174214
- async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt, image, model, provider: provider2, language, region, n, ratio, width, height, duration, maxPages, maxChars, attach }) => {
174369
+ async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt, image, firstFrameImageUrl, model, provider: provider2, language, region, n, ratio, width, height, duration, maxPages, maxChars, attach }) => {
174215
174370
  const unresolved = requireResolvedProject("manage controlled media");
174216
174371
  if (unresolved) return unresolved;
174217
174372
  const safeError4 = (error) => sanitizeCredentials(
@@ -174381,6 +174536,7 @@ ${lines.join("\n")}` }] };
174381
174536
  dataDir: projectDir2,
174382
174537
  projectId: project.id,
174383
174538
  prompt,
174539
+ firstFrameImageUrl,
174384
174540
  ...model === "MiniMax-H3" ? { model } : {},
174385
174541
  ...region ? { region } : {},
174386
174542
  ...videoRatio ? { ratio: videoRatio } : {},