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/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({
@@ -145080,7 +145188,20 @@ function normalizeMiniMaxVideoRequest(input) {
145080
145188
  if (ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1") {
145081
145189
  throw new Error("MiniMax video ratio must be adaptive, 16:9, 9:16, or 1:1");
145082
145190
  }
145083
- return { region, model, prompt, resolution, duration, ratio };
145191
+ let firstFrameImageUrl;
145192
+ if (input.firstFrameImageUrl !== void 0) {
145193
+ firstFrameImageUrl = input.firstFrameImageUrl.trim();
145194
+ let parsed;
145195
+ try {
145196
+ parsed = new URL(firstFrameImageUrl);
145197
+ } catch {
145198
+ throw new Error("MiniMax first-frame image must be a public HTTPS URL");
145199
+ }
145200
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || sanitizeCredentials(firstFrameImageUrl) !== firstFrameImageUrl) {
145201
+ throw new Error("MiniMax first-frame image must be an HTTPS URL without credentials");
145202
+ }
145203
+ }
145204
+ return { region, model, prompt, resolution, duration, ratio, ...firstFrameImageUrl ? { firstFrameImageUrl } : {} };
145084
145205
  }
145085
145206
  function videoRequestConfig(input) {
145086
145207
  const normalized = normalizeMiniMaxVideoRequest(input);
@@ -145157,7 +145278,14 @@ async function createMiniMaxVideoTask(input, dependencies = {}) {
145157
145278
  headers: videoHeaders(config.apiKey),
145158
145279
  body: JSON.stringify({
145159
145280
  model: config.model,
145160
- content: [{ type: "text", text: config.prompt }],
145281
+ content: [
145282
+ { type: "text", text: config.prompt },
145283
+ ...config.firstFrameImageUrl ? [{
145284
+ type: "image_url",
145285
+ image_url: { url: config.firstFrameImageUrl },
145286
+ role: "first_frame"
145287
+ }] : []
145288
+ ],
145161
145289
  resolution: config.resolution,
145162
145290
  duration: config.duration,
145163
145291
  ratio: config.ratio
@@ -145405,10 +145533,19 @@ function parseStoredRequest(value) {
145405
145533
  const duration = value.duration;
145406
145534
  const ratio = value.ratio;
145407
145535
  const maxBytes = value.maxBytes;
145408
- 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) {
145536
+ 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) {
145409
145537
  throw new Error("Media video job has an invalid request payload");
145410
145538
  }
145411
- return { provider: provider2, region, model, prompt, resolution, duration, ratio, maxBytes };
145539
+ const normalized = normalizeMiniMaxVideoRequest({
145540
+ region,
145541
+ model,
145542
+ prompt,
145543
+ resolution,
145544
+ duration,
145545
+ ratio,
145546
+ firstFrameImageUrl: value.firstFrameImageUrl
145547
+ });
145548
+ return { provider: provider2, ...normalized, maxBytes };
145412
145549
  }
145413
145550
  function mediaJobIdFromMaintenance(job) {
145414
145551
  const mediaJobId = job.payload.mediaJobId;
@@ -145420,6 +145557,7 @@ function mediaJobIdFromMaintenance(job) {
145420
145557
  function toVideoRequest(request) {
145421
145558
  return {
145422
145559
  prompt: request.prompt,
145560
+ firstFrameImageUrl: request.firstFrameImageUrl,
145423
145561
  model: request.model,
145424
145562
  region: request.region,
145425
145563
  resolution: request.resolution,
@@ -145511,6 +145649,7 @@ function queueMiniMaxVideoGeneration(input) {
145511
145649
  region: normalized.region,
145512
145650
  model: normalized.model,
145513
145651
  prompt: normalized.prompt,
145652
+ ...normalized.firstFrameImageUrl ? { firstFrameImageUrl: normalized.firstFrameImageUrl } : {},
145514
145653
  resolution: normalized.resolution,
145515
145654
  duration: normalized.duration,
145516
145655
  ratio: normalized.ratio,
@@ -154230,16 +154369,18 @@ async function initSessionStore(dataDir) {
154230
154369
  if (_store3 && _storeDataDir3 === dataDir) return _store3;
154231
154370
  _store3 = null;
154232
154371
  _storeDataDir3 = null;
154372
+ let degradedReason = "SQLite runtime unavailable";
154233
154373
  try {
154234
- const store2 = new SessionSqliteStore();
154235
- await store2.init(dataDir);
154374
+ const store2 = await initializeSqliteStore(() => new SessionSqliteStore(), dataDir, "Session store");
154236
154375
  _store3 = store2;
154237
154376
  _storeDataDir3 = dataDir;
154238
154377
  return store2;
154239
154378
  } catch (err) {
154240
- console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${err instanceof Error ? err.message : err}`);
154379
+ if (!isSqliteUnavailableError(err)) throw err;
154380
+ degradedReason = formatSqliteFailure(err);
154381
+ console.error(`[memorix] SessionSqliteStore unavailable, running in degraded read-only mode: ${degradedReason}`);
154241
154382
  }
154242
- const store = new SessionGracefulDegrade();
154383
+ const store = new SessionGracefulDegrade(degradedReason);
154243
154384
  await store.init(dataDir);
154244
154385
  _store3 = store;
154245
154386
  _storeDataDir3 = dataDir;
@@ -154251,6 +154392,7 @@ var init_session_store = __esm({
154251
154392
  "use strict";
154252
154393
  init_esm_shims();
154253
154394
  init_sqlite_db();
154395
+ init_sqlite_reliability();
154254
154396
  SessionSqliteStore = class {
154255
154397
  db = null;
154256
154398
  dataDir = "";
@@ -154339,7 +154481,7 @@ var init_session_store = __esm({
154339
154481
  }
154340
154482
  const stmtComplete = this._stmtCompleteActive;
154341
154483
  const stmtIns = this.stmtInsert;
154342
- const result = this.db.transaction(() => {
154484
+ return retrySqliteBusy(async () => this.db.transaction(() => {
154343
154485
  let completedCount = 0;
154344
154486
  for (const pid of projectIds) {
154345
154487
  const info = stmtComplete.run({ pid, now: now3 });
@@ -154347,18 +154489,24 @@ var init_session_store = __esm({
154347
154489
  }
154348
154490
  stmtIns.run(sessionToRow(newSession));
154349
154491
  return completedCount;
154350
- })();
154351
- return result;
154492
+ })(), "Session rollover");
154352
154493
  }
154353
154494
  getBackendName() {
154354
154495
  return "sqlite";
154355
154496
  }
154356
154497
  };
154357
154498
  SessionGracefulDegrade = class {
154499
+ constructor(reason = "SQLite runtime unavailable") {
154500
+ this.reason = reason;
154501
+ }
154502
+ reason;
154358
154503
  warned = false;
154504
+ unavailableRead() {
154505
+ return degradedReadError("sessions");
154506
+ }
154359
154507
  warn() {
154360
154508
  if (!this.warned) {
154361
- console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions are disabled (read-only empty). Install better-sqlite3 for full functionality.");
154509
+ console.error("[memorix] SessionStore: SQLite unavailable \u2014 sessions cannot be read or written. Install a supported SQLite runtime.");
154362
154510
  this.warned = true;
154363
154511
  }
154364
154512
  }
@@ -154366,33 +154514,39 @@ var init_session_store = __esm({
154366
154514
  this.warn();
154367
154515
  }
154368
154516
  async loadAll() {
154369
- return [];
154517
+ throw this.unavailableRead();
154370
154518
  }
154371
154519
  async getById(_id) {
154372
- return void 0;
154520
+ throw this.unavailableRead();
154373
154521
  }
154374
154522
  async loadByProject(_projectId) {
154375
- return [];
154523
+ throw this.unavailableRead();
154376
154524
  }
154377
154525
  async loadActive(_projectId) {
154378
- return [];
154526
+ throw this.unavailableRead();
154379
154527
  }
154380
154528
  async insert(_session) {
154381
154529
  this.warn();
154530
+ throw degradedWriteError("sessions");
154382
154531
  }
154383
154532
  async update(_session) {
154384
154533
  this.warn();
154534
+ throw degradedWriteError("sessions");
154385
154535
  }
154386
154536
  async bulkUpdate(_sessions) {
154387
154537
  this.warn();
154538
+ throw degradedWriteError("sessions");
154388
154539
  }
154389
154540
  async atomicRolloverInsert(_newSession, _projectIds, _now) {
154390
154541
  this.warn();
154391
- return 0;
154542
+ throw degradedWriteError("sessions");
154392
154543
  }
154393
154544
  getBackendName() {
154394
154545
  return "degraded";
154395
154546
  }
154547
+ getBackendReason() {
154548
+ return this.reason;
154549
+ }
154396
154550
  };
154397
154551
  _store3 = null;
154398
154552
  _storeDataDir3 = null;
@@ -159648,7 +159802,7 @@ var init_request_context = __esm({
159648
159802
  function getMcpServerInfo() {
159649
159803
  return {
159650
159804
  name: "memorix",
159651
- version: true ? "1.9.2" : "1.0.1"
159805
+ version: true ? "1.9.4" : "1.0.1"
159652
159806
  };
159653
159807
  }
159654
159808
  function createMcpDiscoverResult(capabilities, serverInfo) {
@@ -174362,6 +174516,7 @@ ${lines.join("\n")}` }] };
174362
174516
  } : {},
174363
174517
  ...mediaGenerationEnabled ? {
174364
174518
  image: z627.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
174519
+ firstFrameImageUrl: z627.string().optional().describe("Public HTTPS first-frame image URL for MiniMax video generation; stored with the queued job."),
174365
174520
  region: z627.enum(["global", "cn"]).optional().describe("MiniMax deployment region."),
174366
174521
  n: z627.number().int().min(1).max(4).optional().describe("Image output count."),
174367
174522
  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."),
@@ -174371,7 +174526,7 @@ ${lines.join("\n")}` }] };
174371
174526
  } : {}
174372
174527
  }
174373
174528
  },
174374
- 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 }) => {
174529
+ 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 }) => {
174375
174530
  const unresolved = requireResolvedProject("manage controlled media");
174376
174531
  if (unresolved) return unresolved;
174377
174532
  const safeError4 = (error) => sanitizeCredentials(
@@ -174541,6 +174696,7 @@ ${lines.join("\n")}` }] };
174541
174696
  dataDir: projectDir2,
174542
174697
  projectId: project.id,
174543
174698
  prompt,
174699
+ firstFrameImageUrl,
174544
174700
  ...model === "MiniMax-H3" ? { model } : {},
174545
174701
  ...region ? { region } : {},
174546
174702
  ...videoRatio ? { ratio: videoRatio } : {},
@@ -175357,7 +175513,7 @@ var init_serve_shared = __esm({
175357
175513
 
175358
175514
  // src/cli/version.ts
175359
175515
  function getCliVersion() {
175360
- return true ? "1.9.2" : pkg.version;
175516
+ return true ? "1.9.4" : pkg.version;
175361
175517
  }
175362
175518
  var init_version3 = __esm({
175363
175519
  "src/cli/version.ts"() {