omnius 1.0.18 → 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();
@@ -534492,7 +534548,7 @@ Respond with your assessment, then take action.`;
534492
534548
  } catch {
534493
534549
  }
534494
534550
  }
534495
- if (!this.options.disablePersistentMemory && !this.options.disableCodebaseMap && 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) {
534496
534552
  try {
534497
534553
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
534498
534554
  let commitSha = "no-commit";
@@ -596290,6 +596346,8 @@ var init_tool_policy = __esm({
596290
596346
  "glob_find",
596291
596347
  "list_directory",
596292
596348
  "code_sandbox",
596349
+ "full_sub_agent",
596350
+ "telegram_sandbox_handoff",
596293
596351
  "codebase_map",
596294
596352
  "diagnostic",
596295
596353
  "git_info",
@@ -596324,7 +596382,6 @@ var init_tool_policy = __esm({
596324
596382
  "generate_tts",
596325
596383
  "create_audio_file",
596326
596384
  "telegram_send_file",
596327
- "telegram_sandbox_handoff",
596328
596385
  "task_complete"
596329
596386
  ]);
596330
596387
  SAFE_GROUP_ADMIN_TOOLS = /* @__PURE__ */ new Set([
@@ -596341,7 +596398,6 @@ var init_tool_policy = __esm({
596341
596398
  "generate_tts",
596342
596399
  "create_audio_file",
596343
596400
  "telegram_send_file",
596344
- "telegram_sandbox_handoff",
596345
596401
  "task_complete"
596346
596402
  ]);
596347
596403
  }
@@ -596359,6 +596415,7 @@ import { mkdir as mkdir17 } from "node:fs/promises";
596359
596415
  import {
596360
596416
  basename as basename22,
596361
596417
  dirname as dirname33,
596418
+ extname as extname14,
596362
596419
  isAbsolute as isAbsolute6,
596363
596420
  join as join119,
596364
596421
  relative as relative12,
@@ -596377,11 +596434,26 @@ function formatTelegramCreativeWorkspacePrompt(root) {
596377
596434
  "Public/group creative workspace is enabled.",
596378
596435
  `Workspace root: ${root}`,
596379
596436
  "Creative file tools are scoped to that folder only.",
596380
- "Allowed: create new files, read/list files in this workspace, and edit/patch files already created in this workspace.",
596381
- "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.",
596382
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."
596383
596440
  ].join("\n");
596384
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
+ }
596385
596457
  function collectGeneratedArtifactPathsFromText(text, root) {
596386
596458
  const rootAbs = resolve38(root);
596387
596459
  const paths = /* @__PURE__ */ new Set();
@@ -596461,6 +596533,10 @@ function scopedTool(base3, root, mode) {
596461
596533
  if (pathKey) {
596462
596534
  const guarded = guardPath(rootAbs, String(next[pathKey]));
596463
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);
596464
596540
  if (mode === "edit" && !manifestHas(rootAbs, guarded.path.rel)) {
596465
596541
  return denied(`Public Telegram creative tools can only edit files created in this chat workspace: ${guarded.path.rel}`);
596466
596542
  }
@@ -596573,7 +596649,7 @@ function denied(error) {
596573
596649
  mutatedFiles: []
596574
596650
  };
596575
596651
  }
596576
- var MANIFEST_FILE, PATH_KEYS, MEDIA_PATH_RE, CreativeAudioFileTool;
596652
+ var MANIFEST_FILE, PATH_KEYS, MEDIA_PATH_RE, PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS, CreativeAudioFileTool;
596577
596653
  var init_telegram_creative_tools = __esm({
596578
596654
  "packages/cli/src/tui/telegram-creative-tools.ts"() {
596579
596655
  "use strict";
@@ -596582,6 +596658,65 @@ var init_telegram_creative_tools = __esm({
596582
596658
  MANIFEST_FILE = ".omnius-creative-manifest.json";
596583
596659
  PATH_KEYS = ["path", "file", "file_path", "filename", "filepath", "filePath"];
596584
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
+ ]);
596585
596720
  CreativeAudioFileTool = class {
596586
596721
  constructor(root) {
596587
596722
  this.root = root;
@@ -596593,6 +596728,8 @@ var init_telegram_creative_tools = __esm({
596593
596728
  type: "object",
596594
596729
  properties: {
596595
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" },
596596
596733
  path: { type: "string", description: "Output .wav path inside the creative workspace" },
596597
596734
  backend: { type: "string", enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper", "espeak"], description: "TTS backend. Defaults to auto." },
596598
596735
  voice: { type: "string", description: "Voice id/name for the selected TTS backend" },
@@ -596600,11 +596737,11 @@ var init_telegram_creative_tools = __esm({
596600
596737
  model: { type: "string", description: "Optional backend model id or raw Piper/ONNX path" },
596601
596738
  speed: { type: "number", description: "Speech speed multiplier or backend-specific rate" }
596602
596739
  },
596603
- required: ["text", "path"]
596740
+ required: []
596604
596741
  };
596605
596742
  async execute(args) {
596606
596743
  const start2 = performance.now();
596607
- 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() : "";
596608
596745
  if (!text) return { success: false, output: "", error: "text is required", durationMs: performance.now() - start2 };
596609
596746
  const rawPath = typeof args["path"] === "string" && args["path"].trim() ? String(args["path"]) : `speech-${Date.now()}.wav`;
596610
596747
  const guarded = guardPath(this.root, rawPath);
@@ -597122,7 +597259,8 @@ function telegramSyntheticHelpSignatures() {
597122
597259
  return [
597123
597260
  { signature: "/help", description: "Show Telegram command help" },
597124
597261
  { signature: "/start", description: "Show Telegram bridge status and authentication instructions" },
597125
- { 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" }
597126
597264
  ];
597127
597265
  }
597128
597266
  function telegramHelpCommandAllowed(cmd, scope) {
@@ -597626,7 +597764,8 @@ path and target. Do not search for Telegram bot tokens, environment secrets, or
597626
597764
  credentials; upload authorization is encapsulated by telegram_send_file.
597627
597765
 
597628
597766
  When asked to generate speech, narration, or TTS, use generate_tts or audio_playback.
597629
- Do not fall back to shell commands for speech synthesis while those tools are available.
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.
597630
597769
 
597631
597770
  Keep responses concise for Telegram but don't withhold information from the admin.
597632
597771
  `.trim();
@@ -599550,6 +599689,9 @@ ${mediaContext}` : ""}`
599550
599689
  runner.onEvent((event) => {
599551
599690
  if (subAgent.aborted) return;
599552
599691
  let suppressExternalEvent = false;
599692
+ if (!isAdminDM && event.type === "status") {
599693
+ suppressExternalEvent = true;
599694
+ }
599553
599695
  if (event.type === "tool_call" && event.toolName) {
599554
599696
  if (event.toolName === "task_complete") {
599555
599697
  subAgent.completionBoundarySeen = true;
@@ -599585,7 +599727,9 @@ ${mediaContext}` : ""}`
599585
599727
  }
599586
599728
  }
599587
599729
  } else if (event.type === "status" && event.content) {
599588
- this.subAgentViewCallbacks?.onWrite(subAgent.viewId, `status: ${event.content}`);
599730
+ if (isAdminDM) {
599731
+ this.subAgentViewCallbacks?.onWrite(subAgent.viewId, `status: ${event.content}`);
599732
+ }
599589
599733
  } else if (event.type === "assistant_text" && event.content && event.source !== "task_complete_summary") {
599590
599734
  subAgent.assistantText = event.content;
599591
599735
  } else if (event.type === "stream_start") {
@@ -600056,10 +600200,27 @@ Scoped workspace: ${scopedRoot}`,
600056
600200
  ).map((tool) => adaptTool5(tool, todoSessionId));
600057
600201
  adaptedTools.push(...creativeTools);
600058
600202
  adaptedTools.push(adaptTool5(this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg), todoSessionId));
600059
- adaptedTools.push(adaptTool5(this.buildTelegramSandboxHandoffTool(context2, repoRoot, `chat:${String(chatId ?? "unknown")}`, chatId), todoSessionId));
600203
+ adaptedTools = this.filterNonAdminTelegramTools(adaptedTools);
600060
600204
  }
600061
600205
  return [...adaptedTools, taskComplete];
600062
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
+ }
600063
600224
  imageGenerationDefaultsForRepo(repoRoot) {
600064
600225
  const settings = resolveSettings(repoRoot);
600065
600226
  return {
@@ -600221,6 +600382,10 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
600221
600382
  if (scopedRoot && !isPathInside(base3, abs)) {
600222
600383
  return { ok: false, error: `Public/group telegram_send_file can only send files inside ${base3}.` };
600223
600384
  }
600385
+ if (scopedRoot) {
600386
+ const policyError = publicCreativeArtifactPolicyError(abs);
600387
+ if (policyError) return { ok: false, error: policyError };
600388
+ }
600224
600389
  if (!existsSync106(abs)) return { ok: false, error: `File does not exist: ${trimmed}` };
600225
600390
  if (!statSync36(abs).isFile()) return { ok: false, error: `Path is not a file: ${trimmed}` };
600226
600391
  return { ok: true, path: abs };
@@ -625026,7 +625191,7 @@ var init_clipboard_media = __esm({
625026
625191
 
625027
625192
  // packages/cli/src/tui/interactive.ts
625028
625193
  import { cwd } from "node:process";
625029
- 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";
625030
625195
  import { createRequire as createRequire8 } from "node:module";
625031
625196
  import { fileURLToPath as fileURLToPath18 } from "node:url";
625032
625197
  import {
@@ -632328,7 +632493,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
632328
632493
  const imgPath = resolve44(repoRoot, cleanPath);
632329
632494
  const imgBuffer = readFileSync100(imgPath);
632330
632495
  const base642 = imgBuffer.toString("base64");
632331
- const ext = extname14(cleanPath).toLowerCase();
632496
+ const ext = extname15(cleanPath).toLowerCase();
632332
632497
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
632333
632498
  const asciiContext = await renderAsciiPreviewForImage(
632334
632499
  imgPath,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.18",
9
+ "version": "1.0.19",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.18",
3
+ "version": "1.0.19",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",