omnius 1.0.368 → 1.0.369

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.
package/dist/index.js CHANGED
@@ -9629,6 +9629,14 @@ var init_vision = __esm({
9629
9629
  type: "string",
9630
9630
  description: "Path to the image file (PNG, JPG, GIF, WebP, BMP, TIFF)"
9631
9631
  },
9632
+ path: {
9633
+ type: "string",
9634
+ description: "Alias for image path"
9635
+ },
9636
+ file: {
9637
+ type: "string",
9638
+ description: "Alias for image path"
9639
+ },
9632
9640
  action: {
9633
9641
  type: "string",
9634
9642
  enum: ["caption", "query", "detect", "point"],
@@ -9664,13 +9672,13 @@ var init_vision = __esm({
9664
9672
  }
9665
9673
  async execute(args) {
9666
9674
  const start2 = performance.now();
9667
- const rawPath = args["image"];
9675
+ const rawPath = args["image"] ?? args["path"] ?? args["file"];
9668
9676
  const action = args["action"] ?? "caption";
9669
9677
  const prompt = args["prompt"];
9670
9678
  const length4 = args["length"] ?? "normal";
9671
9679
  const preferredModel = normalizeVisionModelName(args["model"]);
9672
9680
  if (!rawPath) {
9673
- return { success: false, output: "", error: "image path is required", durationMs: 0 };
9681
+ return { success: false, output: "", error: "image path is required (accepted keys: image, path, file)", durationMs: 0 };
9674
9682
  }
9675
9683
  if ((action === "query" || action === "detect" || action === "point") && !prompt) {
9676
9684
  return {
@@ -35488,11 +35496,13 @@ var init_image = __esm({
35488
35496
  ImageReadTool = class {
35489
35497
  workingDir;
35490
35498
  name = "image_read";
35491
- description = "Read an image file and return its base64 encoding, dimensions, and OCR text extraction. The base64 data can be used for multimodal model input. Supports: PNG, JPG, GIF, WebP, BMP, TIFF, SVG.";
35499
+ description = "Read an image file and return its base64 encoding, dimensions, and OCR text extraction. The base64 data can be used for multimodal model input. For semantic visual understanding, object/person identification, or captions, prefer vision first (Moondream) and use image_read for metadata/OCR/raw image ingress. Supports: PNG, JPG, GIF, WebP, BMP, TIFF, SVG.";
35492
35500
  parameters = {
35493
35501
  type: "object",
35494
35502
  properties: {
35495
35503
  path: { type: "string", description: "Path to the image file" },
35504
+ file: { type: "string", description: "Alias for path" },
35505
+ image: { type: "string", description: "Alias for path" },
35496
35506
  ocr: { type: "boolean", description: "Extract text via OCR (default: true if tesseract available)" },
35497
35507
  max_size_kb: { type: "number", description: "Maximum file size in KB to read (default: 10240 = 10MB)" }
35498
35508
  },
@@ -35503,11 +35513,11 @@ var init_image = __esm({
35503
35513
  }
35504
35514
  async execute(args) {
35505
35515
  const start2 = Date.now();
35506
- const rawPath = String(args["path"] ?? "");
35516
+ const rawPath = String(args["path"] ?? args["file"] ?? args["image"] ?? "");
35507
35517
  const doOcr = args["ocr"] !== false;
35508
35518
  const maxSizeKb = typeof args["max_size_kb"] === "number" ? args["max_size_kb"] : 10240;
35509
35519
  if (!rawPath) {
35510
- return { success: false, output: "", error: "path is required", durationMs: 0 };
35520
+ return { success: false, output: "", error: "path is required (accepted keys: path, file, image)", durationMs: 0 };
35511
35521
  }
35512
35522
  const fullPath = resolve19(this.workingDir, rawPath);
35513
35523
  if (!existsSync31(fullPath)) {
@@ -269267,8 +269277,8 @@ var require_pattern = __commonJS({
269267
269277
  }
269268
269278
  exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
269269
269279
  function isAffectDepthOfReadingPattern(pattern) {
269270
- const basename39 = path12.basename(pattern);
269271
- return endsWithSlashGlobStar(pattern) || isStaticPattern(basename39);
269280
+ const basename40 = path12.basename(pattern);
269281
+ return endsWithSlashGlobStar(pattern) || isStaticPattern(basename40);
269272
269282
  }
269273
269283
  exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
269274
269284
  function expandPatternsWithBraceExpansion(patterns) {
@@ -273144,7 +273154,7 @@ print("${sentinel}")
273144
273154
  if (!this.proc || this.proc.killed) {
273145
273155
  return { success: false, path: "" };
273146
273156
  }
273147
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
273157
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
273148
273158
  const sessionDir2 = join43(this.cwd, ".omnius", "rlm");
273149
273159
  mkdirSync106(sessionDir2, { recursive: true });
273150
273160
  const sessionPath2 = join43(sessionDir2, "session.json");
@@ -273170,7 +273180,7 @@ print("__SESSION__" + json.dumps(_session) + "__SESSION__")
273170
273180
  trajectoryCount: this.trajectory.length,
273171
273181
  subCallCount: this.subCallCount
273172
273182
  };
273173
- writeFileSync91(sessionPath2, JSON.stringify(sessionData, null, 2), "utf8");
273183
+ writeFileSync90(sessionPath2, JSON.stringify(sessionData, null, 2), "utf8");
273174
273184
  return { success: true, path: sessionPath2 };
273175
273185
  }
273176
273186
  } catch {
@@ -273789,7 +273799,7 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
273789
273799
  /** Update memory scores based on task outcome. Called after task completion.
273790
273800
  * Memories used in successful tasks get boosted. Memories present during failures get decayed. */
273791
273801
  updateFromOutcomeSync(surfacedMemoryText, succeeded) {
273792
- const { readFileSync: readFileSync133, writeFileSync: writeFileSync91, existsSync: existsSync164, mkdirSync: mkdirSync106 } = __require("node:fs");
273802
+ const { readFileSync: readFileSync133, writeFileSync: writeFileSync90, existsSync: existsSync164, mkdirSync: mkdirSync106 } = __require("node:fs");
273793
273803
  const metaDir = join44(this.cwd, ".omnius", "memory", "metabolism");
273794
273804
  const storeFile = join44(metaDir, "store.json");
273795
273805
  if (!existsSync164(storeFile))
@@ -273821,7 +273831,7 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
273821
273831
  }
273822
273832
  if (updated) {
273823
273833
  mkdirSync106(metaDir, { recursive: true });
273824
- writeFileSync91(storeFile, JSON.stringify(store2, null, 2));
273834
+ writeFileSync90(storeFile, JSON.stringify(store2, null, 2));
273825
273835
  }
273826
273836
  }
273827
273837
  // ── Storage ──────────────────────────────────────────────────────────
@@ -274267,7 +274277,7 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
274267
274277
  }
274268
274278
  /** Archive a strategy variant synchronously (for task completion path) */
274269
274279
  archiveVariantSync(strategy, outcome, tags = []) {
274270
- const { readFileSync: readFileSync133, writeFileSync: writeFileSync91, existsSync: existsSync164, mkdirSync: mkdirSync106 } = __require("node:fs");
274280
+ const { readFileSync: readFileSync133, writeFileSync: writeFileSync90, existsSync: existsSync164, mkdirSync: mkdirSync106 } = __require("node:fs");
274271
274281
  const dir = join46(this.cwd, ".omnius", "arche");
274272
274282
  const archiveFile = join46(dir, "variants.json");
274273
274283
  let variants = [];
@@ -274289,7 +274299,7 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
274289
274299
  if (variants.length > 50)
274290
274300
  variants = variants.slice(-50);
274291
274301
  mkdirSync106(dir, { recursive: true });
274292
- writeFileSync91(archiveFile, JSON.stringify(variants, null, 2));
274302
+ writeFileSync90(archiveFile, JSON.stringify(variants, null, 2));
274293
274303
  }
274294
274304
  async saveArchive(variants) {
274295
274305
  const dir = join46(this.cwd, ".omnius", "arche");
@@ -321828,9 +321838,9 @@ ${lanes.join("\n")}
321828
321838
  function isJsonEqual(a2, b) {
321829
321839
  return a2 === b || typeof a2 === "object" && a2 !== null && typeof b === "object" && b !== null && equalOwnProperties(a2, b, isJsonEqual);
321830
321840
  }
321831
- function parsePseudoBigInt(stringValue2) {
321841
+ function parsePseudoBigInt(stringValue3) {
321832
321842
  let log2Base;
321833
- switch (stringValue2.charCodeAt(1)) {
321843
+ switch (stringValue3.charCodeAt(1)) {
321834
321844
  // "x" in "0x123"
321835
321845
  case 98:
321836
321846
  case 66:
@@ -321845,19 +321855,19 @@ ${lanes.join("\n")}
321845
321855
  log2Base = 4;
321846
321856
  break;
321847
321857
  default:
321848
- const nIndex = stringValue2.length - 1;
321858
+ const nIndex = stringValue3.length - 1;
321849
321859
  let nonZeroStart = 0;
321850
- while (stringValue2.charCodeAt(nonZeroStart) === 48) {
321860
+ while (stringValue3.charCodeAt(nonZeroStart) === 48) {
321851
321861
  nonZeroStart++;
321852
321862
  }
321853
- return stringValue2.slice(nonZeroStart, nIndex) || "0";
321863
+ return stringValue3.slice(nonZeroStart, nIndex) || "0";
321854
321864
  }
321855
- const startIndex = 2, endIndex = stringValue2.length - 1;
321865
+ const startIndex = 2, endIndex = stringValue3.length - 1;
321856
321866
  const bitsNeeded = (endIndex - startIndex) * log2Base;
321857
321867
  const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0));
321858
321868
  for (let i2 = endIndex - 1, bitOffset = 0; i2 >= startIndex; i2--, bitOffset += log2Base) {
321859
321869
  const segment = bitOffset >>> 4;
321860
- const digitChar = stringValue2.charCodeAt(i2);
321870
+ const digitChar = stringValue3.charCodeAt(i2);
321861
321871
  const digit = digitChar <= 57 ? digitChar - 48 : 10 + digitChar - (digitChar <= 70 ? 65 : 97);
321862
321872
  const shiftedDigit = digit << (bitOffset & 15);
321863
321873
  segments[segment] |= shiftedDigit;
@@ -435385,9 +435395,9 @@ ${lanes.join("\n")}
435385
435395
  /*ignoreCase*/
435386
435396
  false
435387
435397
  )) {
435388
- const basename39 = getBaseFileName(a2.fileName);
435389
- if (basename39 === "lib.d.ts" || basename39 === "lib.es6.d.ts") return 0;
435390
- const name10 = removeSuffix(removePrefix(basename39, "lib."), ".d.ts");
435398
+ const basename40 = getBaseFileName(a2.fileName);
435399
+ if (basename40 === "lib.d.ts" || basename40 === "lib.es6.d.ts") return 0;
435400
+ const name10 = removeSuffix(removePrefix(basename40, "lib."), ".d.ts");
435391
435401
  const index = libs.indexOf(name10);
435392
435402
  if (index !== -1) return index + 1;
435393
435403
  }
@@ -499313,8 +499323,8 @@ ${options2.prefix}` : "\n" : options2.prefix
499313
499323
  }
499314
499324
  };
499315
499325
  for (const file of files) {
499316
- const basename39 = getBaseFileName(file);
499317
- if (basename39 === "package.json" || basename39 === "bower.json") {
499326
+ const basename40 = getBaseFileName(file);
499327
+ if (basename40 === "package.json" || basename40 === "bower.json") {
499318
499328
  createProjectWatcher(
499319
499329
  file,
499320
499330
  "FileWatcher"
@@ -502998,8 +503008,8 @@ All files are: ${JSON.stringify(names)}`,
502998
503008
  var _a;
502999
503009
  const fileOrDirectoryPath = removeIgnoredPath(this.toPath(fileOrDirectory));
503000
503010
  if (!fileOrDirectoryPath) return;
503001
- const basename39 = getBaseFileName(fileOrDirectoryPath);
503002
- if (((_a = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a.size) && (basename39 === "package.json" || basename39 === "node_modules")) {
503011
+ const basename40 = getBaseFileName(fileOrDirectoryPath);
503012
+ if (((_a = result.affectedModuleSpecifierCacheProjects) == null ? void 0 : _a.size) && (basename40 === "package.json" || basename40 === "node_modules")) {
503003
503013
  result.affectedModuleSpecifierCacheProjects.forEach((project) => {
503004
503014
  var _a2;
503005
503015
  (_a2 = project.getModuleSpecifierCache()) == null ? void 0 : _a2.clear();
@@ -511902,7 +511912,7 @@ var require_path_browserify = __commonJS({
511902
511912
  if (hasRoot && end === 1) return "//";
511903
511913
  return path12.slice(0, end);
511904
511914
  },
511905
- basename: function basename39(path12, ext) {
511915
+ basename: function basename40(path12, ext) {
511906
511916
  if (ext !== void 0 && typeof ext !== "string") throw new TypeError('"ext" argument must be a string');
511907
511917
  assertPath(path12);
511908
511918
  var start2 = 0;
@@ -546587,7 +546597,57 @@ function visualMemoryPythonEnv(extra = {}) {
546587
546597
  applyMediaCudaDeviceFilterToEnv(env2, "vision");
546588
546598
  return env2;
546589
546599
  }
546590
- var VMEM_DIR, VENV_DIR2, VENV_PY, VENV_PIP2, VisualMemoryTool;
546600
+ function stringArg2(args, ...keys) {
546601
+ for (const key of keys) {
546602
+ const value2 = args[key];
546603
+ if (typeof value2 === "string" && value2.trim())
546604
+ return value2.trim();
546605
+ }
546606
+ return "";
546607
+ }
546608
+ function stringListArg(value2) {
546609
+ if (Array.isArray(value2)) {
546610
+ return value2.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
546611
+ }
546612
+ if (typeof value2 === "string" && value2.trim())
546613
+ return [value2.trim()];
546614
+ return [];
546615
+ }
546616
+ function imagePathArg(args) {
546617
+ return stringArg2(args, "image", "path", "file", "media");
546618
+ }
546619
+ function associationLabelArg(args) {
546620
+ return stringArg2(args, "name", "label", "title", "object", "person") || stringListArg(args["labels"])[0] || stringListArg(args["aliases"])[0] || "";
546621
+ }
546622
+ function pyLiteral(value2) {
546623
+ return JSON.stringify(value2);
546624
+ }
546625
+ function normalizeVisualMemoryAction(args) {
546626
+ const raw = stringArg2(args, "action").toLowerCase();
546627
+ if (!raw) {
546628
+ if (imagePathArg(args) && associationLabelArg(args))
546629
+ return "teach";
546630
+ if (imagePathArg(args))
546631
+ return "recognize";
546632
+ return "";
546633
+ }
546634
+ if (raw === "learn" || raw === "remember" || raw === "associate")
546635
+ return "teach";
546636
+ if (raw === "score" || raw === "classify")
546637
+ return "describe";
546638
+ return raw;
546639
+ }
546640
+ function summarizeProcessFailure(stdout, stderr) {
546641
+ const normalize2 = (text2) => text2.replace(/\r/g, "\n").split("\n").map((line) => line.trim()).filter(Boolean);
546642
+ const lines = [
546643
+ ...normalize2(stderr).map((line) => `stderr: ${line}`),
546644
+ ...normalize2(stdout).map((line) => `stdout: ${line}`)
546645
+ ];
546646
+ if (lines.length === 0)
546647
+ return "Vision ML script failed";
546648
+ return lines.slice(-24).join("\n").slice(-1800);
546649
+ }
546650
+ var VMEM_DIR, VENV_DIR2, VENV_PY, VENV_PIP2, VISUAL_MEMORY_ACTIONS, VisualMemoryTool;
546591
546651
  var init_visual_memory = __esm({
546592
546652
  "packages/execution/dist/tools/visual-memory.js"() {
546593
546653
  "use strict";
@@ -546596,24 +546656,45 @@ var init_visual_memory = __esm({
546596
546656
  VENV_DIR2 = join80(homedir20(), ".omnius", "vision-ml-venv");
546597
546657
  VENV_PY = join80(VENV_DIR2, "bin", "python3");
546598
546658
  VENV_PIP2 = join80(VENV_DIR2, "bin", "pip");
546659
+ VISUAL_MEMORY_ACTIONS = /* @__PURE__ */ new Set(["detect", "enroll", "identify", "teach", "recognize", "describe", "list", "forget"]);
546599
546660
  VisualMemoryTool = class {
546600
546661
  name = "visual_memory";
546601
- description = "Persistent visual memory — recognize faces and objects across sessions. FACE actions: 'detect' faces in image, 'enroll' a person (name + photo), 'identify' known faces. OBJECT actions: 'teach' an object (label + photo), 'recognize' taught objects. MEMORY actions: 'list' all enrolled faces/objects, 'forget' a person/object. Use this to remember people, learn to recognize tools/items, and build persistent visual knowledge about the physical environment. Works with camera_capture output.";
546662
+ description = "Persistent visual memory — automatically associate names/labels with images and recognize familiar faces/objects across sessions. When a user, filename, manifest, or surrounding text provides a name or label for a new image, call teach/enroll immediately to store that association. If an image is unlabeled, call vision first for a Moondream caption, then recognize/identify against this memory; ask the user for a label only when the identity is still unknown. FACE actions: 'detect' faces in image, 'enroll' a person (name + photo), 'identify' known faces. OBJECT actions: 'teach' an object (label + photo), 'recognize' taught objects, 'describe' CLIP scores for candidate labels. MEMORY actions: 'list' all enrolled faces/objects, 'forget' a person/object. Use this to remember people, learn to recognize tools/items, and build persistent visual knowledge about the physical environment. Works with camera_capture output.";
546602
546663
  parameters = {
546603
546664
  type: "object",
546604
546665
  properties: {
546605
546666
  action: {
546606
546667
  type: "string",
546607
- enum: ["detect", "enroll", "identify", "teach", "recognize", "list", "forget"],
546608
- description: "Action: detect/enroll/identify faces, teach/recognize objects, list/forget memory"
546668
+ enum: ["detect", "enroll", "identify", "teach", "recognize", "describe", "list", "forget"],
546669
+ description: "Action: detect/enroll/identify faces, teach/recognize/describe objects, list/forget memory. If omitted with image+label/name, teach is inferred; with image only, recognize is inferred."
546609
546670
  },
546610
546671
  image: {
546611
546672
  type: "string",
546612
- description: "Path to image file (JPEG/PNG). Required for detect/enroll/identify/teach/recognize."
546673
+ description: "Path to image file (JPEG/PNG). Required for detect/enroll/identify/teach/recognize/describe."
546674
+ },
546675
+ path: {
546676
+ type: "string",
546677
+ description: "Alias for image path."
546678
+ },
546679
+ file: {
546680
+ type: "string",
546681
+ description: "Alias for image path."
546682
+ },
546683
+ media: {
546684
+ type: "string",
546685
+ description: "Alias for image path."
546613
546686
  },
546614
546687
  name: {
546615
546688
  type: "string",
546616
- description: "Person name (for 'enroll') or object label (for 'teach'). Required for enrollment."
546689
+ description: "Person name (for 'enroll') or object label (for 'teach')."
546690
+ },
546691
+ label: {
546692
+ type: "string",
546693
+ description: "Alias for name when teaching an object or enrolling a person from an associated label."
546694
+ },
546695
+ title: {
546696
+ type: "string",
546697
+ description: "Alias for name/label when a manifest or metadata title identifies the visual subject."
546617
546698
  },
546618
546699
  id: {
546619
546700
  type: "string",
@@ -546630,15 +546711,23 @@ var init_visual_memory = __esm({
546630
546711
  },
546631
546712
  labels: {
546632
546713
  type: "array",
546633
- description: "Text labels to score against image (for CLIP 'recognize' or 'describe').",
546714
+ description: "Candidate text labels to score against image for CLIP recognize/describe. For teach, labels[0] is accepted as the object label when name/label is omitted.",
546715
+ items: { type: "string" }
546716
+ },
546717
+ aliases: {
546718
+ type: "array",
546719
+ description: "Additional label aliases. aliases[0] is accepted as the primary label when name/label/labels are omitted.",
546634
546720
  items: { type: "string" }
546635
546721
  }
546636
546722
  },
546637
- required: ["action"]
546723
+ required: []
546638
546724
  };
546639
546725
  async execute(args) {
546640
- const action = args["action"];
546726
+ const action = normalizeVisualMemoryAction(args);
546641
546727
  const start2 = performance.now();
546728
+ if (!VISUAL_MEMORY_ACTIONS.has(action)) {
546729
+ return { success: false, output: "", error: `Unknown action: ${action || "(none)"}. Use teach with image+name/label to remember, recognize with image to recall, or vision first for unlabeled semantic captioning.`, durationMs: performance.now() - start2 };
546730
+ }
546642
546731
  if (!await this.ensureVenv()) {
546643
546732
  return { success: false, output: "", error: "Could not set up vision ML environment. Needs Python 3.10+.", durationMs: performance.now() - start2 };
546644
546733
  }
@@ -546656,6 +546745,11 @@ var init_visual_memory = __esm({
546656
546745
  return await this.teachObject(args, start2);
546657
546746
  case "recognize":
546658
546747
  return await this.recognizeObjects(args, start2);
546748
+ case "describe":
546749
+ return await this.recognizeObjects({
546750
+ ...args,
546751
+ threshold: typeof args["threshold"] === "number" ? args["threshold"] : -1
546752
+ }, start2);
546659
546753
  case "list":
546660
546754
  return await this.listMemory(start2);
546661
546755
  case "forget":
@@ -546671,7 +546765,7 @@ var init_visual_memory = __esm({
546671
546765
  // Face Detection
546672
546766
  // =========================================================================
546673
546767
  async detectFaces(args, start2) {
546674
- const image = args["image"];
546768
+ const image = imagePathArg(args);
546675
546769
  if (!image || !existsSync66(image)) {
546676
546770
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
546677
546771
  }
@@ -546684,7 +546778,7 @@ app = FaceAnalysis(name='buffalo_s', providers=['CUDAExecutionProvider', 'CPUExe
546684
546778
  app.prepare(ctx_id=0, det_size=(640, 640))
546685
546779
 
546686
546780
  import cv2
546687
- img = cv2.imread("${image}")
546781
+ img = cv2.imread(${pyLiteral(image)})
546688
546782
  if img is None:
546689
546783
  print(json.dumps({"success": False, "error": "Could not read image"}))
546690
546784
  sys.exit(0)
@@ -546716,8 +546810,8 @@ ${JSON.stringify(result.faces, null, 2)}`, durationMs: performance.now() - start
546716
546810
  // Face Enrollment
546717
546811
  // =========================================================================
546718
546812
  async enrollFace(args, start2) {
546719
- const image = args["image"];
546720
- const name10 = args["name"];
546813
+ const image = imagePathArg(args);
546814
+ const name10 = associationLabelArg(args);
546721
546815
  if (!image || !existsSync66(image))
546722
546816
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
546723
546817
  if (!name10)
@@ -546730,7 +546824,7 @@ app = FaceAnalysis(name='buffalo_s', providers=['CUDAExecutionProvider', 'CPUExe
546730
546824
  app.prepare(ctx_id=0, det_size=(640, 640))
546731
546825
 
546732
546826
  import cv2
546733
- img = cv2.imread("${image}")
546827
+ img = cv2.imread(${pyLiteral(image)})
546734
546828
  if img is None:
546735
546829
  print(json.dumps({"success": False, "error": "Could not read image"}))
546736
546830
  sys.exit(0)
@@ -546751,9 +546845,9 @@ if os.path.exists(db_path):
546751
546845
  with open(db_path) as f:
546752
546846
  db = json.load(f)
546753
546847
 
546754
- person_id = "${name10}".lower().replace(" ", "_").replace("'", "")
546848
+ person_id = ${pyLiteral(name10)}.lower().replace(" ", "_").replace("'", "")
546755
546849
  if person_id not in db:
546756
- db[person_id] = {"name": "${name10}", "embeddings": [], "created_at": time.time()}
546850
+ db[person_id] = {"name": ${pyLiteral(name10)}, "embeddings": [], "created_at": time.time()}
546757
546851
 
546758
546852
  db[person_id]["embeddings"].append(embedding)
546759
546853
  db[person_id]["updated_at"] = time.time()
@@ -546765,7 +546859,7 @@ with open(db_path, "w") as f:
546765
546859
  print(json.dumps({
546766
546860
  "success": True,
546767
546861
  "person_id": person_id,
546768
- "name": "${name10}",
546862
+ "name": ${pyLiteral(name10)},
546769
546863
  "samples": sample_count,
546770
546864
  "total_people": len(db),
546771
546865
  "confidence": float(face.det_score),
@@ -546786,7 +546880,7 @@ print(json.dumps({
546786
546880
  // Face Identification
546787
546881
  // =========================================================================
546788
546882
  async identifyFaces(args, start2) {
546789
- const image = args["image"];
546883
+ const image = imagePathArg(args);
546790
546884
  if (!image || !existsSync66(image))
546791
546885
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
546792
546886
  const threshold = args["threshold"] || 0.5;
@@ -546799,7 +546893,7 @@ app = FaceAnalysis(name='buffalo_s', providers=['CUDAExecutionProvider', 'CPUExe
546799
546893
  app.prepare(ctx_id=0, det_size=(640, 640))
546800
546894
 
546801
546895
  import cv2
546802
- img = cv2.imread("${image}")
546896
+ img = cv2.imread(${pyLiteral(image)})
546803
546897
  if img is None:
546804
546898
  print(json.dumps({"success": False, "error": "Could not read image"}))
546805
546899
  sys.exit(0)
@@ -546883,23 +546977,34 @@ ${lines.join("\n")}`,
546883
546977
  // Object Teaching (CLIP/SigLIP)
546884
546978
  // =========================================================================
546885
546979
  async teachObject(args, start2) {
546886
- const image = args["image"];
546887
- const label = args["name"];
546980
+ const image = imagePathArg(args);
546981
+ const label = associationLabelArg(args);
546888
546982
  if (!image || !existsSync66(image))
546889
546983
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
546890
546984
  if (!label)
546891
546985
  return { success: false, output: "", error: "Provide a label for the object.", durationMs: performance.now() - start2 };
546986
+ const aliases = [...new Set([
546987
+ label,
546988
+ ...stringListArg(args["labels"]),
546989
+ ...stringListArg(args["aliases"])
546990
+ ].map((item) => item.trim()).filter(Boolean))];
546892
546991
  const result = await this.runVisionPython(`
546893
- import json, sys, os, time
546992
+ import json, sys, os, time, hashlib
546894
546993
  import torch
546895
546994
  from PIL import Image
546896
546995
  from transformers import CLIPProcessor, CLIPModel
546897
546996
 
546997
+ image_path = ${pyLiteral(image)}
546998
+ label = ${pyLiteral(label)}
546999
+ aliases = ${JSON.stringify(aliases)}
547000
+ with open(image_path, "rb") as f:
547001
+ image_hash = hashlib.sha256(f.read()).hexdigest()
547002
+
546898
547003
  # Use CLIP ViT-B/32 — good balance of speed and quality
546899
547004
  model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
546900
547005
  processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
546901
547006
 
546902
- img = Image.open("${image}").convert("RGB")
547007
+ img = Image.open(image_path).convert("RGB")
546903
547008
  inputs = processor(images=img, return_tensors="pt")
546904
547009
  with torch.no_grad():
546905
547010
  image_features = model.get_image_features(**inputs)
@@ -546907,13 +547012,16 @@ with torch.no_grad():
546907
547012
 
546908
547013
  embedding = image_features[0].cpu().numpy().tolist()
546909
547014
 
546910
- # Also embed the text label for cross-modal retrieval
546911
- text_inputs = processor(text=["${label}"], return_tensors="pt", padding=True)
547015
+ # Also embed the text label and aliases for cross-modal retrieval
547016
+ text_inputs = processor(text=aliases, return_tensors="pt", padding=True)
546912
547017
  with torch.no_grad():
546913
547018
  text_features = model.get_text_features(**text_inputs)
546914
547019
  text_features = text_features / text_features.norm(dim=-1, keepdim=True)
546915
547020
 
546916
- text_embedding = text_features[0].cpu().numpy().tolist()
547021
+ text_embeddings = {}
547022
+ for i, alias in enumerate(aliases):
547023
+ text_embeddings[alias] = text_features[i].cpu().numpy().tolist()
547024
+ text_embedding = text_embeddings[aliases[0]]
546917
547025
 
546918
547026
  # Store in object database
546919
547027
  db_path = "${VMEM_DIR}/objects.json"
@@ -546922,12 +547030,19 @@ if os.path.exists(db_path):
546922
547030
  with open(db_path) as f:
546923
547031
  db = json.load(f)
546924
547032
 
546925
- obj_id = "${label}".lower().replace(" ", "_").replace("'", "")
547033
+ obj_id = label.lower().replace(" ", "_").replace("'", "")
546926
547034
  if obj_id not in db:
546927
- db[obj_id] = {"label": "${label}", "image_embeddings": [], "text_embedding": text_embedding, "created_at": time.time()}
547035
+ db[obj_id] = {"label": label, "aliases": [], "image_embeddings": [], "image_hashes": [], "text_embeddings": {}, "text_embedding": text_embedding, "created_at": time.time()}
546928
547036
 
546929
- db[obj_id]["image_embeddings"].append(embedding)
547037
+ existing_hashes = set(db[obj_id].get("image_hashes", []))
547038
+ duplicate_sample = image_hash in existing_hashes
547039
+ if not duplicate_sample:
547040
+ db[obj_id].setdefault("image_embeddings", []).append(embedding)
547041
+ db[obj_id].setdefault("image_hashes", []).append(image_hash)
546930
547042
  db[obj_id]["text_embedding"] = text_embedding # update with latest
547043
+ db[obj_id].setdefault("text_embeddings", {}).update(text_embeddings)
547044
+ merged_aliases = list(dict.fromkeys([db[obj_id].get("label", label)] + db[obj_id].get("aliases", []) + aliases))
547045
+ db[obj_id]["aliases"] = merged_aliases
546931
547046
  db[obj_id]["updated_at"] = time.time()
546932
547047
  sample_count = len(db[obj_id]["image_embeddings"])
546933
547048
 
@@ -546937,10 +547052,12 @@ with open(db_path, "w") as f:
546937
547052
  print(json.dumps({
546938
547053
  "success": True,
546939
547054
  "object_id": obj_id,
546940
- "label": "${label}",
547055
+ "label": label,
547056
+ "aliases": merged_aliases,
546941
547057
  "samples": sample_count,
546942
547058
  "total_objects": len(db),
546943
547059
  "embedding_dim": len(embedding),
547060
+ "duplicate_sample": duplicate_sample,
546944
547061
  }))
546945
547062
  `, 12e4);
546946
547063
  if (!result.success)
@@ -546949,7 +547066,9 @@ print(json.dumps({
546949
547066
  success: true,
546950
547067
  output: `Taught object "${result.label}" (ID: ${result.object_id})
546951
547068
  Samples: ${result.samples}
546952
- Embedding: ${result.embedding_dim}d CLIP vector
547069
+ Aliases: ${(result.aliases || []).join(", ") || result.label}
547070
+ ` + (result.duplicate_sample ? ` Duplicate sample skipped for same image hash
547071
+ ` : "") + ` Embedding: ${result.embedding_dim}d CLIP vector
546953
547072
  Total objects in memory: ${result.total_objects}`,
546954
547073
  durationMs: performance.now() - start2
546955
547074
  };
@@ -546958,11 +547077,11 @@ print(json.dumps({
546958
547077
  // Object Recognition
546959
547078
  // =========================================================================
546960
547079
  async recognizeObjects(args, start2) {
546961
- const image = args["image"];
547080
+ const image = imagePathArg(args);
546962
547081
  if (!image || !existsSync66(image))
546963
547082
  return { success: false, output: "", error: "Provide a valid image path.", durationMs: performance.now() - start2 };
546964
- const threshold = args["threshold"] || 0.3;
546965
- const extraLabels = args["labels"] || [];
547083
+ const threshold = typeof args["threshold"] === "number" ? args["threshold"] : 0.3;
547084
+ const extraLabels = stringListArg(args["labels"]);
546966
547085
  const wantsJson = args["format"] === "json" || args["json"] === true;
546967
547086
  const result = await this.runVisionPython(`
546968
547087
  import json, sys, os, numpy as np
@@ -546973,7 +547092,7 @@ from transformers import CLIPProcessor, CLIPModel
546973
547092
  model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
546974
547093
  processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
546975
547094
 
546976
- img = Image.open("${image}").convert("RGB")
547095
+ img = Image.open(${pyLiteral(image)}).convert("RGB")
546977
547096
 
546978
547097
  # Get image embedding
546979
547098
  inputs = processor(images=img, return_tensors="pt")
@@ -546994,13 +547113,30 @@ if os.path.exists(db_path):
546994
547113
  matches = []
546995
547114
  for obj_id, data in db.items():
546996
547115
  best_sim = -1
546997
- for emb in data["image_embeddings"]:
547116
+ for emb in data.get("image_embeddings", []):
546998
547117
  sim = float(np.dot(query, np.array(emb, dtype=np.float32)))
546999
547118
  if sim > best_sim:
547000
547119
  best_sim = sim
547001
547120
 
547002
- # Also score against text label embedding
547003
- text_sim = float(np.dot(query, np.array(data["text_embedding"], dtype=np.float32)))
547121
+ # Also score against all stored text label/alias embeddings
547122
+ text_sim = -1
547123
+ best_text_label = data.get("label", obj_id)
547124
+ text_embeddings = data.get("text_embeddings")
547125
+ if isinstance(text_embeddings, dict) and text_embeddings:
547126
+ iterable = text_embeddings.items()
547127
+ elif data.get("text_embedding") is not None:
547128
+ iterable = [(data.get("label", obj_id), data["text_embedding"])]
547129
+ else:
547130
+ iterable = []
547131
+ for alias, emb in iterable:
547132
+ sim = float(np.dot(query, np.array(emb, dtype=np.float32)))
547133
+ if sim > text_sim:
547134
+ text_sim = sim
547135
+ best_text_label = alias
547136
+ if text_sim < 0:
547137
+ text_sim = 0
547138
+ if best_sim < 0:
547139
+ best_sim = 0
547004
547140
 
547005
547141
  # Blend: 60% image similarity, 40% text similarity
547006
547142
  blended = 0.6 * best_sim + 0.4 * text_sim
@@ -547008,6 +547144,8 @@ for obj_id, data in db.items():
547008
547144
  matches.append({
547009
547145
  "object_id": obj_id,
547010
547146
  "label": data["label"],
547147
+ "matched_alias": best_text_label,
547148
+ "aliases": data.get("aliases", []),
547011
547149
  "image_similarity": round(best_sim, 3),
547012
547150
  "text_similarity": round(text_sim, 3),
547013
547151
  "blended_score": round(blended, 3),
@@ -547089,7 +547227,7 @@ ${objects.join("\n") || " (none taught)"}`,
547089
547227
  };
547090
547228
  }
547091
547229
  async forgetEntry(args, start2) {
547092
- const id = (args["id"] || args["name"] || "").toLowerCase().replace(/\s/g, "_");
547230
+ const id = stringArg2(args, "id", "name", "label").toLowerCase().replace(/\s/g, "_");
547093
547231
  if (!id)
547094
547232
  return { success: false, output: "", error: "Provide the ID or name of the person/object to forget.", durationMs: performance.now() - start2 };
547095
547233
  let removed = false;
@@ -547165,7 +547303,7 @@ ${objects.join("\n") || " (none taught)"}`,
547165
547303
  }
547166
547304
  }
547167
547305
  }
547168
- throw new Error(stderr.slice(0, 300) || stdout.slice(0, 300) || "Vision ML script failed");
547306
+ throw new Error(summarizeProcessFailure(stdout, stderr));
547169
547307
  }
547170
547308
  }
547171
547309
  };
@@ -558355,6 +558493,15 @@ function nowIso2(now2 = /* @__PURE__ */ new Date()) {
558355
558493
  function cleanText(value2, max = 600) {
558356
558494
  return String(value2 ?? "").replace(/\s+/g, " ").trim().slice(0, max);
558357
558495
  }
558496
+ function normalizeEvidencePath(value2) {
558497
+ return String(value2 ?? "").trim().replace(/^file:\/\//i, "").replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\.\//, "");
558498
+ }
558499
+ function cleanTargetPaths(paths) {
558500
+ if (!Array.isArray(paths))
558501
+ return [];
558502
+ const cleaned = paths.map(normalizeEvidencePath).filter((path12) => path12.length > 0).slice(0, 20);
558503
+ return [...new Set(cleaned)];
558504
+ }
558358
558505
  function convertPipesToUnicode(text2) {
558359
558506
  return text2.replace(/\|/g, "│");
558360
558507
  }
@@ -558430,6 +558577,7 @@ function deriveClaimsFromProposedText(input) {
558430
558577
  });
558431
558578
  }
558432
558579
  function recordCompletionEvidence(ledger, evidence) {
558580
+ const targetPaths = cleanTargetPaths(evidence.targetPaths);
558433
558581
  const entry = {
558434
558582
  id: evidence.id ?? nextId2("ev", ledger.evidence.length),
558435
558583
  kind: evidence.kind,
@@ -558437,7 +558585,8 @@ function recordCompletionEvidence(ledger, evidence) {
558437
558585
  summary: cleanText(evidence.summary, 1200),
558438
558586
  toolName: evidence.toolName,
558439
558587
  success: evidence.success,
558440
- rawRef: evidence.rawRef
558588
+ rawRef: evidence.rawRef,
558589
+ ...targetPaths.length > 0 ? { targetPaths } : {}
558441
558590
  };
558442
558591
  return {
558443
558592
  ...ledger,
@@ -558457,7 +558606,8 @@ function recordToolEvidence(ledger, input) {
558457
558606
  toolName: input.name,
558458
558607
  success: input.success,
558459
558608
  summary,
558460
- rawRef: input.rawRef
558609
+ rawRef: input.rawRef,
558610
+ targetPaths: input.targetPaths
558461
558611
  });
558462
558612
  }
558463
558613
  function addCompletionClaims(ledger, claims) {
@@ -558596,6 +558746,55 @@ function isMutationEvidence(entry) {
558596
558746
  return true;
558597
558747
  return entry.success === true && /^(file_write|file_edit|file_patch|batch_edit)$/.test(entry.toolName ?? "");
558598
558748
  }
558749
+ function evidenceTargetPaths(entry) {
558750
+ const explicit = cleanTargetPaths(entry.targetPaths);
558751
+ if (explicit.length > 0)
558752
+ return explicit;
558753
+ const paths = [];
558754
+ if (entry.rawRef?.startsWith("file://")) {
558755
+ paths.push(entry.rawRef.slice("file://".length));
558756
+ }
558757
+ const fileChange = entry.summary.match(/\bfile change:\s*([^\s,;]+)/i);
558758
+ if (fileChange?.[1])
558759
+ paths.push(fileChange[1]);
558760
+ const quotedPathRe = /"(?:path|file_path|file|filename|filepath|filePath)"\s*:\s*"([^"]+)"/g;
558761
+ for (const match of entry.summary.matchAll(quotedPathRe)) {
558762
+ if (match[1])
558763
+ paths.push(match[1]);
558764
+ }
558765
+ const loosePathRe = /\b(?:path|file_path|file|filename|filepath|filePath)=([^,\s}\]]+)/g;
558766
+ for (const match of entry.summary.matchAll(loosePathRe)) {
558767
+ if (match[1])
558768
+ paths.push(match[1].replace(/^["']|["']$/g, ""));
558769
+ }
558770
+ return cleanTargetPaths(paths);
558771
+ }
558772
+ function pathsEquivalent(a2, b) {
558773
+ const left = normalizeEvidencePath(a2);
558774
+ const right = normalizeEvidencePath(b);
558775
+ if (!left || !right)
558776
+ return false;
558777
+ return left === right || left.endsWith(`/${right}`) || right.endsWith(`/${left}`);
558778
+ }
558779
+ function isVerificationNeutralPath(path12) {
558780
+ const normalized = normalizeEvidencePath(path12).toLowerCase();
558781
+ const base3 = normalized.split("/").pop() ?? normalized;
558782
+ if (/\.(md|mdx|markdown|txt|rst|adoc)$/.test(base3))
558783
+ return true;
558784
+ if (/^(readme|changelog|changes|license|licence|notice|implementation_notes)(?:\.[^.]+)?$/.test(base3))
558785
+ return true;
558786
+ if (normalized.includes("/docs/") || normalized.startsWith("docs/"))
558787
+ return true;
558788
+ return false;
558789
+ }
558790
+ function isVerificationInvalidatingMutation(entry) {
558791
+ if (!isMutationEvidence(entry))
558792
+ return false;
558793
+ const paths = evidenceTargetPaths(entry);
558794
+ if (paths.length === 0)
558795
+ return true;
558796
+ return paths.some((path12) => !isVerificationNeutralPath(path12));
558797
+ }
558599
558798
  function isSuccessfulVerificationEvidence(entry) {
558600
558799
  if (entry.success !== true)
558601
558800
  return false;
@@ -558627,14 +558826,72 @@ function isStaleEditEvidence(entry) {
558627
558826
  const text2 = entry.summary.toLowerCase();
558628
558827
  return /stale edit loop blocked|old[_ -]?string|old text|expected[_ -]?hash|context mismatch|no occurrences|could not find/.test(text2);
558629
558828
  }
558829
+ function staleEditResolvedByLaterMutation(evidence, failedEntry, failedIndex) {
558830
+ const failedPaths = evidenceTargetPaths(failedEntry);
558831
+ if (failedPaths.length === 0)
558832
+ return false;
558833
+ for (const later of evidence.slice(failedIndex + 1)) {
558834
+ if (!isMutationEvidence(later) || later.success !== true)
558835
+ continue;
558836
+ const laterPaths = evidenceTargetPaths(later);
558837
+ if (laterPaths.some((laterPath) => failedPaths.some((failedPath) => pathsEquivalent(laterPath, failedPath)))) {
558838
+ return true;
558839
+ }
558840
+ }
558841
+ return false;
558842
+ }
558843
+ function extractClaimedAllTestsPassedCount(text2) {
558844
+ const match = text2.match(/\ball\s+(\d{1,6})\s+tests?\s+(?:pass|passed|passing)\b/i);
558845
+ return match?.[1] ? Number(match[1]) : null;
558846
+ }
558847
+ function extractObservedSuccessfulTestCount(text2) {
558848
+ const normalized = text2.replace(/\s+/g, " ");
558849
+ const testsPassed = normalized.match(/\btests?\s+(\d{1,6})\s+passed\b/i);
558850
+ if (testsPassed?.[1])
558851
+ return Number(testsPassed[1]);
558852
+ const testsLine = normalized.match(/\btests?\s*:?\s*(\d{1,6})\b/i);
558853
+ const passLine = normalized.match(/\bpass(?:ed)?\s*:?\s*(\d{1,6})\b/i);
558854
+ if (testsLine?.[1])
558855
+ return Number(testsLine[1]);
558856
+ if (passLine?.[1])
558857
+ return Number(passLine[1]);
558858
+ const passedTests = normalized.match(/\b(\d{1,6})\s+tests?\s+passed\b/i);
558859
+ if (passedTests?.[1])
558860
+ return Number(passedTests[1]);
558861
+ return null;
558862
+ }
558863
+ function latestObservedSuccessfulTestCount(evidence) {
558864
+ for (let index = evidence.length - 1; index >= 0; index--) {
558865
+ const entry = evidence[index];
558866
+ if (entry.success !== true || verificationFamily(entry) !== "test")
558867
+ continue;
558868
+ const count = extractObservedSuccessfulTestCount(entry.summary);
558869
+ if (count !== null && Number.isFinite(count))
558870
+ return count;
558871
+ }
558872
+ return null;
558873
+ }
558874
+ function appendTestCountOverclaims(ledger, unresolved) {
558875
+ const observed = latestObservedSuccessfulTestCount(ledger.evidence);
558876
+ if (observed === null)
558877
+ return unresolved;
558878
+ let next = unresolved;
558879
+ for (const claim of ledger.proposedClaims) {
558880
+ const claimed = extractClaimedAllTestsPassedCount(claim.text);
558881
+ if (claimed === null || claimed === observed)
558882
+ continue;
558883
+ next = appendUnresolved(next, `Completion claim overstates verification: claimed all ${claimed} tests pass, but latest successful test evidence reported ${observed}.`, claim.id);
558884
+ }
558885
+ return next;
558886
+ }
558630
558887
  function finalizeCompletionLedgerTruth(ledger) {
558631
558888
  let unresolved = [...ledger.unresolved];
558632
- let lastMutation = -1;
558889
+ let lastVerificationInvalidatingMutation = -1;
558633
558890
  let lastVerification = -1;
558634
558891
  const lastSuccessfulVerificationByFamily = /* @__PURE__ */ new Map();
558635
558892
  ledger.evidence.forEach((entry, index) => {
558636
- if (isMutationEvidence(entry))
558637
- lastMutation = index;
558893
+ if (isVerificationInvalidatingMutation(entry))
558894
+ lastVerificationInvalidatingMutation = index;
558638
558895
  if (isSuccessfulVerificationEvidence(entry)) {
558639
558896
  lastVerification = index;
558640
558897
  const family = verificationFamily(entry);
@@ -558650,13 +558907,14 @@ function finalizeCompletionLedgerTruth(ledger) {
558650
558907
  unresolved = appendUnresolved(unresolved, `Verification failed or did not prove success: ${entry.summary}`, entry.id);
558651
558908
  }
558652
558909
  }
558653
- if (isStaleEditEvidence(entry)) {
558910
+ if (isStaleEditEvidence(entry) && !staleEditResolvedByLaterMutation(ledger.evidence, entry, index)) {
558654
558911
  unresolved = appendUnresolved(unresolved, `Stale edit failure remains unresolved: ${entry.summary}`, entry.id);
558655
558912
  }
558656
558913
  });
558657
- if (lastVerification >= 0 && lastMutation > lastVerification) {
558914
+ if (lastVerification >= 0 && lastVerificationInvalidatingMutation > lastVerification) {
558658
558915
  unresolved = appendUnresolved(unresolved, "File changes occurred after the last successful verification; final verification is stale.", "verification_freshness");
558659
558916
  }
558917
+ unresolved = appendTestCountOverclaims(ledger, unresolved);
558660
558918
  const status = ledger.status === "blocked" || ledger.status === "request_changes" ? ledger.status : unresolved.length > 0 ? "incomplete_verification" : ledger.status;
558661
558919
  return {
558662
558920
  ...ledger,
@@ -564299,12 +564557,12 @@ var init_reflectionBuffer = __esm({
564299
564557
  if (!this.persistPath)
564300
564558
  return;
564301
564559
  try {
564302
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
564560
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
564303
564561
  const { join: join179 } = __require("node:path");
564304
564562
  const dir = join179(this.persistPath, "..");
564305
564563
  if (!existsSync164(dir))
564306
564564
  mkdirSync106(dir, { recursive: true });
564307
- writeFileSync91(this.persistPath, JSON.stringify(this.state, null, 2));
564565
+ writeFileSync90(this.persistPath, JSON.stringify(this.state, null, 2));
564308
564566
  } catch {
564309
564567
  }
564310
564568
  }
@@ -567915,12 +568173,12 @@ var init_adversaryStream = __esm({
567915
568173
  if (!this.persistPath)
567916
568174
  return;
567917
568175
  try {
567918
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
568176
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
567919
568177
  const { dirname: dirname54 } = __require("node:path");
567920
568178
  const dir = dirname54(this.persistPath);
567921
568179
  if (!existsSync164(dir))
567922
568180
  mkdirSync106(dir, { recursive: true });
567923
- writeFileSync91(this.persistPath, JSON.stringify({ ledger: this.ledger }, null, 2));
568181
+ writeFileSync90(this.persistPath, JSON.stringify({ ledger: this.ledger }, null, 2));
567924
568182
  } catch {
567925
568183
  }
567926
568184
  }
@@ -571961,12 +572219,14 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
571961
572219
  if (!this._completionLedger || input.toolName === "task_complete")
571962
572220
  return;
571963
572221
  const realFileMutation = input.realFileMutation ?? this._isRealProjectMutation(input.toolName, input.result);
571964
- const realMutationPaths = input.realMutationPaths ?? (realFileMutation ? this._extractToolTargetPaths(input.toolName, input.args, input.result) : []);
572222
+ const attemptedTargetPaths = this._extractToolTargetPaths(input.toolName, input.args, input.result);
572223
+ const realMutationPaths = input.realMutationPaths ?? (realFileMutation ? attemptedTargetPaths : []);
571965
572224
  this._completionLedger = recordToolEvidence(this._completionLedger, {
571966
572225
  name: input.toolName,
571967
572226
  success: input.result.success,
571968
572227
  outputPreview: (input.outputPreview ?? this._toolEvidencePreview(input.result)).toString().slice(0, 500),
571969
- argsKey: input.argsKey.slice(0, 300)
572228
+ argsKey: input.argsKey.slice(0, 300),
572229
+ targetPaths: attemptedTargetPaths
571970
572230
  });
571971
572231
  if (realFileMutation && realMutationPaths.length > 0) {
571972
572232
  for (const filePath of realMutationPaths) {
@@ -571975,7 +572235,8 @@ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
571975
572235
  toolName: input.toolName,
571976
572236
  success: true,
571977
572237
  summary: `file change: ${filePath}`,
571978
- rawRef: `file://${filePath}`
572238
+ rawRef: `file://${filePath}`,
572239
+ targetPaths: [filePath]
571979
572240
  });
571980
572241
  }
571981
572242
  }
@@ -575393,7 +575654,8 @@ Rewrite it now for ${ctx3.model}.`;
575393
575654
  run_in_background: ["background", "background_run", "async"],
575394
575655
  max_turns: ["maxTurns", "turns"],
575395
575656
  timeout_ms: ["timeoutMs", "timeout"],
575396
- path: ["file", "filepath", "file_path", "filename"],
575657
+ path: ["file", "image", "media", "filepath", "file_path", "filename"],
575658
+ image: ["path", "file", "media", "filepath", "file_path", "filename"],
575397
575659
  command: ["cmd", "shell_command"],
575398
575660
  query: ["prompt", "task", "message", "input", "text"]
575399
575661
  };
@@ -582419,7 +582681,7 @@ ${caveat}` : caveat;
582419
582681
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
582420
582682
  });
582421
582683
  try {
582422
- const { mkdirSync: mkdirSync106, readdirSync: readdirSync59, statSync: statSync59, unlinkSync: unlinkSync36, writeFileSync: writeFileSync91 } = __require("node:fs");
582684
+ const { mkdirSync: mkdirSync106, readdirSync: readdirSync59, statSync: statSync59, unlinkSync: unlinkSync36, writeFileSync: writeFileSync90 } = __require("node:fs");
582423
582685
  const { join: join179 } = __require("node:path");
582424
582686
  const contextDir = join179(this._workingDirectory || process.cwd(), ".omnius", "context");
582425
582687
  mkdirSync106(contextDir, { recursive: true });
@@ -582466,14 +582728,14 @@ ${caveat}` : caveat;
582466
582728
  mkdirSync106(kgSummaryDir, { recursive: true });
582467
582729
  const summaryFilename = `kg-summary-${this._sessionId}.md`;
582468
582730
  const outPath = join179(kgSummaryDir, summaryFilename);
582469
- writeFileSync91(outPath, lines.join("\n"), "utf-8");
582470
- writeFileSync91(join179(kgSummaryDir, "latest.md"), lines.join("\n"), "utf-8");
582471
- writeFileSync91(join179(contextDir, `kg-summary-latest.md`), [
582731
+ writeFileSync90(outPath, lines.join("\n"), "utf-8");
582732
+ writeFileSync90(join179(kgSummaryDir, "latest.md"), lines.join("\n"), "utf-8");
582733
+ writeFileSync90(join179(contextDir, `kg-summary-latest.md`), [
582472
582734
  "Latest KG summary moved to `.omnius/context/kg-summary/latest.md`.",
582473
582735
  "",
582474
582736
  lines.join("\n")
582475
582737
  ].join("\n"), "utf-8");
582476
- writeFileSync91(join179(kgSummaryDir, "index.json"), JSON.stringify({
582738
+ writeFileSync90(join179(kgSummaryDir, "index.json"), JSON.stringify({
582477
582739
  schema: "omnius.kg-summary-index.v1",
582478
582740
  latest: "latest.md",
582479
582741
  latestSessionFile: summaryFilename,
@@ -583310,7 +583572,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
583310
583572
  if (!this._workingDirectory)
583311
583573
  return;
583312
583574
  try {
583313
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = __require("node:fs");
583575
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = __require("node:fs");
583314
583576
  const { join: join179 } = __require("node:path");
583315
583577
  const sessionDir2 = this.options.stateDir ? join179(this.omniusStateDir(), "session", this._sessionId) : join179(this._workingDirectory, ".omnius", "session", this._sessionId);
583316
583578
  mkdirSync106(sessionDir2, { recursive: true });
@@ -583325,7 +583587,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
583325
583587
  memexEntryCount: this._memexArchive.size,
583326
583588
  fileRegistrySize: this._fileRegistry.size
583327
583589
  };
583328
- writeFileSync91(join179(sessionDir2, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
583590
+ writeFileSync90(join179(sessionDir2, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
583329
583591
  } catch {
583330
583592
  }
583331
583593
  }
@@ -583462,7 +583724,7 @@ ${tail}`;
583462
583724
  const analysis = await this.analyzeImageDataForContext(image.mime, image.base64, image.textWithoutImage.slice(0, 2e3));
583463
583725
  const imageNote = analysis.contextBlock ? `${analysis.contextBlock}
583464
583726
 
583465
- Use this image analysis. Do not repeat ${toolName} with the same arguments unless the scene has changed.` : `[Embedded image data omitted from model context; ${analysis.errorReason || "vision and OCR returned no text"}. Use any saved image path above with vision/image_read if further inspection is needed.]`;
583727
+ Use this image analysis. Do not repeat ${toolName} with the same arguments unless the scene has changed.` : `[Embedded image data omitted from model context; ${analysis.errorReason || "vision and OCR returned no text"}. If further inspection is needed, call vision first with the saved image path (Moondream caption/query), then use image_read only for metadata/OCR/raw image ingress.]`;
583466
583728
  return {
583467
583729
  ...result,
583468
583730
  llmContent: `${image.textWithoutImage.trim()}
@@ -583498,7 +583760,7 @@ ${imageNote}`.trim()
583498
583760
  const imageUrl = `data:${mime};base64,${base642}`;
583499
583761
  const tmpImgPath = this.writeTempImageForOcr(mime, base642);
583500
583762
  const [visionOutcome, ocrOutcome] = await Promise.allSettled([
583501
- this.describeImageViaVisionSubagent(imageUrl, textContent),
583763
+ tmpImgPath ? this.describeImageViaPrimaryVision(tmpImgPath, imageUrl, textContent) : this.describeImageViaVisionSubagent(imageUrl, textContent),
583502
583764
  tmpImgPath ? this.extractImageOcrText(tmpImgPath) : Promise.resolve("")
583503
583765
  ]);
583504
583766
  const visionDesc = visionOutcome.status === "fulfilled" ? visionOutcome.value.trim() : "";
@@ -583513,6 +583775,26 @@ ${imageNote}`.trim()
583513
583775
  const errorReason = visionOutcome.status === "rejected" ? String(visionOutcome.reason?.message ?? visionOutcome.reason) : void 0;
583514
583776
  return { contextBlock: "", errorReason };
583515
583777
  }
583778
+ async describeImageViaPrimaryVision(imagePath, imageUrl, textContent) {
583779
+ try {
583780
+ const vision = new VisionTool(this._workingDirectory || process.cwd());
583781
+ const prompt = textContent ? `User context: ${textContent}
583782
+
583783
+ Describe the image in detail. Identify people, objects, text, UI elements, diagrams, errors, and any task-relevant visual metadata.` : "Describe the image in detail. Identify people, objects, text, UI elements, diagrams, errors, and any task-relevant visual metadata.";
583784
+ const result = await vision.execute({
583785
+ image: imagePath,
583786
+ action: "query",
583787
+ prompt,
583788
+ length: "long"
583789
+ });
583790
+ const output = String(result.llmContent || result.output || "").trim();
583791
+ if (result.success && output.length > 20)
583792
+ return output;
583793
+ throw new Error(result.error || "vision tool returned no description");
583794
+ } catch {
583795
+ return await this.describeImageViaVisionSubagent(imageUrl, textContent);
583796
+ }
583797
+ }
583516
583798
  async describeImageViaVisionSubagent(imageUrl, textContent) {
583517
583799
  const visionMessages = [
583518
583800
  {
@@ -585985,12 +586267,12 @@ ${result}`
585985
586267
  let resizedBase64 = null;
585986
586268
  try {
585987
586269
  const { execSync: execSync63 } = await import("node:child_process");
585988
- const { writeFileSync: writeFileSync91, readFileSync: readFileSync133, unlinkSync: unlinkSync36 } = await import("node:fs");
586270
+ const { writeFileSync: writeFileSync90, readFileSync: readFileSync133, unlinkSync: unlinkSync36 } = await import("node:fs");
585989
586271
  const { join: join179 } = await import("node:path");
585990
586272
  const { tmpdir: tmpdir24 } = await import("node:os");
585991
586273
  const tmpIn = join179(tmpdir24(), `omnius_img_in_${Date.now()}.png`);
585992
586274
  const tmpOut = join179(tmpdir24(), `omnius_img_out_${Date.now()}.jpg`);
585993
- writeFileSync91(tmpIn, buffer2);
586275
+ writeFileSync90(tmpIn, buffer2);
585994
586276
  const pyBin = process.platform === "win32" ? "python" : "python3";
585995
586277
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
585996
586278
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
@@ -594971,10 +595253,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
594971
595253
  wordTimestamps: false
594972
595254
  });
594973
595255
  if (outputDir2) {
594974
- const { basename: basename39 } = await import("node:path");
595256
+ const { basename: basename40 } = await import("node:path");
594975
595257
  const transcriptDir = join120(outputDir2, ".omnius", "transcripts");
594976
595258
  mkdirSync63(transcriptDir, { recursive: true });
594977
- const outFile = join120(transcriptDir, `${basename39(filePath)}.txt`);
595259
+ const outFile = join120(transcriptDir, `${basename40(filePath)}.txt`);
594978
595260
  writeFileSync53(outFile, result.text, "utf-8");
594979
595261
  }
594980
595262
  return {
@@ -594990,10 +595272,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
594990
595272
  const fb = await transcribeFileViaWhisper(filePath, this.config.model);
594991
595273
  if (fb) {
594992
595274
  if (outputDir2) {
594993
- const { basename: basename39 } = await import("node:path");
595275
+ const { basename: basename40 } = await import("node:path");
594994
595276
  const transcriptDir = join120(outputDir2, ".omnius", "transcripts");
594995
595277
  mkdirSync63(transcriptDir, { recursive: true });
594996
- const outFile = join120(transcriptDir, `${basename39(filePath)}.txt`);
595278
+ const outFile = join120(transcriptDir, `${basename40(filePath)}.txt`);
594997
595279
  writeFileSync53(outFile, fb.text, "utf-8");
594998
595280
  }
594999
595281
  return fb;
@@ -635942,13 +636224,13 @@ async function handleSlashCommand(input, ctx3) {
635942
636224
  try {
635943
636225
  const { randomBytes: randomBytes30 } = await import("node:crypto");
635944
636226
  const { homedir: homedir62 } = await import("node:os");
635945
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
636227
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
635946
636228
  const { join: join179 } = await import("node:path");
635947
636229
  const newKey = randomBytes30(16).toString("hex");
635948
636230
  process.env["OMNIUS_API_KEY"] = newKey;
635949
636231
  const dir = join179(homedir62(), ".omnius");
635950
636232
  mkdirSync106(dir, { recursive: true });
635951
- writeFileSync91(join179(dir, "api.key"), newKey + "\n", "utf8");
636233
+ writeFileSync90(join179(dir, "api.key"), newKey + "\n", "utf8");
635952
636234
  renderInfo(`New API key: ${c3.bold(c3.yellow(newKey))}`);
635953
636235
  renderInfo(
635954
636236
  "Restart the daemon to apply if needed. Use /access any to restart quickly."
@@ -636219,11 +636501,11 @@ async function handleSlashCommand(input, ctx3) {
636219
636501
  );
636220
636502
  try {
636221
636503
  const { homedir: homedir63 } = await import("node:os");
636222
- const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync92 } = await import("node:fs");
636504
+ const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync91 } = await import("node:fs");
636223
636505
  const { join: join180 } = await import("node:path");
636224
636506
  const dir = join180(homedir63(), ".omnius");
636225
636507
  mkdirSync107(dir, { recursive: true });
636226
- writeFileSync92(join180(dir, "api.key"), apiKey + "\n", "utf8");
636508
+ writeFileSync91(join180(dir, "api.key"), apiKey + "\n", "utf8");
636227
636509
  } catch {
636228
636510
  }
636229
636511
  }
@@ -636235,11 +636517,11 @@ async function handleSlashCommand(input, ctx3) {
636235
636517
  const port2 = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
636236
636518
  try {
636237
636519
  const { homedir: homedir63 } = await import("node:os");
636238
- const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync92 } = await import("node:fs");
636520
+ const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync91 } = await import("node:fs");
636239
636521
  const { join: join180 } = await import("node:path");
636240
636522
  const dir = join180(homedir63(), ".omnius");
636241
636523
  mkdirSync107(dir, { recursive: true });
636242
- writeFileSync92(join180(dir, "access"), `${val2}
636524
+ writeFileSync91(join180(dir, "access"), `${val2}
636243
636525
  `, "utf8");
636244
636526
  } catch {
636245
636527
  }
@@ -636339,11 +636621,11 @@ async function handleSlashCommand(input, ctx3) {
636339
636621
  );
636340
636622
  try {
636341
636623
  const { homedir: homedir63 } = await import("node:os");
636342
- const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync92 } = await import("node:fs");
636624
+ const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync91 } = await import("node:fs");
636343
636625
  const { join: join180 } = await import("node:path");
636344
636626
  const dir = join180(homedir63(), ".omnius");
636345
636627
  mkdirSync107(dir, { recursive: true });
636346
- writeFileSync92(join180(dir, "api.key"), apiKey + "\n", "utf8");
636628
+ writeFileSync91(join180(dir, "api.key"), apiKey + "\n", "utf8");
636347
636629
  } catch {
636348
636630
  }
636349
636631
  }
@@ -636354,12 +636636,12 @@ async function handleSlashCommand(input, ctx3) {
636354
636636
  }
636355
636637
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
636356
636638
  const { homedir: homedir62 } = await import("node:os");
636357
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
636639
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
636358
636640
  const { join: join179 } = await import("node:path");
636359
636641
  try {
636360
636642
  const dir = join179(homedir62(), ".omnius");
636361
636643
  mkdirSync106(dir, { recursive: true });
636362
- writeFileSync91(join179(dir, "access"), `${val}
636644
+ writeFileSync90(join179(dir, "access"), `${val}
636363
636645
  `, "utf8");
636364
636646
  } catch (e2) {
636365
636647
  renderWarning(
@@ -645195,14 +645477,14 @@ async function handleVoiceMenu(ctx3, save3, hasLocal) {
645195
645477
  if (!jsonDrop.confirmed || !jsonDrop.path) {
645196
645478
  continue;
645197
645479
  }
645198
- const { basename: basename39, join: pathJoin } = await import("node:path");
645480
+ const { basename: basename40, join: pathJoin } = await import("node:path");
645199
645481
  const {
645200
645482
  copyFileSync: copyFileSync8,
645201
645483
  mkdirSync: mkdirSync106,
645202
645484
  existsSync: exists2
645203
645485
  } = await import("node:fs");
645204
645486
  const { homedir: homedir62 } = await import("node:os");
645205
- const modelName = basename39(onnxDrop.path, ".onnx").replace(
645487
+ const modelName = basename40(onnxDrop.path, ".onnx").replace(
645206
645488
  /[^a-zA-Z0-9_-]/g,
645207
645489
  "-"
645208
645490
  );
@@ -645722,7 +646004,7 @@ async function handleVoiceList(ctx3, focusFilename) {
645722
646004
  copyFileSync: cpf,
645723
646005
  mkdirSync: mkd
645724
646006
  } = __require("node:fs");
645725
- const { basename: basename39, join: pjoin } = __require("node:path");
646007
+ const { basename: basename40, join: pjoin } = __require("node:path");
645726
646008
  if (!fe(src2)) {
645727
646009
  renderError(`File not found: ${src2}`);
645728
646010
  helpers.render();
@@ -645735,7 +646017,7 @@ async function handleVoiceList(ctx3, focusFilename) {
645735
646017
  "clone-refs"
645736
646018
  );
645737
646019
  mkd(refsDir, { recursive: true });
645738
- const destName = basename39(src2);
646020
+ const destName = basename40(src2);
645739
646021
  const dest = pjoin(refsDir, destName);
645740
646022
  cpf(src2, dest);
645741
646023
  renderInfo(`Imported "${destName}" → ${dest}`);
@@ -646992,13 +647274,13 @@ async function handleSponsoredEndpoint(ctx3, local) {
646992
647274
  sponsors.push(...verified);
646993
647275
  if (verified.length > 0) {
646994
647276
  try {
646995
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = __require("node:fs");
647277
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = __require("node:fs");
646996
647278
  mkdirSync106(sponsorDir2, { recursive: true });
646997
647279
  const cached = verified.map((s2) => ({
646998
647280
  ...s2,
646999
647281
  lastVerified: Date.now()
647000
647282
  }));
647001
- writeFileSync91(knownFile, JSON.stringify(cached, null, 2));
647283
+ writeFileSync90(knownFile, JSON.stringify(cached, null, 2));
647002
647284
  } catch {
647003
647285
  }
647004
647286
  }
@@ -647202,7 +647484,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647202
647484
  }
647203
647485
  if (models.length > 0) {
647204
647486
  try {
647205
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106 } = await import("node:fs");
647487
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106 } = await import("node:fs");
647206
647488
  const { join: join179, dirname: dirname54 } = await import("node:path");
647207
647489
  const cachePath2 = join179(
647208
647490
  ctx3.repoRoot || process.cwd(),
@@ -647211,7 +647493,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647211
647493
  "peer-models-cache.json"
647212
647494
  );
647213
647495
  mkdirSync106(dirname54(cachePath2), { recursive: true });
647214
- writeFileSync91(
647496
+ writeFileSync90(
647215
647497
  cachePath2,
647216
647498
  JSON.stringify(
647217
647499
  {
@@ -647285,7 +647567,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647285
647567
  "Live model probe failed; using sponsor directory model advertisement."
647286
647568
  );
647287
647569
  try {
647288
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106 } = await import("node:fs");
647570
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106 } = await import("node:fs");
647289
647571
  const { join: join179, dirname: dirname54 } = await import("node:path");
647290
647572
  const cachePath2 = join179(
647291
647573
  ctx3.repoRoot || process.cwd(),
@@ -647294,7 +647576,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647294
647576
  "peer-models-cache.json"
647295
647577
  );
647296
647578
  mkdirSync106(dirname54(cachePath2), { recursive: true });
647297
- writeFileSync91(
647579
+ writeFileSync90(
647298
647580
  cachePath2,
647299
647581
  JSON.stringify(
647300
647582
  {
@@ -649992,13 +650274,13 @@ var init_commands = __esm({
649992
650274
  try {
649993
650275
  const { randomBytes: randomBytes30 } = await import("node:crypto");
649994
650276
  const { homedir: homedir62 } = await import("node:os");
649995
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
650277
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
649996
650278
  const { join: join179 } = await import("node:path");
649997
650279
  const apiKey = randomBytes30(16).toString("hex");
649998
650280
  process.env["OMNIUS_API_KEY"] = apiKey;
649999
650281
  const dir = join179(homedir62(), ".omnius");
650000
650282
  mkdirSync106(dir, { recursive: true });
650001
- writeFileSync91(join179(dir, "api.key"), apiKey + "\n", "utf8");
650283
+ writeFileSync90(join179(dir, "api.key"), apiKey + "\n", "utf8");
650002
650284
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
650003
650285
  renderInfo(
650004
650286
  "Use Authorization: Bearer <key> or click 'key' in the Web UI header to paste it."
@@ -650017,11 +650299,11 @@ var init_commands = __esm({
650017
650299
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
650018
650300
  try {
650019
650301
  const { homedir: homedir62 } = await import("node:os");
650020
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
650302
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
650021
650303
  const { join: join179 } = await import("node:path");
650022
650304
  const dir = join179(homedir62(), ".omnius");
650023
650305
  mkdirSync106(dir, { recursive: true });
650024
- writeFileSync91(join179(dir, "access"), `${val}
650306
+ writeFileSync90(join179(dir, "access"), `${val}
650025
650307
  `, "utf8");
650026
650308
  } catch {
650027
650309
  }
@@ -661171,6 +661453,175 @@ var init_soul_observations = __esm({
661171
661453
  }
661172
661454
  });
661173
661455
 
661456
+ // packages/cli/src/tui/visual-object-association.ts
661457
+ var visual_object_association_exports = {};
661458
+ __export(visual_object_association_exports, {
661459
+ associateVisualObjectFromImage: () => associateVisualObjectFromImage,
661460
+ extractExplicitVisualObjectLabels: () => extractExplicitVisualObjectLabels,
661461
+ formatVisualObjectMemoryContext: () => formatVisualObjectMemoryContext
661462
+ });
661463
+ import { basename as basename35 } from "node:path";
661464
+ function stringValue2(value2) {
661465
+ return typeof value2 === "string" ? value2.trim() : "";
661466
+ }
661467
+ function stringList(value2) {
661468
+ if (Array.isArray(value2)) {
661469
+ return value2.filter((item) => typeof item === "string" && item.trim().length > 0).map((item) => item.trim());
661470
+ }
661471
+ const single = stringValue2(value2);
661472
+ return single ? [single] : [];
661473
+ }
661474
+ function splitLabels(value2) {
661475
+ return value2.split(/[;,]/).map((part) => part.trim()).filter(Boolean).slice(0, 12);
661476
+ }
661477
+ function normalizeLabel(value2) {
661478
+ return value2.replace(/\s+/g, " ").replace(/^["'`]+|["'`]+$/g, "").trim();
661479
+ }
661480
+ function uniqueLabels(values) {
661481
+ const seen = /* @__PURE__ */ new Set();
661482
+ const out = [];
661483
+ for (const raw of values) {
661484
+ const label = normalizeLabel(raw);
661485
+ const key = label.toLowerCase();
661486
+ if (!label || seen.has(key)) continue;
661487
+ seen.add(key);
661488
+ out.push(label);
661489
+ }
661490
+ return out;
661491
+ }
661492
+ function explicitCaptionLabels(caption) {
661493
+ const text2 = stringValue2(caption);
661494
+ if (!text2) return [];
661495
+ const match = text2.match(/^\s*(?:object[_ -]?label|object|label|labels)\s*:\s*(.+)$/i);
661496
+ return match?.[1] ? splitLabels(match[1]) : [];
661497
+ }
661498
+ function identityNames(payload) {
661499
+ const names = /* @__PURE__ */ new Set();
661500
+ const add3 = (value2) => {
661501
+ const name10 = normalizeLabel(String(value2 ?? ""));
661502
+ if (name10) names.add(name10.toLowerCase());
661503
+ };
661504
+ for (const key of ["person", "person_name", "personName", "face_name", "faceName"]) {
661505
+ add3(payload[key]);
661506
+ }
661507
+ for (const person of stringList(payload["people"])) add3(person);
661508
+ const assertions = Array.isArray(payload["identityAssertions"]) ? payload["identityAssertions"] : Array.isArray(payload["identity_assertions"]) ? payload["identity_assertions"] : [];
661509
+ for (const item of assertions) {
661510
+ if (item && typeof item === "object") add3(item["name"]);
661511
+ }
661512
+ return names;
661513
+ }
661514
+ function isRejectedObjectLabel(label, payload) {
661515
+ const key = normalizeLabel(label).toLowerCase();
661516
+ if (!key) return true;
661517
+ if (/^(visual_identity|pending_visual_identity|person|face|speaker)(?:[_:\s-]|$)/i.test(label)) return true;
661518
+ if (identityNames(payload).has(key)) return true;
661519
+ return false;
661520
+ }
661521
+ function extractExplicitVisualObjectLabels(payload) {
661522
+ const media = payload["media"] && typeof payload["media"] === "object" ? payload["media"] : {};
661523
+ const candidates = uniqueLabels([
661524
+ ...stringList(payload["object_label"]),
661525
+ ...stringList(payload["objectLabel"]),
661526
+ ...stringList(payload["object_labels"]),
661527
+ ...stringList(payload["objectLabels"]),
661528
+ ...stringList(payload["visualObjectLabel"]),
661529
+ ...stringList(payload["visual_object_label"]),
661530
+ ...stringList(payload["visualObjectLabels"]),
661531
+ ...stringList(payload["visual_object_labels"]),
661532
+ ...stringList(payload["label"]),
661533
+ ...stringList(payload["labels"]),
661534
+ ...stringList(media["objectLabel"]),
661535
+ ...stringList(media["object_label"]),
661536
+ ...stringList(media["objectLabels"]),
661537
+ ...stringList(media["object_labels"]),
661538
+ ...stringList(media["label"]),
661539
+ ...stringList(media["labels"]),
661540
+ ...explicitCaptionLabels(payload["caption"]),
661541
+ ...explicitCaptionLabels(media["caption"])
661542
+ ]).filter((label) => !isRejectedObjectLabel(label, payload));
661543
+ const aliases = uniqueLabels([
661544
+ ...stringList(payload["aliases"]),
661545
+ ...stringList(payload["object_aliases"]),
661546
+ ...stringList(payload["objectAliases"]),
661547
+ ...stringList(payload["visualObjectAliases"]),
661548
+ ...stringList(payload["visual_object_aliases"]),
661549
+ ...stringList(media["aliases"]),
661550
+ ...stringList(media["objectAliases"]),
661551
+ ...stringList(media["object_aliases"]),
661552
+ ...candidates.slice(1)
661553
+ ]).filter((label) => !isRejectedObjectLabel(label, payload));
661554
+ return {
661555
+ label: candidates[0],
661556
+ aliases,
661557
+ source: candidates.length > 0 ? "explicit_visual_object_label" : void 0
661558
+ };
661559
+ }
661560
+ function formatVisualObjectMemoryContext(result) {
661561
+ if (!result.taught || !result.label) return "";
661562
+ const lines = [
661563
+ "Visual Object Memory",
661564
+ `- Learned object label: ${result.label}`
661565
+ ];
661566
+ if (result.aliases.length > 0) lines.push(`- Aliases: ${result.aliases.join(", ")}`);
661567
+ return lines.join("\n");
661568
+ }
661569
+ async function associateVisualObjectFromImage(options2) {
661570
+ const payload = options2.payload ?? {};
661571
+ const extraction = extractExplicitVisualObjectLabels({
661572
+ ...payload,
661573
+ media: options2.media ?? payload["media"]
661574
+ });
661575
+ if (!extraction.label) {
661576
+ return {
661577
+ attempted: false,
661578
+ taught: false,
661579
+ aliases: [],
661580
+ contextBlock: ""
661581
+ };
661582
+ }
661583
+ const visual = options2.visualMemoryTool ?? new VisualMemoryTool();
661584
+ const aliases = uniqueLabels(extraction.aliases.filter((alias) => alias.toLowerCase() !== extraction.label.toLowerCase()));
661585
+ const labels = uniqueLabels([extraction.label, ...aliases]);
661586
+ const teach = await visual.execute({
661587
+ action: "teach",
661588
+ image: options2.imagePath,
661589
+ label: extraction.label,
661590
+ aliases,
661591
+ labels,
661592
+ sourceSurface: options2.sourceSurface,
661593
+ sessionId: options2.sessionId,
661594
+ fileUniqueId: options2.media?.fileUniqueId
661595
+ });
661596
+ if (!teach.success) {
661597
+ return {
661598
+ attempted: true,
661599
+ taught: false,
661600
+ label: extraction.label,
661601
+ aliases,
661602
+ contextBlock: "",
661603
+ output: teach.output,
661604
+ degradedReason: teach.error || teach.output || `visual_memory teach failed for ${basename35(options2.imagePath)}`
661605
+ };
661606
+ }
661607
+ const result = {
661608
+ attempted: true,
661609
+ taught: true,
661610
+ label: extraction.label,
661611
+ aliases,
661612
+ contextBlock: "",
661613
+ output: teach.output
661614
+ };
661615
+ result.contextBlock = formatVisualObjectMemoryContext(result);
661616
+ return result;
661617
+ }
661618
+ var init_visual_object_association = __esm({
661619
+ "packages/cli/src/tui/visual-object-association.ts"() {
661620
+ "use strict";
661621
+ init_dist5();
661622
+ }
661623
+ });
661624
+
661174
661625
  // packages/cli/src/tui/telegram-channel-dmn.ts
661175
661626
  import {
661176
661627
  existsSync as existsSync144,
@@ -663671,55 +664122,16 @@ function advancedOcr(imagePath) {
663671
664122
  async function queryVisionModel(modelName, imagePath, prompt = "Describe what you see in this image in detail. Include any text, UI elements, code, diagrams, or visual content.") {
663672
664123
  if (!isVisionModel(modelName)) return "";
663673
664124
  if (!existsSync145(imagePath)) return "";
663674
- const imageBuffer = readFileSync118(imagePath);
663675
- const base64Image = imageBuffer.toString("base64");
663676
- const broker = getModelBroker();
663677
- const decision2 = await broker.ensureModelLoadable({
663678
- name: modelName,
663679
- domain: "vision",
663680
- host: "ollama",
663681
- owner: "vision-ingress",
663682
- requestedNumCtx: 2048
663683
- });
663684
- let effectiveModel = modelName;
663685
- let numCtx;
663686
- if (decision2.kind === "reject") {
663687
- return "";
663688
- } else if (decision2.kind === "degrade") {
663689
- effectiveModel = decision2.fallback.name;
663690
- } else if (decision2.kind === "evict") {
663691
- for (const target of decision2.evictTargets) {
663692
- await broker.evict(target.host, target.name, "make-room-for-vision");
663693
- }
663694
- numCtx = decision2.effectiveNumCtx;
663695
- } else if (decision2.kind === "ok") {
663696
- numCtx = decision2.effectiveNumCtx;
663697
- } else if (decision2.kind === "wait-for-inflight") {
663698
- const inner = await decision2.promise;
663699
- if (inner.kind === "ok") numCtx = inner.effectiveNumCtx;
663700
- else if (inner.kind === "degrade") effectiveModel = inner.fallback.name;
663701
- else if (inner.kind === "reject") return "";
663702
- }
663703
- if (numCtx === void 0) {
663704
- const trainCtx = await broker.getNctxTrain(effectiveModel);
663705
- numCtx = trainCtx && trainCtx > 0 ? Math.min(trainCtx, 4096) : 2048;
663706
- }
663707
664125
  try {
663708
- const response = await fetch("http://localhost:11434/api/generate", {
663709
- method: "POST",
663710
- headers: { "Content-Type": "application/json" },
663711
- body: JSON.stringify({
663712
- model: effectiveModel,
663713
- prompt,
663714
- images: [base64Image],
663715
- stream: false,
663716
- options: { temperature: 0.3, num_predict: 1024, num_ctx: numCtx }
663717
- })
664126
+ const tool = new VisionTool(process.cwd());
664127
+ const result = await tool.execute({
664128
+ image: imagePath,
664129
+ action: "query",
664130
+ prompt,
664131
+ model: modelName
663718
664132
  });
663719
- if (!response.ok) return "";
663720
- broker.touch("ollama", effectiveModel);
663721
- const data = await response.json();
663722
- return (data.response || "").trim();
664133
+ if (!result.success) return "";
664134
+ return String(result.llmContent || result.output || "").trim();
663723
664135
  } catch {
663724
664136
  return "";
663725
664137
  }
@@ -663790,13 +664202,13 @@ import {
663790
664202
  statSync as statSync50,
663791
664203
  statfsSync as statfsSync7,
663792
664204
  readFileSync as readFileSync119,
663793
- writeFileSync as writeFileSync78,
664205
+ writeFileSync as writeFileSync77,
663794
664206
  appendFileSync as appendFileSync15
663795
664207
  } from "node:fs";
663796
664208
  import {
663797
664209
  join as join158,
663798
664210
  resolve as resolve62,
663799
- basename as basename35,
664211
+ basename as basename36,
663800
664212
  relative as relative16,
663801
664213
  isAbsolute as isAbsolute11,
663802
664214
  extname as extname21
@@ -666265,6 +666677,7 @@ var init_telegram_bridge = __esm({
666265
666677
  init_soul_observations();
666266
666678
  init_identity_memory_tool();
666267
666679
  init_visual_identity_association();
666680
+ init_visual_object_association();
666268
666681
  init_telegram_channel_dmn();
666269
666682
  init_telegram_reflection_corpus();
666270
666683
  init_memory_paths();
@@ -669265,6 +669678,14 @@ ${mediaContext}` : ""
669265
669678
  const messageText = isReplyMedia ? msg.replyContext?.text || msg.replyContext?.caption || msg.replyToText || media.caption : msg.text || media.caption;
669266
669679
  const sender = isReplyMedia ? this.telegramMemorySenderFromReply(msg) : this.telegramMemorySenderFromMessage(msg);
669267
669680
  const modality = media.type === "audio" || media.type === "voice" ? "audio" : telegramMediaIsImage(media) ? "visual" : "text";
669681
+ const objectLabels = extractExplicitVisualObjectLabels({
669682
+ caption: media.caption,
669683
+ media: { caption: media.caption }
669684
+ });
669685
+ const structuredObjectLabels = [
669686
+ objectLabels.label,
669687
+ ...objectLabels.aliases
669688
+ ].filter((label) => typeof label === "string" && label.trim().length > 0);
669268
669689
  const payload = {
669269
669690
  sourceSurface: "telegram",
669270
669691
  sessionId: this.sessionKeyForMessage(msg),
@@ -669300,8 +669721,12 @@ ${mediaContext}` : ""
669300
669721
  sender_id: sender?.id,
669301
669722
  username: sender?.username,
669302
669723
  display_name: sender?.displayName,
669303
- labels: [media.caption].filter(Boolean)
669724
+ labels: structuredObjectLabels
669304
669725
  };
669726
+ if (objectLabels.label) {
669727
+ payload.object_label = objectLabels.label;
669728
+ payload.aliases = objectLabels.aliases;
669729
+ }
669305
669730
  if (extractedContent) {
669306
669731
  if (modality === "audio") payload.transcript = extractedContent;
669307
669732
  else payload.extractedContent = extractedContent;
@@ -670202,7 +670627,7 @@ ${mediaContext}` : ""
670202
670627
  autoFollowup: false
670203
670628
  }
670204
670629
  };
670205
- writeFileSync78(
670630
+ writeFileSync77(
670206
670631
  this.telegramConversationPath(sessionKey),
670207
670632
  JSON.stringify(payload, null, 2) + "\n",
670208
670633
  "utf8"
@@ -671031,7 +671456,7 @@ ${mediaContext}` : ""
671031
671456
  }
671032
671457
  const matchingEntry = mediaEntries.find((entry) => {
671033
671458
  if (resolve62(entry.localPath) === resolve62(raw)) return true;
671034
- if (basename35(entry.localPath) === raw) return true;
671459
+ if (basename36(entry.localPath) === raw) return true;
671035
671460
  if (entry.fileUniqueId === raw || entry.fileId === raw) return true;
671036
671461
  if (entry.messageId && String(entry.messageId) === raw) return true;
671037
671462
  if (entry.messageId && `message_id:${entry.messageId}` === raw.toLowerCase())
@@ -671072,7 +671497,7 @@ ${mediaContext}` : ""
671072
671497
  sourceMessageId,
671073
671498
  chatKey,
671074
671499
  mediaKind,
671075
- safeAlias: basename35(result.path)
671500
+ safeAlias: basename36(result.path)
671076
671501
  }
671077
671502
  };
671078
671503
  }
@@ -671113,7 +671538,7 @@ ${mediaContext}` : ""
671113
671538
  }
671114
671539
  return entries.find((entry2) => {
671115
671540
  if (resolve62(entry2.localPath) === resolve62(ref)) return true;
671116
- if (basename35(entry2.localPath) === ref) return true;
671541
+ if (basename36(entry2.localPath) === ref) return true;
671117
671542
  if (entry2.fileUniqueId === ref || entry2.fileId === ref) return true;
671118
671543
  if (entry2.messageId && String(entry2.messageId) === ref) return true;
671119
671544
  return false;
@@ -671141,7 +671566,7 @@ ${mediaContext}` : ""
671141
671566
  caption: entry.caption
671142
671567
  },
671143
671568
  modality,
671144
- label: `Telegram message_id ${entry.messageId || "unknown"} ${basename35(entry.localPath)}`,
671569
+ label: `Telegram message_id ${entry.messageId || "unknown"} ${basename36(entry.localPath)}`,
671145
671570
  extractedContent: entry.extractedContent
671146
671571
  };
671147
671572
  }
@@ -672307,8 +672732,8 @@ ${cardLines.join("\n")}`
672307
672732
  const caption = entry.caption ? ` caption=${telegramContextJsonString(entry.caption, 120)}` : "";
672308
672733
  const extracted = entry.extractedContent ? `
672309
672734
  extracted=${telegramContextJsonString(entry.extractedContent.replace(/\s+/g, " "), 220)}` : "";
672310
- const alias = entry.messageId ? `message_id:${entry.messageId}` : basename35(entry.localPath);
672311
- return `- ${alias}${replyMark}: ${kind}; file ${basename35(entry.localPath)}${caption}${extracted}`;
672735
+ const alias = entry.messageId ? `message_id:${entry.messageId}` : basename36(entry.localPath);
672736
+ return `- ${alias}${replyMark}: ${kind}; file ${basename36(entry.localPath)}${caption}${extracted}`;
672312
672737
  });
672313
672738
  sections.push(
672314
672739
  [
@@ -674805,7 +675230,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
674805
675230
  throw e2;
674806
675231
  }
674807
675232
  }
674808
- writeFileSync78(
675233
+ writeFileSync77(
674809
675234
  lockFile,
674810
675235
  JSON.stringify(
674811
675236
  {
@@ -679758,7 +680183,7 @@ Scoped workspace: ${scopedRoot}`,
679758
680183
  }
679759
680184
  writeTelegramToolButtonState(state) {
679760
680185
  mkdirSync89(this.telegramToolButtonDir, { recursive: true });
679761
- writeFileSync78(
680186
+ writeFileSync77(
679762
680187
  this.telegramToolButtonPath(state.nonce),
679763
680188
  JSON.stringify(state, null, 2) + "\n",
679764
680189
  "utf-8"
@@ -680661,12 +681086,12 @@ Advanced text extraction signal: chars=${advancedSignal.chars}, lines=${advanced
680661
681086
  };
680662
681087
  }
680663
681088
  const lines = entries.map((entry, index) => {
680664
- const pathAlias = entry.messageId ? `message_id:${entry.messageId}` : basename35(entry.localPath);
681089
+ const pathAlias = entry.messageId ? `message_id:${entry.messageId}` : basename36(entry.localPath);
680665
681090
  const parts = [
680666
681091
  `${index + 1}. message_id ${entry.messageId || "unknown"}`,
680667
681092
  currentMsg?.replyToMessageId === entry.messageId ? "replied-to" : "",
680668
681093
  telegramCachedMediaIsImage(entry) ? "image" : telegramCachedMediaIsPdf(entry) ? "pdf" : telegramCachedMediaIsAudio(entry) ? "audio" : telegramCachedMediaIsVideo(entry) ? "video" : entry.mediaType,
680669
- `file=${basename35(entry.localPath)}`,
681094
+ `file=${basename36(entry.localPath)}`,
680670
681095
  `path_alias=${pathAlias}`,
680671
681096
  entry.caption ? `caption=${telegramContextJsonString(entry.caption, 140)}` : ""
680672
681097
  ].filter(Boolean);
@@ -680849,8 +681274,8 @@ Advanced text extraction signal: chars=${advancedSignal.chars}, lines=${advanced
680849
681274
  )) {
680850
681275
  return {
680851
681276
  success: true,
680852
- output: `Telegram file already sent in this turn: ${basename35(file.path)} as ${kind} to ${String(target.chatId)}`,
680853
- llmContent: `Already sent ${basename35(file.path)} to Telegram as ${kind}; do not send it again.`,
681277
+ output: `Telegram file already sent in this turn: ${basename36(file.path)} as ${kind} to ${String(target.chatId)}`,
681278
+ llmContent: `Already sent ${basename36(file.path)} to Telegram as ${kind}; do not send it again.`,
680854
681279
  durationMs: performance.now() - start2,
680855
681280
  mutated: false,
680856
681281
  mutatedFiles: []
@@ -680877,8 +681302,8 @@ Advanced text extraction signal: chars=${advancedSignal.chars}, lines=${advanced
680877
681302
  );
680878
681303
  return {
680879
681304
  success: true,
680880
- output: `Sent Telegram file: ${basename35(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
680881
- llmContent: `Sent ${basename35(file.path)} to Telegram as ${kind}.`,
681305
+ output: `Sent Telegram file: ${basename36(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
681306
+ llmContent: `Sent ${basename36(file.path)} to Telegram as ${kind}.`,
680882
681307
  durationMs: performance.now() - start2,
680883
681308
  mutated: false,
680884
681309
  mutatedFiles: []
@@ -681164,6 +681589,7 @@ ${visionContext}]`;
681164
681589
  cacheEntry.extractedContent
681165
681590
  );
681166
681591
  let visualIdentityContext = "";
681592
+ let visualObjectContext = "";
681167
681593
  let ingestReachedDaemon = false;
681168
681594
  try {
681169
681595
  const ingestResponse = await fetch(
@@ -681181,6 +681607,9 @@ ${visionContext}]`;
681181
681607
  const block = ingested?.visualIdentity?.contextBlock;
681182
681608
  if (typeof block === "string" && block.trim())
681183
681609
  visualIdentityContext = block.trim();
681610
+ const objectBlock = ingested?.visualObjectMemory?.contextBlock;
681611
+ if (typeof objectBlock === "string" && objectBlock.trim())
681612
+ visualObjectContext = objectBlock.trim();
681184
681613
  }
681185
681614
  } catch {
681186
681615
  }
@@ -681202,8 +681631,29 @@ ${visionContext}]`;
681202
681631
  visualIdentityContext = association.contextBlock;
681203
681632
  } catch {
681204
681633
  }
681634
+ try {
681635
+ const objectMemory = await associateVisualObjectFromImage({
681636
+ repoRoot: this.repoRoot,
681637
+ imagePath: localPath,
681638
+ sourceSurface: "telegram",
681639
+ scope: ingestPayload["scope"],
681640
+ sender: ingestPayload["sender"],
681641
+ message: ingestPayload["message"],
681642
+ replyTo: ingestPayload["replyTo"],
681643
+ sessionId: typeof ingestPayload["sessionId"] === "string" ? ingestPayload["sessionId"] : this.sessionKeyForMessage(msg),
681644
+ media: ingestPayload["media"],
681645
+ extractedContent: cacheEntry.extractedContent,
681646
+ payload: ingestPayload
681647
+ });
681648
+ if (objectMemory.contextBlock)
681649
+ visualObjectContext = objectMemory.contextBlock;
681650
+ } catch {
681651
+ }
681205
681652
  }
681206
- description = appendMediaContextBlock(description, visualIdentityContext);
681653
+ description = appendMediaContextBlock(
681654
+ description,
681655
+ [visualIdentityContext, visualObjectContext].filter(Boolean).join("\n\n")
681656
+ );
681207
681657
  } else if (isImageMedia) {
681208
681658
  description = `[${sourceLabel}image received: path_alias=${mediaAlias}${safeCaption}. Full visual comprehension pending; use image='${source === "reply" ? "reply" : "latest"}' or image='${mediaAlias}' with telegram_image_analyze detail='full'.]`;
681209
681659
  cacheEntry.analysisComplete = false;
@@ -681512,7 +681962,7 @@ ${text2}`.trim()
681512
681962
  throw new Error(`File does not exist: ${media.value}`);
681513
681963
  const buffer2 = readFileSync119(media.value);
681514
681964
  const boundary = `----omnius-media-${Date.now()}-${Math.random().toString(36).slice(2)}`;
681515
- const filename = basename35(media.value);
681965
+ const filename = basename36(media.value);
681516
681966
  const contentType = mimeForPath(media.value, media.kind);
681517
681967
  const parts = [];
681518
681968
  const addField = (name10, value2) => {
@@ -681750,7 +682200,7 @@ Content-Type: ${contentType}\r
681750
682200
  audioAsVoice: false
681751
682201
  },
681752
682202
  {
681753
- caption: `Vision action loop screenshot: ${basename35(abs)}`
682203
+ caption: `Vision action loop screenshot: ${basename36(abs)}`
681754
682204
  }
681755
682205
  ).catch(() => null);
681756
682206
  }
@@ -681830,7 +682280,7 @@ Content-Type: ${contentType}\r
681830
682280
  continue;
681831
682281
  }
681832
682282
  const buffer2 = readFileSync119(pathOrFileId);
681833
- const filename = basename35(pathOrFileId);
682283
+ const filename = basename36(pathOrFileId);
681834
682284
  parts.push(Buffer.from(`--${boundary}\r
681835
682285
  `));
681836
682286
  parts.push(
@@ -683607,9 +684057,9 @@ __export(projects_exports, {
683607
684057
  setCurrentProject: () => setCurrentProject,
683608
684058
  unregisterProject: () => unregisterProject
683609
684059
  });
683610
- import { readFileSync as readFileSync120, writeFileSync as writeFileSync79, mkdirSync as mkdirSync90, existsSync as existsSync147, statSync as statSync51, renameSync as renameSync12 } from "node:fs";
684060
+ import { readFileSync as readFileSync120, writeFileSync as writeFileSync78, mkdirSync as mkdirSync90, existsSync as existsSync147, statSync as statSync51, renameSync as renameSync12 } from "node:fs";
683611
684061
  import { homedir as homedir52 } from "node:os";
683612
- import { basename as basename36, join as join159, resolve as resolve63 } from "node:path";
684062
+ import { basename as basename37, join as join159, resolve as resolve63 } from "node:path";
683613
684063
  import { randomUUID as randomUUID19 } from "node:crypto";
683614
684064
  function readAll2() {
683615
684065
  try {
@@ -683625,7 +684075,7 @@ function readAll2() {
683625
684075
  function writeAll(file) {
683626
684076
  mkdirSync90(OMNIUS_DIR3, { recursive: true });
683627
684077
  const tmp = `${PROJECTS_FILE}.${randomUUID19().slice(0, 8)}.tmp`;
683628
- writeFileSync79(tmp, JSON.stringify(file, null, 2), "utf8");
684078
+ writeFileSync78(tmp, JSON.stringify(file, null, 2), "utf8");
683629
684079
  renameSync12(tmp, PROJECTS_FILE);
683630
684080
  }
683631
684081
  function listProjects() {
@@ -683657,7 +684107,7 @@ function registerProject(root, pid) {
683657
684107
  } else {
683658
684108
  entry = {
683659
684109
  root: canonical,
683660
- name: basename36(canonical) || canonical,
684110
+ name: basename37(canonical) || canonical,
683661
684111
  firstSeen: now2,
683662
684112
  lastSeen: now2,
683663
684113
  pid: pid ?? null,
@@ -683708,7 +684158,7 @@ function setCurrentProject(root) {
683708
684158
  currentRoot = canonical;
683709
684159
  try {
683710
684160
  mkdirSync90(OMNIUS_DIR3, { recursive: true });
683711
- writeFileSync79(CURRENT_FILE, `${canonical}
684161
+ writeFileSync78(CURRENT_FILE, `${canonical}
683712
684162
  `, "utf8");
683713
684163
  } catch {
683714
684164
  }
@@ -684605,7 +685055,7 @@ var init_access_policy = __esm({
684605
685055
 
684606
685056
  // packages/cli/src/api/project-preferences.ts
684607
685057
  import { createHash as createHash42 } from "node:crypto";
684608
- import { existsSync as existsSync148, mkdirSync as mkdirSync91, readFileSync as readFileSync121, renameSync as renameSync13, writeFileSync as writeFileSync80, unlinkSync as unlinkSync34 } from "node:fs";
685058
+ import { existsSync as existsSync148, mkdirSync as mkdirSync91, readFileSync as readFileSync121, renameSync as renameSync13, writeFileSync as writeFileSync79, unlinkSync as unlinkSync34 } from "node:fs";
684609
685059
  import { homedir as homedir53 } from "node:os";
684610
685060
  import { join as join160, resolve as resolve64 } from "node:path";
684611
685061
  import { randomUUID as randomUUID20 } from "node:crypto";
@@ -684628,7 +685078,7 @@ function ensureDir(root) {
684628
685078
  const sentinel = rootSentinelPath(root);
684629
685079
  try {
684630
685080
  if (!existsSync148(sentinel)) {
684631
- writeFileSync80(sentinel, `${resolve64(root)}
685081
+ writeFileSync79(sentinel, `${resolve64(root)}
684632
685082
  `, "utf8");
684633
685083
  }
684634
685084
  } catch {
@@ -684657,12 +685107,12 @@ function writeProjectPreferences(root, partial) {
684657
685107
  };
684658
685108
  const file = prefsPath(root);
684659
685109
  const tmp = `${file}.${randomUUID20().slice(0, 8)}.tmp`;
684660
- writeFileSync80(tmp, JSON.stringify(merged, null, 2), "utf8");
685110
+ writeFileSync79(tmp, JSON.stringify(merged, null, 2), "utf8");
684661
685111
  try {
684662
685112
  renameSync13(tmp, file);
684663
685113
  } catch (err) {
684664
685114
  try {
684665
- writeFileSync80(file, JSON.stringify(merged, null, 2), "utf8");
685115
+ writeFileSync79(file, JSON.stringify(merged, null, 2), "utf8");
684666
685116
  } catch {
684667
685117
  }
684668
685118
  try {
@@ -685067,7 +685517,7 @@ data: ${JSON.stringify(ev)}
685067
685517
 
685068
685518
  // packages/cli/src/api/routes-media.ts
685069
685519
  import { existsSync as existsSync151, mkdirSync as mkdirSync94, statSync as statSync53, copyFileSync as copyFileSync7, createReadStream } from "node:fs";
685070
- import { basename as basename37, join as join162, resolve as pathResolve2 } from "node:path";
685520
+ import { basename as basename38, join as join162, resolve as pathResolve2 } from "node:path";
685071
685521
  function mediaWorkDir() {
685072
685522
  return join162(omniusHomeDir(), "media", "_work");
685073
685523
  }
@@ -685089,7 +685539,7 @@ function extractGeneratedPath(output) {
685089
685539
  function publishToGallery(srcPath, kind) {
685090
685540
  ensureGlobalMediaDirs();
685091
685541
  const dir = globalMediaDir(kind);
685092
- const name10 = basename37(srcPath);
685542
+ const name10 = basename38(srcPath);
685093
685543
  const dest = join162(dir, name10);
685094
685544
  try {
685095
685545
  if (pathResolve2(srcPath) !== pathResolve2(dest)) {
@@ -685290,7 +685740,7 @@ async function handleAvAnalyze(ctx3) {
685290
685740
  adapterStatus[role] = a2 && !a2.meta.isMock ? "live" : "mock";
685291
685741
  if (adapterStatus[role] === "live") anyLive = true;
685292
685742
  }
685293
- const episodeId = `rest-${basename37(filePath)}-${Date.now().toString(36)}`;
685743
+ const episodeId = `rest-${basename38(filePath)}-${Date.now().toString(36)}`;
685294
685744
  const ingest = await ingestMedia(episodeId, `file://${filePath}`);
685295
685745
  const store2 = await analyzeEpisode({ episodeId, mediaUri: `file://${filePath}`, durationSec: ingest.source.durationSec, registry: registry4, windows: ingest.shots, roles: AV_ROLES });
685296
685746
  const world = store2.snapshot();
@@ -685460,7 +685910,7 @@ function handleFile(ctx3) {
685460
685910
  return true;
685461
685911
  }
685462
685912
  const dir = globalMediaDir(kind);
685463
- const full = pathResolve2(dir, basename37(name10));
685913
+ const full = pathResolve2(dir, basename38(name10));
685464
685914
  if (!full.startsWith(pathResolve2(dir)) || !existsSync151(full)) {
685465
685915
  sendProblem(ctx3.res, problemDetails({
685466
685916
  type: P.notFound,
@@ -685705,7 +686155,7 @@ var init_direct_tool_registry = __esm({
685705
686155
  });
685706
686156
 
685707
686157
  // packages/cli/src/api/external-tool-registry.ts
685708
- import { existsSync as existsSync152, mkdirSync as mkdirSync95, readFileSync as readFileSync123, writeFileSync as writeFileSync81, renameSync as renameSync14 } from "node:fs";
686158
+ import { existsSync as existsSync152, mkdirSync as mkdirSync95, readFileSync as readFileSync123, writeFileSync as writeFileSync80, renameSync as renameSync14 } from "node:fs";
685709
686159
  import { join as join163 } from "node:path";
685710
686160
  function externalToolStorePath(workingDir) {
685711
686161
  return join163(workingDir, ".omnius", "external-tools.json");
@@ -685728,7 +686178,7 @@ function persist3(workingDir, tools) {
685728
686178
  mkdirSync95(join163(workingDir, ".omnius"), { recursive: true });
685729
686179
  const file = { version: STORE_VERSION, tools };
685730
686180
  const tmp = `${path12}.tmp`;
685731
- writeFileSync81(tmp, JSON.stringify(file, null, 2), { mode: 384 });
686181
+ writeFileSync80(tmp, JSON.stringify(file, null, 2), { mode: 384 });
685732
686182
  renameSync14(tmp, path12);
685733
686183
  }
685734
686184
  function validateManifest2(input, existing) {
@@ -686840,7 +687290,7 @@ __export(runtime_keys_exports, {
686840
687290
  mintKey: () => mintKey,
686841
687291
  revokeByPrefix: () => revokeByPrefix
686842
687292
  });
686843
- import { existsSync as existsSync154, readFileSync as readFileSync125, writeFileSync as writeFileSync82, mkdirSync as mkdirSync96, chmodSync as chmodSync4 } from "node:fs";
687293
+ import { existsSync as existsSync154, readFileSync as readFileSync125, writeFileSync as writeFileSync81, mkdirSync as mkdirSync96, chmodSync as chmodSync4 } from "node:fs";
686844
687294
  import { join as join165 } from "node:path";
686845
687295
  import { homedir as homedir55 } from "node:os";
686846
687296
  import { randomBytes as randomBytes27 } from "node:crypto";
@@ -686861,7 +687311,7 @@ function loadAll() {
686861
687311
  }
686862
687312
  function persistAll(records) {
686863
687313
  ensureDir2();
686864
- writeFileSync82(KEYS_FILE, JSON.stringify(records, null, 2), "utf-8");
687314
+ writeFileSync81(KEYS_FILE, JSON.stringify(records, null, 2), "utf-8");
686865
687315
  try {
686866
687316
  chmodSync4(KEYS_FILE, 384);
686867
687317
  } catch {
@@ -687095,7 +687545,7 @@ __export(graphical_sudo_exports, {
687095
687545
  runGraphicalSudo: () => runGraphicalSudo
687096
687546
  });
687097
687547
  import { spawn as spawn31 } from "node:child_process";
687098
- import { existsSync as existsSync156, mkdirSync as mkdirSync97, writeFileSync as writeFileSync83, chmodSync as chmodSync5 } from "node:fs";
687548
+ import { existsSync as existsSync156, mkdirSync as mkdirSync97, writeFileSync as writeFileSync82, chmodSync as chmodSync5 } from "node:fs";
687099
687549
  import { join as join167 } from "node:path";
687100
687550
  import { tmpdir as tmpdir22 } from "node:os";
687101
687551
  function detectSudoHelper() {
@@ -687131,7 +687581,7 @@ exec zenity --password --title="Omnius needs sudo" --text="${description.replace
687131
687581
  exec kdialog --password "${description.replace(/"/g, '\\"')}" 2>/dev/null
687132
687582
  `;
687133
687583
  }
687134
- writeFileSync83(shim, body, "utf-8");
687584
+ writeFileSync82(shim, body, "utf-8");
687135
687585
  chmodSync5(shim, 493);
687136
687586
  return shim;
687137
687587
  }
@@ -702167,7 +702617,7 @@ var init_auth_oidc = __esm({
702167
702617
  });
702168
702618
 
702169
702619
  // packages/cli/src/api/usage-tracker.ts
702170
- import { mkdirSync as mkdirSync99, readFileSync as readFileSync128, writeFileSync as writeFileSync84, existsSync as existsSync158 } from "node:fs";
702620
+ import { mkdirSync as mkdirSync99, readFileSync as readFileSync128, writeFileSync as writeFileSync83, existsSync as existsSync158 } from "node:fs";
702171
702621
  import { join as join169 } from "node:path";
702172
702622
  function initUsageTracker(omniusDir) {
702173
702623
  const dir = join169(omniusDir, "usage");
@@ -702211,7 +702661,7 @@ function flush2() {
702211
702661
  if (!initialized2 || !dirty) return;
702212
702662
  try {
702213
702663
  store.lastSaved = (/* @__PURE__ */ new Date()).toISOString();
702214
- writeFileSync84(usageFile, JSON.stringify(store, null, 2), "utf-8");
702664
+ writeFileSync83(usageFile, JSON.stringify(store, null, 2), "utf-8");
702215
702665
  dirty = false;
702216
702666
  } catch {
702217
702667
  }
@@ -702386,7 +702836,7 @@ var init_chat_followup = __esm({
702386
702836
 
702387
702837
  // packages/cli/src/docker.ts
702388
702838
  import { execSync as execSync59, spawn as spawn32 } from "node:child_process";
702389
- import { existsSync as existsSync159, mkdirSync as mkdirSync100, writeFileSync as writeFileSync85 } from "node:fs";
702839
+ import { existsSync as existsSync159, mkdirSync as mkdirSync100, writeFileSync as writeFileSync84 } from "node:fs";
702390
702840
  import { join as join170, resolve as resolve65, dirname as dirname50 } from "node:path";
702391
702841
  import { homedir as homedir58 } from "node:os";
702392
702842
  import { fileURLToPath as fileURLToPath19 } from "node:url";
@@ -702611,8 +703061,8 @@ chown -R node:node /workspace /home/node/.omnius 2>/dev/null || true
702611
703061
  if [ "$1" = "omnius" ]; then shift; exec su - node -c "cd /workspace && omnius $*"; fi
702612
703062
  exec "$@"
702613
703063
  `;
702614
- writeFileSync85(join170(dir, "Dockerfile"), dockerfile);
702615
- writeFileSync85(join170(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
703064
+ writeFileSync84(join170(dir, "Dockerfile"), dockerfile);
703065
+ writeFileSync84(join170(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
702616
703066
  }
702617
703067
  function hasNvidiaGpu() {
702618
703068
  try {
@@ -702685,7 +703135,7 @@ __export(embedding_workers_exports, {
702685
703135
  startEmbeddingWorkers: () => startEmbeddingWorkers,
702686
703136
  stopEmbeddingWorkers: () => stopEmbeddingWorkers
702687
703137
  });
702688
- import { basename as basename38, join as join171 } from "node:path";
703138
+ import { basename as basename39, join as join171 } from "node:path";
702689
703139
  function startEmbeddingWorkers(opts) {
702690
703140
  if (_running) return;
702691
703141
  _running = true;
@@ -702751,7 +703201,7 @@ async function runEmbeddingTask(modality, episodeId, taskId, opts) {
702751
703201
  try {
702752
703202
  if (!_aligner) {
702753
703203
  const stateRoot = process.env.OMNIUS_DIR || process.cwd();
702754
- const omniusDir = basename38(stateRoot) === ".omnius" ? stateRoot : join171(stateRoot, ".omnius");
703204
+ const omniusDir = basename39(stateRoot) === ".omnius" ? stateRoot : join171(stateRoot, ".omnius");
702755
703205
  const memDir = join171(omniusDir, "memory");
702756
703206
  _aligner = new EmbeddingAligner(
702757
703207
  `${modality}-${emb.length}`,
@@ -702873,7 +703323,7 @@ import { spawn as spawn33, execSync as execSync60 } from "node:child_process";
702873
703323
  import {
702874
703324
  createReadStream as createReadStream2,
702875
703325
  mkdirSync as mkdirSync101,
702876
- writeFileSync as writeFileSync86,
703326
+ writeFileSync as writeFileSync85,
702877
703327
  readFileSync as readFileSync129,
702878
703328
  readdirSync as readdirSync56,
702879
703329
  existsSync as existsSync160,
@@ -704534,11 +704984,11 @@ function atomicJobWrite(dir, id, job) {
704534
704984
  const finalPath = join172(dir, `${id}.json`);
704535
704985
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
704536
704986
  try {
704537
- writeFileSync86(tmpPath, JSON.stringify(job, null, 2), "utf-8");
704987
+ writeFileSync85(tmpPath, JSON.stringify(job, null, 2), "utf-8");
704538
704988
  renameSync15(tmpPath, finalPath);
704539
704989
  } catch {
704540
704990
  try {
704541
- writeFileSync86(finalPath, JSON.stringify(job, null, 2), "utf-8");
704991
+ writeFileSync85(finalPath, JSON.stringify(job, null, 2), "utf-8");
704542
704992
  } catch {
704543
704993
  }
704544
704994
  try {
@@ -706370,7 +706820,7 @@ function writeUpdateState(state) {
706370
706820
  mkdirSync101(dir, { recursive: true });
706371
706821
  const finalPath = updateStateFile();
706372
706822
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
706373
- writeFileSync86(tmpPath, JSON.stringify(state, null, 2), "utf-8");
706823
+ writeFileSync85(tmpPath, JSON.stringify(state, null, 2), "utf-8");
706374
706824
  renameSync15(tmpPath, finalPath);
706375
706825
  } catch {
706376
706826
  }
@@ -708720,13 +709170,13 @@ async function handleRequest(req3, res, ollamaUrl, verbose, runtimeDefaults = {}
708720
709170
  return;
708721
709171
  }
708722
709172
  const { tmpdir: tmpdir24 } = await import("node:os");
708723
- const { writeFileSync: writeFileSync91, unlinkSync: unlinkSync36 } = await import("node:fs");
709173
+ const { writeFileSync: writeFileSync90, unlinkSync: unlinkSync36 } = await import("node:fs");
708724
709174
  const { join: pjoin } = await import("node:path");
708725
709175
  const tmpPath = pjoin(
708726
709176
  tmpdir24(),
708727
709177
  `omnius-clone-upload-${Date.now()}-${safeName3}`
708728
709178
  );
708729
- writeFileSync91(tmpPath, buf);
709179
+ writeFileSync90(tmpPath, buf);
708730
709180
  try {
708731
709181
  const ve = getVoiceEngine();
708732
709182
  const msg = await ve.setCloneVoice(tmpPath);
@@ -709450,7 +709900,7 @@ data: ${JSON.stringify(data)}
709450
709900
  }
709451
709901
  for (const f2 of seenFiles) {
709452
709902
  try {
709453
- writeFileSync86(f2, JSON.stringify({ tasks: [] }, null, 2));
709903
+ writeFileSync85(f2, JSON.stringify({ tasks: [] }, null, 2));
709454
709904
  deleted++;
709455
709905
  } catch {
709456
709906
  }
@@ -711249,11 +711699,11 @@ function setScheduledEnabled(id, enabled2) {
711249
711699
  arr[target.index].enabled = enabled2;
711250
711700
  if (Array.isArray(json?.tasks)) {
711251
711701
  json.tasks = arr;
711252
- writeFileSync86(target.file, JSON.stringify(json, null, 2));
711702
+ writeFileSync85(target.file, JSON.stringify(json, null, 2));
711253
711703
  } else if (Array.isArray(json)) {
711254
- writeFileSync86(target.file, JSON.stringify(arr, null, 2));
711704
+ writeFileSync85(target.file, JSON.stringify(arr, null, 2));
711255
711705
  } else {
711256
- writeFileSync86(target.file, JSON.stringify({ tasks: arr }, null, 2));
711706
+ writeFileSync85(target.file, JSON.stringify({ tasks: arr }, null, 2));
711257
711707
  }
711258
711708
  if (!enabled2) {
711259
711709
  try {
@@ -711283,11 +711733,11 @@ function deleteScheduledById(id) {
711283
711733
  arr.splice(target.index, 1);
711284
711734
  if (Array.isArray(json?.tasks)) {
711285
711735
  json.tasks = arr;
711286
- writeFileSync86(target.file, JSON.stringify(json, null, 2));
711736
+ writeFileSync85(target.file, JSON.stringify(json, null, 2));
711287
711737
  } else if (Array.isArray(json)) {
711288
- writeFileSync86(target.file, JSON.stringify(arr, null, 2));
711738
+ writeFileSync85(target.file, JSON.stringify(arr, null, 2));
711289
711739
  } else {
711290
- writeFileSync86(target.file, JSON.stringify({ tasks: arr }, null, 2));
711740
+ writeFileSync85(target.file, JSON.stringify({ tasks: arr }, null, 2));
711291
711741
  }
711292
711742
  const candidates = [];
711293
711743
  if (id) candidates.push(id);
@@ -711521,7 +711971,7 @@ function reconcileScheduledTasks(apply) {
711521
711971
  mkdirSync101(join172(wdir, ".omnius", "scheduled", "logs"), {
711522
711972
  recursive: true
711523
711973
  });
711524
- writeFileSync86(file, JSON.stringify(toWrite, null, 2));
711974
+ writeFileSync85(file, JSON.stringify(toWrite, null, 2));
711525
711975
  adopted.push({ file, index: arr.length - 1 });
711526
711976
  }
711527
711977
  } else {
@@ -711699,8 +712149,8 @@ WantedBy=timers.target
711699
712149
  `;
711700
712150
  if (!dryRun) {
711701
712151
  mkdirSync101(unitDir, { recursive: true });
711702
- writeFileSync86(svc, svcText);
711703
- writeFileSync86(tim, timText);
712152
+ writeFileSync85(svc, svcText);
712153
+ writeFileSync85(tim, timText);
711704
712154
  try {
711705
712155
  const { execSync: es } = require4("node:child_process");
711706
712156
  es("systemctl --user daemon-reload", { stdio: "pipe" });
@@ -712084,7 +712534,7 @@ function startApiServer(options2 = {}) {
712084
712534
  try {
712085
712535
  const dir = join172(homedir59(), ".omnius");
712086
712536
  mkdirSync101(dir, { recursive: true });
712087
- writeFileSync86(
712537
+ writeFileSync85(
712088
712538
  join172(dir, "access"),
712089
712539
  `${runtimeAccessMode}
712090
712540
  `,
@@ -712497,7 +712947,7 @@ function startApiServer(options2 = {}) {
712497
712947
  }
712498
712948
  try {
712499
712949
  const {
712500
- writeFileSync: writeFileSync91,
712950
+ writeFileSync: writeFileSync90,
712501
712951
  mkdirSync: mkdirSync106,
712502
712952
  existsSync: _exists,
712503
712953
  readFileSync: _rfs
@@ -712536,7 +712986,7 @@ function startApiServer(options2 = {}) {
712536
712986
  for (const dir of dirSet) {
712537
712987
  try {
712538
712988
  if (!_exists(dir)) mkdirSync106(dir, { recursive: true });
712539
- writeFileSync91(_join(dir, "api-port.json"), apiHint);
712989
+ writeFileSync90(_join(dir, "api-port.json"), apiHint);
712540
712990
  written++;
712541
712991
  } catch {
712542
712992
  }
@@ -712905,7 +713355,7 @@ async function handleChatAttachmentUpload(req3, res) {
712905
713355
  dir,
712906
713356
  `${Date.now()}-${randomUUID21().slice(0, 8)}-${safeName3}`
712907
713357
  );
712908
- writeFileSync86(localPath, Buffer.from(base642, "base64"));
713358
+ writeFileSync85(localPath, Buffer.from(base642, "base64"));
712909
713359
  const mimeType = typeof b.mimeType === "string" ? b.mimeType : typeof b.mime_type === "string" ? b.mime_type : "";
712910
713360
  const isImage = mimeType.toLowerCase().startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|tiff?)$/i.test(safeName3);
712911
713361
  const sessionId = typeof b.sessionId === "string" ? b.sessionId : typeof b.session_id === "string" ? b.session_id : void 0;
@@ -712962,6 +713412,7 @@ async function handleChatAttachmentUpload(req3, res) {
712962
713412
  graph.close();
712963
713413
  }
712964
713414
  let visualIdentity = void 0;
713415
+ let visualObjectMemory = void 0;
712965
713416
  let contextBlock = [
712966
713417
  `GUI attachment saved: ${safeName3}`,
712967
713418
  `path: ${localPath}`,
@@ -713000,6 +713451,40 @@ ${association.contextBlock}`;
713000
713451
  degradedReason: err instanceof Error ? err.message : String(err)
713001
713452
  };
713002
713453
  }
713454
+ try {
713455
+ const { associateVisualObjectFromImage: associateVisualObjectFromImage2 } = await Promise.resolve().then(() => (init_visual_object_association(), visual_object_association_exports));
713456
+ const objectMemory = await associateVisualObjectFromImage2({
713457
+ repoRoot: process.cwd(),
713458
+ imagePath: localPath,
713459
+ sourceSurface: "gui",
713460
+ scope,
713461
+ sender,
713462
+ message: message2,
713463
+ sessionId,
713464
+ media,
713465
+ payload: b
713466
+ });
713467
+ visualObjectMemory = {
713468
+ attempted: objectMemory.attempted,
713469
+ taught: objectMemory.taught,
713470
+ label: objectMemory.label,
713471
+ aliases: objectMemory.aliases,
713472
+ contextBlock: objectMemory.contextBlock,
713473
+ degradedReason: objectMemory.degradedReason
713474
+ };
713475
+ if (objectMemory.contextBlock)
713476
+ contextBlock += `
713477
+
713478
+ ${objectMemory.contextBlock}`;
713479
+ } catch (err) {
713480
+ visualObjectMemory = {
713481
+ attempted: true,
713482
+ taught: false,
713483
+ aliases: [],
713484
+ contextBlock: "",
713485
+ degradedReason: err instanceof Error ? err.message : String(err)
713486
+ };
713487
+ }
713003
713488
  }
713004
713489
  jsonResponse(res, 200, {
713005
713490
  id: ingestId,
@@ -713008,7 +713493,8 @@ ${association.contextBlock}`;
713008
713493
  mimeType,
713009
713494
  modality,
713010
713495
  contextBlock,
713011
- visualIdentity
713496
+ visualIdentity,
713497
+ visualObjectMemory
713012
713498
  });
713013
713499
  } catch (err) {
713014
713500
  jsonResponse(res, 400, {
@@ -713141,6 +713627,7 @@ async function handleMemoryIngest2(req3, res, ollamaUrl) {
713141
713627
  embeddings
713142
713628
  });
713143
713629
  let visualIdentity = void 0;
713630
+ let visualObjectMemory = void 0;
713144
713631
  if (modality === "visual" && mediaPath) {
713145
713632
  try {
713146
713633
  const { associateVisualIdentityFromImage: associateVisualIdentityFromImage2 } = await Promise.resolve().then(() => (init_visual_identity_association(), visual_identity_association_exports));
@@ -713174,8 +713661,40 @@ async function handleMemoryIngest2(req3, res, ollamaUrl) {
713174
713661
  degradedReason: err instanceof Error ? err.message : String(err)
713175
713662
  };
713176
713663
  }
713664
+ try {
713665
+ const { associateVisualObjectFromImage: associateVisualObjectFromImage2 } = await Promise.resolve().then(() => (init_visual_object_association(), visual_object_association_exports));
713666
+ const objectMemory = await associateVisualObjectFromImage2({
713667
+ repoRoot: process.cwd(),
713668
+ imagePath: mediaPath,
713669
+ sourceSurface,
713670
+ scope,
713671
+ sender,
713672
+ message: message2,
713673
+ replyTo,
713674
+ sessionId,
713675
+ media,
713676
+ extractedContent: b.extracted_content || b.extractedContent,
713677
+ payload: b
713678
+ });
713679
+ visualObjectMemory = {
713680
+ attempted: objectMemory.attempted,
713681
+ taught: objectMemory.taught,
713682
+ label: objectMemory.label,
713683
+ aliases: objectMemory.aliases,
713684
+ contextBlock: objectMemory.contextBlock,
713685
+ degradedReason: objectMemory.degradedReason
713686
+ };
713687
+ } catch (err) {
713688
+ visualObjectMemory = {
713689
+ attempted: true,
713690
+ taught: false,
713691
+ aliases: [],
713692
+ contextBlock: "",
713693
+ degradedReason: err instanceof Error ? err.message : String(err)
713694
+ };
713695
+ }
713177
713696
  }
713178
- jsonResponse(res, 200, { id: result.episodeId, ...result, visualIdentity });
713697
+ jsonResponse(res, 200, { id: result.episodeId, ...result, visualIdentity, visualObjectMemory });
713179
713698
  } catch (err) {
713180
713699
  jsonResponse(res, 400, {
713181
713700
  error: "bad_request",
@@ -713391,7 +713910,7 @@ __export(clipboard_media_exports, {
713391
713910
  pasteClipboardImageToFile: () => pasteClipboardImageToFile
713392
713911
  });
713393
713912
  import { execFileSync as execFileSync11, execSync as execSync61 } from "node:child_process";
713394
- import { mkdirSync as mkdirSync102, readFileSync as readFileSync130, rmSync as rmSync13, writeFileSync as writeFileSync87 } from "node:fs";
713913
+ import { mkdirSync as mkdirSync102, readFileSync as readFileSync130, rmSync as rmSync13, writeFileSync as writeFileSync86 } from "node:fs";
713395
713914
  import { join as join173 } from "node:path";
713396
713915
  function pasteClipboardImageToFile(repoRoot) {
713397
713916
  const image = readClipboardImage();
@@ -713399,7 +713918,7 @@ function pasteClipboardImageToFile(repoRoot) {
713399
713918
  const dir = join173(repoRoot, ".omnius", "clipboard");
713400
713919
  mkdirSync102(dir, { recursive: true });
713401
713920
  const path12 = join173(dir, `clipboard-${Date.now()}${image.ext}`);
713402
- writeFileSync87(path12, image.buffer);
713921
+ writeFileSync86(path12, image.buffer);
713403
713922
  return { path: path12, buffer: image.buffer, mime: image.mime };
713404
713923
  }
713405
713924
  function readClipboardImage() {
@@ -713468,7 +713987,7 @@ import { createRequire as createRequire9 } from "node:module";
713468
713987
  import { fileURLToPath as fileURLToPath21 } from "node:url";
713469
713988
  import {
713470
713989
  readFileSync as readFileSync131,
713471
- writeFileSync as writeFileSync88,
713990
+ writeFileSync as writeFileSync87,
713472
713991
  appendFileSync as appendFileSync17,
713473
713992
  rmSync as rmSync14,
713474
713993
  readdirSync as readdirSync57,
@@ -714595,7 +715114,7 @@ function createFanoutExploreTool(config, repoRoot, ctxWindowSize) {
714595
715114
  );
714596
715115
  if (debug) {
714597
715116
  try {
714598
- writeFileSync88(
715117
+ writeFileSync87(
714599
715118
  join174(repoRoot, ".omnius", "fanout-debug.json"),
714600
715119
  JSON.stringify({ objective, regions, rawReturns, digests }, null, 2)
714601
715120
  );
@@ -715381,7 +715900,7 @@ ${skillPack}`;
715381
715900
  You have vision capabilities. Choose the RIGHT tool for each situation:
715382
715901
 
715383
715902
  FOR IMAGE FILES (photos, screenshots, diagrams):
715384
- image_read(image="path") — Read an image file (base64 + OCR)
715903
+ image_read(path="path") — Read image metadata/base64/OCR; use vision(image="path") for semantic Moondream analysis
715385
715904
  vision(image="path", action="caption") — Describe image contents via Moondream
715386
715905
  vision(image="path", action="query", prompt="question") — Ask questions about an image
715387
715906
  vision(image="path", action="detect", prompt="object") — Find objects (bounding boxes)
@@ -718910,7 +719429,7 @@ This is an independent background session started from /background.`
718910
719429
  if (Math.random() < 0.02) {
718911
719430
  const all2 = readFileSync131(HISTORY_FILE, "utf8").trim().split("\n");
718912
719431
  if (all2.length > MAX_HISTORY_LINES) {
718913
- writeFileSync88(
719432
+ writeFileSync87(
718914
719433
  HISTORY_FILE,
718915
719434
  all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n",
718916
719435
  "utf8"
@@ -719851,7 +720370,7 @@ Log: ${nexusLogPath}`
719851
720370
  sessionTitle = title.trim() || null;
719852
720371
  try {
719853
720372
  mkdirSync103(join174(repoRoot, ".omnius"), { recursive: true });
719854
- writeFileSync88(
720373
+ writeFileSync87(
719855
720374
  join174(repoRoot, ".omnius", "session-title"),
719856
720375
  `${sessionTitle ?? ""}
719857
720376
  `,
@@ -719945,7 +720464,7 @@ Log: ${nexusLogPath}`
719945
720464
  queuePrompt(
719946
720465
  imageContext ? `${imageContext}
719947
720466
 
719948
- The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analysis, and ASCII preview above to understand and respond to the image content.` : `The user pasted a clipboard image saved at ${relPath}. Read it with image_read or vision and respond to what is shown.`,
720467
+ The user pasted a clipboard image saved at ${relPath}. Use the OCR, vision analysis, and ASCII preview above to understand and respond to the image content.` : `The user pasted a clipboard image saved at ${relPath}. Use vision(image="${relPath}") for semantic Moondream analysis, and image_read(path="${relPath}") only if metadata/OCR/raw ingress is needed.`,
719949
720468
  "clipboard"
719950
720469
  );
719951
720470
  return {
@@ -722575,7 +723094,7 @@ ${result.text}`;
722575
723094
 
722576
723095
  ${imageContext}
722577
723096
 
722578
- Read it with image_read or vision if more detail is needed. Describe what you see and extract any text with OCR if relevant.` : `The user has provided an image file: ${cleanPath}. Read it with image_read and describe what you see. Extract any text with OCR if relevant.`;
723097
+ Use vision(image="${cleanPath}") for additional semantic detail, and image_read(path="${cleanPath}") only if metadata/OCR/raw ingress is needed.` : `The user has provided an image file: ${cleanPath}. Use vision(image="${cleanPath}") for semantic Moondream analysis. Use image_read(path="${cleanPath}") only if metadata/OCR/raw ingress is needed.`;
722579
723098
  }
722580
723099
  if (isMedia && fullInput === input && (voiceEngine.isLuxtts || voiceEngine.isMisotts)) {
722581
723100
  const cloneExts = [".wav", ".mp3", ".ogg", ".flac", ".m4a"];
@@ -723303,7 +723822,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723303
723822
  );
723304
723823
  ikState.session_count = (ikState.session_count || 0) + 1;
723305
723824
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
723306
- writeFileSync88(ikFile, JSON.stringify(ikState, null, 2));
723825
+ writeFileSync87(ikFile, JSON.stringify(ikState, null, 2));
723307
723826
  } catch (ikErr) {
723308
723827
  }
723309
723828
  try {
@@ -723336,7 +723855,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723336
723855
  });
723337
723856
  if (variants.length > 50) variants = variants.slice(-50);
723338
723857
  mkdirSync103(archeDir, { recursive: true });
723339
- writeFileSync88(archeFile, JSON.stringify(variants, null, 2));
723858
+ writeFileSync87(archeFile, JSON.stringify(variants, null, 2));
723340
723859
  } catch {
723341
723860
  }
723342
723861
  }
@@ -723367,7 +723886,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723367
723886
  updated = true;
723368
723887
  }
723369
723888
  if (updated) {
723370
- writeFileSync88(metaFile2, JSON.stringify(store2, null, 2));
723889
+ writeFileSync87(metaFile2, JSON.stringify(store2, null, 2));
723371
723890
  }
723372
723891
  }
723373
723892
  } catch {
@@ -723476,7 +723995,7 @@ Rules:
723476
723995
  });
723477
723996
  if (store2.length > 100) store2 = store2.slice(-100);
723478
723997
  mkdirSync103(metaDir, { recursive: true });
723479
- writeFileSync88(storeFile, JSON.stringify(store2, null, 2));
723998
+ writeFileSync87(storeFile, JSON.stringify(store2, null, 2));
723480
723999
  }
723481
724000
  }
723482
724001
  } catch {
@@ -723540,7 +724059,7 @@ Rules:
723540
724059
  );
723541
724060
  ikState.session_count = (ikState.session_count || 0) + 1;
723542
724061
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
723543
- writeFileSync88(ikFile, JSON.stringify(ikState, null, 2));
724062
+ writeFileSync87(ikFile, JSON.stringify(ikState, null, 2));
723544
724063
  }
723545
724064
  const metaFile2 = join174(
723546
724065
  repoRoot,
@@ -723568,7 +724087,7 @@ Rules:
723568
724087
  (item.scores.confidence || 0.5) - 0.02
723569
724088
  );
723570
724089
  }
723571
- writeFileSync88(metaFile2, JSON.stringify(store2, null, 2));
724090
+ writeFileSync87(metaFile2, JSON.stringify(store2, null, 2));
723572
724091
  }
723573
724092
  try {
723574
724093
  const archeDir = join174(repoRoot, ".omnius", "arche");
@@ -723591,7 +724110,7 @@ Rules:
723591
724110
  });
723592
724111
  if (variants.length > 50) variants = variants.slice(-50);
723593
724112
  mkdirSync103(archeDir, { recursive: true });
723594
- writeFileSync88(archeFile, JSON.stringify(variants, null, 2));
724113
+ writeFileSync87(archeFile, JSON.stringify(variants, null, 2));
723595
724114
  } catch {
723596
724115
  }
723597
724116
  } catch {
@@ -723704,7 +724223,7 @@ import { resolve as resolve68 } from "node:path";
723704
724223
  import { spawn as spawn34 } from "node:child_process";
723705
724224
  import {
723706
724225
  mkdirSync as mkdirSync104,
723707
- writeFileSync as writeFileSync89,
724226
+ writeFileSync as writeFileSync88,
723708
724227
  readFileSync as readFileSync132,
723709
724228
  readdirSync as readdirSync58,
723710
724229
  existsSync as existsSync162
@@ -723844,7 +724363,7 @@ async function runBackground(task, config, opts) {
723844
724363
  }
723845
724364
  });
723846
724365
  job.pid = child.pid ?? 0;
723847
- writeFileSync89(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
724366
+ writeFileSync88(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
723848
724367
  let output = "";
723849
724368
  child.stdout?.on("data", (chunk) => {
723850
724369
  output += chunk.toString();
@@ -723860,7 +724379,7 @@ async function runBackground(task, config, opts) {
723860
724379
  job.summary = result.summary;
723861
724380
  job.durationMs = result.durationMs;
723862
724381
  job.error = result.error;
723863
- writeFileSync89(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
724382
+ writeFileSync88(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
723864
724383
  } catch {
723865
724384
  }
723866
724385
  });
@@ -724499,7 +725018,7 @@ __export(eval_exports, {
724499
725018
  evalCommand: () => evalCommand
724500
725019
  });
724501
725020
  import { tmpdir as tmpdir23 } from "node:os";
724502
- import { mkdirSync as mkdirSync105, writeFileSync as writeFileSync90 } from "node:fs";
725021
+ import { mkdirSync as mkdirSync105, writeFileSync as writeFileSync89 } from "node:fs";
724503
725022
  import { join as join177 } from "node:path";
724504
725023
  async function evalCommand(opts, config) {
724505
725024
  const suiteName = opts.suite ?? "basic";
@@ -724631,7 +725150,7 @@ async function evalCommand(opts, config) {
724631
725150
  function createTempEvalRepo() {
724632
725151
  const dir = join177(tmpdir23(), `omnius-eval-${Date.now()}`);
724633
725152
  mkdirSync105(dir, { recursive: true });
724634
- writeFileSync90(
725153
+ writeFileSync89(
724635
725154
  join177(dir, "package.json"),
724636
725155
  JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n",
724637
725156
  "utf8"
@@ -724868,8 +725387,16 @@ function routeCommand(command) {
724868
725387
  }
724869
725388
  function parseCliArgs(argv) {
724870
725389
  const args = argv.slice(2);
725390
+ const parseInput = [...args];
725391
+ const selfTestFlagIndex = parseInput.indexOf("--self-test");
725392
+ if (selfTestFlagIndex >= 0) {
725393
+ const next = parseInput[selfTestFlagIndex + 1];
725394
+ if (!next || next.startsWith("-")) {
725395
+ parseInput.splice(selfTestFlagIndex, 1, "--self-test=crossmodal");
725396
+ }
725397
+ }
724871
725398
  const { values, positionals } = nodeParseArgs2({
724872
- args,
725399
+ args: parseInput,
724873
725400
  options: {
724874
725401
  "dry-run": { type: "boolean" },
724875
725402
  verbose: { type: "boolean", short: "v" },
@@ -724886,6 +725413,7 @@ function parseCliArgs(argv) {
724886
725413
  live: { type: "boolean" },
724887
725414
  json: { type: "boolean", short: "j" },
724888
725415
  background: { type: "boolean" },
725416
+ "self-test": { type: "string" },
724889
725417
  help: { type: "boolean", short: "h" },
724890
725418
  version: { type: "boolean", short: "V" }
724891
725419
  },
@@ -724908,6 +725436,7 @@ function parseCliArgs(argv) {
724908
725436
  local: values.local === true,
724909
725437
  json: values.json === true,
724910
725438
  background: values.background === true,
725439
+ selfTest: typeof values["self-test"] === "string" ? values["self-test"] : void 0,
724911
725440
  help: values.help === true,
724912
725441
  version: values.version === true
724913
725442
  };
@@ -725013,6 +725542,23 @@ Examples:
725013
725542
  `.trim();
725014
725543
  process.stdout.write(text2 + "\n");
725015
725544
  }
725545
+ async function runSelfTest(mode) {
725546
+ if (mode !== "crossmodal") {
725547
+ throw new Error(`Unknown self-test mode: ${mode}`);
725548
+ }
725549
+ process.stdout.write("Running crossmodal smoke tests...\n");
725550
+ const { spawn: spawn35 } = await import("node:child_process");
725551
+ const run2 = (file) => new Promise((resolve71, reject) => {
725552
+ const p2 = spawn35(process.execPath, [file], { stdio: ["ignore", "pipe", "pipe"] });
725553
+ p2.stdout.on("data", (d2) => process.stdout.write(d2));
725554
+ p2.stderr.on("data", (d2) => process.stdout.write(d2));
725555
+ onChildExit(p2, (code8) => code8 === 0 ? resolve71() : reject(new Error(`${file} exited ${code8}`)));
725556
+ });
725557
+ const base3 = process.cwd();
725558
+ await run2(`${base3}/eval/test-crossmodal.mjs`);
725559
+ await run2(`${base3}/eval/test-memory-search.mjs`);
725560
+ process.stdout.write("Self-test complete.\n");
725561
+ }
725016
725562
  async function main() {
725017
725563
  const version4 = getVersion5();
725018
725564
  const parsed = parseCliArgs(process.argv);
@@ -725026,6 +725572,10 @@ async function main() {
725026
725572
  printHelp2(version4);
725027
725573
  return;
725028
725574
  }
725575
+ if (parsed.selfTest) {
725576
+ await runSelfTest(parsed.selfTest);
725577
+ return;
725578
+ }
725029
725579
  if (!parsed.json) {
725030
725580
  const updateInfo = await checkForUpdate(version4);
725031
725581
  if (updateInfo) {
@@ -725044,27 +725594,6 @@ async function main() {
725044
725594
  });
725045
725595
  try {
725046
725596
  switch (parsed.command) {
725047
- case void 0: {
725048
- if (process.argv.includes("--self-test")) {
725049
- const mode = process.argv[process.argv.indexOf("--self-test") + 1] || "crossmodal";
725050
- if (mode === "crossmodal") {
725051
- process.stdout.write("Running crossmodal smoke tests...\n");
725052
- const { spawn: spawn35 } = await import("node:child_process");
725053
- const run2 = (file) => new Promise((resolve71, reject) => {
725054
- const p2 = spawn35(process.execPath, [file], { stdio: ["ignore", "pipe", "pipe"] });
725055
- p2.stdout.on("data", (d2) => process.stdout.write(d2));
725056
- p2.stderr.on("data", (d2) => process.stdout.write(d2));
725057
- onChildExit(p2, (code8) => code8 === 0 ? resolve71() : reject(new Error(`${file} exited ${code8}`)));
725058
- });
725059
- const base3 = process.cwd();
725060
- await run2(`${base3}/eval/test-crossmodal.mjs`);
725061
- await run2(`${base3}/eval/test-memory-search.mjs`);
725062
- process.stdout.write("Self-test complete.\n");
725063
- return;
725064
- }
725065
- }
725066
- break;
725067
- }
725068
725597
  case "run": {
725069
725598
  const { runCommand: runCommand3 } = await Promise.resolve().then(() => (init_run(), run_exports));
725070
725599
  await runCommand3(