memorix 1.4.3 → 1.4.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 (144) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +11 -4
  3. package/README.zh-CN.md +7 -1
  4. package/TEAM.md +86 -86
  5. package/assets/star-history-dark.svg +8 -8
  6. package/assets/star-history-light.svg +8 -8
  7. package/assets/star-history.json +16 -2
  8. package/dist/cli/index.js +194 -109
  9. package/dist/cli/memcode.js +0 -0
  10. package/dist/index.js +39 -12
  11. package/dist/maintenance-runner.js +9 -7
  12. package/dist/media-audio-runner.js +9 -7
  13. package/dist/media-video-runner.js +9 -7
  14. package/dist/memcode/index.js +9 -7
  15. package/dist/memcode-runtime/CHANGELOG.md +19 -0
  16. package/dist/sdk.js +38 -11
  17. package/dist/vector-backfill-runner.js +9 -7
  18. package/docs/AGENT_OPERATOR_PLAYBOOK.md +3 -3
  19. package/docs/API_REFERENCE.md +9 -5
  20. package/docs/CONFIGURATION.md +15 -5
  21. package/docs/DESIGN_DECISIONS.md +357 -357
  22. package/docs/DEVELOPMENT.md +2 -2
  23. package/docs/KNOWN_ISSUES_AND_ROADMAP.md +3 -3
  24. package/docs/MODULES.md +1 -1
  25. package/docs/README.md +1 -1
  26. package/docs/SETUP.md +3 -3
  27. package/docs/hooks-architecture.md +2 -1
  28. package/llms-full.txt +4 -4
  29. package/llms.txt +2 -2
  30. package/package.json +1 -1
  31. package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
  32. package/plugins/codebuddy/memorix/.codebuddy-plugin/plugin.json +1 -1
  33. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  34. package/plugins/copilot/memorix/plugin.json +1 -1
  35. package/plugins/gemini/memorix/gemini-extension.json +1 -1
  36. package/plugins/omp/memorix/package.json +1 -1
  37. package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
  38. package/plugins/pi/memorix/package.json +1 -1
  39. package/server.json +2 -2
  40. package/src/audit/index.ts +156 -156
  41. package/src/cli/commands/audit-list.ts +89 -89
  42. package/src/cli/commands/formation.ts +48 -48
  43. package/src/cli/commands/git-hook-install.ts +111 -111
  44. package/src/cli/commands/handoff.ts +54 -54
  45. package/src/cli/commands/hooks-status.ts +63 -63
  46. package/src/cli/commands/ingest.ts +44 -44
  47. package/src/cli/commands/integrate-shared.ts +15 -15
  48. package/src/cli/commands/lock.ts +82 -82
  49. package/src/cli/commands/media-image.ts +27 -0
  50. package/src/cli/commands/media.ts +8 -1
  51. package/src/cli/commands/memory.ts +44 -34
  52. package/src/cli/commands/message.ts +104 -104
  53. package/src/cli/commands/operator-shared.ts +62 -18
  54. package/src/cli/commands/poll.ts +58 -58
  55. package/src/cli/commands/purge-all-memory.ts +85 -85
  56. package/src/cli/commands/purge-project-memory.ts +83 -83
  57. package/src/cli/commands/reasoning.ts +118 -118
  58. package/src/cli/commands/skills.ts +114 -114
  59. package/src/cli/commands/task.ts +167 -167
  60. package/src/cli/commands/transfer.ts +47 -47
  61. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  62. package/src/cli/tui/ChatView.tsx +234 -234
  63. package/src/cli/tui/CommandBar.tsx +312 -312
  64. package/src/cli/tui/ContextRail.tsx +118 -118
  65. package/src/cli/tui/HeaderBar.tsx +72 -72
  66. package/src/cli/tui/LogoBanner.tsx +51 -51
  67. package/src/cli/tui/Sidebar.tsx +179 -179
  68. package/src/cli/tui/index.ts +41 -41
  69. package/src/cli/tui/markdown-render.tsx +371 -371
  70. package/src/cli/tui/use-mouse.ts +157 -157
  71. package/src/cli/tui/useNavigation.ts +56 -56
  72. package/src/cli/version.ts +7 -7
  73. package/src/cli/workbench.ts +1 -1
  74. package/src/dashboard/project-classification.ts +64 -64
  75. package/src/git/hooks-path.ts +85 -85
  76. package/src/hooks/pattern-detector.ts +173 -173
  77. package/src/hooks/significance-filter.ts +250 -250
  78. package/src/llm/memory-manager.ts +328 -328
  79. package/src/llm/provider.ts +885 -885
  80. package/src/llm/quality.ts +248 -248
  81. package/src/media/minimax.ts +9 -1
  82. package/src/memory/attribution-guard.ts +249 -249
  83. package/src/memory/disclosure-policy.ts +135 -135
  84. package/src/memory/entity-extractor.ts +197 -197
  85. package/src/memory/formation/evaluate.ts +217 -217
  86. package/src/memory/formation/extract.ts +361 -361
  87. package/src/memory/formation/index.ts +417 -417
  88. package/src/memory/formation/resolve.ts +344 -344
  89. package/src/memory/formation/types.ts +315 -315
  90. package/src/memory/freshness.ts +122 -122
  91. package/src/memory/graph.ts +197 -197
  92. package/src/memory/refs.ts +94 -94
  93. package/src/memory/secret-filter.ts +79 -79
  94. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  95. package/src/orchestrate/adapters/claude.ts +111 -111
  96. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  97. package/src/orchestrate/adapters/codex.ts +41 -41
  98. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  99. package/src/orchestrate/adapters/gemini.ts +42 -42
  100. package/src/orchestrate/adapters/index.ts +73 -73
  101. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  102. package/src/orchestrate/adapters/opencode.ts +47 -47
  103. package/src/orchestrate/adapters/types.ts +77 -77
  104. package/src/orchestrate/capability-router.ts +284 -284
  105. package/src/orchestrate/context-compact.ts +188 -188
  106. package/src/orchestrate/cost-tracker.ts +219 -219
  107. package/src/orchestrate/error-recovery.ts +191 -191
  108. package/src/orchestrate/evidence.ts +140 -140
  109. package/src/orchestrate/ledger.ts +110 -110
  110. package/src/orchestrate/memorix-bridge.ts +343 -343
  111. package/src/orchestrate/output-budget.ts +80 -80
  112. package/src/orchestrate/permission.ts +152 -152
  113. package/src/orchestrate/pipeline-trace.ts +131 -131
  114. package/src/orchestrate/prompt-builder.ts +155 -155
  115. package/src/orchestrate/ring-buffer.ts +37 -37
  116. package/src/orchestrate/task-graph.ts +389 -389
  117. package/src/project/aliases.ts +374 -374
  118. package/src/rules/adapters/trae.ts +56 -56
  119. package/src/rules/syncer.ts +235 -235
  120. package/src/search/query-expansion.ts +52 -52
  121. package/src/server/formation-timeout.ts +27 -27
  122. package/src/server.ts +23 -1
  123. package/src/skills/mini-skills.ts +386 -386
  124. package/src/store/chat-store.ts +119 -119
  125. package/src/store/graph-store.ts +249 -249
  126. package/src/store/mini-skill-store.ts +349 -349
  127. package/src/store/persistence-json.ts +212 -212
  128. package/src/store/persistence.ts +291 -291
  129. package/src/store/project-affinity.ts +195 -195
  130. package/src/team/event-bus.ts +76 -76
  131. package/src/team/file-locks.ts +173 -173
  132. package/src/team/handoff.ts +161 -161
  133. package/src/team/messages.ts +203 -203
  134. package/src/team/poll.ts +132 -132
  135. package/src/team/tasks.ts +211 -211
  136. package/src/workspace/mcp-adapters/codex.ts +191 -191
  137. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  138. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  139. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  140. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  141. package/src/workspace/mcp-adapters/trae.ts +134 -134
  142. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  143. package/src/workspace/sanitizer.ts +60 -60
  144. package/dist/memcode-runtime/docs/images/exy.png +0 -0
package/dist/cli/index.js CHANGED
@@ -3221,7 +3221,7 @@ ${import_picocolors.default.gray(m3)} ${s4}
3221
3221
 
3222
3222
  // src/cli/version.ts
3223
3223
  function getCliVersion() {
3224
- return true ? "1.4.3" : pkg.version;
3224
+ return true ? "1.4.4" : pkg.version;
3225
3225
  }
3226
3226
  var init_version = __esm({
3227
3227
  "src/cli/version.ts"() {
@@ -10246,8 +10246,8 @@ async function acquireLock(lockPath) {
10246
10246
  const code = err instanceof Error && "code" in err ? err.code : void 0;
10247
10247
  if (code === "EEXIST" || code === "EPERM") {
10248
10248
  try {
10249
- const stat4 = await fs2.stat(lockPath);
10250
- if (Date.now() - stat4.mtimeMs > LOCK_STALE_MS) {
10249
+ const stat5 = await fs2.stat(lockPath);
10250
+ if (Date.now() - stat5.mtimeMs > LOCK_STALE_MS) {
10251
10251
  await fs2.unlink(lockPath).catch(() => {
10252
10252
  });
10253
10253
  continue;
@@ -27247,11 +27247,11 @@ async function importKnownMedia(input) {
27247
27247
  async function importMediaFile(input) {
27248
27248
  const maxBytes = input.maxBytes ?? DEFAULT_MAX_MEDIA_BYTES;
27249
27249
  const source = path12.resolve(input.filePath);
27250
- const stat4 = await lstat(source);
27251
- if (stat4.isSymbolicLink()) throw new Error("Media import does not follow symbolic links");
27252
- if (!stat4.isFile()) throw new Error("Media import accepts regular files only");
27253
- if (stat4.size <= 0) throw new Error("Media import rejects empty files");
27254
- if (stat4.size > maxBytes) throw new Error(`Media file is too large (${stat4.size} bytes; limit ${maxBytes})`);
27250
+ const stat5 = await lstat(source);
27251
+ if (stat5.isSymbolicLink()) throw new Error("Media import does not follow symbolic links");
27252
+ if (!stat5.isFile()) throw new Error("Media import accepts regular files only");
27253
+ if (stat5.size <= 0) throw new Error("Media import rejects empty files");
27254
+ if (stat5.size > maxBytes) throw new Error(`Media file is too large (${stat5.size} bytes; limit ${maxBytes})`);
27255
27255
  const header = await readHeader(source);
27256
27256
  const detected = detectMedia(header);
27257
27257
  const sha2562 = await sha256File(source);
@@ -27259,7 +27259,7 @@ async function importMediaFile(input) {
27259
27259
  dataDir: input.dataDir,
27260
27260
  projectId: input.projectId,
27261
27261
  sha256: sha2562,
27262
- bytes: stat4.size,
27262
+ bytes: stat5.size,
27263
27263
  detected,
27264
27264
  persist: (destination) => persistFile(source, destination),
27265
27265
  sourceKind: input.sourceKind ?? "import",
@@ -35222,7 +35222,7 @@ var init_image_models_generated = __esm({
35222
35222
  api: "minimax-images",
35223
35223
  provider: "minimax",
35224
35224
  baseUrl: "https://api.minimax.io/v1/image_generation",
35225
- input: ["text"],
35225
+ input: ["text", "image"],
35226
35226
  output: ["image"],
35227
35227
  cost: {
35228
35228
  "input": 0,
@@ -35237,7 +35237,7 @@ var init_image_models_generated = __esm({
35237
35237
  api: "minimax-images",
35238
35238
  provider: "minimax",
35239
35239
  baseUrl: "https://api.minimax.io/v1/image_generation",
35240
- input: ["text"],
35240
+ input: ["text", "image"],
35241
35241
  output: ["image"],
35242
35242
  cost: {
35243
35243
  "input": 0,
@@ -35254,7 +35254,7 @@ var init_image_models_generated = __esm({
35254
35254
  api: "minimax-images",
35255
35255
  provider: "minimax-cn",
35256
35256
  baseUrl: "https://api.minimaxi.com/v1/image_generation",
35257
- input: ["text"],
35257
+ input: ["text", "image"],
35258
35258
  output: ["image"],
35259
35259
  cost: {
35260
35260
  "input": 0,
@@ -35269,7 +35269,7 @@ var init_image_models_generated = __esm({
35269
35269
  api: "minimax-images",
35270
35270
  provider: "minimax-cn",
35271
35271
  baseUrl: "https://api.minimaxi.com/v1/image_generation",
35272
- input: ["text"],
35272
+ input: ["text", "image"],
35273
35273
  output: ["image"],
35274
35274
  cost: {
35275
35275
  "input": 0,
@@ -35368,9 +35368,10 @@ __export(minimax_exports, {
35368
35368
  });
35369
35369
  import { isIP as isIP2 } from "net";
35370
35370
  function buildPayload(model, context, options) {
35371
- if (context.input.some((item) => item.type === "image")) {
35372
- throw new Error(`Model ${model.id} does not support image input`);
35373
- }
35371
+ const subjectReference = context.input.filter((item) => item.type === "image").map((item) => ({
35372
+ type: "character",
35373
+ image_file: `data:${item.mimeType};base64,${item.data}`
35374
+ }));
35374
35375
  const prompt2 = context.input.filter((item) => item.type === "text").map((item) => sanitizeSurrogates(item.text).trim()).filter(Boolean).join("\n");
35375
35376
  if (!prompt2) {
35376
35377
  throw new Error("MiniMax image generation requires a text prompt");
@@ -35381,6 +35382,7 @@ function buildPayload(model, context, options) {
35381
35382
  return {
35382
35383
  model: model.id,
35383
35384
  prompt: prompt2,
35385
+ ...subjectReference.length > 0 ? { subject_reference: subjectReference } : {},
35384
35386
  ...options?.aspectRatio !== void 0 ? { aspect_ratio: options.aspectRatio } : {},
35385
35387
  ...options?.width !== void 0 ? { width: options.width } : {},
35386
35388
  ...options?.height !== void 0 ? { height: options.height } : {},
@@ -78440,21 +78442,21 @@ var init_from = __esm({
78440
78442
  init_fetch_blob();
78441
78443
  ({ stat: stat2 } = fs9);
78442
78444
  blobFromSync = (path47, type) => fromBlob(statSync3(path47), path47, type);
78443
- blobFrom = (path47, type) => stat2(path47).then((stat4) => fromBlob(stat4, path47, type));
78444
- fileFrom = (path47, type) => stat2(path47).then((stat4) => fromFile(stat4, path47, type));
78445
+ blobFrom = (path47, type) => stat2(path47).then((stat5) => fromBlob(stat5, path47, type));
78446
+ fileFrom = (path47, type) => stat2(path47).then((stat5) => fromFile(stat5, path47, type));
78445
78447
  fileFromSync = (path47, type) => fromFile(statSync3(path47), path47, type);
78446
- fromBlob = (stat4, path47, type = "") => new fetch_blob_default([new BlobDataItem({
78448
+ fromBlob = (stat5, path47, type = "") => new fetch_blob_default([new BlobDataItem({
78447
78449
  path: path47,
78448
- size: stat4.size,
78449
- lastModified: stat4.mtimeMs,
78450
+ size: stat5.size,
78451
+ lastModified: stat5.mtimeMs,
78450
78452
  start: 0
78451
78453
  })], { type });
78452
- fromFile = (stat4, path47, type = "") => new file_default([new BlobDataItem({
78454
+ fromFile = (stat5, path47, type = "") => new file_default([new BlobDataItem({
78453
78455
  path: path47,
78454
- size: stat4.size,
78455
- lastModified: stat4.mtimeMs,
78456
+ size: stat5.size,
78457
+ lastModified: stat5.mtimeMs,
78456
78458
  start: 0
78457
- })], basename(path47), { type, lastModified: stat4.mtimeMs });
78459
+ })], basename(path47), { type, lastModified: stat5.mtimeMs });
78458
78460
  BlobDataItem = class _BlobDataItem {
78459
78461
  #path;
78460
78462
  #start;
@@ -85748,7 +85750,7 @@ var require_getCredentials = __commonJS({
85748
85750
  var fs31 = __require("fs");
85749
85751
  var util_1 = __require("util");
85750
85752
  var errorWithCode_1 = require_errorWithCode();
85751
- var readFile9 = fs31.readFile ? (0, util_1.promisify)(fs31.readFile) : async () => {
85753
+ var readFile10 = fs31.readFile ? (0, util_1.promisify)(fs31.readFile) : async () => {
85752
85754
  throw new errorWithCode_1.ErrorWithCode("use key rather than keyFile.", "MISSING_CREDENTIALS");
85753
85755
  };
85754
85756
  var ExtensionFiles;
@@ -85770,7 +85772,7 @@ var require_getCredentials = __commonJS({
85770
85772
  * @returns A promise that resolves with the credentials.
85771
85773
  */
85772
85774
  async getCredentials() {
85773
- const key = await readFile9(this.keyFilePath, "utf8");
85775
+ const key = await readFile10(this.keyFilePath, "utf8");
85774
85776
  let body;
85775
85777
  try {
85776
85778
  body = JSON.parse(key);
@@ -85796,7 +85798,7 @@ var require_getCredentials = __commonJS({
85796
85798
  * @returns A promise that resolves with the private key.
85797
85799
  */
85798
85800
  async getCredentials() {
85799
- const privateKey = await readFile9(this.keyFilePath, "utf8");
85801
+ const privateKey = await readFile10(this.keyFilePath, "utf8");
85800
85802
  return { privateKey };
85801
85803
  }
85802
85804
  };
@@ -87437,7 +87439,7 @@ var require_filesubjecttokensupplier = __commonJS({
87437
87439
  exports.FileSubjectTokenSupplier = void 0;
87438
87440
  var util_1 = __require("util");
87439
87441
  var fs31 = __require("fs");
87440
- var readFile9 = (0, util_1.promisify)(fs31.readFile ?? (() => {
87442
+ var readFile10 = (0, util_1.promisify)(fs31.readFile ?? (() => {
87441
87443
  }));
87442
87444
  var realpath = (0, util_1.promisify)(fs31.realpath ?? (() => {
87443
87445
  }));
@@ -87477,7 +87479,7 @@ var require_filesubjecttokensupplier = __commonJS({
87477
87479
  throw err;
87478
87480
  }
87479
87481
  let subjectToken;
87480
- const rawText = await readFile9(parsedFilePath, { encoding: "utf8" });
87482
+ const rawText = await readFile10(parsedFilePath, { encoding: "utf8" });
87481
87483
  if (this.formatType === "text") {
87482
87484
  subjectToken = rawText;
87483
87485
  } else if (this.formatType === "json" && this.subjectTokenFieldName) {
@@ -176394,6 +176396,7 @@ async function generateMiniMaxImages(input, dependencies = {}) {
176394
176396
  ...input.height !== void 0 ? { height: input.height } : {},
176395
176397
  ...input.seed !== void 0 ? { seed: input.seed } : {},
176396
176398
  ...input.promptOptimizer !== void 0 ? { promptOptimizer: input.promptOptimizer } : {},
176399
+ ...input.subjectImages !== void 0 && input.subjectImages.length > 0 ? { subjectImages: input.subjectImages } : {},
176397
176400
  timeoutMs
176398
176401
  });
176399
176402
  } catch (error40) {
@@ -176433,9 +176436,13 @@ var init_minimax2 = __esm({
176433
176436
  const model = getImageModel(input.provider, input.model);
176434
176437
  if (!model) throw new Error(`MiniMax image model is not registered: ${input.model}`);
176435
176438
  const configuredModel = input.baseUrl ? { ...model, baseUrl: input.baseUrl } : model;
176439
+ const content = [
176440
+ ...(input.subjectImages ?? []).map((image) => ({ type: "image", data: image.data, mimeType: image.mimeType })),
176441
+ { type: "text", text: input.prompt }
176442
+ ];
176436
176443
  const response = await generateImages(
176437
176444
  configuredModel,
176438
- { input: [{ type: "text", text: input.prompt }] },
176445
+ { input: content },
176439
176446
  {
176440
176447
  apiKey: input.apiKey,
176441
176448
  timeoutMs: input.timeoutMs,
@@ -178523,7 +178530,7 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
178523
178530
  const rel = normalizeCodePath(relative(projectRoot, abs));
178524
178531
  try {
178525
178532
  const text2 = readFileSync7(abs, "utf-8");
178526
- const stat4 = fileStat ?? statSync4(abs);
178533
+ const stat5 = fileStat ?? statSync4(abs);
178527
178534
  const file2 = {
178528
178535
  id: makeCodeFileId(projectId, rel),
178529
178536
  projectId,
@@ -178532,8 +178539,8 @@ function indexFileLite(projectId, projectRoot, abs, indexedAt, fileStat) {
178532
178539
  contentHash: hashText(text2),
178533
178540
  // Preserve filesystem precision. Rounding here made same-size edits in
178534
178541
  // a short Windows timestamp window indistinguishable to refresh.
178535
- mtimeMs: Number(stat4.mtimeMs),
178536
- sizeBytes: Number(stat4.size),
178542
+ mtimeMs: Number(stat5.mtimeMs),
178543
+ sizeBytes: Number(stat5.size),
178537
178544
  indexedAt
178538
178545
  };
178539
178546
  return {
@@ -178560,18 +178567,18 @@ async function indexProjectLite(options) {
178560
178567
  let skippedOversizedFiles = 0;
178561
178568
  let unreadableFiles = 0;
178562
178569
  for (const abs of paths) {
178563
- let stat4;
178570
+ let stat5;
178564
178571
  try {
178565
- stat4 = statSync4(abs);
178572
+ stat5 = statSync4(abs);
178566
178573
  } catch {
178567
178574
  unreadableFiles++;
178568
178575
  continue;
178569
178576
  }
178570
- if (Number(stat4.size) > maxFileBytes) {
178577
+ if (Number(stat5.size) > maxFileBytes) {
178571
178578
  skippedOversizedFiles++;
178572
178579
  continue;
178573
178580
  }
178574
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
178581
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
178575
178582
  if (!attempt.delta) {
178576
178583
  if (attempt.unreadable) unreadableFiles++;
178577
178584
  continue;
@@ -178599,16 +178606,16 @@ async function refreshProjectLite(store2, options) {
178599
178606
  for (const abs of paths) {
178600
178607
  const rel = normalizeCodePath(relative(options.projectRoot, abs));
178601
178608
  seenPaths.add(rel);
178602
- let stat4;
178609
+ let stat5;
178603
178610
  try {
178604
- stat4 = statSync4(abs);
178611
+ stat5 = statSync4(abs);
178605
178612
  } catch {
178606
178613
  unreadableFiles++;
178607
178614
  continue;
178608
178615
  }
178609
178616
  const existing = existingByPath.get(rel);
178610
- const mtimeMs = Number(stat4.mtimeMs);
178611
- const sizeBytes = Number(stat4.size);
178617
+ const mtimeMs = Number(stat5.mtimeMs);
178618
+ const sizeBytes = Number(stat5.size);
178612
178619
  if (sizeBytes > maxFileBytes) {
178613
178620
  skippedOversizedFiles++;
178614
178621
  if (existing) oversizedExistingFileIds.add(existing.id);
@@ -178618,7 +178625,7 @@ async function refreshProjectLite(store2, options) {
178618
178625
  unchangedFiles++;
178619
178626
  continue;
178620
178627
  }
178621
- const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat4);
178628
+ const attempt = indexFileLite(options.projectId, options.projectRoot, abs, indexedAt, stat5);
178622
178629
  if (!attempt.delta) {
178623
178630
  if (attempt.unreadable) unreadableFiles++;
178624
178631
  continue;
@@ -187607,9 +187614,30 @@ function emitError(message, asJson) {
187607
187614
  console.error(asJson ? JSON.stringify({ error: message }, null, 2) : `Error: ${message}`);
187608
187615
  process.exitCode = 1;
187609
187616
  }
187617
+ function asStringArg(value) {
187618
+ if (value == null) return void 0;
187619
+ if (typeof value === "string") {
187620
+ const trimmed = value.trim();
187621
+ return trimmed.length > 0 ? trimmed : void 0;
187622
+ }
187623
+ if (Array.isArray(value)) {
187624
+ const joined = value.filter((item) => typeof item === "string").map((item) => item.trim()).filter(Boolean).join(" ");
187625
+ return joined.length > 0 ? joined : void 0;
187626
+ }
187627
+ return void 0;
187628
+ }
187629
+ function lastStringArg(value) {
187630
+ if (typeof value === "string") return value;
187631
+ if (Array.isArray(value)) {
187632
+ const strings = value.filter((item) => typeof item === "string");
187633
+ return strings[strings.length - 1];
187634
+ }
187635
+ return void 0;
187636
+ }
187610
187637
  function parseCsvList(input) {
187611
187638
  if (!input) return [];
187612
- return input.split(",").map((item) => item.trim()).filter(Boolean);
187639
+ const source = Array.isArray(input) ? input.filter((item) => typeof item === "string").join(",") : input;
187640
+ return source.split(",").map((item) => item.trim()).filter(Boolean);
187613
187641
  }
187614
187642
  function parseOptionalJsonObject(input, field = "value") {
187615
187643
  if (!input) return void 0;
@@ -187629,43 +187657,44 @@ function shortId(id) {
187629
187657
  return id ? `${id.slice(0, 8)}\u2026` : "-";
187630
187658
  }
187631
187659
  function parsePositiveInt(input, fallback) {
187632
- if (input == null || input.trim() === "") return fallback;
187633
- if (!/^\d+$/.test(input.trim())) {
187634
- throw new Error(`Expected a positive integer, received "${input}".`);
187660
+ const value = lastStringArg(input);
187661
+ if (value == null || value.trim() === "") return fallback;
187662
+ if (!/^\d+$/.test(value.trim())) {
187663
+ throw new Error(`Expected a positive integer, received "${value}".`);
187635
187664
  }
187636
- const parsed = Number(input);
187665
+ const parsed = Number(value);
187637
187666
  if (!Number.isSafeInteger(parsed) || parsed <= 0) {
187638
- throw new Error(`Expected a positive integer, received "${input}".`);
187667
+ throw new Error(`Expected a positive integer, received "${value}".`);
187639
187668
  }
187640
187669
  return parsed;
187641
187670
  }
187642
187671
  function coerceObservationType(input) {
187643
- const normalized = input ?? "discovery";
187672
+ const normalized = lastStringArg(input) ?? "discovery";
187644
187673
  if (!OBSERVATION_TYPES.includes(normalized)) {
187645
187674
  throw new Error(
187646
- `Unknown observation type "${input}". Valid types: ${OBSERVATION_TYPES.join(", ")}`
187675
+ `Unknown observation type "${String(input ?? "")}". Valid types: ${OBSERVATION_TYPES.join(", ")}`
187647
187676
  );
187648
187677
  }
187649
187678
  return normalized;
187650
187679
  }
187651
187680
  function coerceObservationStatus(input) {
187652
- const normalized = input ?? "resolved";
187681
+ const normalized = lastStringArg(input) ?? "resolved";
187653
187682
  if (!OBSERVATION_STATUSES.includes(normalized)) {
187654
187683
  throw new Error(
187655
- `Unknown observation status "${input}". Valid statuses: ${OBSERVATION_STATUSES.join(", ")}`
187684
+ `Unknown observation status "${String(input ?? "")}". Valid statuses: ${OBSERVATION_STATUSES.join(", ")}`
187656
187685
  );
187657
187686
  }
187658
187687
  return normalized;
187659
187688
  }
187660
187689
  function coerceRetrievalQuality(input) {
187661
- const normalized = (input ?? "balanced").trim().toLowerCase();
187690
+ const normalized = (lastStringArg(input) ?? "balanced").trim().toLowerCase();
187662
187691
  if (!RETRIEVAL_QUALITIES.includes(normalized)) {
187663
187692
  throw new Error("quality must be fast, balanced, or thorough");
187664
187693
  }
187665
187694
  return normalized;
187666
187695
  }
187667
187696
  function coerceObservationVisibility(input) {
187668
- const normalized = (input ?? "project").trim().toLowerCase();
187697
+ const normalized = (lastStringArg(input) ?? "project").trim().toLowerCase();
187669
187698
  if (normalized === "personal" || normalized === "project" || normalized === "team") {
187670
187699
  return normalized;
187671
187700
  }
@@ -188551,23 +188580,23 @@ __export(memory_exports2, {
188551
188580
  default: () => memory_default
188552
188581
  });
188553
188582
  function getStringArg(named, positional) {
188554
- const value = named?.trim() || positional.join(" ").trim();
188583
+ const value = asStringArg(named) || positional.join(" ").trim();
188555
188584
  return value || void 0;
188556
188585
  }
188557
188586
  function getIdArg(args, positional) {
188558
- return args.ids || args.id || positional.join(",");
188587
+ return lastStringArg(args.ids) || lastStringArg(args.id) || positional.join(",");
188559
188588
  }
188560
188589
  function parseObservationId(value) {
188561
- const normalized = value.trim();
188590
+ const normalized = (asStringArg(value) ?? "").trim();
188562
188591
  if (!/^[1-9]\d*$/.test(normalized)) {
188563
- throw new Error(`Invalid observation ID: ${value}`);
188592
+ throw new Error(`Invalid observation ID: ${String(value ?? "")}`);
188564
188593
  }
188565
188594
  const id = Number(normalized);
188566
- if (!Number.isSafeInteger(id)) throw new Error(`Invalid observation ID: ${value}`);
188595
+ if (!Number.isSafeInteger(id)) throw new Error(`Invalid observation ID: ${String(value ?? "")}`);
188567
188596
  return id;
188568
188597
  }
188569
188598
  function parseObservationIds(value) {
188570
- const values = parseCsvList(value);
188599
+ const values = parseCsvList(typeof value === "string" || Array.isArray(value) ? value : void 0);
188571
188600
  if (values.length === 0) return [];
188572
188601
  return values.map(parseObservationId);
188573
188602
  }
@@ -188595,22 +188624,22 @@ function printMemoryUsage() {
188595
188624
  console.log(' memorix memory long-term supersede --id <old-id> --superseded-by <qualified-id> --reason "replaced"');
188596
188625
  }
188597
188626
  function longTermKind(value) {
188598
- const normalized = (value ?? "semantic").trim().toLowerCase();
188627
+ const normalized = (asStringArg(value) ?? "semantic").toLowerCase();
188599
188628
  if (normalized === "episodic" || normalized === "semantic" || normalized === "procedural") return normalized;
188600
188629
  throw new Error("long-term kind must be episodic, semantic, or procedural.");
188601
188630
  }
188602
188631
  function longTermScope(value) {
188603
- const normalized = (value ?? "project").trim().toLowerCase();
188632
+ const normalized = (asStringArg(value) ?? "project").toLowerCase();
188604
188633
  if (normalized === "project" || normalized === "user" || normalized === "team") return normalized;
188605
188634
  throw new Error("long-term scope must be project, user, or team.");
188606
188635
  }
188607
188636
  function longTermPortability(value) {
188608
- const normalized = (value ?? "project-bound").trim().toLowerCase();
188637
+ const normalized = (asStringArg(value) ?? "project-bound").toLowerCase();
188609
188638
  if (normalized === "project-bound" || normalized === "portable") return normalized;
188610
188639
  throw new Error("long-term portability must be project-bound or portable.");
188611
188640
  }
188612
188641
  function requiredLongTermReason(value) {
188613
- const reason = value?.trim();
188642
+ const reason = asStringArg(value);
188614
188643
  if (!reason) throw new Error("reason is required for this long-term memory transition.");
188615
188644
  return reason;
188616
188645
  }
@@ -188674,7 +188703,7 @@ var init_memory3 = __esm({
188674
188703
  run: async ({ args }) => {
188675
188704
  const action = args._?.[0] || "";
188676
188705
  const positional = (args._ ?? []).slice(1);
188677
- const longTermAction = action === "long-term" ? (positional[0] || args.action || "list").trim().toLowerCase() : void 0;
188706
+ const longTermAction = action === "long-term" ? (positional[0] || asStringArg(args.action) || "list").trim().toLowerCase() : void 0;
188678
188707
  const asJson = !!args.json;
188679
188708
  try {
188680
188709
  const readOnlyActions = /* @__PURE__ */ new Set(["", "search", "graph-context", "recent", "suggest-topic-key", "detail", "timeline"]);
@@ -188707,7 +188736,7 @@ var init_memory3 = __esm({
188707
188736
  query,
188708
188737
  limit: parsePositiveInt(args.graphLimit, 5)
188709
188738
  });
188710
- const format = args.format?.trim().toLowerCase();
188739
+ const format = asStringArg(args.format)?.toLowerCase();
188711
188740
  const formatted = format === "prompt" ? formatGraphContextPrompt(packet) : [
188712
188741
  `Graph context packet for ${project.name}`,
188713
188742
  `- ${packet.summary}`,
@@ -188737,15 +188766,15 @@ var init_memory3 = __esm({
188737
188766
  emitError('text is required for "memorix memory store"', asJson);
188738
188767
  return;
188739
188768
  }
188740
- const title = args.title?.trim() || narrative.slice(0, 80);
188769
+ const title = asStringArg(args.title) || narrative.slice(0, 80);
188741
188770
  const type = coerceObservationType(args.type);
188742
- const topicKey = args.topicKey?.trim() || suggestTopicKey(type, title) || void 0;
188771
+ const topicKey = asStringArg(args.topicKey) || suggestTopicKey(type, title) || void 0;
188743
188772
  const writeScope = resolveCliWriteScope(
188744
188773
  { reader, identity },
188745
188774
  args.visibility
188746
188775
  );
188747
188776
  const result = await storeObservation({
188748
- entityName: args.entity?.trim() || "general",
188777
+ entityName: asStringArg(args.entity) || "general",
188749
188778
  type,
188750
188779
  title,
188751
188780
  narrative,
@@ -188766,7 +188795,7 @@ var init_memory3 = __esm({
188766
188795
  }
188767
188796
  case "suggest-topic-key": {
188768
188797
  const type = coerceObservationType(args.type);
188769
- const title = args.title?.trim();
188798
+ const title = asStringArg(args.title);
188770
188799
  if (!title) {
188771
188800
  emitError('title is required for "memorix memory suggest-topic-key"', asJson);
188772
188801
  return;
@@ -188855,7 +188884,7 @@ var init_memory3 = __esm({
188855
188884
  return;
188856
188885
  }
188857
188886
  case "deduplicate": {
188858
- const query = args.query?.trim();
188887
+ const query = asStringArg(args.query);
188859
188888
  const dryRun = !!args.dryRun || !!args["dry-run"];
188860
188889
  const { isLLMEnabled: isLLMEnabled2 } = await Promise.resolve().then(() => (init_provider2(), provider_exports2));
188861
188890
  if (!isLLMEnabled2()) {
@@ -189036,7 +189065,7 @@ var init_memory3 = __esm({
189036
189065
  ...reader.isTeamMember ? { isTeamMember: true } : {}
189037
189066
  };
189038
189067
  const memoryId = () => {
189039
- const id = args.id?.trim() || args.fromObservation?.trim() || longTermPositional[0]?.trim();
189068
+ const id = asStringArg(args.id) || asStringArg(args.fromObservation) || longTermPositional[0]?.trim();
189040
189069
  if (!id) throw new Error("long-term memory id is required.");
189041
189070
  return id;
189042
189071
  };
@@ -189085,11 +189114,11 @@ var init_memory3 = __esm({
189085
189114
  projectId: project.id,
189086
189115
  scope: scope(),
189087
189116
  kind: kind(),
189088
- title: args.title?.trim() || content.slice(0, 100),
189117
+ title: asStringArg(args.title) || content.slice(0, 100),
189089
189118
  content,
189090
189119
  facts: parseCsvList(args.facts),
189091
189120
  tags: parseCsvList(args.tags),
189092
- applicability: args.applicability?.trim(),
189121
+ applicability: asStringArg(args.applicability),
189093
189122
  portability: portability(),
189094
189123
  reader: longTermReader
189095
189124
  });
@@ -189101,7 +189130,7 @@ var init_memory3 = __esm({
189101
189130
  return;
189102
189131
  }
189103
189132
  case "promote": {
189104
- const rawId = args.fromObservation?.trim() || args.id?.trim() || longTermPositional[0]?.trim();
189133
+ const rawId = asStringArg(args.fromObservation) || asStringArg(args.id) || longTermPositional[0]?.trim();
189105
189134
  const observationId2 = Number.parseInt(rawId || "", 10);
189106
189135
  if (!Number.isFinite(observationId2)) {
189107
189136
  emitError('fromObservation or id must be an observation ID for "memorix memory long-term promote"', asJson);
@@ -189118,7 +189147,7 @@ var init_memory3 = __esm({
189118
189147
  scope: scope(),
189119
189148
  kind: kind(),
189120
189149
  tags: parseCsvList(args.tags),
189121
- applicability: args.applicability?.trim(),
189150
+ applicability: asStringArg(args.applicability),
189122
189151
  reader: longTermReader
189123
189152
  });
189124
189153
  emitResult(
@@ -189147,7 +189176,7 @@ var init_memory3 = __esm({
189147
189176
  return;
189148
189177
  }
189149
189178
  case "supersede": {
189150
- const supersededBy = args.supersededBy?.trim() || args["superseded-by"]?.trim();
189179
+ const supersededBy = asStringArg(args.supersededBy) || asStringArg(args["superseded-by"]);
189151
189180
  if (!supersededBy) {
189152
189181
  emitError('supersededBy is required for "memorix memory long-term supersede"', asJson);
189153
189182
  return;
@@ -196908,9 +196937,9 @@ function existingLensCandidates(rootPath, lens) {
196908
196937
  const absolute = path33.join(rootPath, candidate2);
196909
196938
  try {
196910
196939
  if (!existsSync17(absolute)) continue;
196911
- const stat4 = statSync5(absolute);
196912
- if (stat4.isFile()) out.push(candidate2);
196913
- if (stat4.isDirectory()) out.push(candidate2.replace(/\\/g, "/"));
196940
+ const stat5 = statSync5(absolute);
196941
+ if (stat5.isFile()) out.push(candidate2);
196942
+ if (stat5.isDirectory()) out.push(candidate2.replace(/\\/g, "/"));
196914
196943
  } catch {
196915
196944
  }
196916
196945
  }
@@ -215641,8 +215670,8 @@ var init_syncer = __esm({
215641
215670
  const dir = path37.dirname(pattern);
215642
215671
  const fileGlob = path37.basename(pattern);
215643
215672
  try {
215644
- const stat4 = await fs22.stat(dir);
215645
- if (!stat4.isDirectory()) {
215673
+ const stat5 = await fs22.stat(dir);
215674
+ if (!stat5.isDirectory()) {
215646
215675
  try {
215647
215676
  await fs22.access(pattern);
215648
215677
  return [pattern];
@@ -257832,11 +257861,11 @@ import path40 from "path";
257832
257861
  function resolveGitDir(projectRoot) {
257833
257862
  const dotGit = path40.join(projectRoot, ".git");
257834
257863
  if (!existsSync24(dotGit)) return null;
257835
- const stat4 = statSync6(dotGit);
257836
- if (stat4.isDirectory()) {
257864
+ const stat5 = statSync6(dotGit);
257865
+ if (stat5.isDirectory()) {
257837
257866
  return dotGit;
257838
257867
  }
257839
- if (stat4.isFile()) {
257868
+ if (stat5.isFile()) {
257840
257869
  try {
257841
257870
  const content = readFileSync15(dotGit, "utf-8").trim();
257842
257871
  const match2 = content.match(/^gitdir:\s*(.+)$/);
@@ -258239,7 +258268,7 @@ The path should point to a directory containing a .git folder.`
258239
258268
  };
258240
258269
  const server = existingServer ?? new McpServer({
258241
258270
  name: "memorix",
258242
- version: true ? "1.4.3" : "1.0.1"
258271
+ version: true ? "1.4.4" : "1.0.1"
258243
258272
  });
258244
258273
  const originalRegisterTool = server.registerTool.bind(server);
258245
258274
  server.registerTool = ((name, ...args) => {
@@ -260243,8 +260272,8 @@ ${Math.max(0, managedIds.size - result.resolved.length)} visible writable observ
260243
260272
  try {
260244
260273
  const { homedir: homedir35 } = await import("os");
260245
260274
  const { join: join33 } = await import("path");
260246
- const { readFile: readFile9 } = await import("fs/promises");
260247
- const raw = await readFile9(join33(homedir35(), ".memorix", "settings.json"), "utf-8");
260275
+ const { readFile: readFile10 } = await import("fs/promises");
260276
+ const raw = await readFile10(join33(homedir35(), ".memorix", "settings.json"), "utf-8");
260248
260277
  const s4 = JSON.parse(raw);
260249
260278
  if (s4.knowledgeGraph === true) enableKG = true;
260250
260279
  } catch {
@@ -261922,6 +261951,16 @@ ${lines.join("\n")}` }] };
261922
261951
  return { content: [{ type: "text", text: lines.filter(Boolean).join("\n") }] };
261923
261952
  }
261924
261953
  );
261954
+ function matchesImagePrefix2(bytes, prefix) {
261955
+ return prefix.every((byte, index) => bytes[index] === byte);
261956
+ }
261957
+ function detectReferenceImageMimeType2(bytes) {
261958
+ if (bytes.length >= 8 && matchesImagePrefix2(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) return "image/png";
261959
+ if (bytes.length >= 3 && matchesImagePrefix2(bytes, [255, 216, 255])) return "image/jpeg";
261960
+ if (bytes.length >= 6 && (Buffer.from(bytes.subarray(0, 6)).toString("ascii") === "GIF87a" || Buffer.from(bytes.subarray(0, 6)).toString("ascii") === "GIF89a")) return "image/gif";
261961
+ if (bytes.length >= 12 && Buffer.from(bytes.subarray(0, 4)).toString("ascii") === "RIFF" && Buffer.from(bytes.subarray(8, 12)).toString("ascii") === "WEBP") return "image/webp";
261962
+ return void 0;
261963
+ }
261925
261964
  server.registerTool(
261926
261965
  "memorix_media",
261927
261966
  {
@@ -261937,6 +261976,7 @@ ${lines.join("\n")}` }] };
261937
261976
  title: external_exports2.string().optional().describe("Observation title when attaching generated/imported output."),
261938
261977
  narrative: external_exports2.string().optional().describe("Short retrieval text when attaching an asset."),
261939
261978
  prompt: external_exports2.string().optional().describe("Explicit MiniMax generation prompt."),
261979
+ image: external_exports2.string().optional().describe("Base64-encoded reference image for image-to-image generation."),
261940
261980
  model: external_exports2.string().max(160).optional().describe("Provider model for the selected media action."),
261941
261981
  provider: external_exports2.enum(["openai", "groq"]).optional().describe("Explicit audio transcription provider."),
261942
261982
  language: external_exports2.string().max(32).optional().describe("Optional ISO-639-1 hint for audio transcription."),
@@ -261951,7 +261991,7 @@ ${lines.join("\n")}` }] };
261951
261991
  attach: external_exports2.boolean().optional().describe("Attach generated/imported output to normal project memory explicitly.")
261952
261992
  }
261953
261993
  },
261954
- async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt: prompt2, model, provider: provider2, language, region, n: n3, ratio, width, height, duration: duration3, maxPages, maxChars, attach }) => {
261994
+ async ({ action, path: assetPath, assetId, jobId, kind, limit: limit2, title, narrative, prompt: prompt2, image, model, provider: provider2, language, region, n: n3, ratio, width, height, duration: duration3, maxPages, maxChars, attach }) => {
261955
261995
  const unresolved = requireResolvedProject("manage controlled media");
261956
261996
  if (unresolved) return unresolved;
261957
261997
  const safeError4 = (error40) => sanitizeCredentials(
@@ -262080,6 +262120,14 @@ ${lines.join("\n")}` }] };
262080
262120
  if (model && !imageModel) throw new Error("generate-image accepts image-01 or image-01-live");
262081
262121
  const imageRatio = ratio === "1:1" || ratio === "16:9" || ratio === "4:3" || ratio === "3:2" || ratio === "2:3" || ratio === "3:4" || ratio === "9:16" || ratio === "21:9" ? ratio : void 0;
262082
262122
  if (ratio && !imageRatio) throw new Error("generate-image received an unsupported aspect ratio");
262123
+ let subjectImages;
262124
+ if (image?.trim()) {
262125
+ const { decodeBase64ImagePayload: decodeBase64ImagePayload2 } = await Promise.resolve().then(() => (init_image_payload(), image_payload_exports));
262126
+ const bytes = decodeBase64ImagePayload2(image);
262127
+ const mimeType = detectReferenceImageMimeType2(bytes);
262128
+ if (!mimeType) throw new Error("Reference image must be a PNG, JPEG, GIF, or WebP image");
262129
+ subjectImages = [{ data: bytes.toString("base64"), mimeType }];
262130
+ }
262083
262131
  const { generateMiniMaxImages: generateMiniMaxImages2 } = await Promise.resolve().then(() => (init_minimax2(), minimax_exports2));
262084
262132
  const generated = await generateMiniMaxImages2({
262085
262133
  dataDir: projectDir2,
@@ -262090,7 +262138,8 @@ ${lines.join("\n")}` }] };
262090
262138
  n: n3,
262091
262139
  aspectRatio: imageRatio,
262092
262140
  width,
262093
- height
262141
+ height,
262142
+ subjectImages
262094
262143
  });
262095
262144
  const observations2 = attach === true ? await Promise.all(generated.assets.map(({ asset }) => attachAsset(
262096
262145
  asset,
@@ -269025,11 +269074,11 @@ function getCommitInfo(cwd, ref = "HEAD") {
269025
269074
  const date5 = lines[3];
269026
269075
  const subject = lines[4];
269027
269076
  const body = lines.slice(5).join("\n").trim();
269028
- const stat4 = runGit3(cwd, ["diff-tree", "--no-commit-id", "--numstat", hash4]).trim();
269077
+ const stat5 = runGit3(cwd, ["diff-tree", "--no-commit-id", "--numstat", hash4]).trim();
269029
269078
  let insertions = 0;
269030
269079
  let deletions = 0;
269031
269080
  const filesChanged = [];
269032
- for (const line of stat4.split("\n").filter(Boolean)) {
269081
+ for (const line of stat5.split("\n").filter(Boolean)) {
269033
269082
  const parts = line.split(" ");
269034
269083
  if (parts.length >= 3) {
269035
269084
  const ins = parseInt(parts[0], 10) || 0;
@@ -269869,6 +269918,37 @@ var init_embedding = __esm({
269869
269918
  }
269870
269919
  });
269871
269920
 
269921
+ // src/cli/commands/media-image.ts
269922
+ import { readFile as readFile8, stat as stat4 } from "fs/promises";
269923
+ function matchesImagePrefix(bytes, prefix) {
269924
+ return prefix.every((byte, index) => bytes[index] === byte);
269925
+ }
269926
+ function detectReferenceImageMimeType(bytes) {
269927
+ if (bytes.length >= 8 && matchesImagePrefix(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) return "image/png";
269928
+ if (bytes.length >= 3 && matchesImagePrefix(bytes, [255, 216, 255])) return "image/jpeg";
269929
+ if (bytes.length >= 6 && (bytes.subarray(0, 6).toString("ascii") === "GIF87a" || bytes.subarray(0, 6).toString("ascii") === "GIF89a")) return "image/gif";
269930
+ if (bytes.length >= 12 && bytes.subarray(0, 4).toString("ascii") === "RIFF" && bytes.subarray(8, 12).toString("ascii") === "WEBP") return "image/webp";
269931
+ return void 0;
269932
+ }
269933
+ async function readSubjectImage(filePath) {
269934
+ const fileStats = await stat4(filePath);
269935
+ if (fileStats.size > MAX_VISION_IMAGE_BYTES) {
269936
+ throw new Error(`Reference image exceeds the ${MAX_VISION_IMAGE_BYTES}-byte visual analysis limit`);
269937
+ }
269938
+ const bytes = await readFile8(filePath);
269939
+ if (bytes.length === 0) throw new Error(`Reference image is empty: ${filePath}`);
269940
+ const mimeType = detectReferenceImageMimeType(bytes);
269941
+ if (!mimeType) throw new Error("Reference image must be a PNG, JPEG, GIF, or WebP file");
269942
+ return { data: bytes.toString("base64"), mimeType };
269943
+ }
269944
+ var init_media_image = __esm({
269945
+ "src/cli/commands/media-image.ts"() {
269946
+ "use strict";
269947
+ init_esm_shims();
269948
+ init_image_payload();
269949
+ }
269950
+ });
269951
+
269872
269952
  // src/cli/commands/media.ts
269873
269953
  var media_exports = {};
269874
269954
  __export(media_exports, {
@@ -269954,7 +270034,7 @@ function help() {
269954
270034
  ' memorix media attach --asset <asset-id> [--title "Architecture"] [--text "..."]',
269955
270035
  " memorix media embed --asset <asset-id>",
269956
270036
  " memorix media similar --asset <asset-id> [--limit 10]",
269957
- ' memorix media generate image --prompt "..." [--model image-01] [--attach]',
270037
+ ' memorix media generate image --prompt "..." [--model image-01] [--image ./reference.png] [--attach]',
269958
270038
  ' memorix media generate video --prompt "..." [--model MiniMax-H3] [--duration 5] [--ratio 16:9] [--attach]',
269959
270039
  " memorix media status --job <media-job-id>",
269960
270040
  " memorix media cancel --job <media-job-id>",
@@ -269979,6 +270059,7 @@ var init_media = __esm({
269979
270059
  init_minimax2();
269980
270060
  init_video_jobs();
269981
270061
  init_audio_jobs();
270062
+ init_media_image();
269982
270063
  init_operator_shared();
269983
270064
  media_default = defineCommand({
269984
270065
  meta: {
@@ -269996,6 +270077,7 @@ var init_media = __esm({
269996
270077
  concepts: { type: "string", description: "Comma-separated concepts when attaching" },
269997
270078
  visibility: { type: "string", description: "Observation visibility when attaching" },
269998
270079
  prompt: { type: "string", description: "Generation prompt" },
270080
+ image: { type: "string", description: "Path to a reference image for image-to-image generation" },
269999
270081
  model: { type: "string", description: "Provider model" },
270000
270082
  provider: { type: "string", description: "Audio transcription provider: openai or groq" },
270001
270083
  language: { type: "string", description: "Optional ISO-639-1 hint for audio transcription" },
@@ -270067,6 +270149,8 @@ var init_media = __esm({
270067
270149
  );
270068
270150
  return;
270069
270151
  }
270152
+ const referenceImagePath = getValue(args.image);
270153
+ const subjectImages = referenceImagePath ? [await readSubjectImage(referenceImagePath)] : void 0;
270070
270154
  const generated = await generateMiniMaxImages({
270071
270155
  dataDir,
270072
270156
  projectId: project.id,
@@ -270079,6 +270163,7 @@ var init_media = __esm({
270079
270163
  height: parseOptionalInteger(args.height, "height", 1, 8192),
270080
270164
  seed: parseOptionalInteger(args.seed, "seed", 0, 2147483647),
270081
270165
  promptOptimizer: args.promptOptimizer === true || args["prompt-optimizer"] === true,
270166
+ subjectImages,
270082
270167
  maxBytes: parseByteLimit(args.maxBytes ?? args["max-bytes"], 100 * 1024 * 1024)
270083
270168
  });
270084
270169
  const observations2 = args.attach === true ? await Promise.all(generated.assets.map(({ asset }) => attachMediaAssetToObservation({
@@ -271045,14 +271130,14 @@ function doLogs(follow, lines) {
271045
271130
  let position = fs26.statSync(logFile).size;
271046
271131
  const watcher = fs26.watch(logFile, () => {
271047
271132
  try {
271048
- const stat4 = fs26.statSync(logFile);
271049
- if (stat4.size > position) {
271133
+ const stat5 = fs26.statSync(logFile);
271134
+ if (stat5.size > position) {
271050
271135
  const fd = fs26.openSync(logFile, "r");
271051
- const buf = Buffer.alloc(stat4.size - position);
271136
+ const buf = Buffer.alloc(stat5.size - position);
271052
271137
  fs26.readSync(fd, buf, 0, buf.length, position);
271053
271138
  fs26.closeSync(fd);
271054
271139
  process.stdout.write(buf.toString("utf-8"));
271055
- position = stat4.size;
271140
+ position = stat5.size;
271056
271141
  }
271057
271142
  } catch {
271058
271143
  }
@@ -271169,7 +271254,7 @@ __export(agent_integrations_exports, {
271169
271254
  });
271170
271255
  import { spawnSync as spawnSync2 } from "child_process";
271171
271256
  import { existsSync as existsSync28 } from "fs";
271172
- import { mkdir as mkdir8, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
271257
+ import { mkdir as mkdir8, readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
271173
271258
  import { basename as basename5, dirname as dirname8, resolve as resolve4 } from "path";
271174
271259
  import { homedir as homedir33 } from "os";
271175
271260
  function requestedAgents(agent) {
@@ -271312,7 +271397,7 @@ async function inspectCodexPluginBundle() {
271312
271397
  }
271313
271398
  let manifest = null;
271314
271399
  try {
271315
- manifest = asRecord3(JSON.parse(await readFile8(manifestPath, "utf-8")));
271400
+ manifest = asRecord3(JSON.parse(await readFile9(manifestPath, "utf-8")));
271316
271401
  } catch {
271317
271402
  return {
271318
271403
  scope: "global",
@@ -271341,7 +271426,7 @@ async function inspectCodexPluginBundle() {
271341
271426
  issues.push("codex-plugin-mcp-manifest-missing");
271342
271427
  } else {
271343
271428
  try {
271344
- const mcpConfig = asRecord3(JSON.parse(await readFile8(mcpPath, "utf-8")));
271429
+ const mcpConfig = asRecord3(JSON.parse(await readFile9(mcpPath, "utf-8")));
271345
271430
  const server = coerceMcpServer("memorix", asRecord3(mcpConfig?.mcpServers)?.memorix);
271346
271431
  if (!server || !isRecommendedStdioServer(server)) {
271347
271432
  issues.push("codex-plugin-mcp-manifest-invalid");
@@ -271355,7 +271440,7 @@ async function inspectCodexPluginBundle() {
271355
271440
  issues.push("codex-hook-manifest-missing");
271356
271441
  } else {
271357
271442
  try {
271358
- const hooksConfig = asRecord3(JSON.parse(await readFile8(hooksPath, "utf-8")));
271443
+ const hooksConfig = asRecord3(JSON.parse(await readFile9(hooksPath, "utf-8")));
271359
271444
  const hooks = asRecord3(hooksConfig?.hooks);
271360
271445
  declared = hooks ? Object.keys(hooks) : [];
271361
271446
  if (CODEX_HOOK_EVENTS.some((event) => !declared.includes(event))) {
@@ -271390,7 +271475,7 @@ async function inspectCodexMarketplace() {
271390
271475
  }
271391
271476
  let catalog = null;
271392
271477
  try {
271393
- catalog = asRecord3(JSON.parse(await readFile8(marketplacePath, "utf-8")));
271478
+ catalog = asRecord3(JSON.parse(await readFile9(marketplacePath, "utf-8")));
271394
271479
  } catch {
271395
271480
  return {
271396
271481
  scope: "global",
@@ -271480,7 +271565,7 @@ async function inspectCodexHookTrust() {
271480
271565
  }
271481
271566
  let config2 = "";
271482
271567
  try {
271483
- config2 = await readFile8(configPath, "utf-8");
271568
+ config2 = await readFile9(configPath, "utf-8");
271484
271569
  } catch {
271485
271570
  return {
271486
271571
  scope: "global",
@@ -271607,7 +271692,7 @@ async function inspectClaudeLocalMcp(projectRoot) {
271607
271692
  }
271608
271693
  let config2 = {};
271609
271694
  try {
271610
- config2 = JSON.parse(await readFile8(configPath, "utf-8"));
271695
+ config2 = JSON.parse(await readFile9(configPath, "utf-8"));
271611
271696
  } catch {
271612
271697
  return {
271613
271698
  scope: "local",
@@ -271656,7 +271741,7 @@ async function installClaudeLocalMcpConfig(projectRoot) {
271656
271741
  const configPath = getClaudeLocalConfigPath();
271657
271742
  let config2 = {};
271658
271743
  try {
271659
- config2 = JSON.parse(await readFile8(configPath, "utf-8"));
271744
+ config2 = JSON.parse(await readFile9(configPath, "utf-8"));
271660
271745
  } catch {
271661
271746
  config2 = {};
271662
271747
  }
@@ -271695,7 +271780,7 @@ async function inspectCodexPluginMcp() {
271695
271780
  };
271696
271781
  }
271697
271782
  try {
271698
- const config2 = asRecord3(JSON.parse(await readFile8(mcpPath, "utf-8")));
271783
+ const config2 = asRecord3(JSON.parse(await readFile9(mcpPath, "utf-8")));
271699
271784
  const server = coerceMcpServer("memorix", asRecord3(config2?.mcpServers)?.memorix);
271700
271785
  if (!server) {
271701
271786
  return {
@@ -271760,7 +271845,7 @@ async function inspectMcp(agent, projectRoot, scope) {
271760
271845
  });
271761
271846
  continue;
271762
271847
  }
271763
- const content = await readFile8(configPath, "utf-8");
271848
+ const content = await readFile9(configPath, "utf-8");
271764
271849
  if (agent === "claude" && targetScope === "global") {
271765
271850
  try {
271766
271851
  const parsed = JSON.parse(content);
@@ -271837,7 +271922,7 @@ async function inspectGuidance(agent, projectRoot, scope) {
271837
271922
  });
271838
271923
  continue;
271839
271924
  }
271840
- const content = await readFile8(rulesPath, "utf-8");
271925
+ const content = await readFile9(rulesPath, "utf-8");
271841
271926
  const issues = isCurrentGuidance(content) ? [] : ["guidance-outdated"];
271842
271927
  checks.push({
271843
271928
  scope: targetScope,