omnius 1.0.367 → 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;
@@ -558608,31 +558807,114 @@ function isFailedVerificationEvidence(entry) {
558608
558807
  const text2 = `${entry.toolName ?? ""} ${entry.summary}`.toLowerCase();
558609
558808
  return /\b(test|tests|verify|verification|build|compile|tsc|vitest|jest|pytest|go test|cargo test)\b/.test(text2);
558610
558809
  }
558810
+ function verificationFamily(entry) {
558811
+ const text2 = `${entry.toolName ?? ""} ${entry.summary}`.toLowerCase();
558812
+ if (/\b(vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|node\s+tests?|test|tests|spec)\b/.test(text2)) {
558813
+ return "test";
558814
+ }
558815
+ if (/\b(typecheck|tsc)\b/.test(text2))
558816
+ return "typecheck";
558817
+ if (/\b(build|built|compile|compiled)\b/.test(text2))
558818
+ return "build";
558819
+ if (/\b(verify|verified|verification)\b/.test(text2))
558820
+ return "verify";
558821
+ return null;
558822
+ }
558611
558823
  function isStaleEditEvidence(entry) {
558612
558824
  if (entry.success !== false)
558613
558825
  return false;
558614
558826
  const text2 = entry.summary.toLowerCase();
558615
558827
  return /stale edit loop blocked|old[_ -]?string|old text|expected[_ -]?hash|context mismatch|no occurrences|could not find/.test(text2);
558616
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
+ }
558617
558887
  function finalizeCompletionLedgerTruth(ledger) {
558618
558888
  let unresolved = [...ledger.unresolved];
558619
- let lastMutation = -1;
558889
+ let lastVerificationInvalidatingMutation = -1;
558620
558890
  let lastVerification = -1;
558891
+ const lastSuccessfulVerificationByFamily = /* @__PURE__ */ new Map();
558621
558892
  ledger.evidence.forEach((entry, index) => {
558622
- if (isMutationEvidence(entry))
558623
- lastMutation = index;
558624
- if (isSuccessfulVerificationEvidence(entry))
558893
+ if (isVerificationInvalidatingMutation(entry))
558894
+ lastVerificationInvalidatingMutation = index;
558895
+ if (isSuccessfulVerificationEvidence(entry)) {
558625
558896
  lastVerification = index;
558897
+ const family = verificationFamily(entry);
558898
+ if (family)
558899
+ lastSuccessfulVerificationByFamily.set(family, index);
558900
+ }
558901
+ });
558902
+ ledger.evidence.forEach((entry, index) => {
558626
558903
  if (isFailedVerificationEvidence(entry)) {
558627
- unresolved = appendUnresolved(unresolved, `Verification failed or did not prove success: ${entry.summary}`, entry.id);
558904
+ const family = verificationFamily(entry);
558905
+ const resolvedByLaterSuccess = family !== null && (lastSuccessfulVerificationByFamily.get(family) ?? -1) > index;
558906
+ if (!resolvedByLaterSuccess) {
558907
+ unresolved = appendUnresolved(unresolved, `Verification failed or did not prove success: ${entry.summary}`, entry.id);
558908
+ }
558628
558909
  }
558629
- if (isStaleEditEvidence(entry)) {
558910
+ if (isStaleEditEvidence(entry) && !staleEditResolvedByLaterMutation(ledger.evidence, entry, index)) {
558630
558911
  unresolved = appendUnresolved(unresolved, `Stale edit failure remains unresolved: ${entry.summary}`, entry.id);
558631
558912
  }
558632
558913
  });
558633
- if (lastVerification >= 0 && lastMutation > lastVerification) {
558914
+ if (lastVerification >= 0 && lastVerificationInvalidatingMutation > lastVerification) {
558634
558915
  unresolved = appendUnresolved(unresolved, "File changes occurred after the last successful verification; final verification is stale.", "verification_freshness");
558635
558916
  }
558917
+ unresolved = appendTestCountOverclaims(ledger, unresolved);
558636
558918
  const status = ledger.status === "blocked" || ledger.status === "request_changes" ? ledger.status : unresolved.length > 0 ? "incomplete_verification" : ledger.status;
558637
558919
  return {
558638
558920
  ...ledger,
@@ -564275,12 +564557,12 @@ var init_reflectionBuffer = __esm({
564275
564557
  if (!this.persistPath)
564276
564558
  return;
564277
564559
  try {
564278
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
564560
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
564279
564561
  const { join: join179 } = __require("node:path");
564280
564562
  const dir = join179(this.persistPath, "..");
564281
564563
  if (!existsSync164(dir))
564282
564564
  mkdirSync106(dir, { recursive: true });
564283
- writeFileSync91(this.persistPath, JSON.stringify(this.state, null, 2));
564565
+ writeFileSync90(this.persistPath, JSON.stringify(this.state, null, 2));
564284
564566
  } catch {
564285
564567
  }
564286
564568
  }
@@ -567891,12 +568173,12 @@ var init_adversaryStream = __esm({
567891
568173
  if (!this.persistPath)
567892
568174
  return;
567893
568175
  try {
567894
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
568176
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106, existsSync: existsSync164 } = __require("node:fs");
567895
568177
  const { dirname: dirname54 } = __require("node:path");
567896
568178
  const dir = dirname54(this.persistPath);
567897
568179
  if (!existsSync164(dir))
567898
568180
  mkdirSync106(dir, { recursive: true });
567899
- writeFileSync91(this.persistPath, JSON.stringify({ ledger: this.ledger }, null, 2));
568181
+ writeFileSync90(this.persistPath, JSON.stringify({ ledger: this.ledger }, null, 2));
567900
568182
  } catch {
567901
568183
  }
567902
568184
  }
@@ -568816,6 +569098,16 @@ function classifyShellIntent(cmd) {
568816
569098
  } else {
568817
569099
  verbToken = first2;
568818
569100
  }
569101
+ if (isRunner && verbToken === "run" && tokens.length >= 3) {
569102
+ const scriptToken = tokens[2].toLowerCase().replace(/^["']|["']$/g, "");
569103
+ const scriptRow = VERB_TABLE[scriptToken];
569104
+ if (scriptRow) {
569105
+ return { klass: scriptRow.klass, verb: scriptRow.canonical, tool: first2 };
569106
+ }
569107
+ if (/\b(test|spec|vitest|jest|pytest|check|verify|lint|typecheck)\b/i.test(scriptToken)) {
569108
+ return { klass: "verify", verb: "test", tool: first2 };
569109
+ }
569110
+ }
568819
569111
  const row = VERB_TABLE[verbToken];
568820
569112
  if (!row)
568821
569113
  return { klass: "other", verb: verbToken, tool: first2 };
@@ -568831,7 +569123,7 @@ function verifyShellOutcome(command, ctx3, result) {
568831
569123
  if (!result.success) {
568832
569124
  return { trustworthy: true, intentBucket: baseBucket, outcomeClass: "broken" };
568833
569125
  }
568834
- if (intent.klass === "read") {
569126
+ if (intent.klass === "read" || intent.klass === "verify") {
568835
569127
  return { trustworthy: true, intentBucket: baseBucket, outcomeClass: "verified" };
568836
569128
  }
568837
569129
  const mtimeCheck = checkMutateMtimeDelta(intent, cwd4, ctx3, result);
@@ -569169,13 +569461,17 @@ var init_postActionVerifier = __esm({
569169
569461
  build: { canonical: "build", klass: "mutate" },
569170
569462
  compile: { canonical: "build", klass: "mutate" },
569171
569463
  make: { canonical: "build", klass: "mutate" },
569172
- // ── test family (mutate typically writes coverage/snapshots) ────────────
569173
- test: { canonical: "test", klass: "mutate" },
569174
- t: { canonical: "test", klass: "mutate" },
569175
- spec: { canonical: "test", klass: "mutate" },
569176
- vitest: { canonical: "test", klass: "mutate" },
569177
- jest: { canonical: "test", klass: "mutate" },
569178
- pytest: { canonical: "test", klass: "mutate" },
569464
+ // ── verification family (read-only by contract for the verifier) ──────────
569465
+ test: { canonical: "test", klass: "verify" },
569466
+ t: { canonical: "test", klass: "verify" },
569467
+ spec: { canonical: "test", klass: "verify" },
569468
+ vitest: { canonical: "test", klass: "verify" },
569469
+ jest: { canonical: "test", klass: "verify" },
569470
+ pytest: { canonical: "test", klass: "verify" },
569471
+ check: { canonical: "check", klass: "verify" },
569472
+ verify: { canonical: "verify", klass: "verify" },
569473
+ lint: { canonical: "lint", klass: "verify" },
569474
+ typecheck: { canonical: "typecheck", klass: "verify" },
569179
569475
  // ── publish / scaffold / generate (mutate) ────────────────────────────────
569180
569476
  publish: { canonical: "publish", klass: "mutate" },
569181
569477
  init: { canonical: "init", klass: "mutate" },
@@ -571912,6 +572208,40 @@ Pick the SMALLEST concrete deliverable from the spec — typically the project e
571912
572208
  return false;
571913
572209
  return true;
571914
572210
  }
572211
+ _toolEvidencePreview(result, displayOutput, max = 500) {
572212
+ const modelVisible = result.llmContent ?? result.output ?? displayOutput ?? "";
572213
+ const failurePrefix = result.success === false && result.error ? `Error: ${result.error}` : "";
572214
+ const combined = failurePrefix && modelVisible && !String(modelVisible).startsWith(failurePrefix) ? `${failurePrefix}
572215
+ ${modelVisible}` : modelVisible || result.error || displayOutput || "";
572216
+ return String(combined).slice(0, max);
572217
+ }
572218
+ _recordCompletionToolEvidenceFromResult(input) {
572219
+ if (!this._completionLedger || input.toolName === "task_complete")
572220
+ return;
572221
+ const realFileMutation = input.realFileMutation ?? this._isRealProjectMutation(input.toolName, input.result);
572222
+ const attemptedTargetPaths = this._extractToolTargetPaths(input.toolName, input.args, input.result);
572223
+ const realMutationPaths = input.realMutationPaths ?? (realFileMutation ? attemptedTargetPaths : []);
572224
+ this._completionLedger = recordToolEvidence(this._completionLedger, {
572225
+ name: input.toolName,
572226
+ success: input.result.success,
572227
+ outputPreview: (input.outputPreview ?? this._toolEvidencePreview(input.result)).toString().slice(0, 500),
572228
+ argsKey: input.argsKey.slice(0, 300),
572229
+ targetPaths: attemptedTargetPaths
572230
+ });
572231
+ if (realFileMutation && realMutationPaths.length > 0) {
572232
+ for (const filePath of realMutationPaths) {
572233
+ this._completionLedger = recordCompletionEvidence(this._completionLedger, {
572234
+ kind: "file_change",
572235
+ toolName: input.toolName,
572236
+ success: true,
572237
+ summary: `file change: ${filePath}`,
572238
+ rawRef: `file://${filePath}`,
572239
+ targetPaths: [filePath]
572240
+ });
572241
+ }
572242
+ }
572243
+ this._saveCompletionLedgerSafe();
572244
+ }
571915
572245
  _isAtomicBatchEditAbort(toolName, result) {
571916
572246
  if (toolName !== "batch_edit" || !result || result.success !== false) {
571917
572247
  return false;
@@ -572605,10 +572935,20 @@ ${_checks}`
572605
572935
  const maxCycles = parseInt(process.env["OMNIUS_BACKWARD_PASS_MAX_CYCLES"] || "2", 10) || 2;
572606
572936
  if (this._backwardPassCyclesUsed >= maxCycles) {
572607
572937
  const lastCritique2 = this._lastBackwardPassCritique;
572608
- const concern = lastCritique2?.rationale?.trim() || this._lastBackwardPassVerdict || "unspecified";
572938
+ const evidenceAfterCritique = lastCritique2 ? toolCallLog.slice(lastCritique2.toolLogLength) : [];
572939
+ const freshVerification = evidenceAfterCritique.filter((entry) => {
572940
+ if (entry.success !== true)
572941
+ return false;
572942
+ const text2 = `${entry.name} ${entry.argsKey} ${entry.outputPreview ?? ""}`.toLowerCase();
572943
+ return /\b(test|tests|vitest|jest|pytest|go test|cargo test|npm test|pnpm test|yarn test|typecheck|tsc|build|verify|verification)\b/.test(text2) && /\b(pass|passed|success|exit:?\s*0|ok|0 failures|0 failed)\b/.test(text2);
572944
+ }).slice(-3);
572945
+ const concern = freshVerification.length > 0 ? [
572946
+ "Prior reviewer concern may be stale; fresh verification evidence was recorded after that critique.",
572947
+ ...freshVerification.map((entry) => `${entry.name}: ${(entry.outputPreview ?? entry.argsKey).slice(0, 220)}`)
572948
+ ].join(" ") : lastCritique2?.rationale?.trim() || this._lastBackwardPassVerdict || "unspecified";
572609
572949
  this._completionCaveat = [
572610
572950
  `[COMPLETION CAVEAT] Backward-pass review did not fully approve after ${this._backwardPassCyclesUsed}/${maxCycles} cycle(s).`,
572611
- `Unresolved reviewer concern: ${concern.slice(0, 600)}`,
572951
+ `${freshVerification.length > 0 ? "Reviewer reconciliation note" : "Unresolved reviewer concern"}: ${concern.slice(0, 600)}`,
572612
572952
  "The work was completed with deliverables present; treat the above as follow-up rather than a blocker."
572613
572953
  ].join("\n");
572614
572954
  this.emit({
@@ -575314,7 +575654,8 @@ Rewrite it now for ${ctx3.model}.`;
575314
575654
  run_in_background: ["background", "background_run", "async"],
575315
575655
  max_turns: ["maxTurns", "turns"],
575316
575656
  timeout_ms: ["timeoutMs", "timeout"],
575317
- path: ["file", "filepath", "file_path", "filename"],
575657
+ path: ["file", "image", "media", "filepath", "file_path", "filename"],
575658
+ image: ["path", "file", "media", "filepath", "file_path", "filename"],
575318
575659
  command: ["cmd", "shell_command"],
575319
575660
  query: ["prompt", "task", "message", "input", "text"]
575320
575661
  };
@@ -580161,7 +580502,7 @@ ${bookkeepingGuidance}` : bookkeepingGuidance;
580161
580502
  currentLogEntry.success = result.success;
580162
580503
  currentLogEntry.mutated = realFileMutation;
580163
580504
  currentLogEntry.mutatedFiles = realMutationPaths;
580164
- currentLogEntry.outputPreview = (result.success ? result.llmContent ?? result.output ?? output : result.error ?? result.output ?? output).toString().slice(0, 500);
580505
+ currentLogEntry.outputPreview = this._toolEvidencePreview(result, output);
580165
580506
  }
580166
580507
  this._toolEvents.push({
580167
580508
  name: tc.name,
@@ -580502,26 +580843,15 @@ Then use file_read on individual FILES inside it.`);
580502
580843
  lastFailureHandoffTurn = turn;
580503
580844
  }
580504
580845
  }
580505
- if (this._completionLedger && tc.name !== "task_complete") {
580506
- this._completionLedger = recordToolEvidence(this._completionLedger, {
580507
- name: tc.name,
580508
- success: result.success,
580509
- outputPreview: (result.success ? result.llmContent ?? result.output ?? output : result.error ?? result.output ?? output).toString().slice(0, 500),
580510
- argsKey: tc.arguments ? JSON.stringify(tc.arguments).slice(0, 300) : ""
580511
- });
580512
- if (realFileMutation && realMutationPaths.length > 0) {
580513
- for (const filePath2 of realMutationPaths) {
580514
- this._completionLedger = recordCompletionEvidence(this._completionLedger, {
580515
- kind: "file_change",
580516
- toolName: tc.name,
580517
- success: true,
580518
- summary: `file change: ${filePath2}`,
580519
- rawRef: `file://${filePath2}`
580520
- });
580521
- }
580522
- }
580523
- this._saveCompletionLedgerSafe();
580524
- }
580846
+ this._recordCompletionToolEvidenceFromResult({
580847
+ toolName: tc.name,
580848
+ argsKey: tc.arguments ? JSON.stringify(tc.arguments) : "",
580849
+ args: tc.arguments,
580850
+ result,
580851
+ outputPreview: this._toolEvidencePreview(result, output),
580852
+ realFileMutation,
580853
+ realMutationPaths
580854
+ });
580525
580855
  this._onTypedEvent?.({
580526
580856
  type: "tool_call_finished",
580527
580857
  runId: this._sessionId ?? "unknown",
@@ -581617,6 +581947,15 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
581617
581947
  } else {
581618
581948
  output = modelContent2;
581619
581949
  }
581950
+ const bfRealFileMutation = this._isRealProjectMutation(tc.name, result);
581951
+ const bfRealMutationPaths = bfRealFileMutation ? this._extractToolTargetPaths(tc.name, tc.arguments, result) : [];
581952
+ const bfLogEntry = toolCallLog[toolCallLog.length - 1];
581953
+ if (bfLogEntry) {
581954
+ bfLogEntry.success = result.success;
581955
+ bfLogEntry.mutated = bfRealFileMutation;
581956
+ bfLogEntry.mutatedFiles = bfRealMutationPaths;
581957
+ bfLogEntry.outputPreview = this._toolEvidencePreview(result, output);
581958
+ }
581620
581959
  this.emit({
581621
581960
  type: "tool_result",
581622
581961
  toolName: tc.name,
@@ -581626,14 +581965,15 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
581626
581965
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
581627
581966
  });
581628
581967
  this._trackDecomp2(tc, result, turn);
581629
- if (this._completionLedger && tc.name !== "task_complete") {
581630
- this._completionLedger = recordToolEvidence(this._completionLedger, {
581631
- name: tc.name,
581632
- success: result.success,
581633
- outputPreview: output.slice(0, 500),
581634
- argsKey: bfArgsKey.slice(0, 300)
581635
- });
581636
- }
581968
+ this._recordCompletionToolEvidenceFromResult({
581969
+ toolName: tc.name,
581970
+ argsKey: bfArgsKey,
581971
+ args: tc.arguments,
581972
+ result,
581973
+ outputPreview: this._toolEvidencePreview(result, output),
581974
+ realFileMutation: bfRealFileMutation,
581975
+ realMutationPaths: bfRealMutationPaths
581976
+ });
581637
581977
  const enoentTools2 = /* @__PURE__ */ new Set([
581638
581978
  "file_read",
581639
581979
  "list_directory",
@@ -582341,7 +582681,7 @@ ${caveat}` : caveat;
582341
582681
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
582342
582682
  });
582343
582683
  try {
582344
- 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");
582345
582685
  const { join: join179 } = __require("node:path");
582346
582686
  const contextDir = join179(this._workingDirectory || process.cwd(), ".omnius", "context");
582347
582687
  mkdirSync106(contextDir, { recursive: true });
@@ -582388,14 +582728,14 @@ ${caveat}` : caveat;
582388
582728
  mkdirSync106(kgSummaryDir, { recursive: true });
582389
582729
  const summaryFilename = `kg-summary-${this._sessionId}.md`;
582390
582730
  const outPath = join179(kgSummaryDir, summaryFilename);
582391
- writeFileSync91(outPath, lines.join("\n"), "utf-8");
582392
- writeFileSync91(join179(kgSummaryDir, "latest.md"), lines.join("\n"), "utf-8");
582393
- 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`), [
582394
582734
  "Latest KG summary moved to `.omnius/context/kg-summary/latest.md`.",
582395
582735
  "",
582396
582736
  lines.join("\n")
582397
582737
  ].join("\n"), "utf-8");
582398
- writeFileSync91(join179(kgSummaryDir, "index.json"), JSON.stringify({
582738
+ writeFileSync90(join179(kgSummaryDir, "index.json"), JSON.stringify({
582399
582739
  schema: "omnius.kg-summary-index.v1",
582400
582740
  latest: "latest.md",
582401
582741
  latestSessionFile: summaryFilename,
@@ -583232,7 +583572,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
583232
583572
  if (!this._workingDirectory)
583233
583573
  return;
583234
583574
  try {
583235
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = __require("node:fs");
583575
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = __require("node:fs");
583236
583576
  const { join: join179 } = __require("node:path");
583237
583577
  const sessionDir2 = this.options.stateDir ? join179(this.omniusStateDir(), "session", this._sessionId) : join179(this._workingDirectory, ".omnius", "session", this._sessionId);
583238
583578
  mkdirSync106(sessionDir2, { recursive: true });
@@ -583247,7 +583587,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
583247
583587
  memexEntryCount: this._memexArchive.size,
583248
583588
  fileRegistrySize: this._fileRegistry.size
583249
583589
  };
583250
- writeFileSync91(join179(sessionDir2, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
583590
+ writeFileSync90(join179(sessionDir2, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
583251
583591
  } catch {
583252
583592
  }
583253
583593
  }
@@ -583384,7 +583724,7 @@ ${tail}`;
583384
583724
  const analysis = await this.analyzeImageDataForContext(image.mime, image.base64, image.textWithoutImage.slice(0, 2e3));
583385
583725
  const imageNote = analysis.contextBlock ? `${analysis.contextBlock}
583386
583726
 
583387
- 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.]`;
583388
583728
  return {
583389
583729
  ...result,
583390
583730
  llmContent: `${image.textWithoutImage.trim()}
@@ -583420,7 +583760,7 @@ ${imageNote}`.trim()
583420
583760
  const imageUrl = `data:${mime};base64,${base642}`;
583421
583761
  const tmpImgPath = this.writeTempImageForOcr(mime, base642);
583422
583762
  const [visionOutcome, ocrOutcome] = await Promise.allSettled([
583423
- this.describeImageViaVisionSubagent(imageUrl, textContent),
583763
+ tmpImgPath ? this.describeImageViaPrimaryVision(tmpImgPath, imageUrl, textContent) : this.describeImageViaVisionSubagent(imageUrl, textContent),
583424
583764
  tmpImgPath ? this.extractImageOcrText(tmpImgPath) : Promise.resolve("")
583425
583765
  ]);
583426
583766
  const visionDesc = visionOutcome.status === "fulfilled" ? visionOutcome.value.trim() : "";
@@ -583435,6 +583775,26 @@ ${imageNote}`.trim()
583435
583775
  const errorReason = visionOutcome.status === "rejected" ? String(visionOutcome.reason?.message ?? visionOutcome.reason) : void 0;
583436
583776
  return { contextBlock: "", errorReason };
583437
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
+ }
583438
583798
  async describeImageViaVisionSubagent(imageUrl, textContent) {
583439
583799
  const visionMessages = [
583440
583800
  {
@@ -585907,12 +586267,12 @@ ${result}`
585907
586267
  let resizedBase64 = null;
585908
586268
  try {
585909
586269
  const { execSync: execSync63 } = await import("node:child_process");
585910
- const { writeFileSync: writeFileSync91, readFileSync: readFileSync133, unlinkSync: unlinkSync36 } = await import("node:fs");
586270
+ const { writeFileSync: writeFileSync90, readFileSync: readFileSync133, unlinkSync: unlinkSync36 } = await import("node:fs");
585911
586271
  const { join: join179 } = await import("node:path");
585912
586272
  const { tmpdir: tmpdir24 } = await import("node:os");
585913
586273
  const tmpIn = join179(tmpdir24(), `omnius_img_in_${Date.now()}.png`);
585914
586274
  const tmpOut = join179(tmpdir24(), `omnius_img_out_${Date.now()}.jpg`);
585915
- writeFileSync91(tmpIn, buffer2);
586275
+ writeFileSync90(tmpIn, buffer2);
585916
586276
  const pyBin = process.platform === "win32" ? "python" : "python3";
585917
586277
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
585918
586278
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
@@ -594893,10 +595253,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
594893
595253
  wordTimestamps: false
594894
595254
  });
594895
595255
  if (outputDir2) {
594896
- const { basename: basename39 } = await import("node:path");
595256
+ const { basename: basename40 } = await import("node:path");
594897
595257
  const transcriptDir = join120(outputDir2, ".omnius", "transcripts");
594898
595258
  mkdirSync63(transcriptDir, { recursive: true });
594899
- const outFile = join120(transcriptDir, `${basename39(filePath)}.txt`);
595259
+ const outFile = join120(transcriptDir, `${basename40(filePath)}.txt`);
594900
595260
  writeFileSync53(outFile, result.text, "utf-8");
594901
595261
  }
594902
595262
  return {
@@ -594912,10 +595272,10 @@ transcribe-cli error: ${transcribeCliError}` : "";
594912
595272
  const fb = await transcribeFileViaWhisper(filePath, this.config.model);
594913
595273
  if (fb) {
594914
595274
  if (outputDir2) {
594915
- const { basename: basename39 } = await import("node:path");
595275
+ const { basename: basename40 } = await import("node:path");
594916
595276
  const transcriptDir = join120(outputDir2, ".omnius", "transcripts");
594917
595277
  mkdirSync63(transcriptDir, { recursive: true });
594918
- const outFile = join120(transcriptDir, `${basename39(filePath)}.txt`);
595278
+ const outFile = join120(transcriptDir, `${basename40(filePath)}.txt`);
594919
595279
  writeFileSync53(outFile, fb.text, "utf-8");
594920
595280
  }
594921
595281
  return fb;
@@ -635864,13 +636224,13 @@ async function handleSlashCommand(input, ctx3) {
635864
636224
  try {
635865
636225
  const { randomBytes: randomBytes30 } = await import("node:crypto");
635866
636226
  const { homedir: homedir62 } = await import("node:os");
635867
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
636227
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
635868
636228
  const { join: join179 } = await import("node:path");
635869
636229
  const newKey = randomBytes30(16).toString("hex");
635870
636230
  process.env["OMNIUS_API_KEY"] = newKey;
635871
636231
  const dir = join179(homedir62(), ".omnius");
635872
636232
  mkdirSync106(dir, { recursive: true });
635873
- writeFileSync91(join179(dir, "api.key"), newKey + "\n", "utf8");
636233
+ writeFileSync90(join179(dir, "api.key"), newKey + "\n", "utf8");
635874
636234
  renderInfo(`New API key: ${c3.bold(c3.yellow(newKey))}`);
635875
636235
  renderInfo(
635876
636236
  "Restart the daemon to apply if needed. Use /access any to restart quickly."
@@ -636141,11 +636501,11 @@ async function handleSlashCommand(input, ctx3) {
636141
636501
  );
636142
636502
  try {
636143
636503
  const { homedir: homedir63 } = await import("node:os");
636144
- const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync92 } = await import("node:fs");
636504
+ const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync91 } = await import("node:fs");
636145
636505
  const { join: join180 } = await import("node:path");
636146
636506
  const dir = join180(homedir63(), ".omnius");
636147
636507
  mkdirSync107(dir, { recursive: true });
636148
- writeFileSync92(join180(dir, "api.key"), apiKey + "\n", "utf8");
636508
+ writeFileSync91(join180(dir, "api.key"), apiKey + "\n", "utf8");
636149
636509
  } catch {
636150
636510
  }
636151
636511
  }
@@ -636157,11 +636517,11 @@ async function handleSlashCommand(input, ctx3) {
636157
636517
  const port2 = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
636158
636518
  try {
636159
636519
  const { homedir: homedir63 } = await import("node:os");
636160
- const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync92 } = await import("node:fs");
636520
+ const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync91 } = await import("node:fs");
636161
636521
  const { join: join180 } = await import("node:path");
636162
636522
  const dir = join180(homedir63(), ".omnius");
636163
636523
  mkdirSync107(dir, { recursive: true });
636164
- writeFileSync92(join180(dir, "access"), `${val2}
636524
+ writeFileSync91(join180(dir, "access"), `${val2}
636165
636525
  `, "utf8");
636166
636526
  } catch {
636167
636527
  }
@@ -636261,11 +636621,11 @@ async function handleSlashCommand(input, ctx3) {
636261
636621
  );
636262
636622
  try {
636263
636623
  const { homedir: homedir63 } = await import("node:os");
636264
- const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync92 } = await import("node:fs");
636624
+ const { mkdirSync: mkdirSync107, writeFileSync: writeFileSync91 } = await import("node:fs");
636265
636625
  const { join: join180 } = await import("node:path");
636266
636626
  const dir = join180(homedir63(), ".omnius");
636267
636627
  mkdirSync107(dir, { recursive: true });
636268
- writeFileSync92(join180(dir, "api.key"), apiKey + "\n", "utf8");
636628
+ writeFileSync91(join180(dir, "api.key"), apiKey + "\n", "utf8");
636269
636629
  } catch {
636270
636630
  }
636271
636631
  }
@@ -636276,12 +636636,12 @@ async function handleSlashCommand(input, ctx3) {
636276
636636
  }
636277
636637
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
636278
636638
  const { homedir: homedir62 } = await import("node:os");
636279
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
636639
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
636280
636640
  const { join: join179 } = await import("node:path");
636281
636641
  try {
636282
636642
  const dir = join179(homedir62(), ".omnius");
636283
636643
  mkdirSync106(dir, { recursive: true });
636284
- writeFileSync91(join179(dir, "access"), `${val}
636644
+ writeFileSync90(join179(dir, "access"), `${val}
636285
636645
  `, "utf8");
636286
636646
  } catch (e2) {
636287
636647
  renderWarning(
@@ -645117,14 +645477,14 @@ async function handleVoiceMenu(ctx3, save3, hasLocal) {
645117
645477
  if (!jsonDrop.confirmed || !jsonDrop.path) {
645118
645478
  continue;
645119
645479
  }
645120
- const { basename: basename39, join: pathJoin } = await import("node:path");
645480
+ const { basename: basename40, join: pathJoin } = await import("node:path");
645121
645481
  const {
645122
645482
  copyFileSync: copyFileSync8,
645123
645483
  mkdirSync: mkdirSync106,
645124
645484
  existsSync: exists2
645125
645485
  } = await import("node:fs");
645126
645486
  const { homedir: homedir62 } = await import("node:os");
645127
- const modelName = basename39(onnxDrop.path, ".onnx").replace(
645487
+ const modelName = basename40(onnxDrop.path, ".onnx").replace(
645128
645488
  /[^a-zA-Z0-9_-]/g,
645129
645489
  "-"
645130
645490
  );
@@ -645644,7 +646004,7 @@ async function handleVoiceList(ctx3, focusFilename) {
645644
646004
  copyFileSync: cpf,
645645
646005
  mkdirSync: mkd
645646
646006
  } = __require("node:fs");
645647
- const { basename: basename39, join: pjoin } = __require("node:path");
646007
+ const { basename: basename40, join: pjoin } = __require("node:path");
645648
646008
  if (!fe(src2)) {
645649
646009
  renderError(`File not found: ${src2}`);
645650
646010
  helpers.render();
@@ -645657,7 +646017,7 @@ async function handleVoiceList(ctx3, focusFilename) {
645657
646017
  "clone-refs"
645658
646018
  );
645659
646019
  mkd(refsDir, { recursive: true });
645660
- const destName = basename39(src2);
646020
+ const destName = basename40(src2);
645661
646021
  const dest = pjoin(refsDir, destName);
645662
646022
  cpf(src2, dest);
645663
646023
  renderInfo(`Imported "${destName}" → ${dest}`);
@@ -646914,13 +647274,13 @@ async function handleSponsoredEndpoint(ctx3, local) {
646914
647274
  sponsors.push(...verified);
646915
647275
  if (verified.length > 0) {
646916
647276
  try {
646917
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = __require("node:fs");
647277
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = __require("node:fs");
646918
647278
  mkdirSync106(sponsorDir2, { recursive: true });
646919
647279
  const cached = verified.map((s2) => ({
646920
647280
  ...s2,
646921
647281
  lastVerified: Date.now()
646922
647282
  }));
646923
- writeFileSync91(knownFile, JSON.stringify(cached, null, 2));
647283
+ writeFileSync90(knownFile, JSON.stringify(cached, null, 2));
646924
647284
  } catch {
646925
647285
  }
646926
647286
  }
@@ -647124,7 +647484,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647124
647484
  }
647125
647485
  if (models.length > 0) {
647126
647486
  try {
647127
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106 } = await import("node:fs");
647487
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106 } = await import("node:fs");
647128
647488
  const { join: join179, dirname: dirname54 } = await import("node:path");
647129
647489
  const cachePath2 = join179(
647130
647490
  ctx3.repoRoot || process.cwd(),
@@ -647133,7 +647493,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647133
647493
  "peer-models-cache.json"
647134
647494
  );
647135
647495
  mkdirSync106(dirname54(cachePath2), { recursive: true });
647136
- writeFileSync91(
647496
+ writeFileSync90(
647137
647497
  cachePath2,
647138
647498
  JSON.stringify(
647139
647499
  {
@@ -647207,7 +647567,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647207
647567
  "Live model probe failed; using sponsor directory model advertisement."
647208
647568
  );
647209
647569
  try {
647210
- const { writeFileSync: writeFileSync91, mkdirSync: mkdirSync106 } = await import("node:fs");
647570
+ const { writeFileSync: writeFileSync90, mkdirSync: mkdirSync106 } = await import("node:fs");
647211
647571
  const { join: join179, dirname: dirname54 } = await import("node:path");
647212
647572
  const cachePath2 = join179(
647213
647573
  ctx3.repoRoot || process.cwd(),
@@ -647216,7 +647576,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local, advertisedModels
647216
647576
  "peer-models-cache.json"
647217
647577
  );
647218
647578
  mkdirSync106(dirname54(cachePath2), { recursive: true });
647219
- writeFileSync91(
647579
+ writeFileSync90(
647220
647580
  cachePath2,
647221
647581
  JSON.stringify(
647222
647582
  {
@@ -649914,13 +650274,13 @@ var init_commands = __esm({
649914
650274
  try {
649915
650275
  const { randomBytes: randomBytes30 } = await import("node:crypto");
649916
650276
  const { homedir: homedir62 } = await import("node:os");
649917
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
650277
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
649918
650278
  const { join: join179 } = await import("node:path");
649919
650279
  const apiKey = randomBytes30(16).toString("hex");
649920
650280
  process.env["OMNIUS_API_KEY"] = apiKey;
649921
650281
  const dir = join179(homedir62(), ".omnius");
649922
650282
  mkdirSync106(dir, { recursive: true });
649923
- writeFileSync91(join179(dir, "api.key"), apiKey + "\n", "utf8");
650283
+ writeFileSync90(join179(dir, "api.key"), apiKey + "\n", "utf8");
649924
650284
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
649925
650285
  renderInfo(
649926
650286
  "Use Authorization: Bearer <key> or click 'key' in the Web UI header to paste it."
@@ -649939,11 +650299,11 @@ var init_commands = __esm({
649939
650299
  const port = parseInt(process.env["OMNIUS_PORT"] || "11435", 10);
649940
650300
  try {
649941
650301
  const { homedir: homedir62 } = await import("node:os");
649942
- const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync91 } = await import("node:fs");
650302
+ const { mkdirSync: mkdirSync106, writeFileSync: writeFileSync90 } = await import("node:fs");
649943
650303
  const { join: join179 } = await import("node:path");
649944
650304
  const dir = join179(homedir62(), ".omnius");
649945
650305
  mkdirSync106(dir, { recursive: true });
649946
- writeFileSync91(join179(dir, "access"), `${val}
650306
+ writeFileSync90(join179(dir, "access"), `${val}
649947
650307
  `, "utf8");
649948
650308
  } catch {
649949
650309
  }
@@ -661093,6 +661453,175 @@ var init_soul_observations = __esm({
661093
661453
  }
661094
661454
  });
661095
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
+
661096
661625
  // packages/cli/src/tui/telegram-channel-dmn.ts
661097
661626
  import {
661098
661627
  existsSync as existsSync144,
@@ -663593,55 +664122,16 @@ function advancedOcr(imagePath) {
663593
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.") {
663594
664123
  if (!isVisionModel(modelName)) return "";
663595
664124
  if (!existsSync145(imagePath)) return "";
663596
- const imageBuffer = readFileSync118(imagePath);
663597
- const base64Image = imageBuffer.toString("base64");
663598
- const broker = getModelBroker();
663599
- const decision2 = await broker.ensureModelLoadable({
663600
- name: modelName,
663601
- domain: "vision",
663602
- host: "ollama",
663603
- owner: "vision-ingress",
663604
- requestedNumCtx: 2048
663605
- });
663606
- let effectiveModel = modelName;
663607
- let numCtx;
663608
- if (decision2.kind === "reject") {
663609
- return "";
663610
- } else if (decision2.kind === "degrade") {
663611
- effectiveModel = decision2.fallback.name;
663612
- } else if (decision2.kind === "evict") {
663613
- for (const target of decision2.evictTargets) {
663614
- await broker.evict(target.host, target.name, "make-room-for-vision");
663615
- }
663616
- numCtx = decision2.effectiveNumCtx;
663617
- } else if (decision2.kind === "ok") {
663618
- numCtx = decision2.effectiveNumCtx;
663619
- } else if (decision2.kind === "wait-for-inflight") {
663620
- const inner = await decision2.promise;
663621
- if (inner.kind === "ok") numCtx = inner.effectiveNumCtx;
663622
- else if (inner.kind === "degrade") effectiveModel = inner.fallback.name;
663623
- else if (inner.kind === "reject") return "";
663624
- }
663625
- if (numCtx === void 0) {
663626
- const trainCtx = await broker.getNctxTrain(effectiveModel);
663627
- numCtx = trainCtx && trainCtx > 0 ? Math.min(trainCtx, 4096) : 2048;
663628
- }
663629
664125
  try {
663630
- const response = await fetch("http://localhost:11434/api/generate", {
663631
- method: "POST",
663632
- headers: { "Content-Type": "application/json" },
663633
- body: JSON.stringify({
663634
- model: effectiveModel,
663635
- prompt,
663636
- images: [base64Image],
663637
- stream: false,
663638
- options: { temperature: 0.3, num_predict: 1024, num_ctx: numCtx }
663639
- })
664126
+ const tool = new VisionTool(process.cwd());
664127
+ const result = await tool.execute({
664128
+ image: imagePath,
664129
+ action: "query",
664130
+ prompt,
664131
+ model: modelName
663640
664132
  });
663641
- if (!response.ok) return "";
663642
- broker.touch("ollama", effectiveModel);
663643
- const data = await response.json();
663644
- return (data.response || "").trim();
664133
+ if (!result.success) return "";
664134
+ return String(result.llmContent || result.output || "").trim();
663645
664135
  } catch {
663646
664136
  return "";
663647
664137
  }
@@ -663712,13 +664202,13 @@ import {
663712
664202
  statSync as statSync50,
663713
664203
  statfsSync as statfsSync7,
663714
664204
  readFileSync as readFileSync119,
663715
- writeFileSync as writeFileSync78,
664205
+ writeFileSync as writeFileSync77,
663716
664206
  appendFileSync as appendFileSync15
663717
664207
  } from "node:fs";
663718
664208
  import {
663719
664209
  join as join158,
663720
664210
  resolve as resolve62,
663721
- basename as basename35,
664211
+ basename as basename36,
663722
664212
  relative as relative16,
663723
664213
  isAbsolute as isAbsolute11,
663724
664214
  extname as extname21
@@ -666187,6 +666677,7 @@ var init_telegram_bridge = __esm({
666187
666677
  init_soul_observations();
666188
666678
  init_identity_memory_tool();
666189
666679
  init_visual_identity_association();
666680
+ init_visual_object_association();
666190
666681
  init_telegram_channel_dmn();
666191
666682
  init_telegram_reflection_corpus();
666192
666683
  init_memory_paths();
@@ -669187,6 +669678,14 @@ ${mediaContext}` : ""
669187
669678
  const messageText = isReplyMedia ? msg.replyContext?.text || msg.replyContext?.caption || msg.replyToText || media.caption : msg.text || media.caption;
669188
669679
  const sender = isReplyMedia ? this.telegramMemorySenderFromReply(msg) : this.telegramMemorySenderFromMessage(msg);
669189
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);
669190
669689
  const payload = {
669191
669690
  sourceSurface: "telegram",
669192
669691
  sessionId: this.sessionKeyForMessage(msg),
@@ -669222,8 +669721,12 @@ ${mediaContext}` : ""
669222
669721
  sender_id: sender?.id,
669223
669722
  username: sender?.username,
669224
669723
  display_name: sender?.displayName,
669225
- labels: [media.caption].filter(Boolean)
669724
+ labels: structuredObjectLabels
669226
669725
  };
669726
+ if (objectLabels.label) {
669727
+ payload.object_label = objectLabels.label;
669728
+ payload.aliases = objectLabels.aliases;
669729
+ }
669227
669730
  if (extractedContent) {
669228
669731
  if (modality === "audio") payload.transcript = extractedContent;
669229
669732
  else payload.extractedContent = extractedContent;
@@ -670124,7 +670627,7 @@ ${mediaContext}` : ""
670124
670627
  autoFollowup: false
670125
670628
  }
670126
670629
  };
670127
- writeFileSync78(
670630
+ writeFileSync77(
670128
670631
  this.telegramConversationPath(sessionKey),
670129
670632
  JSON.stringify(payload, null, 2) + "\n",
670130
670633
  "utf8"
@@ -670953,7 +671456,7 @@ ${mediaContext}` : ""
670953
671456
  }
670954
671457
  const matchingEntry = mediaEntries.find((entry) => {
670955
671458
  if (resolve62(entry.localPath) === resolve62(raw)) return true;
670956
- if (basename35(entry.localPath) === raw) return true;
671459
+ if (basename36(entry.localPath) === raw) return true;
670957
671460
  if (entry.fileUniqueId === raw || entry.fileId === raw) return true;
670958
671461
  if (entry.messageId && String(entry.messageId) === raw) return true;
670959
671462
  if (entry.messageId && `message_id:${entry.messageId}` === raw.toLowerCase())
@@ -670994,7 +671497,7 @@ ${mediaContext}` : ""
670994
671497
  sourceMessageId,
670995
671498
  chatKey,
670996
671499
  mediaKind,
670997
- safeAlias: basename35(result.path)
671500
+ safeAlias: basename36(result.path)
670998
671501
  }
670999
671502
  };
671000
671503
  }
@@ -671035,7 +671538,7 @@ ${mediaContext}` : ""
671035
671538
  }
671036
671539
  return entries.find((entry2) => {
671037
671540
  if (resolve62(entry2.localPath) === resolve62(ref)) return true;
671038
- if (basename35(entry2.localPath) === ref) return true;
671541
+ if (basename36(entry2.localPath) === ref) return true;
671039
671542
  if (entry2.fileUniqueId === ref || entry2.fileId === ref) return true;
671040
671543
  if (entry2.messageId && String(entry2.messageId) === ref) return true;
671041
671544
  return false;
@@ -671063,7 +671566,7 @@ ${mediaContext}` : ""
671063
671566
  caption: entry.caption
671064
671567
  },
671065
671568
  modality,
671066
- label: `Telegram message_id ${entry.messageId || "unknown"} ${basename35(entry.localPath)}`,
671569
+ label: `Telegram message_id ${entry.messageId || "unknown"} ${basename36(entry.localPath)}`,
671067
671570
  extractedContent: entry.extractedContent
671068
671571
  };
671069
671572
  }
@@ -672229,8 +672732,8 @@ ${cardLines.join("\n")}`
672229
672732
  const caption = entry.caption ? ` caption=${telegramContextJsonString(entry.caption, 120)}` : "";
672230
672733
  const extracted = entry.extractedContent ? `
672231
672734
  extracted=${telegramContextJsonString(entry.extractedContent.replace(/\s+/g, " "), 220)}` : "";
672232
- const alias = entry.messageId ? `message_id:${entry.messageId}` : basename35(entry.localPath);
672233
- 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}`;
672234
672737
  });
672235
672738
  sections.push(
672236
672739
  [
@@ -674727,7 +675230,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`
674727
675230
  throw e2;
674728
675231
  }
674729
675232
  }
674730
- writeFileSync78(
675233
+ writeFileSync77(
674731
675234
  lockFile,
674732
675235
  JSON.stringify(
674733
675236
  {
@@ -679680,7 +680183,7 @@ Scoped workspace: ${scopedRoot}`,
679680
680183
  }
679681
680184
  writeTelegramToolButtonState(state) {
679682
680185
  mkdirSync89(this.telegramToolButtonDir, { recursive: true });
679683
- writeFileSync78(
680186
+ writeFileSync77(
679684
680187
  this.telegramToolButtonPath(state.nonce),
679685
680188
  JSON.stringify(state, null, 2) + "\n",
679686
680189
  "utf-8"
@@ -680583,12 +681086,12 @@ Advanced text extraction signal: chars=${advancedSignal.chars}, lines=${advanced
680583
681086
  };
680584
681087
  }
680585
681088
  const lines = entries.map((entry, index) => {
680586
- const pathAlias = entry.messageId ? `message_id:${entry.messageId}` : basename35(entry.localPath);
681089
+ const pathAlias = entry.messageId ? `message_id:${entry.messageId}` : basename36(entry.localPath);
680587
681090
  const parts = [
680588
681091
  `${index + 1}. message_id ${entry.messageId || "unknown"}`,
680589
681092
  currentMsg?.replyToMessageId === entry.messageId ? "replied-to" : "",
680590
681093
  telegramCachedMediaIsImage(entry) ? "image" : telegramCachedMediaIsPdf(entry) ? "pdf" : telegramCachedMediaIsAudio(entry) ? "audio" : telegramCachedMediaIsVideo(entry) ? "video" : entry.mediaType,
680591
- `file=${basename35(entry.localPath)}`,
681094
+ `file=${basename36(entry.localPath)}`,
680592
681095
  `path_alias=${pathAlias}`,
680593
681096
  entry.caption ? `caption=${telegramContextJsonString(entry.caption, 140)}` : ""
680594
681097
  ].filter(Boolean);
@@ -680771,8 +681274,8 @@ Advanced text extraction signal: chars=${advancedSignal.chars}, lines=${advanced
680771
681274
  )) {
680772
681275
  return {
680773
681276
  success: true,
680774
- output: `Telegram file already sent in this turn: ${basename35(file.path)} as ${kind} to ${String(target.chatId)}`,
680775
- 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.`,
680776
681279
  durationMs: performance.now() - start2,
680777
681280
  mutated: false,
680778
681281
  mutatedFiles: []
@@ -680799,8 +681302,8 @@ Advanced text extraction signal: chars=${advancedSignal.chars}, lines=${advanced
680799
681302
  );
680800
681303
  return {
680801
681304
  success: true,
680802
- output: `Sent Telegram file: ${basename35(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
680803
- 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}.`,
680804
681307
  durationMs: performance.now() - start2,
680805
681308
  mutated: false,
680806
681309
  mutatedFiles: []
@@ -681086,6 +681589,7 @@ ${visionContext}]`;
681086
681589
  cacheEntry.extractedContent
681087
681590
  );
681088
681591
  let visualIdentityContext = "";
681592
+ let visualObjectContext = "";
681089
681593
  let ingestReachedDaemon = false;
681090
681594
  try {
681091
681595
  const ingestResponse = await fetch(
@@ -681103,6 +681607,9 @@ ${visionContext}]`;
681103
681607
  const block = ingested?.visualIdentity?.contextBlock;
681104
681608
  if (typeof block === "string" && block.trim())
681105
681609
  visualIdentityContext = block.trim();
681610
+ const objectBlock = ingested?.visualObjectMemory?.contextBlock;
681611
+ if (typeof objectBlock === "string" && objectBlock.trim())
681612
+ visualObjectContext = objectBlock.trim();
681106
681613
  }
681107
681614
  } catch {
681108
681615
  }
@@ -681124,8 +681631,29 @@ ${visionContext}]`;
681124
681631
  visualIdentityContext = association.contextBlock;
681125
681632
  } catch {
681126
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
+ }
681127
681652
  }
681128
- description = appendMediaContextBlock(description, visualIdentityContext);
681653
+ description = appendMediaContextBlock(
681654
+ description,
681655
+ [visualIdentityContext, visualObjectContext].filter(Boolean).join("\n\n")
681656
+ );
681129
681657
  } else if (isImageMedia) {
681130
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'.]`;
681131
681659
  cacheEntry.analysisComplete = false;
@@ -681434,7 +681962,7 @@ ${text2}`.trim()
681434
681962
  throw new Error(`File does not exist: ${media.value}`);
681435
681963
  const buffer2 = readFileSync119(media.value);
681436
681964
  const boundary = `----omnius-media-${Date.now()}-${Math.random().toString(36).slice(2)}`;
681437
- const filename = basename35(media.value);
681965
+ const filename = basename36(media.value);
681438
681966
  const contentType = mimeForPath(media.value, media.kind);
681439
681967
  const parts = [];
681440
681968
  const addField = (name10, value2) => {
@@ -681672,7 +682200,7 @@ Content-Type: ${contentType}\r
681672
682200
  audioAsVoice: false
681673
682201
  },
681674
682202
  {
681675
- caption: `Vision action loop screenshot: ${basename35(abs)}`
682203
+ caption: `Vision action loop screenshot: ${basename36(abs)}`
681676
682204
  }
681677
682205
  ).catch(() => null);
681678
682206
  }
@@ -681752,7 +682280,7 @@ Content-Type: ${contentType}\r
681752
682280
  continue;
681753
682281
  }
681754
682282
  const buffer2 = readFileSync119(pathOrFileId);
681755
- const filename = basename35(pathOrFileId);
682283
+ const filename = basename36(pathOrFileId);
681756
682284
  parts.push(Buffer.from(`--${boundary}\r
681757
682285
  `));
681758
682286
  parts.push(
@@ -683529,9 +684057,9 @@ __export(projects_exports, {
683529
684057
  setCurrentProject: () => setCurrentProject,
683530
684058
  unregisterProject: () => unregisterProject
683531
684059
  });
683532
- 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";
683533
684061
  import { homedir as homedir52 } from "node:os";
683534
- 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";
683535
684063
  import { randomUUID as randomUUID19 } from "node:crypto";
683536
684064
  function readAll2() {
683537
684065
  try {
@@ -683547,7 +684075,7 @@ function readAll2() {
683547
684075
  function writeAll(file) {
683548
684076
  mkdirSync90(OMNIUS_DIR3, { recursive: true });
683549
684077
  const tmp = `${PROJECTS_FILE}.${randomUUID19().slice(0, 8)}.tmp`;
683550
- writeFileSync79(tmp, JSON.stringify(file, null, 2), "utf8");
684078
+ writeFileSync78(tmp, JSON.stringify(file, null, 2), "utf8");
683551
684079
  renameSync12(tmp, PROJECTS_FILE);
683552
684080
  }
683553
684081
  function listProjects() {
@@ -683579,7 +684107,7 @@ function registerProject(root, pid) {
683579
684107
  } else {
683580
684108
  entry = {
683581
684109
  root: canonical,
683582
- name: basename36(canonical) || canonical,
684110
+ name: basename37(canonical) || canonical,
683583
684111
  firstSeen: now2,
683584
684112
  lastSeen: now2,
683585
684113
  pid: pid ?? null,
@@ -683630,7 +684158,7 @@ function setCurrentProject(root) {
683630
684158
  currentRoot = canonical;
683631
684159
  try {
683632
684160
  mkdirSync90(OMNIUS_DIR3, { recursive: true });
683633
- writeFileSync79(CURRENT_FILE, `${canonical}
684161
+ writeFileSync78(CURRENT_FILE, `${canonical}
683634
684162
  `, "utf8");
683635
684163
  } catch {
683636
684164
  }
@@ -684527,7 +685055,7 @@ var init_access_policy = __esm({
684527
685055
 
684528
685056
  // packages/cli/src/api/project-preferences.ts
684529
685057
  import { createHash as createHash42 } from "node:crypto";
684530
- 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";
684531
685059
  import { homedir as homedir53 } from "node:os";
684532
685060
  import { join as join160, resolve as resolve64 } from "node:path";
684533
685061
  import { randomUUID as randomUUID20 } from "node:crypto";
@@ -684550,7 +685078,7 @@ function ensureDir(root) {
684550
685078
  const sentinel = rootSentinelPath(root);
684551
685079
  try {
684552
685080
  if (!existsSync148(sentinel)) {
684553
- writeFileSync80(sentinel, `${resolve64(root)}
685081
+ writeFileSync79(sentinel, `${resolve64(root)}
684554
685082
  `, "utf8");
684555
685083
  }
684556
685084
  } catch {
@@ -684579,12 +685107,12 @@ function writeProjectPreferences(root, partial) {
684579
685107
  };
684580
685108
  const file = prefsPath(root);
684581
685109
  const tmp = `${file}.${randomUUID20().slice(0, 8)}.tmp`;
684582
- writeFileSync80(tmp, JSON.stringify(merged, null, 2), "utf8");
685110
+ writeFileSync79(tmp, JSON.stringify(merged, null, 2), "utf8");
684583
685111
  try {
684584
685112
  renameSync13(tmp, file);
684585
685113
  } catch (err) {
684586
685114
  try {
684587
- writeFileSync80(file, JSON.stringify(merged, null, 2), "utf8");
685115
+ writeFileSync79(file, JSON.stringify(merged, null, 2), "utf8");
684588
685116
  } catch {
684589
685117
  }
684590
685118
  try {
@@ -684989,7 +685517,7 @@ data: ${JSON.stringify(ev)}
684989
685517
 
684990
685518
  // packages/cli/src/api/routes-media.ts
684991
685519
  import { existsSync as existsSync151, mkdirSync as mkdirSync94, statSync as statSync53, copyFileSync as copyFileSync7, createReadStream } from "node:fs";
684992
- 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";
684993
685521
  function mediaWorkDir() {
684994
685522
  return join162(omniusHomeDir(), "media", "_work");
684995
685523
  }
@@ -685011,7 +685539,7 @@ function extractGeneratedPath(output) {
685011
685539
  function publishToGallery(srcPath, kind) {
685012
685540
  ensureGlobalMediaDirs();
685013
685541
  const dir = globalMediaDir(kind);
685014
- const name10 = basename37(srcPath);
685542
+ const name10 = basename38(srcPath);
685015
685543
  const dest = join162(dir, name10);
685016
685544
  try {
685017
685545
  if (pathResolve2(srcPath) !== pathResolve2(dest)) {
@@ -685212,7 +685740,7 @@ async function handleAvAnalyze(ctx3) {
685212
685740
  adapterStatus[role] = a2 && !a2.meta.isMock ? "live" : "mock";
685213
685741
  if (adapterStatus[role] === "live") anyLive = true;
685214
685742
  }
685215
- const episodeId = `rest-${basename37(filePath)}-${Date.now().toString(36)}`;
685743
+ const episodeId = `rest-${basename38(filePath)}-${Date.now().toString(36)}`;
685216
685744
  const ingest = await ingestMedia(episodeId, `file://${filePath}`);
685217
685745
  const store2 = await analyzeEpisode({ episodeId, mediaUri: `file://${filePath}`, durationSec: ingest.source.durationSec, registry: registry4, windows: ingest.shots, roles: AV_ROLES });
685218
685746
  const world = store2.snapshot();
@@ -685382,7 +685910,7 @@ function handleFile(ctx3) {
685382
685910
  return true;
685383
685911
  }
685384
685912
  const dir = globalMediaDir(kind);
685385
- const full = pathResolve2(dir, basename37(name10));
685913
+ const full = pathResolve2(dir, basename38(name10));
685386
685914
  if (!full.startsWith(pathResolve2(dir)) || !existsSync151(full)) {
685387
685915
  sendProblem(ctx3.res, problemDetails({
685388
685916
  type: P.notFound,
@@ -685627,7 +686155,7 @@ var init_direct_tool_registry = __esm({
685627
686155
  });
685628
686156
 
685629
686157
  // packages/cli/src/api/external-tool-registry.ts
685630
- 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";
685631
686159
  import { join as join163 } from "node:path";
685632
686160
  function externalToolStorePath(workingDir) {
685633
686161
  return join163(workingDir, ".omnius", "external-tools.json");
@@ -685650,7 +686178,7 @@ function persist3(workingDir, tools) {
685650
686178
  mkdirSync95(join163(workingDir, ".omnius"), { recursive: true });
685651
686179
  const file = { version: STORE_VERSION, tools };
685652
686180
  const tmp = `${path12}.tmp`;
685653
- writeFileSync81(tmp, JSON.stringify(file, null, 2), { mode: 384 });
686181
+ writeFileSync80(tmp, JSON.stringify(file, null, 2), { mode: 384 });
685654
686182
  renameSync14(tmp, path12);
685655
686183
  }
685656
686184
  function validateManifest2(input, existing) {
@@ -686762,7 +687290,7 @@ __export(runtime_keys_exports, {
686762
687290
  mintKey: () => mintKey,
686763
687291
  revokeByPrefix: () => revokeByPrefix
686764
687292
  });
686765
- 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";
686766
687294
  import { join as join165 } from "node:path";
686767
687295
  import { homedir as homedir55 } from "node:os";
686768
687296
  import { randomBytes as randomBytes27 } from "node:crypto";
@@ -686783,7 +687311,7 @@ function loadAll() {
686783
687311
  }
686784
687312
  function persistAll(records) {
686785
687313
  ensureDir2();
686786
- writeFileSync82(KEYS_FILE, JSON.stringify(records, null, 2), "utf-8");
687314
+ writeFileSync81(KEYS_FILE, JSON.stringify(records, null, 2), "utf-8");
686787
687315
  try {
686788
687316
  chmodSync4(KEYS_FILE, 384);
686789
687317
  } catch {
@@ -687017,7 +687545,7 @@ __export(graphical_sudo_exports, {
687017
687545
  runGraphicalSudo: () => runGraphicalSudo
687018
687546
  });
687019
687547
  import { spawn as spawn31 } from "node:child_process";
687020
- 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";
687021
687549
  import { join as join167 } from "node:path";
687022
687550
  import { tmpdir as tmpdir22 } from "node:os";
687023
687551
  function detectSudoHelper() {
@@ -687053,7 +687581,7 @@ exec zenity --password --title="Omnius needs sudo" --text="${description.replace
687053
687581
  exec kdialog --password "${description.replace(/"/g, '\\"')}" 2>/dev/null
687054
687582
  `;
687055
687583
  }
687056
- writeFileSync83(shim, body, "utf-8");
687584
+ writeFileSync82(shim, body, "utf-8");
687057
687585
  chmodSync5(shim, 493);
687058
687586
  return shim;
687059
687587
  }
@@ -702089,7 +702617,7 @@ var init_auth_oidc = __esm({
702089
702617
  });
702090
702618
 
702091
702619
  // packages/cli/src/api/usage-tracker.ts
702092
- 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";
702093
702621
  import { join as join169 } from "node:path";
702094
702622
  function initUsageTracker(omniusDir) {
702095
702623
  const dir = join169(omniusDir, "usage");
@@ -702133,7 +702661,7 @@ function flush2() {
702133
702661
  if (!initialized2 || !dirty) return;
702134
702662
  try {
702135
702663
  store.lastSaved = (/* @__PURE__ */ new Date()).toISOString();
702136
- writeFileSync84(usageFile, JSON.stringify(store, null, 2), "utf-8");
702664
+ writeFileSync83(usageFile, JSON.stringify(store, null, 2), "utf-8");
702137
702665
  dirty = false;
702138
702666
  } catch {
702139
702667
  }
@@ -702308,7 +702836,7 @@ var init_chat_followup = __esm({
702308
702836
 
702309
702837
  // packages/cli/src/docker.ts
702310
702838
  import { execSync as execSync59, spawn as spawn32 } from "node:child_process";
702311
- 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";
702312
702840
  import { join as join170, resolve as resolve65, dirname as dirname50 } from "node:path";
702313
702841
  import { homedir as homedir58 } from "node:os";
702314
702842
  import { fileURLToPath as fileURLToPath19 } from "node:url";
@@ -702533,8 +703061,8 @@ chown -R node:node /workspace /home/node/.omnius 2>/dev/null || true
702533
703061
  if [ "$1" = "omnius" ]; then shift; exec su - node -c "cd /workspace && omnius $*"; fi
702534
703062
  exec "$@"
702535
703063
  `;
702536
- writeFileSync85(join170(dir, "Dockerfile"), dockerfile);
702537
- 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 });
702538
703066
  }
702539
703067
  function hasNvidiaGpu() {
702540
703068
  try {
@@ -702607,7 +703135,7 @@ __export(embedding_workers_exports, {
702607
703135
  startEmbeddingWorkers: () => startEmbeddingWorkers,
702608
703136
  stopEmbeddingWorkers: () => stopEmbeddingWorkers
702609
703137
  });
702610
- import { basename as basename38, join as join171 } from "node:path";
703138
+ import { basename as basename39, join as join171 } from "node:path";
702611
703139
  function startEmbeddingWorkers(opts) {
702612
703140
  if (_running) return;
702613
703141
  _running = true;
@@ -702673,7 +703201,7 @@ async function runEmbeddingTask(modality, episodeId, taskId, opts) {
702673
703201
  try {
702674
703202
  if (!_aligner) {
702675
703203
  const stateRoot = process.env.OMNIUS_DIR || process.cwd();
702676
- const omniusDir = basename38(stateRoot) === ".omnius" ? stateRoot : join171(stateRoot, ".omnius");
703204
+ const omniusDir = basename39(stateRoot) === ".omnius" ? stateRoot : join171(stateRoot, ".omnius");
702677
703205
  const memDir = join171(omniusDir, "memory");
702678
703206
  _aligner = new EmbeddingAligner(
702679
703207
  `${modality}-${emb.length}`,
@@ -702795,7 +703323,7 @@ import { spawn as spawn33, execSync as execSync60 } from "node:child_process";
702795
703323
  import {
702796
703324
  createReadStream as createReadStream2,
702797
703325
  mkdirSync as mkdirSync101,
702798
- writeFileSync as writeFileSync86,
703326
+ writeFileSync as writeFileSync85,
702799
703327
  readFileSync as readFileSync129,
702800
703328
  readdirSync as readdirSync56,
702801
703329
  existsSync as existsSync160,
@@ -704456,11 +704984,11 @@ function atomicJobWrite(dir, id, job) {
704456
704984
  const finalPath = join172(dir, `${id}.json`);
704457
704985
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
704458
704986
  try {
704459
- writeFileSync86(tmpPath, JSON.stringify(job, null, 2), "utf-8");
704987
+ writeFileSync85(tmpPath, JSON.stringify(job, null, 2), "utf-8");
704460
704988
  renameSync15(tmpPath, finalPath);
704461
704989
  } catch {
704462
704990
  try {
704463
- writeFileSync86(finalPath, JSON.stringify(job, null, 2), "utf-8");
704991
+ writeFileSync85(finalPath, JSON.stringify(job, null, 2), "utf-8");
704464
704992
  } catch {
704465
704993
  }
704466
704994
  try {
@@ -706292,7 +706820,7 @@ function writeUpdateState(state) {
706292
706820
  mkdirSync101(dir, { recursive: true });
706293
706821
  const finalPath = updateStateFile();
706294
706822
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
706295
- writeFileSync86(tmpPath, JSON.stringify(state, null, 2), "utf-8");
706823
+ writeFileSync85(tmpPath, JSON.stringify(state, null, 2), "utf-8");
706296
706824
  renameSync15(tmpPath, finalPath);
706297
706825
  } catch {
706298
706826
  }
@@ -708642,13 +709170,13 @@ async function handleRequest(req3, res, ollamaUrl, verbose, runtimeDefaults = {}
708642
709170
  return;
708643
709171
  }
708644
709172
  const { tmpdir: tmpdir24 } = await import("node:os");
708645
- const { writeFileSync: writeFileSync91, unlinkSync: unlinkSync36 } = await import("node:fs");
709173
+ const { writeFileSync: writeFileSync90, unlinkSync: unlinkSync36 } = await import("node:fs");
708646
709174
  const { join: pjoin } = await import("node:path");
708647
709175
  const tmpPath = pjoin(
708648
709176
  tmpdir24(),
708649
709177
  `omnius-clone-upload-${Date.now()}-${safeName3}`
708650
709178
  );
708651
- writeFileSync91(tmpPath, buf);
709179
+ writeFileSync90(tmpPath, buf);
708652
709180
  try {
708653
709181
  const ve = getVoiceEngine();
708654
709182
  const msg = await ve.setCloneVoice(tmpPath);
@@ -709372,7 +709900,7 @@ data: ${JSON.stringify(data)}
709372
709900
  }
709373
709901
  for (const f2 of seenFiles) {
709374
709902
  try {
709375
- writeFileSync86(f2, JSON.stringify({ tasks: [] }, null, 2));
709903
+ writeFileSync85(f2, JSON.stringify({ tasks: [] }, null, 2));
709376
709904
  deleted++;
709377
709905
  } catch {
709378
709906
  }
@@ -711171,11 +711699,11 @@ function setScheduledEnabled(id, enabled2) {
711171
711699
  arr[target.index].enabled = enabled2;
711172
711700
  if (Array.isArray(json?.tasks)) {
711173
711701
  json.tasks = arr;
711174
- writeFileSync86(target.file, JSON.stringify(json, null, 2));
711702
+ writeFileSync85(target.file, JSON.stringify(json, null, 2));
711175
711703
  } else if (Array.isArray(json)) {
711176
- writeFileSync86(target.file, JSON.stringify(arr, null, 2));
711704
+ writeFileSync85(target.file, JSON.stringify(arr, null, 2));
711177
711705
  } else {
711178
- writeFileSync86(target.file, JSON.stringify({ tasks: arr }, null, 2));
711706
+ writeFileSync85(target.file, JSON.stringify({ tasks: arr }, null, 2));
711179
711707
  }
711180
711708
  if (!enabled2) {
711181
711709
  try {
@@ -711205,11 +711733,11 @@ function deleteScheduledById(id) {
711205
711733
  arr.splice(target.index, 1);
711206
711734
  if (Array.isArray(json?.tasks)) {
711207
711735
  json.tasks = arr;
711208
- writeFileSync86(target.file, JSON.stringify(json, null, 2));
711736
+ writeFileSync85(target.file, JSON.stringify(json, null, 2));
711209
711737
  } else if (Array.isArray(json)) {
711210
- writeFileSync86(target.file, JSON.stringify(arr, null, 2));
711738
+ writeFileSync85(target.file, JSON.stringify(arr, null, 2));
711211
711739
  } else {
711212
- writeFileSync86(target.file, JSON.stringify({ tasks: arr }, null, 2));
711740
+ writeFileSync85(target.file, JSON.stringify({ tasks: arr }, null, 2));
711213
711741
  }
711214
711742
  const candidates = [];
711215
711743
  if (id) candidates.push(id);
@@ -711443,7 +711971,7 @@ function reconcileScheduledTasks(apply) {
711443
711971
  mkdirSync101(join172(wdir, ".omnius", "scheduled", "logs"), {
711444
711972
  recursive: true
711445
711973
  });
711446
- writeFileSync86(file, JSON.stringify(toWrite, null, 2));
711974
+ writeFileSync85(file, JSON.stringify(toWrite, null, 2));
711447
711975
  adopted.push({ file, index: arr.length - 1 });
711448
711976
  }
711449
711977
  } else {
@@ -711621,8 +712149,8 @@ WantedBy=timers.target
711621
712149
  `;
711622
712150
  if (!dryRun) {
711623
712151
  mkdirSync101(unitDir, { recursive: true });
711624
- writeFileSync86(svc, svcText);
711625
- writeFileSync86(tim, timText);
712152
+ writeFileSync85(svc, svcText);
712153
+ writeFileSync85(tim, timText);
711626
712154
  try {
711627
712155
  const { execSync: es } = require4("node:child_process");
711628
712156
  es("systemctl --user daemon-reload", { stdio: "pipe" });
@@ -712006,7 +712534,7 @@ function startApiServer(options2 = {}) {
712006
712534
  try {
712007
712535
  const dir = join172(homedir59(), ".omnius");
712008
712536
  mkdirSync101(dir, { recursive: true });
712009
- writeFileSync86(
712537
+ writeFileSync85(
712010
712538
  join172(dir, "access"),
712011
712539
  `${runtimeAccessMode}
712012
712540
  `,
@@ -712419,7 +712947,7 @@ function startApiServer(options2 = {}) {
712419
712947
  }
712420
712948
  try {
712421
712949
  const {
712422
- writeFileSync: writeFileSync91,
712950
+ writeFileSync: writeFileSync90,
712423
712951
  mkdirSync: mkdirSync106,
712424
712952
  existsSync: _exists,
712425
712953
  readFileSync: _rfs
@@ -712458,7 +712986,7 @@ function startApiServer(options2 = {}) {
712458
712986
  for (const dir of dirSet) {
712459
712987
  try {
712460
712988
  if (!_exists(dir)) mkdirSync106(dir, { recursive: true });
712461
- writeFileSync91(_join(dir, "api-port.json"), apiHint);
712989
+ writeFileSync90(_join(dir, "api-port.json"), apiHint);
712462
712990
  written++;
712463
712991
  } catch {
712464
712992
  }
@@ -712827,7 +713355,7 @@ async function handleChatAttachmentUpload(req3, res) {
712827
713355
  dir,
712828
713356
  `${Date.now()}-${randomUUID21().slice(0, 8)}-${safeName3}`
712829
713357
  );
712830
- writeFileSync86(localPath, Buffer.from(base642, "base64"));
713358
+ writeFileSync85(localPath, Buffer.from(base642, "base64"));
712831
713359
  const mimeType = typeof b.mimeType === "string" ? b.mimeType : typeof b.mime_type === "string" ? b.mime_type : "";
712832
713360
  const isImage = mimeType.toLowerCase().startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|tiff?)$/i.test(safeName3);
712833
713361
  const sessionId = typeof b.sessionId === "string" ? b.sessionId : typeof b.session_id === "string" ? b.session_id : void 0;
@@ -712884,6 +713412,7 @@ async function handleChatAttachmentUpload(req3, res) {
712884
713412
  graph.close();
712885
713413
  }
712886
713414
  let visualIdentity = void 0;
713415
+ let visualObjectMemory = void 0;
712887
713416
  let contextBlock = [
712888
713417
  `GUI attachment saved: ${safeName3}`,
712889
713418
  `path: ${localPath}`,
@@ -712922,6 +713451,40 @@ ${association.contextBlock}`;
712922
713451
  degradedReason: err instanceof Error ? err.message : String(err)
712923
713452
  };
712924
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
+ }
712925
713488
  }
712926
713489
  jsonResponse(res, 200, {
712927
713490
  id: ingestId,
@@ -712930,7 +713493,8 @@ ${association.contextBlock}`;
712930
713493
  mimeType,
712931
713494
  modality,
712932
713495
  contextBlock,
712933
- visualIdentity
713496
+ visualIdentity,
713497
+ visualObjectMemory
712934
713498
  });
712935
713499
  } catch (err) {
712936
713500
  jsonResponse(res, 400, {
@@ -713063,6 +713627,7 @@ async function handleMemoryIngest2(req3, res, ollamaUrl) {
713063
713627
  embeddings
713064
713628
  });
713065
713629
  let visualIdentity = void 0;
713630
+ let visualObjectMemory = void 0;
713066
713631
  if (modality === "visual" && mediaPath) {
713067
713632
  try {
713068
713633
  const { associateVisualIdentityFromImage: associateVisualIdentityFromImage2 } = await Promise.resolve().then(() => (init_visual_identity_association(), visual_identity_association_exports));
@@ -713096,8 +713661,40 @@ async function handleMemoryIngest2(req3, res, ollamaUrl) {
713096
713661
  degradedReason: err instanceof Error ? err.message : String(err)
713097
713662
  };
713098
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
+ }
713099
713696
  }
713100
- jsonResponse(res, 200, { id: result.episodeId, ...result, visualIdentity });
713697
+ jsonResponse(res, 200, { id: result.episodeId, ...result, visualIdentity, visualObjectMemory });
713101
713698
  } catch (err) {
713102
713699
  jsonResponse(res, 400, {
713103
713700
  error: "bad_request",
@@ -713313,7 +713910,7 @@ __export(clipboard_media_exports, {
713313
713910
  pasteClipboardImageToFile: () => pasteClipboardImageToFile
713314
713911
  });
713315
713912
  import { execFileSync as execFileSync11, execSync as execSync61 } from "node:child_process";
713316
- 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";
713317
713914
  import { join as join173 } from "node:path";
713318
713915
  function pasteClipboardImageToFile(repoRoot) {
713319
713916
  const image = readClipboardImage();
@@ -713321,7 +713918,7 @@ function pasteClipboardImageToFile(repoRoot) {
713321
713918
  const dir = join173(repoRoot, ".omnius", "clipboard");
713322
713919
  mkdirSync102(dir, { recursive: true });
713323
713920
  const path12 = join173(dir, `clipboard-${Date.now()}${image.ext}`);
713324
- writeFileSync87(path12, image.buffer);
713921
+ writeFileSync86(path12, image.buffer);
713325
713922
  return { path: path12, buffer: image.buffer, mime: image.mime };
713326
713923
  }
713327
713924
  function readClipboardImage() {
@@ -713390,7 +713987,7 @@ import { createRequire as createRequire9 } from "node:module";
713390
713987
  import { fileURLToPath as fileURLToPath21 } from "node:url";
713391
713988
  import {
713392
713989
  readFileSync as readFileSync131,
713393
- writeFileSync as writeFileSync88,
713990
+ writeFileSync as writeFileSync87,
713394
713991
  appendFileSync as appendFileSync17,
713395
713992
  rmSync as rmSync14,
713396
713993
  readdirSync as readdirSync57,
@@ -714517,7 +715114,7 @@ function createFanoutExploreTool(config, repoRoot, ctxWindowSize) {
714517
715114
  );
714518
715115
  if (debug) {
714519
715116
  try {
714520
- writeFileSync88(
715117
+ writeFileSync87(
714521
715118
  join174(repoRoot, ".omnius", "fanout-debug.json"),
714522
715119
  JSON.stringify({ objective, regions, rawReturns, digests }, null, 2)
714523
715120
  );
@@ -715303,7 +715900,7 @@ ${skillPack}`;
715303
715900
  You have vision capabilities. Choose the RIGHT tool for each situation:
715304
715901
 
715305
715902
  FOR IMAGE FILES (photos, screenshots, diagrams):
715306
- 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
715307
715904
  vision(image="path", action="caption") — Describe image contents via Moondream
715308
715905
  vision(image="path", action="query", prompt="question") — Ask questions about an image
715309
715906
  vision(image="path", action="detect", prompt="object") — Find objects (bounding boxes)
@@ -718832,7 +719429,7 @@ This is an independent background session started from /background.`
718832
719429
  if (Math.random() < 0.02) {
718833
719430
  const all2 = readFileSync131(HISTORY_FILE, "utf8").trim().split("\n");
718834
719431
  if (all2.length > MAX_HISTORY_LINES) {
718835
- writeFileSync88(
719432
+ writeFileSync87(
718836
719433
  HISTORY_FILE,
718837
719434
  all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n",
718838
719435
  "utf8"
@@ -719773,7 +720370,7 @@ Log: ${nexusLogPath}`
719773
720370
  sessionTitle = title.trim() || null;
719774
720371
  try {
719775
720372
  mkdirSync103(join174(repoRoot, ".omnius"), { recursive: true });
719776
- writeFileSync88(
720373
+ writeFileSync87(
719777
720374
  join174(repoRoot, ".omnius", "session-title"),
719778
720375
  `${sessionTitle ?? ""}
719779
720376
  `,
@@ -719867,7 +720464,7 @@ Log: ${nexusLogPath}`
719867
720464
  queuePrompt(
719868
720465
  imageContext ? `${imageContext}
719869
720466
 
719870
- 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.`,
719871
720468
  "clipboard"
719872
720469
  );
719873
720470
  return {
@@ -722497,7 +723094,7 @@ ${result.text}`;
722497
723094
 
722498
723095
  ${imageContext}
722499
723096
 
722500
- 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.`;
722501
723098
  }
722502
723099
  if (isMedia && fullInput === input && (voiceEngine.isLuxtts || voiceEngine.isMisotts)) {
722503
723100
  const cloneExts = [".wav", ".mp3", ".ogg", ".flac", ".m4a"];
@@ -723225,7 +723822,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723225
723822
  );
723226
723823
  ikState.session_count = (ikState.session_count || 0) + 1;
723227
723824
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
723228
- writeFileSync88(ikFile, JSON.stringify(ikState, null, 2));
723825
+ writeFileSync87(ikFile, JSON.stringify(ikState, null, 2));
723229
723826
  } catch (ikErr) {
723230
723827
  }
723231
723828
  try {
@@ -723258,7 +723855,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723258
723855
  });
723259
723856
  if (variants.length > 50) variants = variants.slice(-50);
723260
723857
  mkdirSync103(archeDir, { recursive: true });
723261
- writeFileSync88(archeFile, JSON.stringify(variants, null, 2));
723858
+ writeFileSync87(archeFile, JSON.stringify(variants, null, 2));
723262
723859
  } catch {
723263
723860
  }
723264
723861
  }
@@ -723289,7 +723886,7 @@ async function runWithTUI(task, config, repoPath2, callbacks) {
723289
723886
  updated = true;
723290
723887
  }
723291
723888
  if (updated) {
723292
- writeFileSync88(metaFile2, JSON.stringify(store2, null, 2));
723889
+ writeFileSync87(metaFile2, JSON.stringify(store2, null, 2));
723293
723890
  }
723294
723891
  }
723295
723892
  } catch {
@@ -723398,7 +723995,7 @@ Rules:
723398
723995
  });
723399
723996
  if (store2.length > 100) store2 = store2.slice(-100);
723400
723997
  mkdirSync103(metaDir, { recursive: true });
723401
- writeFileSync88(storeFile, JSON.stringify(store2, null, 2));
723998
+ writeFileSync87(storeFile, JSON.stringify(store2, null, 2));
723402
723999
  }
723403
724000
  }
723404
724001
  } catch {
@@ -723462,7 +724059,7 @@ Rules:
723462
724059
  );
723463
724060
  ikState.session_count = (ikState.session_count || 0) + 1;
723464
724061
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
723465
- writeFileSync88(ikFile, JSON.stringify(ikState, null, 2));
724062
+ writeFileSync87(ikFile, JSON.stringify(ikState, null, 2));
723466
724063
  }
723467
724064
  const metaFile2 = join174(
723468
724065
  repoRoot,
@@ -723490,7 +724087,7 @@ Rules:
723490
724087
  (item.scores.confidence || 0.5) - 0.02
723491
724088
  );
723492
724089
  }
723493
- writeFileSync88(metaFile2, JSON.stringify(store2, null, 2));
724090
+ writeFileSync87(metaFile2, JSON.stringify(store2, null, 2));
723494
724091
  }
723495
724092
  try {
723496
724093
  const archeDir = join174(repoRoot, ".omnius", "arche");
@@ -723513,7 +724110,7 @@ Rules:
723513
724110
  });
723514
724111
  if (variants.length > 50) variants = variants.slice(-50);
723515
724112
  mkdirSync103(archeDir, { recursive: true });
723516
- writeFileSync88(archeFile, JSON.stringify(variants, null, 2));
724113
+ writeFileSync87(archeFile, JSON.stringify(variants, null, 2));
723517
724114
  } catch {
723518
724115
  }
723519
724116
  } catch {
@@ -723626,7 +724223,7 @@ import { resolve as resolve68 } from "node:path";
723626
724223
  import { spawn as spawn34 } from "node:child_process";
723627
724224
  import {
723628
724225
  mkdirSync as mkdirSync104,
723629
- writeFileSync as writeFileSync89,
724226
+ writeFileSync as writeFileSync88,
723630
724227
  readFileSync as readFileSync132,
723631
724228
  readdirSync as readdirSync58,
723632
724229
  existsSync as existsSync162
@@ -723766,7 +724363,7 @@ async function runBackground(task, config, opts) {
723766
724363
  }
723767
724364
  });
723768
724365
  job.pid = child.pid ?? 0;
723769
- writeFileSync89(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
724366
+ writeFileSync88(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
723770
724367
  let output = "";
723771
724368
  child.stdout?.on("data", (chunk) => {
723772
724369
  output += chunk.toString();
@@ -723782,7 +724379,7 @@ async function runBackground(task, config, opts) {
723782
724379
  job.summary = result.summary;
723783
724380
  job.durationMs = result.durationMs;
723784
724381
  job.error = result.error;
723785
- writeFileSync89(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
724382
+ writeFileSync88(join175(dir, `${id}.json`), JSON.stringify(job, null, 2));
723786
724383
  } catch {
723787
724384
  }
723788
724385
  });
@@ -724421,7 +725018,7 @@ __export(eval_exports, {
724421
725018
  evalCommand: () => evalCommand
724422
725019
  });
724423
725020
  import { tmpdir as tmpdir23 } from "node:os";
724424
- import { mkdirSync as mkdirSync105, writeFileSync as writeFileSync90 } from "node:fs";
725021
+ import { mkdirSync as mkdirSync105, writeFileSync as writeFileSync89 } from "node:fs";
724425
725022
  import { join as join177 } from "node:path";
724426
725023
  async function evalCommand(opts, config) {
724427
725024
  const suiteName = opts.suite ?? "basic";
@@ -724553,7 +725150,7 @@ async function evalCommand(opts, config) {
724553
725150
  function createTempEvalRepo() {
724554
725151
  const dir = join177(tmpdir23(), `omnius-eval-${Date.now()}`);
724555
725152
  mkdirSync105(dir, { recursive: true });
724556
- writeFileSync90(
725153
+ writeFileSync89(
724557
725154
  join177(dir, "package.json"),
724558
725155
  JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n",
724559
725156
  "utf8"
@@ -724790,8 +725387,16 @@ function routeCommand(command) {
724790
725387
  }
724791
725388
  function parseCliArgs(argv) {
724792
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
+ }
724793
725398
  const { values, positionals } = nodeParseArgs2({
724794
- args,
725399
+ args: parseInput,
724795
725400
  options: {
724796
725401
  "dry-run": { type: "boolean" },
724797
725402
  verbose: { type: "boolean", short: "v" },
@@ -724808,6 +725413,7 @@ function parseCliArgs(argv) {
724808
725413
  live: { type: "boolean" },
724809
725414
  json: { type: "boolean", short: "j" },
724810
725415
  background: { type: "boolean" },
725416
+ "self-test": { type: "string" },
724811
725417
  help: { type: "boolean", short: "h" },
724812
725418
  version: { type: "boolean", short: "V" }
724813
725419
  },
@@ -724830,6 +725436,7 @@ function parseCliArgs(argv) {
724830
725436
  local: values.local === true,
724831
725437
  json: values.json === true,
724832
725438
  background: values.background === true,
725439
+ selfTest: typeof values["self-test"] === "string" ? values["self-test"] : void 0,
724833
725440
  help: values.help === true,
724834
725441
  version: values.version === true
724835
725442
  };
@@ -724935,6 +725542,23 @@ Examples:
724935
725542
  `.trim();
724936
725543
  process.stdout.write(text2 + "\n");
724937
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
+ }
724938
725562
  async function main() {
724939
725563
  const version4 = getVersion5();
724940
725564
  const parsed = parseCliArgs(process.argv);
@@ -724948,6 +725572,10 @@ async function main() {
724948
725572
  printHelp2(version4);
724949
725573
  return;
724950
725574
  }
725575
+ if (parsed.selfTest) {
725576
+ await runSelfTest(parsed.selfTest);
725577
+ return;
725578
+ }
724951
725579
  if (!parsed.json) {
724952
725580
  const updateInfo = await checkForUpdate(version4);
724953
725581
  if (updateInfo) {
@@ -724966,27 +725594,6 @@ async function main() {
724966
725594
  });
724967
725595
  try {
724968
725596
  switch (parsed.command) {
724969
- case void 0: {
724970
- if (process.argv.includes("--self-test")) {
724971
- const mode = process.argv[process.argv.indexOf("--self-test") + 1] || "crossmodal";
724972
- if (mode === "crossmodal") {
724973
- process.stdout.write("Running crossmodal smoke tests...\n");
724974
- const { spawn: spawn35 } = await import("node:child_process");
724975
- const run2 = (file) => new Promise((resolve71, reject) => {
724976
- const p2 = spawn35(process.execPath, [file], { stdio: ["ignore", "pipe", "pipe"] });
724977
- p2.stdout.on("data", (d2) => process.stdout.write(d2));
724978
- p2.stderr.on("data", (d2) => process.stdout.write(d2));
724979
- onChildExit(p2, (code8) => code8 === 0 ? resolve71() : reject(new Error(`${file} exited ${code8}`)));
724980
- });
724981
- const base3 = process.cwd();
724982
- await run2(`${base3}/eval/test-crossmodal.mjs`);
724983
- await run2(`${base3}/eval/test-memory-search.mjs`);
724984
- process.stdout.write("Self-test complete.\n");
724985
- return;
724986
- }
724987
- }
724988
- break;
724989
- }
724990
725597
  case "run": {
724991
725598
  const { runCommand: runCommand3 } = await Promise.resolve().then(() => (init_run(), run_exports));
724992
725599
  await runCommand3(