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
@@ -580,70 +580,79 @@ function getDatabase(dataDir) {
580
580
  loadBetterSqlite3();
581
581
  fs.mkdirSync(dataDir, { recursive: true });
582
582
  const dbPath = path3.join(dataDir, "memorix.db");
583
- const db2 = createDatabase(dbPath);
584
- db2.pragma("journal_mode = WAL");
585
- db2.pragma("busy_timeout = 5000");
586
- db2.pragma("foreign_keys = ON");
587
- db2.exec(CREATE_OBSERVATIONS_TABLE);
588
- db2.exec(CREATE_MINI_SKILLS_TABLE);
589
- db2.exec(CREATE_SESSIONS_TABLE);
590
- db2.exec(CREATE_META_TABLE);
591
- db2.exec(CREATE_TEAM_AGENTS_TABLE);
592
- db2.exec(CREATE_TEAM_TASKS_TABLE);
593
- db2.exec(CREATE_TEAM_TASK_DEPS_TABLE);
594
- db2.exec(CREATE_TEAM_MESSAGES_TABLE);
595
- db2.exec(CREATE_TEAM_LOCKS_TABLE);
596
- db2.exec(CREATE_TEAM_ROLES_TABLE);
597
- db2.exec(CREATE_GRAPH_ENTITIES_TABLE);
598
- db2.exec(CREATE_GRAPH_RELATIONS_TABLE);
599
- db2.exec(CREATE_CHAT_TRANSCRIPT_TABLE);
600
- db2.exec(CREATE_CODE_FILES_TABLE);
601
- db2.exec(CREATE_CODE_SYMBOLS_TABLE);
602
- db2.exec(CREATE_CODE_EDGES_TABLE);
603
- db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
604
- db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
605
- db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
606
- db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
583
+ let db2;
607
584
  try {
608
- db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
609
- } catch {
610
- }
611
- try {
612
- db2.exec(`ALTER TABLE mini_skills ADD COLUMN updatedAt TEXT`);
613
- } catch {
614
- }
615
- try {
616
- db2.exec(`ALTER TABLE observations ADD COLUMN createdByAgentId TEXT`);
617
- } catch {
618
- }
619
- try {
620
- db2.exec(`ALTER TABLE observations ADD COLUMN writeGeneration INTEGER DEFAULT 0`);
621
- } catch {
622
- }
623
- try {
624
- db2.exec(`ALTER TABLE team_tasks ADD COLUMN required_role TEXT`);
625
- } catch {
626
- }
627
- try {
628
- db2.exec(`ALTER TABLE team_tasks ADD COLUMN preferred_role TEXT`);
629
- } catch {
630
- }
631
- try {
632
- db2.exec(`ALTER TABLE team_messages ADD COLUMN to_role TEXT`);
633
- } catch {
634
- }
635
- try {
636
- db2.exec(`ALTER TABLE team_messages ADD COLUMN handoff_status TEXT`);
637
- } catch {
585
+ db2 = createDatabase(dbPath);
586
+ db2.pragma("busy_timeout = 5000");
587
+ db2.pragma("journal_mode = WAL");
588
+ db2.pragma("foreign_keys = ON");
589
+ db2.exec(CREATE_OBSERVATIONS_TABLE);
590
+ db2.exec(CREATE_MINI_SKILLS_TABLE);
591
+ db2.exec(CREATE_SESSIONS_TABLE);
592
+ db2.exec(CREATE_META_TABLE);
593
+ db2.exec(CREATE_TEAM_AGENTS_TABLE);
594
+ db2.exec(CREATE_TEAM_TASKS_TABLE);
595
+ db2.exec(CREATE_TEAM_TASK_DEPS_TABLE);
596
+ db2.exec(CREATE_TEAM_MESSAGES_TABLE);
597
+ db2.exec(CREATE_TEAM_LOCKS_TABLE);
598
+ db2.exec(CREATE_TEAM_ROLES_TABLE);
599
+ db2.exec(CREATE_GRAPH_ENTITIES_TABLE);
600
+ db2.exec(CREATE_GRAPH_RELATIONS_TABLE);
601
+ db2.exec(CREATE_CHAT_TRANSCRIPT_TABLE);
602
+ db2.exec(CREATE_CODE_FILES_TABLE);
603
+ db2.exec(CREATE_CODE_SYMBOLS_TABLE);
604
+ db2.exec(CREATE_CODE_EDGES_TABLE);
605
+ db2.exec(CREATE_OBSERVATION_CODE_REFS_TABLE);
606
+ db2.exec(CREATE_MAINTENANCE_JOBS_TABLE);
607
+ db2.exec(CREATE_MAINTENANCE_TARGETS_TABLE);
608
+ db2.exec(CREATE_COMPACTION_CHECKPOINTS_TABLE);
609
+ try {
610
+ db2.exec(`ALTER TABLE mini_skills ADD COLUMN sourceSnapshot TEXT NOT NULL DEFAULT ''`);
611
+ } catch {
612
+ }
613
+ try {
614
+ db2.exec(`ALTER TABLE mini_skills ADD COLUMN updatedAt TEXT`);
615
+ } catch {
616
+ }
617
+ try {
618
+ db2.exec(`ALTER TABLE observations ADD COLUMN createdByAgentId TEXT`);
619
+ } catch {
620
+ }
621
+ try {
622
+ db2.exec(`ALTER TABLE observations ADD COLUMN writeGeneration INTEGER DEFAULT 0`);
623
+ } catch {
624
+ }
625
+ try {
626
+ db2.exec(`ALTER TABLE team_tasks ADD COLUMN required_role TEXT`);
627
+ } catch {
628
+ }
629
+ try {
630
+ db2.exec(`ALTER TABLE team_tasks ADD COLUMN preferred_role TEXT`);
631
+ } catch {
632
+ }
633
+ try {
634
+ db2.exec(`ALTER TABLE team_messages ADD COLUMN to_role TEXT`);
635
+ } catch {
636
+ }
637
+ try {
638
+ db2.exec(`ALTER TABLE team_messages ADD COLUMN handoff_status TEXT`);
639
+ } catch {
640
+ }
641
+ applySchemaMigrations(db2);
642
+ db2.exec(CREATE_INDEXES);
643
+ initializeObservationLexicalIndex(db2);
644
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
645
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
646
+ db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
647
+ _dbCache.set(normalized, db2);
648
+ return db2;
649
+ } catch (error) {
650
+ try {
651
+ db2?.close();
652
+ } catch {
653
+ }
654
+ throw error;
638
655
  }
639
- applySchemaMigrations(db2);
640
- db2.exec(CREATE_INDEXES);
641
- initializeObservationLexicalIndex(db2);
642
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('storage_generation', '0')`).run();
643
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('next_id', '1')`).run();
644
- db2.prepare(`INSERT OR IGNORE INTO meta (key, value) VALUES ('mini_skills_generation', '0')`).run();
645
- _dbCache.set(normalized, db2);
646
- return db2;
647
656
  }
648
657
  function closeAllDatabases() {
649
658
  for (const [key, db2] of _dbCache) {
@@ -1633,6 +1642,74 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_maintenance_jobs_active_dedupe
1633
1642
  }
1634
1643
  });
1635
1644
 
1645
+ // src/store/sqlite-reliability.ts
1646
+ function isTransientSqliteError(error) {
1647
+ const code = error?.code;
1648
+ if (typeof code === "string") {
1649
+ return /^SQLITE_BUSY(?:_|$)/i.test(code);
1650
+ }
1651
+ return error instanceof Error && /\bdatabase is locked\b|\bdatabase is busy\b/i.test(error.message);
1652
+ }
1653
+ function isSqliteUnavailableError(error) {
1654
+ return error instanceof SqliteUnavailableError || error instanceof Error && /SQLite is not available|better-sqlite3.*failed|node:sqlite.*failed|bun:sqlite.*failed/i.test(error.message);
1655
+ }
1656
+ function degradedReadError(storeName) {
1657
+ return new Error(`[memorix] Cannot read ${storeName}: SQLite backend unavailable (memory is not loaded)`);
1658
+ }
1659
+ function degradedWriteError(storeName) {
1660
+ return new Error(`[memorix] Cannot write ${storeName}: SQLite backend unavailable (degraded mode; read-only)`);
1661
+ }
1662
+ function formatSqliteFailure(error) {
1663
+ return error instanceof Error && error.message ? error.message : String(error);
1664
+ }
1665
+ function delay(ms) {
1666
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
1667
+ }
1668
+ async function retrySqliteBusy(operation, label) {
1669
+ for (let attempt = 0; ; attempt++) {
1670
+ try {
1671
+ return await operation();
1672
+ } catch (error) {
1673
+ if (!isTransientSqliteError(error) || attempt >= SQLITE_BUSY_RETRY_DELAYS_MS.length) throw error;
1674
+ const waitMs = SQLITE_BUSY_RETRY_DELAYS_MS[attempt];
1675
+ console.error(
1676
+ `[memorix] ${label} SQLite busy (attempt ${attempt + 1}/${SQLITE_BUSY_RETRY_DELAYS_MS.length + 1}), retrying in ${waitMs}ms`
1677
+ );
1678
+ await delay(waitMs);
1679
+ }
1680
+ }
1681
+ }
1682
+ async function initializeSqliteStore(factory, dataDir, label) {
1683
+ try {
1684
+ loadBetterSqlite3();
1685
+ } catch (error) {
1686
+ throw new SqliteUnavailableError(error);
1687
+ }
1688
+ return retrySqliteBusy(async () => {
1689
+ const store = factory();
1690
+ await store.init(dataDir);
1691
+ return store;
1692
+ }, label);
1693
+ }
1694
+ var SQLITE_BUSY_RETRY_DELAYS_MS, SqliteUnavailableError;
1695
+ var init_sqlite_reliability = __esm({
1696
+ "src/store/sqlite-reliability.ts"() {
1697
+ "use strict";
1698
+ init_esm_shims();
1699
+ init_sqlite_db();
1700
+ SQLITE_BUSY_RETRY_DELAYS_MS = [50, 150, 400, 1e3, 2500];
1701
+ SqliteUnavailableError = class extends Error {
1702
+ code = "MEMORIX_SQLITE_UNAVAILABLE";
1703
+ constructor(cause) {
1704
+ super(
1705
+ cause instanceof Error && cause.message ? cause.message : "[memorix] SQLite is unavailable (install a supported SQLite runtime)"
1706
+ );
1707
+ this.name = "SqliteUnavailableError";
1708
+ }
1709
+ };
1710
+ }
1711
+ });
1712
+
1636
1713
  // src/store/file-lock.ts
1637
1714
  import { promises as fs2 } from "fs";
1638
1715
  import { randomUUID } from "crypto";
@@ -3556,10 +3633,10 @@ async function fetchWithRetry(url, apiKey, body, options = {}, attempt = 0, nati
3556
3633
  );
3557
3634
  }
3558
3635
  if (options.retry !== false && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES2) {
3559
- const delay2 = BASE_DELAY_MS * Math.pow(2, attempt);
3636
+ const delay3 = BASE_DELAY_MS * Math.pow(2, attempt);
3560
3637
  const retryAfter = response.headers.get("retry-after");
3561
3638
  const parsedRetryAfter = retryAfter ? parseInt(retryAfter, 10) * 1e3 : NaN;
3562
- const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay2;
3639
+ const waitMs = Number.isFinite(parsedRetryAfter) ? parsedRetryAfter : delay3;
3563
3640
  console.error(`[memorix] Embedding API ${response.status}, retry ${attempt + 1}/${MAX_RETRIES2} in ${waitMs}ms`);
3564
3641
  await waitForRetry(waitMs, options.signal);
3565
3642
  return fetchWithRetry(url, apiKey, body, options, attempt + 1, nativeGemini);
@@ -5591,6 +5668,7 @@ var init_sqlite_store = __esm({
5591
5668
  "use strict";
5592
5669
  init_esm_shims();
5593
5670
  init_sqlite_db();
5671
+ init_sqlite_reliability();
5594
5672
  init_sqlite_fts();
5595
5673
  SqliteBackend = class {
5596
5674
  db = null;
@@ -5868,7 +5946,9 @@ var init_sqlite_store = __esm({
5868
5946
  // ── Compound atomic operation ────────────────────────────────────
5869
5947
  async atomic(fn) {
5870
5948
  const run = this._atomicQueue.catch(() => void 0).then(async () => {
5871
- this.db.prepare("BEGIN IMMEDIATE").run();
5949
+ await retrySqliteBusy(async () => {
5950
+ this.db.prepare("BEGIN IMMEDIATE").run();
5951
+ }, "Observation atomic write");
5872
5952
  try {
5873
5953
  const tx = {
5874
5954
  loadAll: async () => this.rawLoadAll(),
@@ -5967,15 +6047,15 @@ function resetObservationStore() {
5967
6047
  async function createObservationStore(dataDir) {
5968
6048
  try {
5969
6049
  const { SqliteBackend: SqliteBackend2 } = await Promise.resolve().then(() => (init_sqlite_store(), sqlite_store_exports));
5970
- const store2 = new SqliteBackend2();
5971
- await store2.init(dataDir);
5972
- return store2;
6050
+ return await initializeSqliteStore(() => new SqliteBackend2(), dataDir, "Observation store");
5973
6051
  } catch (err) {
5974
- console.error(`[memorix] SQLite backend unavailable \u2014 degraded mode (read-only): ${err instanceof Error ? err.message : err}`);
6052
+ if (!isSqliteUnavailableError(err)) throw err;
6053
+ const reason = formatSqliteFailure(err);
6054
+ console.error(`[memorix] SQLite module unavailable \u2014 degraded mode (reads/writes disabled): ${reason}`);
6055
+ const degraded = new DegradedBackend(reason);
6056
+ await degraded.init(dataDir);
6057
+ return degraded;
5975
6058
  }
5976
- const store = new DegradedBackend();
5977
- await store.init(dataDir);
5978
- return store;
5979
6059
  }
5980
6060
  async function initObservationStore(dataDir) {
5981
6061
  if (_store2 && _storeDataDir === dataDir) {
@@ -5999,36 +6079,44 @@ var init_obs_store = __esm({
5999
6079
  "src/store/obs-store.ts"() {
6000
6080
  "use strict";
6001
6081
  init_esm_shims();
6082
+ init_sqlite_reliability();
6002
6083
  _store2 = null;
6003
6084
  _storeDataDir = null;
6004
6085
  DegradedBackend = class {
6086
+ constructor(reason = "SQLite runtime unavailable") {
6087
+ this.reason = reason;
6088
+ }
6089
+ reason;
6005
6090
  dataDir = "";
6091
+ unavailableRead() {
6092
+ return degradedReadError("observations");
6093
+ }
6006
6094
  async init(dataDir) {
6007
6095
  this.dataDir = dataDir;
6008
6096
  }
6009
6097
  async loadAll() {
6010
- return [];
6098
+ throw this.unavailableRead();
6011
6099
  }
6012
6100
  async loadByProject(_projectId, _options) {
6013
- return [];
6101
+ throw this.unavailableRead();
6014
6102
  }
6015
6103
  async countByProject(_projectId, _options) {
6016
- return 0;
6104
+ throw this.unavailableRead();
6017
6105
  }
6018
6106
  async getById(_id) {
6019
- return void 0;
6107
+ throw this.unavailableRead();
6020
6108
  }
6021
6109
  async loadIdCounter() {
6022
- return 1;
6110
+ throw this.unavailableRead();
6023
6111
  }
6024
6112
  async countAll() {
6025
- return 0;
6113
+ throw this.unavailableRead();
6026
6114
  }
6027
6115
  async listProjectIds() {
6028
- return [];
6116
+ throw this.unavailableRead();
6029
6117
  }
6030
6118
  async searchLexical(_options) {
6031
- return [];
6119
+ throw this.unavailableRead();
6032
6120
  }
6033
6121
  hasLexicalIndex() {
6034
6122
  return false;
@@ -6037,25 +6125,25 @@ var init_obs_store = __esm({
6037
6125
  return false;
6038
6126
  }
6039
6127
  async insert(_obs) {
6040
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6128
+ throw degradedWriteError("observations");
6041
6129
  }
6042
6130
  async update(_obs) {
6043
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6131
+ throw degradedWriteError("observations");
6044
6132
  }
6045
6133
  async remove(_id) {
6046
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6134
+ throw degradedWriteError("observations");
6047
6135
  }
6048
6136
  async bulkReplace(_obs) {
6049
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6137
+ throw degradedWriteError("observations");
6050
6138
  }
6051
6139
  async bulkRemoveByIds(_ids) {
6052
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6140
+ throw degradedWriteError("observations");
6053
6141
  }
6054
6142
  async saveIdCounter(_nextId) {
6055
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6143
+ throw degradedWriteError("observations");
6056
6144
  }
6057
6145
  async atomic(_fn) {
6058
- throw new Error("[memorix] Cannot write observations: SQLite backend unavailable (degraded mode)");
6146
+ throw degradedWriteError("observations");
6059
6147
  }
6060
6148
  async ensureFresh() {
6061
6149
  return false;
@@ -6068,6 +6156,9 @@ var init_obs_store = __esm({
6068
6156
  getBackendName() {
6069
6157
  return "degraded";
6070
6158
  }
6159
+ getBackendReason() {
6160
+ return this.reason;
6161
+ }
6071
6162
  };
6072
6163
  }
6073
6164
  });
@@ -55275,10 +55366,10 @@ var require_retry3 = __commonJS({
55275
55366
  if (!await shouldRetryFn(err)) {
55276
55367
  return { shouldRetry: false, config: err.config };
55277
55368
  }
55278
- const delay2 = getNextRetryDelay(config);
55369
+ const delay3 = getNextRetryDelay(config);
55279
55370
  err.config.retryConfig.currentRetryAttempt += 1;
55280
- const backoff = config.retryBackoff ? config.retryBackoff(err, delay2) : new Promise((resolve2) => {
55281
- setTimeout(resolve2, delay2);
55371
+ const backoff = config.retryBackoff ? config.retryBackoff(err, delay3) : new Promise((resolve2) => {
55372
+ setTimeout(resolve2, delay3);
55282
55373
  });
55283
55374
  if (config.onRetryAttempt) {
55284
55375
  await config.onRetryAttempt(err);
@@ -97064,7 +97155,7 @@ async function retryBackoff(fn, strategy) {
97064
97155
  retryInterval = initialInterval * Math.pow(x2, exponent) + Math.random() * 1e3;
97065
97156
  }
97066
97157
  const d = Math.min(retryInterval, maxInterval);
97067
- await delay(d);
97158
+ await delay2(d);
97068
97159
  x2++;
97069
97160
  }
97070
97161
  }
@@ -97085,8 +97176,8 @@ function retryIntervalFromResponse(res) {
97085
97176
  }
97086
97177
  return 0;
97087
97178
  }
97088
- async function delay(delay2) {
97089
- return new Promise((resolve2) => setTimeout(resolve2, delay2));
97179
+ async function delay2(delay3) {
97180
+ return new Promise((resolve2) => setTimeout(resolve2, delay3));
97090
97181
  }
97091
97182
  var defaultBackoff, PermanentError, TemporaryError, codeRangeRE2;
97092
97183
  var init_retries = __esm({
@@ -143363,7 +143454,20 @@ function normalizeMiniMaxVideoRequest(input) {
143363
143454
  if (ratio !== "adaptive" && ratio !== "16:9" && ratio !== "9:16" && ratio !== "1:1") {
143364
143455
  throw new Error("MiniMax video ratio must be adaptive, 16:9, 9:16, or 1:1");
143365
143456
  }
143366
- return { region, model, prompt, resolution, duration, ratio };
143457
+ let firstFrameImageUrl;
143458
+ if (input.firstFrameImageUrl !== void 0) {
143459
+ firstFrameImageUrl = input.firstFrameImageUrl.trim();
143460
+ let parsed;
143461
+ try {
143462
+ parsed = new URL(firstFrameImageUrl);
143463
+ } catch {
143464
+ throw new Error("MiniMax first-frame image must be a public HTTPS URL");
143465
+ }
143466
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || sanitizeCredentials(firstFrameImageUrl) !== firstFrameImageUrl) {
143467
+ throw new Error("MiniMax first-frame image must be an HTTPS URL without credentials");
143468
+ }
143469
+ }
143470
+ return { region, model, prompt, resolution, duration, ratio, ...firstFrameImageUrl ? { firstFrameImageUrl } : {} };
143367
143471
  }
143368
143472
  function videoRequestConfig(input) {
143369
143473
  const normalized = normalizeMiniMaxVideoRequest(input);
@@ -143440,7 +143544,14 @@ async function createMiniMaxVideoTask(input, dependencies = {}) {
143440
143544
  headers: videoHeaders(config.apiKey),
143441
143545
  body: JSON.stringify({
143442
143546
  model: config.model,
143443
- content: [{ type: "text", text: config.prompt }],
143547
+ content: [
143548
+ { type: "text", text: config.prompt },
143549
+ ...config.firstFrameImageUrl ? [{
143550
+ type: "image_url",
143551
+ image_url: { url: config.firstFrameImageUrl },
143552
+ role: "first_frame"
143553
+ }] : []
143554
+ ],
143444
143555
  resolution: config.resolution,
143445
143556
  duration: config.duration,
143446
143557
  ratio: config.ratio
@@ -143601,10 +143712,19 @@ function parseStoredRequest(value) {
143601
143712
  const duration = value.duration;
143602
143713
  const ratio = value.ratio;
143603
143714
  const maxBytes = value.maxBytes;
143604
- 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) {
143715
+ 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) {
143605
143716
  throw new Error("Media video job has an invalid request payload");
143606
143717
  }
143607
- return { provider: provider2, region, model, prompt, resolution, duration, ratio, maxBytes };
143718
+ const normalized = normalizeMiniMaxVideoRequest({
143719
+ region,
143720
+ model,
143721
+ prompt,
143722
+ resolution,
143723
+ duration,
143724
+ ratio,
143725
+ firstFrameImageUrl: value.firstFrameImageUrl
143726
+ });
143727
+ return { provider: provider2, ...normalized, maxBytes };
143608
143728
  }
143609
143729
  function mediaJobIdFromMaintenance(job) {
143610
143730
  const mediaJobId = job.payload.mediaJobId;
@@ -143616,6 +143736,7 @@ function mediaJobIdFromMaintenance(job) {
143616
143736
  function toVideoRequest(request) {
143617
143737
  return {
143618
143738
  prompt: request.prompt,
143739
+ firstFrameImageUrl: request.firstFrameImageUrl,
143619
143740
  model: request.model,
143620
143741
  region: request.region,
143621
143742
  resolution: request.resolution,
@@ -143707,6 +143828,7 @@ function queueMiniMaxVideoGeneration(input) {
143707
143828
  region: normalized.region,
143708
143829
  model: normalized.model,
143709
143830
  prompt: normalized.prompt,
143831
+ ...normalized.firstFrameImageUrl ? { firstFrameImageUrl: normalized.firstFrameImageUrl } : {},
143710
143832
  resolution: normalized.resolution,
143711
143833
  duration: normalized.duration,
143712
143834
  ratio: normalized.ratio,
@@ -4,6 +4,33 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.9.4] - 2026-09-18
8
+
9
+ ### Added
10
+ - **MiniMax first-frame video input** -- image-to-video generation now accepts
11
+ a public HTTPS first-frame image URL through the CLI and MCP surfaces,
12
+ preserves it in durable media jobs, and submits it with the provider's
13
+ `first_frame` image role.
14
+
15
+ ### Fixed
16
+ - **First-frame URL safety** -- rejects non-HTTPS URLs and embedded credentials
17
+ before a media request is submitted or persisted.
18
+
19
+ ### Changed
20
+ - **Grok Build documentation** -- the README comparison now uses the official
21
+ Grok Build logo assets.
22
+
23
+ ## [1.9.3] - 2026-09-14
24
+
25
+ ### Fixed
26
+ - **SQLite storage reliability** -- transient cross-process locks no longer
27
+ turn a process into a permanently empty read-only store. Observations,
28
+ sessions, and mini-skills share the same initialization and retry contract;
29
+ persistent runtime failures remain visible.
30
+ - **Storage diagnostics** -- `memorix doctor` reports the active backend,
31
+ distinguishes unavailable storage from an empty project, and returns a
32
+ failing exit status for storage failures.
33
+
7
34
  ## [1.9.2] - 2026-09-11
8
35
 
9
36
  ### Fixed