omnius 1.0.17 → 1.0.19

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
@@ -84452,7 +84452,7 @@ var require_mime_types = __commonJS({
84452
84452
  "../node_modules/mime-types/index.js"(exports) {
84453
84453
  "use strict";
84454
84454
  var db = require_mime_db();
84455
- var extname15 = __require("path").extname;
84455
+ var extname16 = __require("path").extname;
84456
84456
  var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
84457
84457
  var TEXT_TYPE_REGEXP = /^text\//i;
84458
84458
  exports.charset = charset;
@@ -84506,7 +84506,7 @@ var require_mime_types = __commonJS({
84506
84506
  if (!path11 || typeof path11 !== "string") {
84507
84507
  return false;
84508
84508
  }
84509
- var extension4 = extname15("x." + path11).toLowerCase().substr(1);
84509
+ var extension4 = extname16("x." + path11).toLowerCase().substr(1);
84510
84510
  if (!extension4) {
84511
84511
  return false;
84512
84512
  }
@@ -477005,7 +477005,7 @@ var require_path_browserify = __commonJS({
477005
477005
  return path11.slice(start2, end);
477006
477006
  }
477007
477007
  },
477008
- extname: function extname15(path11) {
477008
+ extname: function extname16(path11) {
477009
477009
  assertPath(path11);
477010
477010
  var startDot = -1;
477011
477011
  var startPart = 0;
@@ -507302,12 +507302,23 @@ function ensureGeneratedVoiceDir() {
507302
507302
  mkdirSync16(generatedVoiceDir(), { recursive: true });
507303
507303
  }
507304
507304
  function ttsOutputPath(args, backend) {
507305
- const output = typeof args["output"] === "string" ? args["output"] : typeof args["output_path"] === "string" ? args["output_path"] : "";
507306
- if (output.trim())
507307
- return resolvePath(output.trim());
507305
+ for (const key of ["output", "output_path", "path", "file_path", "file"]) {
507306
+ const output = args[key];
507307
+ if (typeof output === "string" && output.trim()) {
507308
+ return resolvePath(output.trim());
507309
+ }
507310
+ }
507308
507311
  ensureGeneratedVoiceDir();
507309
507312
  return join58(generatedVoiceDir(), `tts-${backend}-${Date.now()}-${Math.random().toString(36).slice(2, 7)}.wav`);
507310
507313
  }
507314
+ function ttsTextArg(args) {
507315
+ for (const key of ["text", "input", "prompt", "content", "message"]) {
507316
+ const value2 = args[key];
507317
+ if (typeof value2 === "string" && value2.trim())
507318
+ return value2.trim();
507319
+ }
507320
+ return "";
507321
+ }
507311
507322
  function listCloneRefs() {
507312
507323
  try {
507313
507324
  return readdirSync14(cloneRefsDir()).filter((file) => /\.(wav|mp3|flac|ogg|m4a)$/i.test(file) && existsSync40(join58(cloneRefsDir(), file))).sort((a2, b) => a2.localeCompare(b));
@@ -507348,6 +507359,12 @@ function supertonicInferScript() {
507348
507359
  function mlxVenvPy() {
507349
507360
  return process.platform === "win32" ? join58(voiceDir(), "mlx-venv", "Scripts", "python.exe") : join58(voiceDir(), "mlx-venv", "bin", "python3");
507350
507361
  }
507362
+ function piperVenvDir() {
507363
+ return join58(voiceDir(), "piper-venv");
507364
+ }
507365
+ function piperVenvBin() {
507366
+ return process.platform === "win32" ? join58(piperVenvDir(), "Scripts", "piper.exe") : join58(piperVenvDir(), "bin", "piper");
507367
+ }
507351
507368
  function ensureSupertonicInstalled() {
507352
507369
  const venvPy = supertonicVenvPy();
507353
507370
  if (!existsSync40(venvPy)) {
@@ -507385,6 +507402,25 @@ function ensureMlxInstalled() {
507385
507402
  }
507386
507403
  return venvPy;
507387
507404
  }
507405
+ function ensurePiperInstalled() {
507406
+ if (hasCommand3("piper"))
507407
+ return "piper";
507408
+ const bin = piperVenvBin();
507409
+ if (!existsSync40(bin)) {
507410
+ const py = findPython32();
507411
+ if (!py)
507412
+ throw new Error("python3 is required to set up Piper TTS.");
507413
+ mkdirSync16(voiceDir(), { recursive: true });
507414
+ execFileSync2(py, ["-m", "venv", piperVenvDir()], { stdio: "pipe", timeout: 18e4 });
507415
+ const venvPy = process.platform === "win32" ? join58(piperVenvDir(), "Scripts", "python.exe") : join58(piperVenvDir(), "bin", "python3");
507416
+ execFileSync2(venvPy, ["-m", "pip", "install", "--quiet", "--upgrade", "pip"], { stdio: "pipe", timeout: 12e4 });
507417
+ execFileSync2(venvPy, ["-m", "pip", "install", "--quiet", "piper-tts"], { stdio: "pipe", timeout: 6e5 });
507418
+ }
507419
+ if (!existsSync40(bin)) {
507420
+ throw new Error("Piper TTS installed but the piper executable was not found in the managed venv.");
507421
+ }
507422
+ return bin;
507423
+ }
507388
507424
  function saveCloneRefFromSample(sample, cloneName) {
507389
507425
  const source = resolvePath(sample);
507390
507426
  if (!existsSync40(source))
@@ -507552,7 +507588,7 @@ except Exception as exc:
507552
507588
  `;
507553
507589
  AudioPlaybackTool = class {
507554
507590
  name = "audio_playback";
507555
- description = "Play audio through speakers, synthesize text-to-speech, and manage TTS clone voices. Actions: 'play' to play an audio file (WAV/MP3/OGG — including recordings from memory episodes), 'speak' to synthesize and play text, 'synthesize' to save TTS to a WAV file, 'clone' to register a voice-clone sample, 'list_voices' to inspect available clone refs/backends, 'volume' to get or set system volume, 'list' to enumerate audio output devices. TTS backends are explicit: auto, luxtts, supertonic, mlx, onnx/piper, or espeak. Use generate_tts when the task is specifically to create a TTS file.";
507591
+ description = "Play audio through speakers, synthesize text-to-speech, and manage TTS clone voices. Actions: 'play' to play an audio file (WAV/MP3/OGG — including recordings from memory episodes), 'speak' to synthesize and play text, 'synthesize' to save TTS to a WAV file, 'clone' to register a voice-clone sample, 'list_voices' to inspect available clone refs/backends, 'volume' to get or set system volume, 'list' to enumerate audio output devices. TTS backends are explicit: auto, luxtts, supertonic, mlx, onnx/piper, or espeak. Neural TTS backends self-provision into ~/.omnius/voice on first use where supported. Use generate_tts when the task is specifically to create a TTS file; do not use shell speech commands or generate_audio for spoken TTS.";
507556
507592
  parameters = {
507557
507593
  type: "object",
507558
507594
  properties: {
@@ -507569,6 +507605,14 @@ except Exception as exc:
507569
507605
  type: "string",
507570
507606
  description: "Text to speak or synthesize"
507571
507607
  },
507608
+ input: {
507609
+ type: "string",
507610
+ description: "Alias for text."
507611
+ },
507612
+ prompt: {
507613
+ type: "string",
507614
+ description: "Alias for text."
507615
+ },
507572
507616
  backend: {
507573
507617
  type: "string",
507574
507618
  enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper", "espeak"],
@@ -507598,6 +507642,10 @@ except Exception as exc:
507598
507642
  type: "string",
507599
507643
  description: "Backend model id or raw ONNX/Piper model path. MLX default is mlx-community/Kokoro-82M-bf16."
507600
507644
  },
507645
+ strict_backend: {
507646
+ type: "boolean",
507647
+ description: "When true, fail instead of falling back if the requested TTS backend is unavailable. Defaults false."
507648
+ },
507601
507649
  volume: {
507602
507650
  type: "number",
507603
507651
  description: "Volume level 0-100 (for 'volume' action to SET volume)"
@@ -507693,7 +507741,7 @@ except Exception as exc:
507693
507741
  ` luxtts: ${existsSync40(join58(voiceDir(), "luxtts-venv", "bin", "python3")) ? "installed" : "not installed"}; clone refs: ${refs.length}`,
507694
507742
  ` supertonic: ${existsSync40(supertonicVenvPy()) ? "installed" : "not installed"}; voices include M1, M2, M3, M4 when package assets are available`,
507695
507743
  ` mlx: ${existsSync40(mlxVenvPy()) ? "installed" : "not installed"}; Apple Silicon only; default model mlx-community/Kokoro-82M-bf16`,
507696
- ` piper/onnx: ${hasCommand3("piper") ? "available" : "not found"}; pass model=<path.onnx> for raw ONNX voices`,
507744
+ ` piper/onnx: ${hasCommand3("piper") || existsSync40(piperVenvBin()) ? "available" : "not installed"}; first use installs piper-tts into ${piperVenvDir()}; pass model=<path.onnx> for raw ONNX voices`,
507697
507745
  ` espeak: ${hasCommand3("espeak-ng") ? "available" : "not found"}`,
507698
507746
  "",
507699
507747
  "Registered clone refs:",
@@ -507702,16 +507750,18 @@ except Exception as exc:
507702
507750
  return { success: true, output: lines.join("\n"), durationMs: performance.now() - start2 };
507703
507751
  }
507704
507752
  async synthesizeText(args, start2, speakDefault) {
507705
- const text = typeof args["text"] === "string" ? args["text"].trim() : "";
507753
+ const text = ttsTextArg(args);
507706
507754
  if (!text) {
507707
507755
  return { success: false, output: "", error: "Missing 'text' parameter. Provide text to synthesize.", durationMs: performance.now() - start2 };
507708
507756
  }
507709
507757
  const requestedBackend = normalizeTtsBackend(args["backend"]);
507758
+ const strictBackend = boolArg(args["strict_backend"] ?? args["strictBackend"], false);
507710
507759
  const playback = playbackArg(args, speakDefault);
507711
507760
  const outputPath2 = ttsOutputPath(args, requestedBackend);
507712
507761
  const device = typeof args["device"] === "string" ? args["device"] : "default";
507713
507762
  const tried = [];
507714
- const candidates = requestedBackend === "auto" ? ["luxtts", "supertonic", ...process.platform === "darwin" && process.arch === "arm64" ? ["mlx"] : [], "piper", "espeak"] : [requestedBackend];
507763
+ const autoCandidates = ["luxtts", "supertonic", ...process.platform === "darwin" && process.arch === "arm64" ? ["mlx"] : [], "piper", "espeak"];
507764
+ const candidates = requestedBackend === "auto" ? autoCandidates : strictBackend ? [requestedBackend] : [requestedBackend, ...autoCandidates.filter((backend) => backend !== requestedBackend)];
507715
507765
  let usedBackend = "";
507716
507766
  let voiceSummary = "";
507717
507767
  for (const backend of candidates) {
@@ -507734,7 +507784,7 @@ except Exception as exc:
507734
507784
  } catch (err) {
507735
507785
  const message2 = err instanceof Error ? err.message : String(err);
507736
507786
  tried.push(`${backend}: ${message2.slice(0, 300)}`);
507737
- if (requestedBackend !== "auto") {
507787
+ if (requestedBackend !== "auto" && strictBackend) {
507738
507788
  return { success: false, output: "", error: `${backend} TTS failed: ${message2}`, durationMs: performance.now() - start2 };
507739
507789
  }
507740
507790
  }
@@ -507754,11 +507804,13 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
507754
507804
  })() : " Playback: disabled";
507755
507805
  const size = Math.round(statSync18(outputPath2).size / 1024);
507756
507806
  const preview = text.length > 140 ? `${text.slice(0, 137)}...` : text;
507807
+ const fallbackLine = requestedBackend !== "auto" && usedBackend !== requestedBackend ? ` Fallback: requested ${requestedBackend}; ${tried[0] ?? "requested backend unavailable"}` : "";
507757
507808
  return {
507758
507809
  success: true,
507759
507810
  output: [
507760
507811
  `TTS generated: ${outputPath2}`,
507761
507812
  ` Backend: ${usedBackend}`,
507813
+ fallbackLine,
507762
507814
  voiceSummary ? ` Voice: ${voiceSummary}` : "",
507763
507815
  ` Size: ${size}KB`,
507764
507816
  playbackLine,
@@ -507783,8 +507835,9 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
507783
507835
  }
507784
507836
  const venvPy = join58(voiceDir(), "luxtts-venv", "bin", "python3");
507785
507837
  const repoDir = join58(voiceDir(), "LuxTTS");
507786
- if (!existsSync40(venvPy) || !existsSync40(repoDir))
507787
- throw new Error("LuxTTS is not installed. Run /voice model luxtts or set up the LuxTTS voice backend first.");
507838
+ if (!existsSync40(venvPy) || !existsSync40(repoDir)) {
507839
+ throw new Error("LuxTTS is not installed in the managed voice environment yet.");
507840
+ }
507788
507841
  const pyScript = [
507789
507842
  "import json, sys, wave",
507790
507843
  "import numpy as np, torch",
@@ -507842,8 +507895,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
507842
507895
  return `${voice} (${model})`;
507843
507896
  }
507844
507897
  synthesizePiper(text, outputPath2, args, requireModel) {
507845
- if (!hasCommand3("piper"))
507846
- throw new Error("piper command not found. Install Piper for ONNX TTS.");
507898
+ const piper = ensurePiperInstalled();
507847
507899
  const rawModel = typeof args["model"] === "string" ? args["model"] : typeof args["voice"] === "string" && /\.(onnx|onnx\.json)$/i.test(args["voice"]) ? args["voice"] : "";
507848
507900
  const argv = ["--output_file", outputPath2];
507849
507901
  let summary = "default piper voice";
@@ -507853,10 +507905,10 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
507853
507905
  throw new Error(`Piper/ONNX model file not found: ${rawModel}`);
507854
507906
  argv.unshift("--model", modelPath);
507855
507907
  summary = basename12(modelPath);
507856
- } else if (requireModel) {
507857
- throw new Error("Raw ONNX TTS requires model=<path.onnx> or voice=<path.onnx>.");
507908
+ } else {
507909
+ throw new Error(`${requireModel ? "Raw ONNX" : "Piper"} TTS requires model=<path.onnx> or voice=<path.onnx>.`);
507858
507910
  }
507859
- execFileSync2("piper", argv, { input: text, stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
507911
+ execFileSync2(piper, argv, { input: text, stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
507860
507912
  return summary;
507861
507913
  }
507862
507914
  synthesizeEspeak(text, outputPath2, args) {
@@ -507943,14 +507995,18 @@ ${devices.join("\n")}`,
507943
507995
  };
507944
507996
  TtsGenerateTool = class {
507945
507997
  name = "generate_tts";
507946
- description = "Generate text-to-speech audio as a WAV file, optionally playing it after synthesis. Supports explicit backends: auto, luxtts voice cloning, supertonic, mlx, onnx/piper, and espeak. Use clone_ref to select a registered LuxTTS voice, sample+clone_name to register a clone sample via audio_playback action=clone, and playback=false for silent file generation.";
507998
+ description = "Generate text-to-speech audio as a WAV file, optionally playing it after synthesis. Supports explicit backends: auto, luxtts voice cloning, supertonic, mlx, onnx/piper, and espeak. Neural TTS backends self-provision into ~/.omnius/voice on first use where supported. Use clone_ref to select a registered LuxTTS voice, sample+clone_name to register a clone sample via audio_playback action=clone, and playback=false for silent file generation. Use this tool for speech/TTS requests; do not use shell commands or generate_audio as a TTS fallback.";
507947
507999
  parameters = {
507948
508000
  type: "object",
507949
508001
  properties: {
507950
508002
  text: { type: "string", description: "Text to synthesize" },
508003
+ input: { type: "string", description: "Alias for text." },
508004
+ prompt: { type: "string", description: "Alias for text." },
507951
508005
  backend: { type: "string", enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper", "espeak"] },
507952
508006
  output: { type: "string", description: "Output WAV path. Defaults to ~/.omnius/voice/generated/tts-*.wav." },
508007
+ path: { type: "string", description: "Alias for output." },
507953
508008
  playback: { type: "boolean", description: "Whether to play after generating. Defaults false for generate_tts." },
508009
+ strict_backend: { type: "boolean", description: "When true, fail instead of falling back if the requested backend is unavailable. Defaults false." },
507954
508010
  voice: { type: "string", description: "Voice id/name, or raw Piper/ONNX path when backend=onnx/piper." },
507955
508011
  clone_ref: { type: "string", description: "LuxTTS clone reference path, filename, or registered clone name." },
507956
508012
  model: { type: "string", description: "Backend model id or raw ONNX/Piper model path." },
@@ -507958,7 +508014,7 @@ ${devices.join("\n")}`,
507958
508014
  speed: { type: "number", description: "Speech speed multiplier or backend-specific rate." },
507959
508015
  device: { type: "string", description: "Playback output device when playback=true." }
507960
508016
  },
507961
- required: ["text"]
508017
+ required: []
507962
508018
  };
507963
508019
  async execute(args) {
507964
508020
  const tool = new AudioPlaybackTool();
@@ -512308,7 +512364,7 @@ var init_agent_tool = __esm({
512308
512364
  })();
512309
512365
  if (isolation === "worktree") {
512310
512366
  this.callbacks.onViewRegister?.(agentId, label);
512311
- const spawn32 = this.callbacks.spawnSubprocess({
512367
+ const spawn31 = this.callbacks.spawnSubprocess({
512312
512368
  id: agentId,
512313
512369
  task: composedPrompt,
512314
512370
  model,
@@ -512318,7 +512374,7 @@ var init_agent_tool = __esm({
512318
512374
  success: true,
512319
512375
  output: `Agent spawned (subprocess, worktree): ${agentId}
512320
512376
  Type: ${subagentType}
512321
- PID: ${spawn32.pid}
512377
+ PID: ${spawn31.pid}
512322
512378
  Task: ${prompt.slice(0, 100)}
512323
512379
  Use task_status("${agentId}") to check progress.`,
512324
512380
  durationMs: performance.now() - start2
@@ -530830,7 +530886,7 @@ RECOVERY: cd to the directory containing '${file}', run a plain install with no
530830
530886
  import { existsSync as _fsExistsSync, readFileSync as _fsReadFileSync, writeFileSync as _fsWriteFileSync, unlinkSync as _fsUnlinkSync, mkdirSync as _fsMkdirSync } from "node:fs";
530831
530887
  import { execFile as _execFile } from "node:child_process";
530832
530888
  import { createHash as _createHash } from "node:crypto";
530833
- import { join as _pathJoin } from "node:path";
530889
+ import { join as _pathJoin, resolve as _pathResolve } from "node:path";
530834
530890
  import { tmpdir as _osTmpdir } from "node:os";
530835
530891
  import { homedir as _osHomedir } from "node:os";
530836
530892
  import { z as z15 } from "zod";
@@ -531578,6 +531634,9 @@ var init_agenticRunner = __esm({
531578
531634
  compactionThreshold: options2?.compactionThreshold ?? 4e4,
531579
531635
  deepContext: options2?.deepContext ?? false,
531580
531636
  dynamicContext: options2?.dynamicContext ?? "",
531637
+ stateDir: options2?.stateDir ?? "",
531638
+ disablePersistentMemory: options2?.disablePersistentMemory ?? false,
531639
+ disableCodebaseMap: options2?.disableCodebaseMap ?? false,
531581
531640
  sessionId: options2?.sessionId ?? "",
531582
531641
  streamEnabled: options2?.streamEnabled ?? false,
531583
531642
  thinking: options2?.thinking ?? true,
@@ -531604,6 +531663,11 @@ var init_agenticRunner = __esm({
531604
531663
  setWorkingDirectory(dir) {
531605
531664
  this._workingDirectory = dir;
531606
531665
  }
531666
+ /** State root for runner-owned memory/artifacts. Defaults to cwd/.omnius. */
531667
+ omniusStateDir() {
531668
+ const configured = (this.options.stateDir || "").trim();
531669
+ return configured ? _pathResolve(configured) : _pathJoin(process.cwd(), ".omnius");
531670
+ }
531607
531671
  /** Get current task state (for external inspection) */
531608
531672
  get taskState() {
531609
531673
  return this._taskState;
@@ -534298,10 +534362,10 @@ Respond with your assessment, then take action.`;
534298
534362
  this._toolLastUsedTurn.clear();
534299
534363
  this._contextTree = null;
534300
534364
  this._lastSurfacedAnchorIds.clear();
534301
- if (!this._memoryInitialized) {
534365
+ if (!this.options.disablePersistentMemory && !this._memoryInitialized) {
534302
534366
  try {
534303
534367
  const path11 = await import("node:path");
534304
- const omniusDir = path11.join(process.cwd(), ".omnius");
534368
+ const omniusDir = this.omniusStateDir();
534305
534369
  const rawEpisodeStore = new EpisodeStore(path11.join(omniusDir, "episodes.db"));
534306
534370
  const crlConfig = getCRLConfigStore(process.cwd());
534307
534371
  if (crlConfig.get().mode !== "json") {
@@ -534334,8 +534398,9 @@ Respond with your assessment, then take action.`;
534334
534398
  const path11 = await import("node:path");
534335
534399
  const os8 = await import("node:os");
534336
534400
  const globalFile = path11.join(os8.homedir(), ".omnius", "error-patterns.json");
534337
- const localFile = path11.join(process.cwd(), ".omnius", "error-patterns.json");
534338
- for (const patternsFile of [globalFile, localFile]) {
534401
+ const localFile = this.options.stateDir ? path11.join(this.omniusStateDir(), "error-patterns.json") : path11.join(process.cwd(), ".omnius", "error-patterns.json");
534402
+ const patternFiles = this.options.stateDir ? [localFile] : [globalFile, localFile];
534403
+ for (const patternsFile of patternFiles) {
534339
534404
  try {
534340
534405
  if (fs10.existsSync(patternsFile)) {
534341
534406
  const saved = JSON.parse(fs10.readFileSync(patternsFile, "utf8"));
@@ -534426,7 +534491,7 @@ Respond with your assessment, then take action.`;
534426
534491
  } catch {
534427
534492
  }
534428
534493
  }
534429
- if (process.env["OMNIUS_DISABLE_STAGE_CONTEXT"] !== "1") {
534494
+ if (!this.options.disablePersistentMemory && process.env["OMNIUS_DISABLE_STAGE_CONTEXT"] !== "1") {
534430
534495
  try {
534431
534496
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
534432
534497
  if (!this._stageContext) {
@@ -534459,7 +534524,7 @@ Respond with your assessment, then take action.`;
534459
534524
  } catch {
534460
534525
  }
534461
534526
  }
534462
- if (process.env["OMNIUS_DISABLE_EMBEDDING_DRIFT_CHECK"] !== "1" && this._episodeStore) {
534527
+ if (!this.options.disablePersistentMemory && process.env["OMNIUS_DISABLE_EMBEDDING_DRIFT_CHECK"] !== "1" && this._episodeStore) {
534463
534528
  try {
534464
534529
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
534465
534530
  const currentModel = process.env["OMNIUS_EMBEDDING_MODEL"] || "default";
@@ -534483,7 +534548,7 @@ Respond with your assessment, then take action.`;
534483
534548
  } catch {
534484
534549
  }
534485
534550
  }
534486
- if (process.env["OMNIUS_DISABLE_CODEBASE_MAP"] !== "1" && this._episodeStore && this._workingDirectory) {
534551
+ if (!this.options.disablePersistentMemory && !this.options.disableCodebaseMap && !this.options.subAgent && !this.options.stateDir && process.env["OMNIUS_DISABLE_CODEBASE_MAP"] !== "1" && this._episodeStore && this._workingDirectory) {
534487
534552
  try {
534488
534553
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
534489
534554
  let commitSha = "no-commit";
@@ -534604,7 +534669,7 @@ TASK: ${task}` : task;
534604
534669
  }
534605
534670
  try {
534606
534671
  if (!this._reflectionBuffer) {
534607
- const omniusDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".omnius", "memory") : null;
534672
+ const omniusDir = this.options.stateDir ? _pathJoin(this.omniusStateDir(), "memory") : this._workingDirectory ? _pathJoin(this._workingDirectory, ".omnius", "memory") : null;
534608
534673
  if (omniusDir) {
534609
534674
  this._reflectionBuffer = new TaskReflectionBuffer(_pathJoin(omniusDir, "reflections.json"));
534610
534675
  }
@@ -536572,7 +536637,7 @@ ${memoryLines.join("\n")}`
536572
536637
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
536573
536638
  });
536574
536639
  try {
536575
- const archDir = __require("node:path").join(process.cwd(), ".omnius", "phases");
536640
+ const archDir = __require("node:path").join(this.omniusStateDir(), "phases");
536576
536641
  __require("node:fs").mkdirSync(archDir, { recursive: true });
536577
536642
  for (const phaseName of contracted) {
536578
536643
  const node = this._contextTree.getSnapshot().phases[phaseName];
@@ -539190,8 +539255,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
539190
539255
  const path11 = await import("node:path");
539191
539256
  const os8 = await import("node:os");
539192
539257
  const globalFile = path11.join(os8.homedir(), ".omnius", "error-patterns.json");
539193
- const localFile = path11.join(process.cwd(), ".omnius", "error-patterns.json");
539194
- for (const targetFile of [globalFile, localFile]) {
539258
+ const localFile = path11.join(this.options.stateDir ? this.omniusStateDir() : path11.join(process.cwd(), ".omnius"), "error-patterns.json");
539259
+ const targetFiles = this.options.stateDir ? [localFile] : [globalFile, localFile];
539260
+ for (const targetFile of targetFiles) {
539195
539261
  try {
539196
539262
  const dir = path11.dirname(targetFile);
539197
539263
  fs10.mkdirSync(dir, { recursive: true });
@@ -539243,10 +539309,11 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
539243
539309
  };
539244
539310
  const fs10 = await import("node:fs");
539245
539311
  const path11 = await import("node:path");
539246
- const consolidDir = path11.join(process.cwd(), ".omnius", "consolidations");
539312
+ const stateDir = this.omniusStateDir();
539313
+ const consolidDir = path11.join(stateDir, "consolidations");
539247
539314
  fs10.mkdirSync(consolidDir, { recursive: true });
539248
539315
  fs10.writeFileSync(path11.join(consolidDir, `${this._sessionId}.json`), JSON.stringify(consolidation, null, 2));
539249
- const provenanceDir = path11.join(process.cwd(), ".omnius", "provenance");
539316
+ const provenanceDir = path11.join(stateDir, "provenance");
539250
539317
  fs10.mkdirSync(provenanceDir, { recursive: true });
539251
539318
  const provenanceGraph = {
539252
539319
  sessionId: this._sessionId,
@@ -539746,7 +539813,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
539746
539813
  try {
539747
539814
  const fs10 = await import("node:fs");
539748
539815
  const path11 = await import("node:path");
539749
- const trajDir = path11.join(process.cwd(), ".omnius", "trajectories");
539816
+ const trajDir = path11.join(this.omniusStateDir(), "trajectories");
539750
539817
  fs10.mkdirSync(trajDir, { recursive: true });
539751
539818
  const trajectory = {
539752
539819
  id: this._sessionId,
@@ -539918,7 +539985,7 @@ ${errOutput}`;
539918
539985
  try {
539919
539986
  const { mkdirSync: mkdirSync76, writeFileSync: writeFileSync71 } = __require("node:fs");
539920
539987
  const { join: join142 } = __require("node:path");
539921
- const resultsDir = join142(process.cwd(), ".omnius", "tool-results");
539988
+ const resultsDir = join142(this.omniusStateDir(), "tool-results");
539922
539989
  mkdirSync76(resultsDir, { recursive: true });
539923
539990
  writeFileSync71(join142(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
539924
539991
  # Turn: ${turn}
@@ -539929,7 +539996,7 @@ ${result.output}`, "utf-8");
539929
539996
  } catch {
539930
539997
  }
539931
539998
  const { join: _pj } = __require("node:path");
539932
- const savedPath = _pj(process.cwd(), ".omnius", "tool-results", `${handleId}.txt`);
539999
+ const savedPath = _pj(this.omniusStateDir(), "tool-results", `${handleId}.txt`);
539933
540000
  const folded = this.foldOutput(modelContent, maxLen);
539934
540001
  return `[Tool output truncated — ${result.output.length} chars, ${lineCount} lines]
539935
540002
  Full output saved to: ${savedPath}
@@ -540283,7 +540350,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
540283
540350
  try {
540284
540351
  const { mkdirSync: mkdirSync76, writeFileSync: writeFileSync71 } = __require("node:fs");
540285
540352
  const { join: join142 } = __require("node:path");
540286
- const sessionDir = join142(this._workingDirectory, ".omnius", "session", this._sessionId);
540353
+ const sessionDir = this.options.stateDir ? join142(this.omniusStateDir(), "session", this._sessionId) : join142(this._workingDirectory, ".omnius", "session", this._sessionId);
540287
540354
  mkdirSync76(sessionDir, { recursive: true });
540288
540355
  const checkpoint = {
540289
540356
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -579164,13 +579231,13 @@ async function runSudoScript(ctx3, script) {
579164
579231
  } catch {
579165
579232
  }
579166
579233
  try {
579167
- const { spawn: spawn32 } = await import("node:child_process");
579234
+ const { spawn: spawn31 } = await import("node:child_process");
579168
579235
  const full = `set -e; ${script}`;
579169
579236
  await new Promise((resolve48) => {
579170
579237
  const usePkexec = process.platform === "linux";
579171
579238
  const cmd = usePkexec ? "pkexec" : "sudo";
579172
579239
  const args = usePkexec ? ["bash", "-lc", full] : ["-n", "bash", "-lc", full];
579173
- const child = spawn32(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
579240
+ const child = spawn31(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
579174
579241
  let stdout = "";
579175
579242
  let stderr = "";
579176
579243
  child.stdout?.on("data", (data) => {
@@ -581357,8 +581424,8 @@ async function handleSlashCommand(input, ctx3) {
581357
581424
  writeFileSync50(jwtFile, JSON.stringify(jwtPayload, null, 2));
581358
581425
  renderInfo(`Launching fortemi-react from ${fDir}...`);
581359
581426
  try {
581360
- const { spawn: spawn32 } = __require("node:child_process");
581361
- const child = spawn32(
581427
+ const { spawn: spawn31 } = __require("node:child_process");
581428
+ const child = spawn31(
581362
581429
  "npx",
581363
581430
  ["vite", "dev", "--host", "0.0.0.0", "--port", "3000"],
581364
581431
  {
@@ -582293,9 +582360,9 @@ systemctl --user daemon-reload || true
582293
582360
  systemctl --user enable --now omnius-daemon.service || true
582294
582361
  sleep 1
582295
582362
  `;
582296
- const { spawn: spawn32 } = await import("node:child_process");
582363
+ const { spawn: spawn31 } = await import("node:child_process");
582297
582364
  await new Promise((resolve48) => {
582298
- const child = spawn32("bash", ["-lc", takeover], {
582365
+ const child = spawn31("bash", ["-lc", takeover], {
582299
582366
  stdio: "inherit"
582300
582367
  });
582301
582368
  onChildExit(child, () => resolve48());
@@ -588307,8 +588374,8 @@ ${escapedContent}EOF'`, {
588307
588374
  }
588308
588375
  await new Promise((r2) => setTimeout(r2, 1e3));
588309
588376
  process.env.OLLAMA_NUM_PARALLEL = String(n2);
588310
- const { spawn: spawn32 } = await import("node:child_process");
588311
- const child = spawn32("ollama", ["serve"], {
588377
+ const { spawn: spawn31 } = await import("node:child_process");
588378
+ const child = spawn31("ollama", ["serve"], {
588312
588379
  stdio: "ignore",
588313
588380
  detached: true,
588314
588381
  env: { ...process.env, OLLAMA_NUM_PARALLEL: String(n2) }
@@ -596226,9 +596293,9 @@ function getDefaultPolicy(context2) {
596226
596293
  case "telegram-admin-dm":
596227
596294
  return { blocked: /* @__PURE__ */ new Set(), allowed: /* @__PURE__ */ new Set() };
596228
596295
  case "telegram-admin-group":
596229
- return { blocked: /* @__PURE__ */ new Set(), allowed: SAFE_GROUP_ADMIN_TOOLS };
596296
+ return { blocked: new Set(SYSTEM_TOOLS), allowed: SAFE_GROUP_ADMIN_TOOLS };
596230
596297
  case "telegram-public":
596231
- return { blocked: /* @__PURE__ */ new Set(), allowed: SAFE_PUBLIC_TOOLS };
596298
+ return { blocked: new Set(SYSTEM_TOOLS), allowed: SAFE_PUBLIC_TOOLS };
596232
596299
  case "api":
596233
596300
  return { blocked: /* @__PURE__ */ new Set(), allowed: /* @__PURE__ */ new Set() };
596234
596301
  default:
@@ -596263,10 +596330,45 @@ function applyToolPolicy(tools, context2, userConfig) {
596263
596330
  return policy.allowed.has(tool.name);
596264
596331
  });
596265
596332
  }
596266
- var SAFE_PUBLIC_TOOLS, SAFE_GROUP_ADMIN_TOOLS;
596333
+ var SYSTEM_TOOLS, SAFE_PUBLIC_TOOLS, SAFE_GROUP_ADMIN_TOOLS;
596267
596334
  var init_tool_policy = __esm({
596268
596335
  "packages/cli/src/tui/tool-policy.ts"() {
596269
596336
  "use strict";
596337
+ SYSTEM_TOOLS = /* @__PURE__ */ new Set([
596338
+ "shell",
596339
+ "file_write",
596340
+ "file_edit",
596341
+ "file_read",
596342
+ "file_patch",
596343
+ "batch_edit",
596344
+ "structured_file",
596345
+ "grep_search",
596346
+ "glob_find",
596347
+ "list_directory",
596348
+ "code_sandbox",
596349
+ "full_sub_agent",
596350
+ "telegram_sandbox_handoff",
596351
+ "codebase_map",
596352
+ "diagnostic",
596353
+ "git_info",
596354
+ "structured_read",
596355
+ "screenshot",
596356
+ "desktop_click",
596357
+ "desktop_describe",
596358
+ "background_run",
596359
+ "task_status",
596360
+ "task_output",
596361
+ "task_stop",
596362
+ "create_tool",
596363
+ "manage_tools",
596364
+ "aiwg_setup",
596365
+ "aiwg_health",
596366
+ "aiwg_workflow",
596367
+ "autoresearch",
596368
+ "scheduler",
596369
+ "reminder",
596370
+ "agenda"
596371
+ ]);
596270
596372
  SAFE_PUBLIC_TOOLS = /* @__PURE__ */ new Set([
596271
596373
  "memory_read",
596272
596374
  "memory_write",
@@ -596280,7 +596382,6 @@ var init_tool_policy = __esm({
596280
596382
  "generate_tts",
596281
596383
  "create_audio_file",
596282
596384
  "telegram_send_file",
596283
- "telegram_sandbox_handoff",
596284
596385
  "task_complete"
596285
596386
  ]);
596286
596387
  SAFE_GROUP_ADMIN_TOOLS = /* @__PURE__ */ new Set([
@@ -596297,14 +596398,12 @@ var init_tool_policy = __esm({
596297
596398
  "generate_tts",
596298
596399
  "create_audio_file",
596299
596400
  "telegram_send_file",
596300
- "telegram_sandbox_handoff",
596301
596401
  "task_complete"
596302
596402
  ]);
596303
596403
  }
596304
596404
  });
596305
596405
 
596306
596406
  // packages/cli/src/tui/telegram-creative-tools.ts
596307
- import { spawn as spawn27 } from "node:child_process";
596308
596407
  import {
596309
596408
  existsSync as existsSync104,
596310
596409
  mkdirSync as mkdirSync59,
@@ -596316,6 +596415,7 @@ import { mkdir as mkdir17 } from "node:fs/promises";
596316
596415
  import {
596317
596416
  basename as basename22,
596318
596417
  dirname as dirname33,
596418
+ extname as extname14,
596319
596419
  isAbsolute as isAbsolute6,
596320
596420
  join as join119,
596321
596421
  relative as relative12,
@@ -596334,11 +596434,26 @@ function formatTelegramCreativeWorkspacePrompt(root) {
596334
596434
  "Public/group creative workspace is enabled.",
596335
596435
  `Workspace root: ${root}`,
596336
596436
  "Creative file tools are scoped to that folder only.",
596337
- "Allowed: create new files, read/list files in this workspace, and edit/patch files already created in this workspace.",
596338
- "Forbidden: delete files, access paths outside this workspace, mutate the project tree, run shell commands, or touch system state.",
596437
+ "Allowed: create and send non-executable creative artifacts in this workspace.",
596438
+ "Forbidden: delete files, create scripts/executables, access paths outside this workspace, mutate the project tree, run shell/Python/code commands, or touch system state.",
596339
596439
  "When a user asks for an artifact to be sent, create it here and then call telegram_send_file. The bridge also auto-attaches recorded artifacts as a fallback. Refer to the attachment naturally; do not expose filesystem paths unless the admin explicitly asks."
596340
596440
  ].join("\n");
596341
596441
  }
596442
+ function publicCreativeArtifactPolicyError(path11) {
596443
+ const ext = extname14(path11).toLowerCase();
596444
+ if (PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS.has(ext)) {
596445
+ return `Public Telegram creative work cannot create or send executable/script/code artifacts (${ext || "no extension"}). Use a non-executable artifact such as .md, .txt, .json, image, audio, or PDF.`;
596446
+ }
596447
+ return null;
596448
+ }
596449
+ function publicCreativeContentPolicyError(args) {
596450
+ const content = args["content"] ?? args["text"] ?? args["data"];
596451
+ if (typeof content !== "string") return null;
596452
+ if (/^\s*#!/.test(content)) {
596453
+ return "Public Telegram creative work cannot create executable scripts or shebang files.";
596454
+ }
596455
+ return null;
596456
+ }
596342
596457
  function collectGeneratedArtifactPathsFromText(text, root) {
596343
596458
  const rootAbs = resolve38(root);
596344
596459
  const paths = /* @__PURE__ */ new Set();
@@ -596418,6 +596533,10 @@ function scopedTool(base3, root, mode) {
596418
596533
  if (pathKey) {
596419
596534
  const guarded = guardPath(rootAbs, String(next[pathKey]));
596420
596535
  if (!guarded.ok) return denied(guarded.error);
596536
+ const pathPolicyError = publicCreativeArtifactPolicyError(guarded.path.abs);
596537
+ if (pathPolicyError) return denied(pathPolicyError);
596538
+ const contentPolicyError = mode === "create" || mode === "edit" ? publicCreativeContentPolicyError(next) : null;
596539
+ if (contentPolicyError) return denied(contentPolicyError);
596421
596540
  if (mode === "edit" && !manifestHas(rootAbs, guarded.path.rel)) {
596422
596541
  return denied(`Public Telegram creative tools can only edit files created in this chat workspace: ${guarded.path.rel}`);
596423
596542
  }
@@ -596530,33 +596649,7 @@ function denied(error) {
596530
596649
  mutatedFiles: []
596531
596650
  };
596532
596651
  }
596533
- function runProcess4(command, args, cwd4, timeoutMs) {
596534
- return new Promise((resolveProcess) => {
596535
- const child = spawn27(command, args, {
596536
- cwd: cwd4,
596537
- stdio: ["ignore", "pipe", "pipe"]
596538
- });
596539
- let stdout = "";
596540
- let stderr = "";
596541
- const timer = setTimeout(() => child.kill("SIGTERM"), timeoutMs);
596542
- timer.unref();
596543
- child.stdout?.on("data", (chunk) => {
596544
- stdout += chunk.toString();
596545
- });
596546
- child.stderr?.on("data", (chunk) => {
596547
- stderr += chunk.toString();
596548
- });
596549
- onChildError(child, (err) => {
596550
- clearTimeout(timer);
596551
- resolveProcess({ code: 127, stdout, stderr: stderr + String(err.message || err) });
596552
- });
596553
- onChildClose(child, (code8) => {
596554
- clearTimeout(timer);
596555
- resolveProcess({ code: code8, stdout, stderr });
596556
- });
596557
- });
596558
- }
596559
- var MANIFEST_FILE, PATH_KEYS, MEDIA_PATH_RE, CreativeAudioFileTool;
596652
+ var MANIFEST_FILE, PATH_KEYS, MEDIA_PATH_RE, PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS, CreativeAudioFileTool;
596560
596653
  var init_telegram_creative_tools = __esm({
596561
596654
  "packages/cli/src/tui/telegram-creative-tools.ts"() {
596562
596655
  "use strict";
@@ -596565,6 +596658,65 @@ var init_telegram_creative_tools = __esm({
596565
596658
  MANIFEST_FILE = ".omnius-creative-manifest.json";
596566
596659
  PATH_KEYS = ["path", "file", "file_path", "filename", "filepath", "filePath"];
596567
596660
  MEDIA_PATH_RE = /(?:^|[\s([])(\/[^\s<>"')\]]+\.[A-Za-z0-9]{1,12})(?:$|[\s),.\]])/g;
596661
+ PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS = /* @__PURE__ */ new Set([
596662
+ ".sh",
596663
+ ".bash",
596664
+ ".zsh",
596665
+ ".fish",
596666
+ ".ksh",
596667
+ ".csh",
596668
+ ".py",
596669
+ ".pyw",
596670
+ ".pyc",
596671
+ ".pyo",
596672
+ ".ipynb",
596673
+ ".js",
596674
+ ".mjs",
596675
+ ".cjs",
596676
+ ".ts",
596677
+ ".tsx",
596678
+ ".jsx",
596679
+ ".rb",
596680
+ ".pl",
596681
+ ".pm",
596682
+ ".php",
596683
+ ".lua",
596684
+ ".r",
596685
+ ".jl",
596686
+ ".ps1",
596687
+ ".psm1",
596688
+ ".bat",
596689
+ ".cmd",
596690
+ ".com",
596691
+ ".exe",
596692
+ ".msi",
596693
+ ".jar",
596694
+ ".class",
596695
+ ".war",
596696
+ ".ear",
596697
+ ".go",
596698
+ ".rs",
596699
+ ".c",
596700
+ ".cc",
596701
+ ".cpp",
596702
+ ".cxx",
596703
+ ".h",
596704
+ ".hpp",
596705
+ ".java",
596706
+ ".kt",
596707
+ ".kts",
596708
+ ".scala",
596709
+ ".swift",
596710
+ ".cs",
596711
+ ".vb",
596712
+ ".fs",
596713
+ ".so",
596714
+ ".dylib",
596715
+ ".dll",
596716
+ ".app",
596717
+ ".appimage",
596718
+ ".desktop"
596719
+ ]);
596568
596720
  CreativeAudioFileTool = class {
596569
596721
  constructor(root) {
596570
596722
  this.root = root;
@@ -596576,15 +596728,20 @@ var init_telegram_creative_tools = __esm({
596576
596728
  type: "object",
596577
596729
  properties: {
596578
596730
  text: { type: "string", description: "Text to synthesize into speech" },
596731
+ input: { type: "string", description: "Alias for text" },
596732
+ prompt: { type: "string", description: "Alias for text" },
596579
596733
  path: { type: "string", description: "Output .wav path inside the creative workspace" },
596580
- voice: { type: "string", description: "espeak-ng voice/language, default en" },
596581
- speed: { type: "number", description: "Speech speed, default 160" }
596734
+ backend: { type: "string", enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper", "espeak"], description: "TTS backend. Defaults to auto." },
596735
+ voice: { type: "string", description: "Voice id/name for the selected TTS backend" },
596736
+ clone_ref: { type: "string", description: "Optional LuxTTS clone reference" },
596737
+ model: { type: "string", description: "Optional backend model id or raw Piper/ONNX path" },
596738
+ speed: { type: "number", description: "Speech speed multiplier or backend-specific rate" }
596582
596739
  },
596583
- required: ["text", "path"]
596740
+ required: []
596584
596741
  };
596585
596742
  async execute(args) {
596586
596743
  const start2 = performance.now();
596587
- const text = typeof args["text"] === "string" ? args["text"].trim() : "";
596744
+ const text = typeof args["text"] === "string" && args["text"].trim() ? args["text"].trim() : typeof args["input"] === "string" && args["input"].trim() ? args["input"].trim() : typeof args["prompt"] === "string" && args["prompt"].trim() ? args["prompt"].trim() : "";
596588
596745
  if (!text) return { success: false, output: "", error: "text is required", durationMs: performance.now() - start2 };
596589
596746
  const rawPath = typeof args["path"] === "string" && args["path"].trim() ? String(args["path"]) : `speech-${Date.now()}.wav`;
596590
596747
  const guarded = guardPath(this.root, rawPath);
@@ -596596,15 +596753,23 @@ var init_telegram_creative_tools = __esm({
596596
596753
  return denied("create_audio_file currently writes WAV files; use a .wav output path.");
596597
596754
  }
596598
596755
  await mkdir17(dirname33(guarded.path.abs), { recursive: true });
596599
- const voice = typeof args["voice"] === "string" && args["voice"].trim() ? String(args["voice"]) : "en";
596600
- const speed = Number.isFinite(Number(args["speed"])) ? String(Math.round(Number(args["speed"]))) : "160";
596601
- const result = await runProcess4("espeak-ng", ["-w", guarded.path.abs, "-v", voice, "-s", speed, text], this.root, 12e4);
596602
- if (result.code !== 0 || !existsSync104(guarded.path.abs)) {
596756
+ const tts = new TtsGenerateTool();
596757
+ const result = await tts.execute({
596758
+ text,
596759
+ output: guarded.path.abs,
596760
+ playback: false,
596761
+ backend: args["backend"],
596762
+ voice: args["voice"],
596763
+ clone_ref: args["clone_ref"],
596764
+ model: args["model"],
596765
+ speed: args["speed"]
596766
+ });
596767
+ if (!result.success || !existsSync104(guarded.path.abs)) {
596603
596768
  return {
596604
596769
  success: false,
596605
596770
  output: "",
596606
- error: `Audio synthesis failed. Install espeak-ng or check voice settings.
596607
- ${(result.stderr || result.stdout).slice(0, 800)}`,
596771
+ error: `Audio synthesis failed through generate_tts.
596772
+ ${(result.error || result.output || "").slice(0, 1200)}`,
596608
596773
  durationMs: performance.now() - start2
596609
596774
  };
596610
596775
  }
@@ -596612,7 +596777,8 @@ ${(result.stderr || result.stdout).slice(0, 800)}`,
596612
596777
  const sizeKB = Math.round(statSync35(guarded.path.abs).size / 1024);
596613
596778
  return {
596614
596779
  success: true,
596615
- output: `Created audio file: ${guarded.path.abs} (${sizeKB}KB WAV)`,
596780
+ output: `Created audio file: ${guarded.path.abs} (${sizeKB}KB WAV)
596781
+ ${result.output}`,
596616
596782
  llmContent: `Created audio file at ${guarded.path.abs}.`,
596617
596783
  durationMs: performance.now() - start2,
596618
596784
  mutated: true,
@@ -596777,7 +596943,7 @@ var init_vision_ingress = __esm({
596777
596943
  });
596778
596944
 
596779
596945
  // packages/cli/src/tui/telegram-bridge.ts
596780
- import { mkdirSync as mkdirSync60, existsSync as existsSync106, unlinkSync as unlinkSync20, statSync as statSync36, readFileSync as readFileSync87, writeFileSync as writeFileSync57 } from "node:fs";
596946
+ import { mkdirSync as mkdirSync60, existsSync as existsSync106, unlinkSync as unlinkSync20, readdirSync as readdirSync36, statSync as statSync36, readFileSync as readFileSync87, writeFileSync as writeFileSync57 } from "node:fs";
596781
596947
  import { join as join121, resolve as resolve39, basename as basename23, relative as relative13, isAbsolute as isAbsolute7 } from "node:path";
596782
596948
  import { writeFile as writeFileAsync } from "node:fs/promises";
596783
596949
  import { createHash as createHash19, randomInt } from "node:crypto";
@@ -597093,7 +597259,8 @@ function telegramSyntheticHelpSignatures() {
597093
597259
  return [
597094
597260
  { signature: "/help", description: "Show Telegram command help" },
597095
597261
  { signature: "/start", description: "Show Telegram bridge status and authentication instructions" },
597096
- { signature: "/auth <code>", description: "Authenticate this Telegram user as bot admin using the TUI code" }
597262
+ { signature: "/auth <code>", description: "Authenticate this Telegram user as bot admin using the TUI code" },
597263
+ { signature: "/call", description: "Get the active voice call link when a call session is running" }
597097
597264
  ];
597098
597265
  }
597099
597266
  function telegramHelpCommandAllowed(cmd, scope) {
@@ -597592,6 +597759,14 @@ You are responding to an ADMIN user in a private Telegram DM. This user has full
597592
597759
  and is the operator of this agent. You may use all available tools including memory read/write,
597593
597760
  file access, and code analysis. Respond thoroughly and helpfully.
597594
597761
 
597762
+ When asked to send a generated or existing file to Telegram, call telegram_send_file with
597763
+ path and target. Do not search for Telegram bot tokens, environment secrets, or Bot API
597764
+ credentials; upload authorization is encapsulated by telegram_send_file.
597765
+
597766
+ When asked to generate speech, narration, or TTS, use generate_tts or audio_playback.
597767
+ Those tools handle first-use backend setup where supported. Do not fall back to shell
597768
+ commands or generic audio generation for speech synthesis while those tools are available.
597769
+
597595
597770
  Keep responses concise for Telegram but don't withhold information from the admin.
597596
597771
  `.trim();
597597
597772
  ADMIN_GROUP_PROMPT = `
@@ -597821,6 +597996,8 @@ Telegram response contract:
597821
597996
  telegramConversationDir;
597822
597997
  /** Session keys loaded from persistent conversation memory */
597823
597998
  loadedConversationState = /* @__PURE__ */ new Set();
597999
+ /** True once persisted Telegram conversation scopes have been bulk-loaded. */
598000
+ loadedAllConversationState = false;
597824
598001
  /** Media metadata store per chat — chatId → metadata entries */
597825
598002
  mediaMetadata = /* @__PURE__ */ new Map();
597826
598003
  /** Cleanup timer for expired media cache entries */
@@ -598027,6 +598204,22 @@ Telegram response contract:
598027
598204
  } catch {
598028
598205
  }
598029
598206
  }
598207
+ ensureAllTelegramConversationsLoaded() {
598208
+ if (this.loadedAllConversationState) return;
598209
+ this.loadedAllConversationState = true;
598210
+ if (!existsSync106(this.telegramConversationDir)) return;
598211
+ try {
598212
+ for (const file of readdirSync36(this.telegramConversationDir)) {
598213
+ if (!file.endsWith(".json")) continue;
598214
+ try {
598215
+ const parsed = JSON.parse(readFileSync87(join121(this.telegramConversationDir, file), "utf8"));
598216
+ if (parsed.sessionKey) this.ensureTelegramConversationLoaded(parsed.sessionKey);
598217
+ } catch {
598218
+ }
598219
+ }
598220
+ } catch {
598221
+ }
598222
+ }
598030
598223
  saveTelegramConversationState(sessionKey) {
598031
598224
  if (!this.repoRoot) return;
598032
598225
  try {
@@ -598505,8 +598698,91 @@ ${lines.join("\n")}${suffix}`;
598505
598698
  Checklist unavailable: ${reason}`;
598506
598699
  }
598507
598700
  }
598701
+ knownTelegramChatTargets() {
598702
+ this.ensureAllTelegramConversationsLoaded();
598703
+ const targets = /* @__PURE__ */ new Map();
598704
+ for (const [sessionKey, history] of this.chatHistory.entries()) {
598705
+ if (history.length === 0) continue;
598706
+ const latest = [...history].reverse().find((entry) => entry.chatId !== void 0);
598707
+ if (!latest || latest.chatId === void 0) continue;
598708
+ const key = String(latest.chatId);
598709
+ const participants = [...this.chatParticipants.get(sessionKey)?.values() ?? []];
598710
+ const existing = targets.get(key);
598711
+ const target = existing ?? {
598712
+ chatId: latest.chatId,
598713
+ chatType: latest.chatType ?? "private",
598714
+ title: latest.chatTitle,
598715
+ sessionKey,
598716
+ lastSeenTs: latest.ts ?? 0,
598717
+ participantUserIds: [],
598718
+ participantUsernames: []
598719
+ };
598720
+ target.chatType = latest.chatType ?? target.chatType;
598721
+ target.title = latest.chatTitle || target.title;
598722
+ target.lastSeenTs = Math.max(target.lastSeenTs, latest.ts ?? 0);
598723
+ for (const participant of participants) {
598724
+ if (participant.fromUserId && !target.participantUserIds.includes(participant.fromUserId)) {
598725
+ target.participantUserIds.push(participant.fromUserId);
598726
+ }
598727
+ const username = (participant.username || "").replace(/^@/, "").trim().toLowerCase();
598728
+ if (username && username !== "unknown" && !target.participantUsernames.includes(username)) {
598729
+ target.participantUsernames.push(username);
598730
+ }
598731
+ }
598732
+ targets.set(key, target);
598733
+ }
598734
+ return [...targets.values()].sort((a2, b) => b.lastSeenTs - a2.lastSeenTs);
598735
+ }
598736
+ telegramGroupTargetsForSender(msg) {
598737
+ const username = (msg.username || "").replace(/^@/, "").trim().toLowerCase();
598738
+ return this.knownTelegramChatTargets().filter((target) => {
598739
+ if (target.chatType !== "group" && target.chatType !== "supergroup") return false;
598740
+ if (msg.fromUserId && target.participantUserIds.includes(msg.fromUserId)) return true;
598741
+ return !!username && target.participantUsernames.includes(username);
598742
+ });
598743
+ }
598744
+ formatTelegramSendTargetList(targets, max = 14) {
598745
+ if (targets.length === 0) return "";
598746
+ const lines = targets.slice(0, max).map((target) => {
598747
+ const label = target.title ? ` "${target.title}"` : "";
598748
+ const participants = target.participantUsernames.length ? ` participants:@${target.participantUsernames.slice(0, 8).join(", @")}` : "";
598749
+ return `- chat_id ${String(target.chatId)} (${target.chatType}${label}; session ${target.sessionKey})${participants}`;
598750
+ });
598751
+ const suffix = targets.length > lines.length ? `
598752
+ - ... ${targets.length - lines.length} more known chat(s)` : "";
598753
+ return `${lines.join("\n")}${suffix}`;
598754
+ }
598755
+ buildTelegramSendTargetContext(msg, isAdminDM) {
598756
+ if (isAdminDM) {
598757
+ const targets = this.knownTelegramChatTargets();
598758
+ const list = this.formatTelegramSendTargetList(targets, 18);
598759
+ return [
598760
+ "## Telegram Send Targets",
598761
+ "telegram_send_file is the only supported way to send generated/existing files to Telegram. Never look for or reveal the bot token; the bridge already owns upload auth.",
598762
+ "Use target with one of these known chat ids when the admin asks to send a file to a group/private chat:",
598763
+ list || "- No known prior Telegram chats are loaded yet. Numeric chat_id or @username can still be used if the admin provides one."
598764
+ ].join("\n");
598765
+ }
598766
+ if (msg.chatType === "private") {
598767
+ const targets = this.telegramGroupTargetsForSender(msg);
598768
+ const list = this.formatTelegramSendTargetList(targets, 10);
598769
+ return [
598770
+ "## Telegram Send Targets",
598771
+ "This private user may send generated files to this DM or to shared public groups where this sender has been observed by the bot. Public groups can never target private DMs.",
598772
+ list ? `Allowed shared group targets:
598773
+ ${list}` : "No shared group target is currently known for this sender. Ask in the group once or provide a numeric group chat_id the bot already participates in."
598774
+ ].join("\n");
598775
+ }
598776
+ return "";
598777
+ }
598778
+ telegramRunnerStateDir(sessionKey) {
598779
+ if (!this.repoRoot) return void 0;
598780
+ const safe = createHash19("sha1").update(sessionKey).digest("hex").slice(0, 20);
598781
+ return join121(this.repoRoot, ".omnius", "telegram-runner-state", safe);
598782
+ }
598508
598783
  buildTelegramAdminOverviewContext(currentSessionKey) {
598509
598784
  const sections = [];
598785
+ this.ensureAllTelegramConversationsLoaded();
598510
598786
  const chatEntries = [...this.chatHistory.entries()].filter(([sessionKey, history]) => sessionKey !== currentSessionKey && history.length > 0).sort(([, a2], [, b]) => (b[b.length - 1]?.ts ?? 0) - (a2[a2.length - 1]?.ts ?? 0)).slice(0, 18);
598511
598787
  for (const [sessionKey, history] of chatEntries) {
598512
598788
  const latest = history[history.length - 1];
@@ -598519,7 +598795,7 @@ Checklist unavailable: ${reason}`;
598519
598795
  ).join("\n");
598520
598796
  const cards = (this.chatMemoryCards.get(sessionKey) ?? []).slice(0, 4).map((card) => ` - ${card.title}: ${card.notes.slice(-1)[0] ?? ""}`).join("\n");
598521
598797
  sections.push([
598522
- `- ${sessionKey} (${latest.chatType || "chat"}${latest.chatTitle ? `: ${latest.chatTitle}` : ""})`,
598798
+ `- ${sessionKey} (chat_id ${String(latest.chatId ?? "unknown")}; ${latest.chatType || "chat"}${latest.chatTitle ? `: ${latest.chatTitle}` : ""})`,
598523
598799
  participants ? ` Participants: ${participants}` : "",
598524
598800
  ` Latest: ${telegramHistorySpeaker(latest)}: ${truncateTelegramContextLine(latest.text, 180)}`,
598525
598801
  recent ? ` Recent:
@@ -598560,6 +598836,8 @@ ${chatLabel}`,
598560
598836
  TELEGRAM_ACTION_RESPONSE_CONTRACT
598561
598837
  ];
598562
598838
  sections.push(conversationStream);
598839
+ const sendTargetContext = this.buildTelegramSendTargetContext(msg, isAdminDM);
598840
+ if (sendTargetContext) sections.push(sendTargetContext);
598563
598841
  if (isAdminDM) {
598564
598842
  sections.push(`## Admin Capability Contract
598565
598843
 
@@ -599374,6 +599652,7 @@ ${mediaContext}` : ""}`
599374
599652
  const creativeWorkspace = subAgent.creativeWorkspaceRoot ? formatTelegramCreativeWorkspacePrompt(subAgent.creativeWorkspaceRoot) : "";
599375
599653
  const sessionContext = this.buildTelegramSessionContext(msg, ctx3, profile, modelTier);
599376
599654
  const contextWindowSize = this.contextWindowSize;
599655
+ const runnerStateDir = isAdminDM ? void 0 : this.telegramRunnerStateDir(sessionContext.sessionKey);
599377
599656
  const backend = new OllamaAgenticBackend(
599378
599657
  config.backendUrl,
599379
599658
  config.model,
@@ -599390,7 +599669,11 @@ ${mediaContext}` : ""}`
599390
599669
  modelTier,
599391
599670
  streamEnabled: true,
599392
599671
  dynamicContext: sessionContext.context,
599393
- sessionId: sessionContext.sessionId
599672
+ stateDir: runnerStateDir,
599673
+ sessionId: sessionContext.sessionId,
599674
+ disablePersistentMemory: false,
599675
+ disableCodebaseMap: !isAdminDM,
599676
+ subAgent: !isAdminDM
599394
599677
  });
599395
599678
  runner.setWorkingDirectory(repoRoot);
599396
599679
  subAgent.runner = runner;
@@ -599406,6 +599689,9 @@ ${mediaContext}` : ""}`
599406
599689
  runner.onEvent((event) => {
599407
599690
  if (subAgent.aborted) return;
599408
599691
  let suppressExternalEvent = false;
599692
+ if (!isAdminDM && event.type === "status") {
599693
+ suppressExternalEvent = true;
599694
+ }
599409
599695
  if (event.type === "tool_call" && event.toolName) {
599410
599696
  if (event.toolName === "task_complete") {
599411
599697
  subAgent.completionBoundarySeen = true;
@@ -599441,7 +599727,9 @@ ${mediaContext}` : ""}`
599441
599727
  }
599442
599728
  }
599443
599729
  } else if (event.type === "status" && event.content) {
599444
- this.subAgentViewCallbacks?.onWrite(subAgent.viewId, `status: ${event.content}`);
599730
+ if (isAdminDM) {
599731
+ this.subAgentViewCallbacks?.onWrite(subAgent.viewId, `status: ${event.content}`);
599732
+ }
599445
599733
  } else if (event.type === "assistant_text" && event.content && event.source !== "task_complete_summary") {
599446
599734
  subAgent.assistantText = event.content;
599447
599735
  } else if (event.type === "stream_start") {
@@ -599456,7 +599744,7 @@ ${mediaContext}` : ""}`
599456
599744
  subAgent.accumulated += event.content;
599457
599745
  }
599458
599746
  const intermediateLine = formatTelegramProgressEvent(event);
599459
- if (intermediateLine) {
599747
+ if (intermediateLine && (isAdminDM || event.type !== "status")) {
599460
599748
  subAgent.intermediateLines.push(intermediateLine);
599461
599749
  }
599462
599750
  if (!msg.guestQueryId) {
@@ -599878,6 +600166,9 @@ Scoped workspace: ${scopedRoot}`,
599878
600166
  new PlaywrightBrowserTool(),
599879
600167
  new ImageGenerateTool(repoRoot, this.agentConfig?.backendUrl, imageDefaults),
599880
600168
  new AudioGenerateTool(repoRoot, audioDefaults),
600169
+ new TtsGenerateTool(),
600170
+ new AudioPlaybackTool(),
600171
+ new SoundPlaybackTool(),
599881
600172
  new NotebookEditTool(),
599882
600173
  new RepoMapTool(repoRoot),
599883
600174
  new ImportGraphTool(repoRoot),
@@ -599886,7 +600177,7 @@ Scoped workspace: ${scopedRoot}`,
599886
600177
  new CodeNeighborsTool(repoRoot),
599887
600178
  new ProcessHealthTool(),
599888
600179
  fullSubAgentTool,
599889
- this.buildTelegramSendFileTool(context2, repoRoot, chatId)
600180
+ this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg)
599890
600181
  ];
599891
600182
  const allTools = context2 === "telegram-admin-dm" ? adminTools : sharedReadMemoryWebTools;
599892
600183
  if (this.contextWindowSize > 0) {
@@ -599908,11 +600199,28 @@ Scoped workspace: ${scopedRoot}`,
599908
600199
  audioDefaults
599909
600200
  ).map((tool) => adaptTool5(tool, todoSessionId));
599910
600201
  adaptedTools.push(...creativeTools);
599911
- adaptedTools.push(adaptTool5(this.buildTelegramSendFileTool(context2, repoRoot, chatId), todoSessionId));
599912
- adaptedTools.push(adaptTool5(this.buildTelegramSandboxHandoffTool(context2, repoRoot, `chat:${String(chatId ?? "unknown")}`, chatId), todoSessionId));
600202
+ adaptedTools.push(adaptTool5(this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg), todoSessionId));
600203
+ adaptedTools = this.filterNonAdminTelegramTools(adaptedTools);
599913
600204
  }
599914
600205
  return [...adaptedTools, taskComplete];
599915
600206
  }
600207
+ filterNonAdminTelegramTools(tools) {
600208
+ const blocked = /* @__PURE__ */ new Set([
600209
+ "shell",
600210
+ "code_sandbox",
600211
+ "background_run",
600212
+ "task_output",
600213
+ "task_status",
600214
+ "task_stop",
600215
+ "full_sub_agent",
600216
+ "telegram_sandbox_handoff",
600217
+ "create_tool",
600218
+ "manage_tools",
600219
+ "aiwg_setup",
600220
+ "aiwg_workflow"
600221
+ ]);
600222
+ return tools.filter((tool) => !blocked.has(tool.name));
600223
+ }
599916
600224
  imageGenerationDefaultsForRepo(repoRoot) {
599917
600225
  const settings = resolveSettings(repoRoot);
599918
600226
  return {
@@ -599929,13 +600237,13 @@ Scoped workspace: ${scopedRoot}`,
599929
600237
  musicBackend: settings.musicBackend
599930
600238
  };
599931
600239
  }
599932
- buildTelegramSendFileTool(context2, repoRoot, currentChatId) {
600240
+ buildTelegramSendFileTool(context2, repoRoot, currentChatId, currentMsg) {
599933
600241
  const bridge = this;
599934
600242
  const adminDm = context2 === "telegram-admin-dm";
599935
600243
  const scopedRoot = adminDm ? void 0 : telegramCreativeWorkspaceRoot(repoRoot, currentChatId);
599936
600244
  return {
599937
600245
  name: "telegram_send_file",
599938
- description: adminDm ? "Upload an existing local file to a Telegram chat. Admin DM scope may target the current chat, a numeric chat/user id, or a @username if the bot is allowed to message it. This only sends the file; it does not create, edit, or delete files." : `Upload an existing file from this chat's scoped creative workspace to the current Telegram chat. Public/group scope cannot target other chats and cannot send files outside ${scopedRoot}.`,
600246
+ description: adminDm ? "Upload an existing local file to a Telegram target. Use target=<chat_id|user_id|@username> to send to a specific group/user the bot can message. This only sends the file; it does not create, edit, delete, or inspect bot tokens." : currentMsg?.chatType === "private" ? `Upload an existing file from this private chat's scoped creative workspace. target may be omitted for this DM or set to a known shared group chat_id for this sender. It cannot target other private users and cannot send files outside ${scopedRoot}.` : `Upload an existing file from this chat's scoped creative workspace to the current Telegram chat. Public/group scope cannot target private DMs or other chats and cannot send files outside ${scopedRoot}.`,
599939
600247
  parameters: {
599940
600248
  type: "object",
599941
600249
  properties: {
@@ -599943,13 +600251,25 @@ Scoped workspace: ${scopedRoot}`,
599943
600251
  type: "string",
599944
600252
  description: adminDm ? "Local file path to send. Relative paths resolve from the repo root; absolute paths are allowed for admin DM." : "File path inside the scoped creative workspace."
599945
600253
  },
600254
+ target: {
600255
+ type: "string",
600256
+ description: adminDm ? "Optional target chat/user. Use a known numeric group chat_id (for example -100...), a numeric user id, or @username. Defaults to the current private chat." : currentMsg?.chatType === "private" ? "Optional target. Omit for this DM, or use a known shared group chat_id/title/session key for this sender." : "Public/group scope ignores external targets and may only send to the current chat."
600257
+ },
599946
600258
  chat_id: {
599947
600259
  type: "string",
599948
- description: "Admin DM only. Optional target chat/user id or @username. Defaults to the current Telegram chat."
600260
+ description: "Alias for target. Admin/private DM only."
600261
+ },
600262
+ target_chat_id: {
600263
+ type: "string",
600264
+ description: "Alias for target/chat_id. Admin/private DM only."
599949
600265
  },
599950
600266
  user_id: {
599951
600267
  type: "string",
599952
- description: "Admin DM only. Optional numeric Telegram user id to send to, if the bot can message that user."
600268
+ description: "Alias for target when sending to a numeric Telegram user id from admin/private DM."
600269
+ },
600270
+ username: {
600271
+ type: "string",
600272
+ description: "Alias for target when sending to @username from admin/private DM."
599953
600273
  },
599954
600274
  kind: {
599955
600275
  type: "string",
@@ -599973,7 +600293,7 @@ Scoped workspace: ${scopedRoot}`,
599973
600293
  if (!rawPath) {
599974
600294
  return { success: false, output: "", error: "path is required", durationMs: performance.now() - start2 };
599975
600295
  }
599976
- const target = bridge.resolveTelegramFileTarget(args, currentChatId, adminDm);
600296
+ const target = bridge.resolveTelegramFileTarget(args, currentChatId, adminDm, currentMsg);
599977
600297
  if (!target.ok) {
599978
600298
  return { success: false, output: "", error: target.error, durationMs: performance.now() - start2 };
599979
600299
  }
@@ -600009,9 +600329,34 @@ Scoped workspace: ${scopedRoot}`,
600009
600329
  }
600010
600330
  };
600011
600331
  }
600012
- resolveTelegramFileTarget(args, currentChatId, adminDm) {
600013
- const rawTarget = args["chat_id"] ?? args["target_chat_id"] ?? args["user_id"] ?? args["username"];
600332
+ resolveTelegramFileTarget(args, currentChatId, adminDm, currentMsg) {
600333
+ const rawTarget = args["target"] ?? args["chat_id"] ?? args["target_chat_id"] ?? args["user_id"] ?? args["username"];
600014
600334
  if (!adminDm) {
600335
+ if (currentMsg?.chatType === "private") {
600336
+ if (rawTarget === void 0 || !String(rawTarget).trim()) {
600337
+ if (currentChatId === void 0) return { ok: false, error: "Current Telegram chat id is unavailable." };
600338
+ return { ok: true, chatId: currentChatId };
600339
+ }
600340
+ const target2 = String(rawTarget).trim();
600341
+ if (currentChatId !== void 0 && target2 === String(currentChatId)) {
600342
+ return { ok: true, chatId: currentChatId };
600343
+ }
600344
+ const knownGroups = this.telegramGroupTargetsForSender(currentMsg);
600345
+ const normalized = target2.replace(/^@/, "").trim().toLowerCase();
600346
+ const match = knownGroups.find((candidate) => {
600347
+ if (String(candidate.chatId) === target2) return true;
600348
+ if (candidate.sessionKey === target2) return true;
600349
+ if (candidate.title && candidate.title.trim().toLowerCase() === normalized) return true;
600350
+ return false;
600351
+ });
600352
+ if (match) return { ok: true, chatId: match.chatId };
600353
+ const knownList = this.formatTelegramSendTargetList(knownGroups, 8);
600354
+ return {
600355
+ ok: false,
600356
+ error: knownList ? `Private-user telegram_send_file target must be this DM or a known shared group for this sender. Known shared groups:
600357
+ ${knownList}` : "Private-user telegram_send_file target must be this DM or a known shared group for this sender. No shared group is currently known."
600358
+ };
600359
+ }
600015
600360
  if (rawTarget !== void 0 && String(rawTarget).trim() && String(rawTarget).trim() !== String(currentChatId)) {
600016
600361
  return { ok: false, error: "Public/group telegram_send_file cannot target another chat. It may only send to the current chat." };
600017
600362
  }
@@ -600037,6 +600382,10 @@ Scoped workspace: ${scopedRoot}`,
600037
600382
  if (scopedRoot && !isPathInside(base3, abs)) {
600038
600383
  return { ok: false, error: `Public/group telegram_send_file can only send files inside ${base3}.` };
600039
600384
  }
600385
+ if (scopedRoot) {
600386
+ const policyError = publicCreativeArtifactPolicyError(abs);
600387
+ if (policyError) return { ok: false, error: policyError };
600388
+ }
600040
600389
  if (!existsSync106(abs)) return { ok: false, error: `File does not exist: ${trimmed}` };
600041
600390
  if (!statSync36(abs).isFile()) return { ok: false, error: `Path is not a file: ${trimmed}` };
600042
600391
  return { ok: true, path: abs };
@@ -604417,7 +604766,7 @@ __export(graphical_sudo_exports, {
604417
604766
  detectSudoHelper: () => detectSudoHelper,
604418
604767
  runGraphicalSudo: () => runGraphicalSudo
604419
604768
  });
604420
- import { spawn as spawn28 } from "node:child_process";
604769
+ import { spawn as spawn27 } from "node:child_process";
604421
604770
  import { existsSync as existsSync115, mkdirSync as mkdirSync67, writeFileSync as writeFileSync62, chmodSync as chmodSync2 } from "node:fs";
604422
604771
  import { join as join129 } from "node:path";
604423
604772
  import { tmpdir as tmpdir21 } from "node:os";
@@ -604487,7 +604836,7 @@ async function runGraphicalSudo(opts) {
604487
604836
  args = ["/bin/bash", opts.scriptPath, ...opts.args ?? []];
604488
604837
  }
604489
604838
  return new Promise((resolve48, reject) => {
604490
- const child = spawn28(cmd, args, {
604839
+ const child = spawn27(cmd, args, {
604491
604840
  env: { ...process.env, ...opts.env || {}, ...extraEnv },
604492
604841
  stdio: ["ignore", "pipe", "pipe"]
604493
604842
  });
@@ -616921,7 +617270,7 @@ var init_profiles = __esm({
616921
617270
  });
616922
617271
 
616923
617272
  // packages/cli/src/docker.ts
616924
- import { execSync as execSync56, spawn as spawn29 } from "node:child_process";
617273
+ import { execSync as execSync56, spawn as spawn28 } from "node:child_process";
616925
617274
  import { existsSync as existsSync119, mkdirSync as mkdirSync70, writeFileSync as writeFileSync65 } from "node:fs";
616926
617275
  import { join as join133, resolve as resolve42, dirname as dirname36 } from "node:path";
616927
617276
  import { homedir as homedir44 } from "node:os";
@@ -617202,7 +617551,7 @@ function runInContainer(opts) {
617202
617551
  if (opts.maxTurns) omniusArgs.push("--max-turns", String(opts.maxTurns));
617203
617552
  if (opts.timeoutS) omniusArgs.push("--timeout", String(opts.timeoutS));
617204
617553
  args.push(...omniusArgs);
617205
- return spawn29("docker", args, {
617554
+ return spawn28("docker", args, {
617206
617555
  stdio: ["ignore", "pipe", "pipe"]
617207
617556
  });
617208
617557
  }
@@ -617405,7 +617754,7 @@ import { createRequire as createRequire7 } from "node:module";
617405
617754
  import { fileURLToPath as fileURLToPath17 } from "node:url";
617406
617755
  import { dirname as dirname37, join as join135, resolve as resolve43 } from "node:path";
617407
617756
  import { homedir as homedir45 } from "node:os";
617408
- import { spawn as spawn30, execSync as execSync57 } from "node:child_process";
617757
+ import { spawn as spawn29, execSync as execSync57 } from "node:child_process";
617409
617758
  import { mkdirSync as mkdirSync71, writeFileSync as writeFileSync66, readFileSync as readFileSync98, readdirSync as readdirSync41, existsSync as existsSync120, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync24 } from "node:fs";
617410
617759
  import { randomBytes as randomBytes23, randomUUID as randomUUID16 } from "node:crypto";
617411
617760
  import { createHash as createHash23 } from "node:crypto";
@@ -619678,7 +620027,7 @@ ${task}` : task;
619678
620027
  runEnv["OMNIUS_RUN_SCOPE"] = req2._authScope || "admin";
619679
620028
  runEnv["OLLAMA_HOST"] = currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
619680
620029
  if (currentCfg.apiKey) runEnv["OMNIUS_API_KEY_INHERIT"] = currentCfg.apiKey;
619681
- const child = spawn30(process.execPath, [omniusBin, ...args], {
620030
+ const child = spawn29(process.execPath, [omniusBin, ...args], {
619682
620031
  cwd: resolve43(process.cwd()),
619683
620032
  env: runEnv,
619684
620033
  stdio: ["ignore", "pipe", "pipe"]
@@ -620007,7 +620356,7 @@ async function handleV1Update(req2, res, requestId) {
620007
620356
  cleanEnv.PATH = pathParts.join(":");
620008
620357
  let child;
620009
620358
  if (isWin2) {
620010
- child = spawn30(npmBin, ["install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
620359
+ child = spawn29(npmBin, ["install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
620011
620360
  detached: true,
620012
620361
  stdio: ["ignore", logFd, logFd],
620013
620362
  windowsHide: true,
@@ -620029,13 +620378,13 @@ async function handleV1Update(req2, res, requestId) {
620029
620378
  }
620030
620379
  }
620031
620380
  if (npmCli) {
620032
- child = spawn30(nodeBin, [npmCli, "install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
620381
+ child = spawn29(nodeBin, [npmCli, "install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
620033
620382
  detached: true,
620034
620383
  stdio: ["ignore", logFd, logFd],
620035
620384
  env: cleanEnv
620036
620385
  });
620037
620386
  } else {
620038
- child = spawn30(npmBin, ["install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
620387
+ child = spawn29(npmBin, ["install", "-g", pkgSpec, "--no-audit", "--no-fund", "--no-progress"], {
620039
620388
  detached: true,
620040
620389
  stdio: ["ignore", logFd, logFd],
620041
620390
  env: cleanEnv
@@ -620046,7 +620395,7 @@ async function handleV1Update(req2, res, requestId) {
620046
620395
  const installPid = child.pid ?? 0;
620047
620396
  if (installPid > 0 && !isWin2) {
620048
620397
  try {
620049
- const follower = spawn30("bash", ["-c", `while kill -0 ${installPid} 2>/dev/null; do sleep 1; done; echo "__EXIT_CODE=0" >> "${logPath3}"`], {
620398
+ const follower = spawn29("bash", ["-c", `while kill -0 ${installPid} 2>/dev/null; do sleep 1; done; echo "__EXIT_CODE=0" >> "${logPath3}"`], {
620050
620399
  detached: true,
620051
620400
  stdio: "ignore"
620052
620401
  });
@@ -620069,7 +620418,7 @@ async function handleV1Update(req2, res, requestId) {
620069
620418
  hasSystemdUnit ? `systemctl --user restart omnius-daemon.service >/dev/null 2>&1 || true` : `${JSON.stringify(omniusAbs)} serve --quiet --daemon >/dev/null 2>&1 & disown`,
620070
620419
  `kill -TERM ${process.pid} >/dev/null 2>&1 || true`
620071
620420
  ].join("; ");
620072
- const relauncher = spawn30("bash", ["-c", relaunchScript], {
620421
+ const relauncher = spawn29("bash", ["-c", relaunchScript], {
620073
620422
  detached: true,
620074
620423
  stdio: "ignore",
620075
620424
  env: cleanEnv
@@ -620291,7 +620640,7 @@ async function handleV1Run(req2, res) {
620291
620640
  });
620292
620641
  job.sandbox = "container";
620293
620642
  } else {
620294
- child = spawn30(process.execPath, [omniusBin, ...args], {
620643
+ child = spawn29(process.execPath, [omniusBin, ...args], {
620295
620644
  cwd: cwd4,
620296
620645
  env: runEnv,
620297
620646
  stdio: ["ignore", "pipe", "pipe"],
@@ -622270,7 +622619,7 @@ ${historyLines}
622270
622619
  }
622271
622620
  runEnv["OLLAMA_HOST"] = currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
622272
622621
  if (currentCfg.apiKey) runEnv["OMNIUS_API_KEY_INHERIT"] = currentCfg.apiKey;
622273
- const child = spawn30(process.execPath, [omniusBin, ...args], {
622622
+ const child = spawn29(process.execPath, [omniusBin, ...args], {
622274
622623
  cwd: cwdPath,
622275
622624
  env: runEnv,
622276
622625
  stdio: ["ignore", "pipe", "pipe"],
@@ -624842,7 +625191,7 @@ var init_clipboard_media = __esm({
624842
625191
 
624843
625192
  // packages/cli/src/tui/interactive.ts
624844
625193
  import { cwd } from "node:process";
624845
- import { resolve as resolve44, join as join137, dirname as dirname38, extname as extname14, relative as relative14 } from "node:path";
625194
+ import { resolve as resolve44, join as join137, dirname as dirname38, extname as extname15, relative as relative14 } from "node:path";
624846
625195
  import { createRequire as createRequire8 } from "node:module";
624847
625196
  import { fileURLToPath as fileURLToPath18 } from "node:url";
624848
625197
  import {
@@ -632144,7 +632493,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
632144
632493
  const imgPath = resolve44(repoRoot, cleanPath);
632145
632494
  const imgBuffer = readFileSync100(imgPath);
632146
632495
  const base642 = imgBuffer.toString("base64");
632147
- const ext = extname14(cleanPath).toLowerCase();
632496
+ const ext = extname15(cleanPath).toLowerCase();
632148
632497
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
632149
632498
  const asciiContext = await renderAsciiPreviewForImage(
632150
632499
  imgPath,
@@ -633325,7 +633674,7 @@ __export(run_exports, {
633325
633674
  statusCommand: () => statusCommand
633326
633675
  });
633327
633676
  import { resolve as resolve45 } from "node:path";
633328
- import { spawn as spawn31 } from "node:child_process";
633677
+ import { spawn as spawn30 } from "node:child_process";
633329
633678
  import { mkdirSync as mkdirSync74, writeFileSync as writeFileSync69, readFileSync as readFileSync101, readdirSync as readdirSync43, existsSync as existsSync122 } from "node:fs";
633330
633679
  import { randomBytes as randomBytes24 } from "node:crypto";
633331
633680
  import { join as join138 } from "node:path";
@@ -633438,7 +633787,7 @@ async function runBackground(task, config, opts) {
633438
633787
  const omniusBin = process.argv[1] || "omnius";
633439
633788
  const args = [task, "--json"];
633440
633789
  if (config.model) args.push("--model", config.model);
633441
- const child = spawn31(process.execPath, [omniusBin, ...args], {
633790
+ const child = spawn30(process.execPath, [omniusBin, ...args], {
633442
633791
  cwd: repoRoot,
633443
633792
  env: { ...process.env, OMNIUS_JOB_ID: id },
633444
633793
  stdio: ["ignore", "pipe", "pipe"],
@@ -634626,9 +634975,9 @@ async function main() {
634626
634975
  const mode = process.argv[process.argv.indexOf("--self-test") + 1] || "crossmodal";
634627
634976
  if (mode === "crossmodal") {
634628
634977
  process.stdout.write("Running crossmodal smoke tests...\n");
634629
- const { spawn: spawn32 } = await import("node:child_process");
634978
+ const { spawn: spawn31 } = await import("node:child_process");
634630
634979
  const run = (file) => new Promise((resolve48, reject) => {
634631
- const p2 = spawn32(process.execPath, [file], { stdio: ["ignore", "pipe", "pipe"] });
634980
+ const p2 = spawn31(process.execPath, [file], { stdio: ["ignore", "pipe", "pipe"] });
634632
634981
  p2.stdout.on("data", (d2) => process.stdout.write(d2));
634633
634982
  p2.stderr.on("data", (d2) => process.stdout.write(d2));
634634
634983
  onChildExit(p2, (code8) => code8 === 0 ? resolve48() : reject(new Error(`${file} exited ${code8}`)));