omnius 1.0.19 → 1.0.21

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
@@ -15546,8 +15546,8 @@ function deleteCustomToolDefinition(name10, scope, repoRoot) {
15546
15546
  const dir = scope === "project" && repoRoot ? projectToolsDir(repoRoot) : globalToolsDir();
15547
15547
  const filePath = join24(dir, `${name10}.json`);
15548
15548
  if (existsSync19(filePath)) {
15549
- const { unlinkSync: unlinkSync25 } = __require("node:fs");
15550
- unlinkSync25(filePath);
15549
+ const { unlinkSync: unlinkSync26 } = __require("node:fs");
15550
+ unlinkSync26(filePath);
15551
15551
  return true;
15552
15552
  }
15553
15553
  return false;
@@ -25254,7 +25254,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
25254
25254
  bits2int_modN: "function"
25255
25255
  });
25256
25256
  ecdsaOpts = Object.assign({}, ecdsaOpts);
25257
- const randomBytes25 = ecdsaOpts.randomBytes || randomBytes7;
25257
+ const randomBytes26 = ecdsaOpts.randomBytes || randomBytes7;
25258
25258
  const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
25259
25259
  const { Fp, Fn } = Point;
25260
25260
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
@@ -25396,7 +25396,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
25396
25396
  throw new Error("invalid private key");
25397
25397
  const seedArgs = [int2octets(d2), int2octets(h1int)];
25398
25398
  if (extraEntropy != null && extraEntropy !== false) {
25399
- const e2 = extraEntropy === true ? randomBytes25(lengths.secretKey) : extraEntropy;
25399
+ const e2 = extraEntropy === true ? randomBytes26(lengths.secretKey) : extraEntropy;
25400
25400
  seedArgs.push(abytes(e2, void 0, "extraEntropy"));
25401
25401
  }
25402
25402
  const seed = concatBytes(...seedArgs);
@@ -61906,9 +61906,9 @@ var init_cookies = __esm({
61906
61906
 
61907
61907
  // ../node_modules/@libp2p/http-peer-id-auth/dist/src/utils.js
61908
61908
  function generateChallenge() {
61909
- const randomBytes25 = new Uint8Array(32);
61910
- crypto.getRandomValues(randomBytes25);
61911
- return toString2(randomBytes25, "base64urlpad");
61909
+ const randomBytes26 = new Uint8Array(32);
61910
+ crypto.getRandomValues(randomBytes26);
61911
+ return toString2(randomBytes26, "base64urlpad");
61912
61912
  }
61913
61913
  function encodeAuthParams(params) {
61914
61914
  const encodedParams = Object.entries(params).map(([key, value2]) => `${key}="${value2}"`).join(", ");
@@ -234104,7 +234104,7 @@ var require_websocket2 = __commonJS({
234104
234104
  var http6 = __require("http");
234105
234105
  var net5 = __require("net");
234106
234106
  var tls2 = __require("tls");
234107
- var { randomBytes: randomBytes25, createHash: createHash24 } = __require("crypto");
234107
+ var { randomBytes: randomBytes26, createHash: createHash24 } = __require("crypto");
234108
234108
  var { Duplex: Duplex3, Readable } = __require("stream");
234109
234109
  var { URL: URL3 } = __require("url");
234110
234110
  var PerMessageDeflate3 = require_permessage_deflate2();
@@ -234634,7 +234634,7 @@ var require_websocket2 = __commonJS({
234634
234634
  }
234635
234635
  }
234636
234636
  const defaultPort = isSecure ? 443 : 80;
234637
- const key = randomBytes25(16).toString("base64");
234637
+ const key = randomBytes26(16).toString("base64");
234638
234638
  const request = isSecure ? https4.request : http6.request;
234639
234639
  const protocolSet = /* @__PURE__ */ new Set();
234640
234640
  let perMessageDeflate;
@@ -250375,6 +250375,22 @@ function optionalNumberArg(value2) {
250375
250375
  const n2 = Number(value2);
250376
250376
  return Number.isFinite(n2) ? n2 : void 0;
250377
250377
  }
250378
+ function booleanArg(value2, fallback) {
250379
+ if (typeof value2 === "boolean")
250380
+ return value2;
250381
+ if (typeof value2 === "string") {
250382
+ if (/^(1|true|yes|on)$/i.test(value2.trim()))
250383
+ return true;
250384
+ if (/^(0|false|no|off)$/i.test(value2.trim()))
250385
+ return false;
250386
+ }
250387
+ return fallback;
250388
+ }
250389
+ function generationFallbackEnabled(args) {
250390
+ if (booleanArg(args["strict_model"] ?? args["strictModel"] ?? args["strict"], false))
250391
+ return false;
250392
+ return booleanArg(args["fallback"] ?? args["allow_fallback"] ?? args["allowFallback"], true);
250393
+ }
250378
250394
  function isBackend(value2) {
250379
250395
  return value2 === "auto" || value2 === "ollama" || value2 === "diffusers" || value2 === "sdcpp";
250380
250396
  }
@@ -250383,6 +250399,9 @@ function getImageGenerationPreset(model) {
250383
250399
  return void 0;
250384
250400
  return IMAGE_GENERATION_MODEL_PRESETS.find((preset) => preset.id === model);
250385
250401
  }
250402
+ function imageGenerationQualityLadder() {
250403
+ return IMAGE_GENERATION_QUALITY_LADDER.map((id) => getImageGenerationPreset(id)).filter((preset) => Boolean(preset));
250404
+ }
250386
250405
  function inferImageGenerationBackend(model, requested) {
250387
250406
  if (requested && isBackend(requested))
250388
250407
  return requested;
@@ -250399,6 +250418,40 @@ function inferImageGenerationBackend(model, requested) {
250399
250418
  return "sdcpp";
250400
250419
  return "diffusers";
250401
250420
  }
250421
+ function imageCandidateFor(model, requestedBackend) {
250422
+ let backend = inferImageGenerationBackend(model, requestedBackend);
250423
+ if (backend === "auto")
250424
+ backend = "diffusers";
250425
+ return {
250426
+ model,
250427
+ backend,
250428
+ preset: getImageGenerationPreset(model)
250429
+ };
250430
+ }
250431
+ function imageGenerationFallbackCandidates(requestedModel, requestedBackend, allowFallback = true) {
250432
+ const ladder = imageGenerationQualityLadder();
250433
+ const candidates = [];
250434
+ const add2 = (candidate) => {
250435
+ const key = `${candidate.backend}:${candidate.model}`;
250436
+ if (!candidates.some((existing) => `${existing.backend}:${existing.model}` === key))
250437
+ candidates.push(candidate);
250438
+ };
250439
+ if (requestedModel) {
250440
+ add2(imageCandidateFor(requestedModel, requestedBackend));
250441
+ } else if (requestedBackend && requestedBackend !== "auto") {
250442
+ const firstForBackend = ladder.find((preset) => preset.backend === requestedBackend);
250443
+ add2(imageCandidateFor(firstForBackend?.id ?? (requestedBackend === "ollama" ? DEFAULT_OLLAMA_IMAGE_MODEL : DEFAULT_DIFFUSERS_IMAGE_MODEL), requestedBackend));
250444
+ } else if (!allowFallback) {
250445
+ add2(imageCandidateFor(DEFAULT_DIFFUSERS_IMAGE_MODEL, requestedBackend));
250446
+ }
250447
+ if (!allowFallback)
250448
+ return candidates.length ? candidates : [imageCandidateFor(DEFAULT_DIFFUSERS_IMAGE_MODEL, requestedBackend)];
250449
+ const primaryIndex = requestedModel ? ladder.findIndex((preset) => preset.id === requestedModel) : requestedBackend && requestedBackend !== "auto" ? ladder.findIndex((preset) => preset.backend === requestedBackend) : 0;
250450
+ const fallbackTail = primaryIndex >= 0 ? ladder.slice(primaryIndex) : ladder;
250451
+ for (const preset of fallbackTail)
250452
+ add2(imageCandidateFor(preset.id));
250453
+ return candidates;
250454
+ }
250402
250455
  function imageGenerationDir(repoRoot = ".") {
250403
250456
  return join36(repoRoot, ".omnius", "image-gen");
250404
250457
  }
@@ -250653,6 +250706,33 @@ function formatSuccessOutput(args) {
250653
250706
  ` Prompt: "${prompt}"`
250654
250707
  ].filter(Boolean).join("\n");
250655
250708
  }
250709
+ function summarizeToolResult(result) {
250710
+ return trimProcessText(String(result.error || result.output || "unknown error"), 700).replace(/\s+/g, " ").trim();
250711
+ }
250712
+ function formatImageAttempt(candidate, reason, index) {
250713
+ return `${index + 1}. ${candidate.model} [${candidate.backend}] - ${reason}`;
250714
+ }
250715
+ function formatImageFallbackFailure(failed) {
250716
+ return [
250717
+ "No image generation model in the fallback ladder completed successfully.",
250718
+ "Attempted, highest quality to lowest:",
250719
+ ...failed.map((attempt, index) => ` ${formatImageAttempt(attempt.candidate, attempt.reason, index)}`)
250720
+ ].join("\n");
250721
+ }
250722
+ function annotateImageFallbackSuccess(result, failed, winner) {
250723
+ if (failed.length === 0)
250724
+ return result;
250725
+ const prefix = [
250726
+ `Fallback ladder succeeded with ${winner.model} [${winner.backend}] after ${failed.length} failed attempt(s).`,
250727
+ "Failed attempts:",
250728
+ ...failed.map((attempt, index) => ` ${formatImageAttempt(attempt.candidate, attempt.reason, index)}`),
250729
+ ""
250730
+ ].join("\n");
250731
+ return {
250732
+ ...result,
250733
+ output: prefix + result.output
250734
+ };
250735
+ }
250656
250736
  function parseRunnerJson(stdout) {
250657
250737
  const lines = stdout.trim().split(/\r?\n/).reverse();
250658
250738
  for (const line of lines) {
@@ -250665,7 +250745,7 @@ function parseRunnerJson(stdout) {
250665
250745
  }
250666
250746
  return null;
250667
250747
  }
250668
- var DEFAULT_DIFFUSERS_IMAGE_MODEL, DEFAULT_OLLAMA_IMAGE_MODEL, DIFFUSERS_PYTHON_PACKAGES, SDCPP_PYTHON_PACKAGES, IMAGE_GENERATION_MODEL_PRESETS, OLLAMA_IMAGE_MODELS, DIFFUSERS_RUNNER, SDCPP_RUNNER, ImageGenerateTool;
250748
+ var DEFAULT_DIFFUSERS_IMAGE_MODEL, DEFAULT_OLLAMA_IMAGE_MODEL, DIFFUSERS_PYTHON_PACKAGES, SDCPP_PYTHON_PACKAGES, IMAGE_GENERATION_MODEL_PRESETS, IMAGE_GENERATION_QUALITY_LADDER, OLLAMA_IMAGE_MODELS, DIFFUSERS_RUNNER, SDCPP_RUNNER, ImageGenerateTool;
250669
250749
  var init_image_generate = __esm({
250670
250750
  "packages/execution/dist/tools/image-generate.js"() {
250671
250751
  "use strict";
@@ -250989,6 +251069,21 @@ var init_image_generate = __esm({
250989
251069
  note: "CPU/GGUF/checkpoint route; requires a local model path."
250990
251070
  }
250991
251071
  ];
251072
+ IMAGE_GENERATION_QUALITY_LADDER = [
251073
+ "black-forest-labs/FLUX.1-dev",
251074
+ "stabilityai/stable-diffusion-3.5-large",
251075
+ DEFAULT_OLLAMA_IMAGE_MODEL,
251076
+ "black-forest-labs/FLUX.1-schnell",
251077
+ "stabilityai/stable-diffusion-3.5-large-turbo",
251078
+ "Tongyi-MAI/Z-Image-Turbo",
251079
+ "black-forest-labs/FLUX.2-klein-4B",
251080
+ DEFAULT_DIFFUSERS_IMAGE_MODEL,
251081
+ "Efficient-Large-Model/Sana_Sprint_0.6B_1024px_diffusers",
251082
+ "SimianLuo/LCM_Dreamshaper_v7",
251083
+ "stabilityai/sd-turbo",
251084
+ "segmind/tiny-sd",
251085
+ "nota-ai/bk-sdm-tiny-2m"
251086
+ ];
250992
251087
  OLLAMA_IMAGE_MODELS = IMAGE_GENERATION_MODEL_PRESETS.filter((preset) => preset.backend === "ollama").map((preset) => preset.id);
250993
251088
  DIFFUSERS_RUNNER = String.raw`#!/usr/bin/env python3
250994
251089
  import argparse
@@ -251170,7 +251265,7 @@ if __name__ == "__main__":
251170
251265
  `;
251171
251266
  ImageGenerateTool = class {
251172
251267
  name = "generate_image";
251173
- description = "Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/z-image-turbo, x/flux2-klein), Python Diffusers models (SDXL Turbo default, FLUX.1 dev, SD3.5 Large, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. Saves a PNG under .omnius/images and returns the file path.";
251268
+ description = "Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/z-image-turbo, x/flux2-klein), Python Diffusers models (SDXL Turbo default, FLUX.1 dev, SD3.5 Large, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first and falls back to smaller models if setup, download, or generation fails. Saves a PNG under .omnius/images and returns the file path.";
251174
251269
  parameters = {
251175
251270
  type: "object",
251176
251271
  properties: {
@@ -251215,6 +251310,14 @@ if __name__ == "__main__":
251215
251310
  type: "string",
251216
251311
  enum: ["generate", "list_models", "setup"],
251217
251312
  description: "Optional utility action. Default is generate."
251313
+ },
251314
+ fallback: {
251315
+ type: "boolean",
251316
+ description: "Whether to try the ranked quality ladder if the selected model/backend fails. Defaults true."
251317
+ },
251318
+ strict_model: {
251319
+ type: "boolean",
251320
+ description: "When true, use only the requested model/backend and do not fall back. Defaults false."
251218
251321
  }
251219
251322
  },
251220
251323
  required: ["prompt"]
@@ -251257,7 +251360,7 @@ if __name__ == "__main__":
251257
251360
  if (action === "list_models") {
251258
251361
  return {
251259
251362
  success: true,
251260
- output: IMAGE_GENERATION_MODEL_PRESETS.map((preset2) => `${preset2.id} [${preset2.backend}] - ${preset2.note}`).join("\n"),
251363
+ output: IMAGE_GENERATION_MODEL_PRESETS.map((preset) => `${preset.id} [${preset.backend}] - ${preset.note}`).join("\n"),
251261
251364
  durationMs: performance.now() - start2
251262
251365
  };
251263
251366
  }
@@ -251281,19 +251384,8 @@ if __name__ == "__main__":
251281
251384
  const rawModel2 = args["model_path"] ? String(args["model_path"]) : args["model"] ? String(args["model"]) : this.defaultModel;
251282
251385
  const requestedModel2 = rawModel2 === "auto" ? void 0 : rawModel2;
251283
251386
  const requestedBackend2 = args["backend"] ? String(args["backend"]) : this.defaultBackend;
251284
- let backend = inferImageGenerationBackend(requestedModel2, requestedBackend2);
251285
- if (backend === "auto") {
251286
- backend = inferImageGenerationBackend(requestedModel2, void 0);
251287
- if (backend === "auto")
251288
- backend = "diffusers";
251289
- }
251290
- const model = requestedModel2 ?? (backend === "diffusers" ? DEFAULT_DIFFUSERS_IMAGE_MODEL : DEFAULT_OLLAMA_IMAGE_MODEL);
251291
- this.emitProgress({ stage: "setup", message: `Preparing image model ${model} (${backend})` });
251292
- if (backend === "ollama")
251293
- return await this.prewarmOllama({ model, start: start2 });
251294
- if (backend === "sdcpp")
251295
- return await this.prewarmSdCpp({ model, start: start2, python: args["python"] });
251296
- return await this.prewarmDiffusers({ model, start: start2, python: args["python"] });
251387
+ const candidates2 = imageGenerationFallbackCandidates(requestedModel2, requestedBackend2, generationFallbackEnabled(args));
251388
+ return await this.prewarmCandidateLadder({ candidates: candidates2, args, start: start2 });
251297
251389
  }
251298
251390
  const prompt = String(args["prompt"] ?? "").trim();
251299
251391
  if (!prompt) {
@@ -251302,31 +251394,10 @@ if __name__ == "__main__":
251302
251394
  const rawModel = args["model_path"] ? String(args["model_path"]) : args["model"] ? String(args["model"]) : this.defaultModel;
251303
251395
  const requestedModel = rawModel === "auto" ? void 0 : rawModel;
251304
251396
  const requestedBackend = args["backend"] ? String(args["backend"]) : this.defaultBackend;
251305
- const preset = getImageGenerationPreset(requestedModel);
251306
- const width = numberArg(args["width"], preset?.width ?? 1024);
251307
- const height = numberArg(args["height"], preset?.height ?? 1024);
251308
- const steps = optionalNumberArg(args["steps"]) ?? preset?.steps;
251309
- const guidance = optionalNumberArg(args["guidance"]) ?? preset?.guidance;
251310
251397
  const seed = optionalNumberArg(args["seed"]);
251398
+ const candidates = imageGenerationFallbackCandidates(requestedModel, requestedBackend, generationFallbackEnabled(args));
251311
251399
  try {
251312
- let backend = inferImageGenerationBackend(requestedModel, requestedBackend);
251313
- let model = requestedModel;
251314
- if (backend === "auto") {
251315
- backend = inferImageGenerationBackend(model, void 0);
251316
- if (backend === "auto")
251317
- backend = "diffusers";
251318
- }
251319
- if (!model) {
251320
- model = backend === "diffusers" ? DEFAULT_DIFFUSERS_IMAGE_MODEL : DEFAULT_OLLAMA_IMAGE_MODEL;
251321
- }
251322
- this.emitProgress({ stage: "setup", message: `Using image model ${model} (${backend})` });
251323
- if (backend === "ollama") {
251324
- return await this.generateWithOllama({ prompt, model, width, height, steps, start: start2 });
251325
- }
251326
- if (backend === "sdcpp") {
251327
- return await this.generateWithSdCpp({ prompt, model, width, height, steps, seed, start: start2, python: args["python"] });
251328
- }
251329
- return await this.generateWithDiffusers({ prompt, model, width, height, steps, guidance, seed, start: start2, python: args["python"] });
251400
+ return await this.generateCandidateLadder({ candidates, prompt, args, seed, start: start2 });
251330
251401
  } catch (err) {
251331
251402
  return {
251332
251403
  success: false,
@@ -251335,6 +251406,64 @@ if __name__ == "__main__":
251335
251406
  };
251336
251407
  }
251337
251408
  }
251409
+ async prewarmCandidateLadder(args) {
251410
+ const failed = [];
251411
+ for (let index = 0; index < args.candidates.length; index++) {
251412
+ const candidate = args.candidates[index];
251413
+ this.emitProgress({
251414
+ stage: "setup",
251415
+ message: `Preparing image model ${candidate.model} (${candidate.backend}) [${index + 1}/${args.candidates.length}]`
251416
+ });
251417
+ const result = candidate.backend === "ollama" ? await this.prewarmOllama({ model: candidate.model, start: args.start }) : candidate.backend === "sdcpp" ? await this.prewarmSdCpp({ model: candidate.model, start: args.start, python: args.args["python"] }) : await this.prewarmDiffusers({ model: candidate.model, start: args.start, python: args.args["python"] });
251418
+ if (result.success)
251419
+ return annotateImageFallbackSuccess(result, failed, candidate);
251420
+ failed.push({ candidate, reason: summarizeToolResult(result) });
251421
+ if (index < args.candidates.length - 1) {
251422
+ this.emitProgress({
251423
+ stage: "setup",
251424
+ message: `${candidate.model} failed; trying ${args.candidates[index + 1].model}`
251425
+ });
251426
+ }
251427
+ }
251428
+ const output = formatImageFallbackFailure(failed);
251429
+ return {
251430
+ success: false,
251431
+ output,
251432
+ error: output,
251433
+ durationMs: performance.now() - args.start
251434
+ };
251435
+ }
251436
+ async generateCandidateLadder(args) {
251437
+ const failed = [];
251438
+ for (let index = 0; index < args.candidates.length; index++) {
251439
+ const candidate = args.candidates[index];
251440
+ const width = numberArg(args.args["width"], candidate.preset?.width ?? 1024);
251441
+ const height = numberArg(args.args["height"], candidate.preset?.height ?? 1024);
251442
+ const steps = optionalNumberArg(args.args["steps"]) ?? candidate.preset?.steps;
251443
+ const guidance = optionalNumberArg(args.args["guidance"]) ?? candidate.preset?.guidance;
251444
+ this.emitProgress({
251445
+ stage: "setup",
251446
+ message: `Using image model ${candidate.model} (${candidate.backend}) [${index + 1}/${args.candidates.length}]`
251447
+ });
251448
+ const result = candidate.backend === "ollama" ? await this.generateWithOllama({ prompt: args.prompt, model: candidate.model, width, height, steps, start: args.start }) : candidate.backend === "sdcpp" ? await this.generateWithSdCpp({ prompt: args.prompt, model: candidate.model, width, height, steps, seed: args.seed, start: args.start, python: args.args["python"] }) : await this.generateWithDiffusers({ prompt: args.prompt, model: candidate.model, width, height, steps, guidance, seed: args.seed, start: args.start, python: args.args["python"] });
251449
+ if (result.success)
251450
+ return annotateImageFallbackSuccess(result, failed, candidate);
251451
+ failed.push({ candidate, reason: summarizeToolResult(result) });
251452
+ if (index < args.candidates.length - 1) {
251453
+ this.emitProgress({
251454
+ stage: "setup",
251455
+ message: `${candidate.model} failed; falling back to ${args.candidates[index + 1].model}`
251456
+ });
251457
+ }
251458
+ }
251459
+ const output = formatImageFallbackFailure(failed);
251460
+ return {
251461
+ success: false,
251462
+ output,
251463
+ error: output,
251464
+ durationMs: performance.now() - args.start
251465
+ };
251466
+ }
251338
251467
  async prewarmOllama(args) {
251339
251468
  const model = args.model || DEFAULT_OLLAMA_IMAGE_MODEL;
251340
251469
  if (await this.ollamaHasModel(model)) {
@@ -251830,7 +251959,7 @@ function backendImportCheck(backend) {
251830
251959
  if (backend === "audiocraft")
251831
251960
  return "import torch, torchaudio, audiocraft\nfrom audiocraft.models import MusicGen, AudioGen\n";
251832
251961
  if (backend === "stable-audio")
251833
- return "import torch, torchaudio, stable_audio_tools\n";
251962
+ return "import torch, torchaudio, diffusers, scipy\nfrom diffusers import StableAudioPipeline\n";
251834
251963
  if (backend === "tangoflux")
251835
251964
  return "import torch, torchaudio\nfrom tangoflux import TangoFluxInference\n";
251836
251965
  return "import torch, diffusers, scipy\nfrom diffusers import AudioLDMPipeline\n";
@@ -252160,11 +252289,31 @@ function playbackRequested(args) {
252160
252289
  return false;
252161
252290
  return true;
252162
252291
  }
252292
+ function booleanArg2(value2, fallback) {
252293
+ if (typeof value2 === "boolean")
252294
+ return value2;
252295
+ if (typeof value2 === "string") {
252296
+ if (/^(1|true|yes|on)$/i.test(value2.trim()))
252297
+ return true;
252298
+ if (/^(0|false|no|off)$/i.test(value2.trim()))
252299
+ return false;
252300
+ }
252301
+ return fallback;
252302
+ }
252303
+ function generationFallbackEnabled2(args) {
252304
+ if (booleanArg2(args["strict_model"] ?? args["strictModel"] ?? args["strict"], false))
252305
+ return false;
252306
+ return booleanArg2(args["fallback"] ?? args["allow_fallback"] ?? args["allowFallback"], true);
252307
+ }
252163
252308
  function getAudioGenerationPreset(model, kind) {
252164
252309
  if (!model)
252165
252310
  return void 0;
252166
252311
  return AUDIO_GENERATION_MODEL_PRESETS.find((preset) => preset.id === model && (!kind || preset.kind === kind)) ?? AUDIO_GENERATION_MODEL_PRESETS.find((preset) => preset.id === model);
252167
252312
  }
252313
+ function audioGenerationQualityLadder(kind) {
252314
+ const ids = kind === "music" ? MUSIC_GENERATION_QUALITY_LADDER : SOUND_GENERATION_QUALITY_LADDER;
252315
+ return ids.map((id) => getAudioGenerationPreset(id, kind)).filter((preset) => Boolean(preset));
252316
+ }
252168
252317
  function inferAudioGenerationBackend(model, requested) {
252169
252318
  if (requested && requested !== "auto") {
252170
252319
  if (requested === "diffusers" || requested === "transformers" || requested === "audiocraft" || requested === "stable-audio" || requested === "tangoflux" || requested === "project")
@@ -252188,6 +252337,41 @@ function inferAudioGenerationBackend(model, requested) {
252188
252337
  return "project";
252189
252338
  return "diffusers";
252190
252339
  }
252340
+ function audioCandidateFor(kind, model, requestedBackend) {
252341
+ const backend = inferAudioGenerationBackend(model, requestedBackend);
252342
+ const resolvedBackend = backend === "auto" ? kind === "music" ? "transformers" : "diffusers" : backend;
252343
+ return {
252344
+ kind,
252345
+ model,
252346
+ backend: resolvedBackend,
252347
+ preset: getAudioGenerationPreset(model, kind)
252348
+ };
252349
+ }
252350
+ function audioGenerationFallbackCandidates(kind, requestedModel, requestedBackend, allowFallback = true) {
252351
+ const ladder = audioGenerationQualityLadder(kind);
252352
+ const candidates = [];
252353
+ const add2 = (candidate) => {
252354
+ const key = `${candidate.kind}:${candidate.backend}:${candidate.model}`;
252355
+ if (!candidates.some((existing) => `${existing.kind}:${existing.backend}:${existing.model}` === key)) {
252356
+ candidates.push(candidate);
252357
+ }
252358
+ };
252359
+ if (requestedModel) {
252360
+ add2(audioCandidateFor(kind, requestedModel, requestedBackend));
252361
+ } else if (requestedBackend && requestedBackend !== "auto") {
252362
+ const firstForBackend = ladder.find((preset) => preset.backend === requestedBackend);
252363
+ add2(audioCandidateFor(kind, firstForBackend?.id ?? (kind === "music" ? DEFAULT_MUSIC_MODEL : DEFAULT_SOUND_MODEL), requestedBackend));
252364
+ } else if (!allowFallback) {
252365
+ add2(audioCandidateFor(kind, kind === "music" ? DEFAULT_MUSIC_MODEL : DEFAULT_SOUND_MODEL, requestedBackend));
252366
+ }
252367
+ if (!allowFallback)
252368
+ return candidates.length ? candidates : [audioCandidateFor(kind, kind === "music" ? DEFAULT_MUSIC_MODEL : DEFAULT_SOUND_MODEL, requestedBackend)];
252369
+ const primaryIndex = requestedModel ? ladder.findIndex((preset) => preset.id === requestedModel) : requestedBackend && requestedBackend !== "auto" ? ladder.findIndex((preset) => preset.backend === requestedBackend) : 0;
252370
+ const fallbackTail = primaryIndex >= 0 ? ladder.slice(primaryIndex) : ladder;
252371
+ for (const preset of fallbackTail)
252372
+ add2(audioCandidateFor(kind, preset.id));
252373
+ return candidates;
252374
+ }
252191
252375
  function audioGenerationSetupPlan(kind, backend, repoRoot = ".", model) {
252192
252376
  const commandName = kind === "music" ? "music" : "sound";
252193
252377
  const fallback = kind === "music" ? DEFAULT_MUSIC_MODEL : DEFAULT_SOUND_MODEL;
@@ -252261,6 +252445,7 @@ function audioGenerationSetupPlan(kind, backend, repoRoot = ".", model) {
252261
252445
  ],
252262
252446
  notes: [
252263
252447
  "Use this path for Stable Audio Open 1.0, the serious stereo audio/music baseline.",
252448
+ "Omnius uses Diffusers StableAudioPipeline here; stable-audio-tools is intentionally not installed because it often pulls build-from-source dependencies.",
252264
252449
  "Expect larger model downloads and higher VRAM pressure than AudioLDM or MusicGen small."
252265
252450
  ]
252266
252451
  };
@@ -252296,7 +252481,34 @@ function audioGenerationSetupPlan(kind, backend, repoRoot = ".", model) {
252296
252481
  ]
252297
252482
  };
252298
252483
  }
252299
- var DEFAULT_SOUND_MODEL, DEFAULT_MUSIC_MODEL, DIFFUSERS_AUDIO_PACKAGES, TRANSFORMERS_AUDIO_PACKAGES, AUDIOCRAFT_PACKAGES, STABLE_AUDIO_PACKAGES, TANGOFLUX_PACKAGES, AUDIO_GENERATION_MODEL_PRESETS, DIFFUSERS_AUDIO_RUNNER, AUDIOCRAFT_RUNNER, TRANSFORMERS_AUDIO_RUNNER, TANGOFLUX_RUNNER, AudioGenerateTool;
252484
+ function summarizeToolResult2(result) {
252485
+ return trimProcessText2(String(result.error || result.output || "unknown error"), 700).replace(/\s+/g, " ").trim();
252486
+ }
252487
+ function formatAudioAttempt(candidate, reason, index) {
252488
+ return `${index + 1}. ${candidate.model} [${candidate.backend}] - ${reason}`;
252489
+ }
252490
+ function formatAudioFallbackFailure(kind, failed) {
252491
+ return [
252492
+ `No ${kind} generation model in the fallback ladder completed successfully.`,
252493
+ "Attempted, highest quality to lowest:",
252494
+ ...failed.map((attempt, index) => ` ${formatAudioAttempt(attempt.candidate, attempt.reason, index)}`)
252495
+ ].join("\n");
252496
+ }
252497
+ function annotateAudioFallbackSuccess(result, failed, winner) {
252498
+ if (failed.length === 0)
252499
+ return result;
252500
+ const prefix = [
252501
+ `Fallback ladder succeeded with ${winner.model} [${winner.backend}] after ${failed.length} failed attempt(s).`,
252502
+ "Failed attempts:",
252503
+ ...failed.map((attempt, index) => ` ${formatAudioAttempt(attempt.candidate, attempt.reason, index)}`),
252504
+ ""
252505
+ ].join("\n");
252506
+ return {
252507
+ ...result,
252508
+ output: prefix + result.output
252509
+ };
252510
+ }
252511
+ var DEFAULT_SOUND_MODEL, DEFAULT_MUSIC_MODEL, DIFFUSERS_AUDIO_PACKAGES, TRANSFORMERS_AUDIO_PACKAGES, AUDIOCRAFT_PACKAGES, STABLE_AUDIO_PACKAGES, TANGOFLUX_PACKAGES, AUDIO_GENERATION_MODEL_PRESETS, SOUND_GENERATION_QUALITY_LADDER, MUSIC_GENERATION_QUALITY_LADDER, DIFFUSERS_AUDIO_RUNNER, AUDIOCRAFT_RUNNER, TRANSFORMERS_AUDIO_RUNNER, TANGOFLUX_RUNNER, AudioGenerateTool;
252300
252512
  var init_audio_generate = __esm({
252301
252513
  "packages/execution/dist/tools/audio-generate.js"() {
252302
252514
  "use strict";
@@ -252338,7 +252550,6 @@ var init_audio_generate = __esm({
252338
252550
  "accelerate",
252339
252551
  "scipy",
252340
252552
  "soundfile",
252341
- "stable-audio-tools",
252342
252553
  "einops"
252343
252554
  ];
252344
252555
  TANGOFLUX_PACKAGES = [
@@ -252644,6 +252855,21 @@ var init_audio_generate = __esm({
252644
252855
  note: "Legacy specialized music-generation path."
252645
252856
  }
252646
252857
  ];
252858
+ SOUND_GENERATION_QUALITY_LADDER = [
252859
+ "stabilityai/stable-audio-open-1.0",
252860
+ "cvssp/audioldm2-large",
252861
+ "cvssp/audioldm2",
252862
+ "facebook/audiogen-medium",
252863
+ "declare-lab/TangoFlux",
252864
+ DEFAULT_SOUND_MODEL
252865
+ ];
252866
+ MUSIC_GENERATION_QUALITY_LADDER = [
252867
+ "stabilityai/stable-audio-open-1.0",
252868
+ "facebook/musicgen-stereo-large",
252869
+ "facebook/musicgen-large",
252870
+ "facebook/musicgen-medium",
252871
+ DEFAULT_MUSIC_MODEL
252872
+ ];
252647
252873
  DIFFUSERS_AUDIO_RUNNER = String.raw`#!/usr/bin/env python3
252648
252874
  import argparse, json, sys, time
252649
252875
  from pathlib import Path
@@ -253030,7 +253256,7 @@ if __name__ == "__main__":
253030
253256
  `;
253031
253257
  AudioGenerateTool = class {
253032
253258
  name = "generate_audio";
253033
- description = "Generate a sound effect or music clip from a text prompt using local audio-generation backends. Supports Diffusers AudioLDM/AudioLDM2, Transformers MusicGen, AudioCraft AudioGen, Stable Audio Open deployment paths, and explicit research-project profiles. Saves WAV files under .omnius/audio and returns the file path.";
253259
+ description = "Generate a sound effect or music clip from a text prompt using local audio-generation backends. Supports Diffusers AudioLDM/AudioLDM2, Transformers MusicGen, AudioCraft AudioGen, Stable Audio Open deployment paths, and explicit research-project profiles. When fallback is enabled, auto generation tries ranked high-quality candidates first and gracefully falls back to smaller models if setup, download, or generation fails. Saves WAV files under .omnius/audio and returns the file path.";
253034
253260
  parameters = {
253035
253261
  type: "object",
253036
253262
  properties: {
@@ -253044,6 +253270,14 @@ if __name__ == "__main__":
253044
253270
  playback: {
253045
253271
  type: "boolean",
253046
253272
  description: "Whether the TUI should play generated audio after saving it. Defaults true; set false for silent generation."
253273
+ },
253274
+ fallback: {
253275
+ type: "boolean",
253276
+ description: "Whether to try the ranked quality ladder if the selected model/backend fails. Defaults true."
253277
+ },
253278
+ strict_model: {
253279
+ type: "boolean",
253280
+ description: "When true, use only the requested model/backend and do not fall back. Defaults false."
253047
253281
  }
253048
253282
  },
253049
253283
  required: ["prompt"]
@@ -253147,14 +253381,14 @@ if __name__ == "__main__":
253147
253381
  if (action === "list_models") {
253148
253382
  return {
253149
253383
  success: true,
253150
- output: AUDIO_GENERATION_MODEL_PRESETS.filter((preset2) => preset2.kind === kind).map((preset2) => `${preset2.id} [${preset2.backend}] - ${preset2.note}`).join("\n"),
253384
+ output: AUDIO_GENERATION_MODEL_PRESETS.filter((preset) => preset.kind === kind).map((preset) => `${preset.id} [${preset.backend}] - ${preset.note}`).join("\n"),
253151
253385
  durationMs: performance.now() - start2
253152
253386
  };
253153
253387
  }
253154
253388
  if (action === "setup") {
253155
253389
  const requested = String(args["backend"] ?? (kind === "music" ? this.defaults.musicBackend : this.defaults.soundBackend) ?? (kind === "music" ? "transformers" : "diffusers"));
253156
- const backend2 = inferAudioGenerationBackend(typeof args["model"] === "string" ? args["model"] : void 0, requested);
253157
- const resolvedBackend = backend2 === "auto" ? kind === "music" ? "transformers" : "diffusers" : backend2;
253390
+ const backend = inferAudioGenerationBackend(typeof args["model"] === "string" ? args["model"] : void 0, requested);
253391
+ const resolvedBackend = backend === "auto" ? kind === "music" ? "transformers" : "diffusers" : backend;
253158
253392
  const plan = audioGenerationSetupPlan(kind, resolvedBackend, this.cwd, typeof args["model"] === "string" ? args["model"] : void 0);
253159
253393
  return {
253160
253394
  success: true,
@@ -253173,37 +253407,9 @@ if __name__ == "__main__":
253173
253407
  const defaultBackend2 = kind === "music" ? this.defaults.musicBackend : this.defaults.soundBackend;
253174
253408
  const rawModel2 = args["model"] ? String(args["model"]) : defaultModel2;
253175
253409
  const requestedModel2 = rawModel2 === "auto" ? void 0 : rawModel2;
253176
- let backend2 = inferAudioGenerationBackend(requestedModel2, args["backend"] ? String(args["backend"]) : defaultBackend2);
253177
- if (backend2 === "auto")
253178
- backend2 = kind === "music" ? "transformers" : "diffusers";
253179
- const model2 = requestedModel2 ?? (kind === "music" ? DEFAULT_MUSIC_MODEL : DEFAULT_SOUND_MODEL);
253180
- const preset2 = getAudioGenerationPreset(model2, kind);
253181
- const duration2 = numberArg2(args["duration"], preset2?.defaultDurationSec ?? (kind === "music" ? 20 : 8));
253182
- if (backend2 === "project") {
253183
- const plan = audioGenerationSetupPlan(kind, "project", this.cwd, model2);
253184
- return {
253185
- success: false,
253186
- output: [
253187
- `${preset2?.label ?? model2} is a project deployment profile, not an automatic generic runner.`,
253188
- "",
253189
- "Setup path:",
253190
- ...plan.commands.map((cmd) => ` ${cmd}`),
253191
- "",
253192
- ...plan.notes.map((note) => `- ${note}`)
253193
- ].join("\n"),
253194
- durationMs: performance.now() - start2
253195
- };
253196
- }
253197
- this.emitProgress({ stage: "setup", message: `Preparing ${kind} model ${model2} (${backend2})` });
253198
- return await this.prewarmPythonBackend({
253199
- kind,
253200
- backend: backend2,
253201
- runnerBackend: backend2,
253202
- model: model2,
253203
- duration: duration2,
253204
- start: start2,
253205
- python: args["python"]
253206
- });
253410
+ const requestedBackend2 = args["backend"] ? String(args["backend"]) : defaultBackend2;
253411
+ const candidates2 = audioGenerationFallbackCandidates(kind, requestedModel2, requestedBackend2, generationFallbackEnabled2(args));
253412
+ return await this.prewarmCandidateLadder({ kind, candidates: candidates2, args, start: start2 });
253207
253413
  }
253208
253414
  const prompt = String(args["prompt"] ?? "").trim();
253209
253415
  if (!prompt) {
@@ -253213,45 +253419,12 @@ if __name__ == "__main__":
253213
253419
  const defaultBackend = kind === "music" ? this.defaults.musicBackend : this.defaults.soundBackend;
253214
253420
  const rawModel = args["model"] ? String(args["model"]) : defaultModel;
253215
253421
  const requestedModel = rawModel === "auto" ? void 0 : rawModel;
253216
- let backend = inferAudioGenerationBackend(requestedModel, args["backend"] ? String(args["backend"]) : defaultBackend);
253217
- if (backend === "auto")
253218
- backend = kind === "music" ? "transformers" : "diffusers";
253219
- const model = requestedModel ?? (kind === "music" ? DEFAULT_MUSIC_MODEL : DEFAULT_SOUND_MODEL);
253220
- const preset = getAudioGenerationPreset(model, kind);
253221
- const duration = numberArg2(args["duration"], preset?.defaultDurationSec ?? (kind === "music" ? 20 : 8));
253222
- const steps = optionalNumberArg2(args["steps"]) ?? preset?.defaultSteps;
253422
+ const requestedBackend = args["backend"] ? String(args["backend"]) : defaultBackend;
253423
+ const candidates = audioGenerationFallbackCandidates(kind, requestedModel, requestedBackend, generationFallbackEnabled2(args));
253223
253424
  const seed = optionalNumberArg2(args["seed"]);
253224
253425
  const playback = playbackRequested(args);
253225
253426
  try {
253226
- this.emitProgress({ stage: "setup", message: `Using ${kind} model ${model} (${backend})` });
253227
- if (backend === "project") {
253228
- const plan = audioGenerationSetupPlan(kind, "project", this.cwd, model);
253229
- return {
253230
- success: false,
253231
- output: [
253232
- `${preset?.label ?? model} is a project deployment profile, not an automatic generic runner.`,
253233
- "",
253234
- "Setup path:",
253235
- ...plan.commands.map((cmd) => ` ${cmd}`),
253236
- "",
253237
- ...plan.notes.map((note) => `- ${note}`)
253238
- ].join("\n"),
253239
- durationMs: performance.now() - start2
253240
- };
253241
- }
253242
- if (backend === "tangoflux") {
253243
- return await this.generateWithPythonBackend({ kind, backend, runnerBackend: "tangoflux", prompt, model, duration, steps, seed, playback, start: start2, python: args["python"] });
253244
- }
253245
- if (backend === "transformers") {
253246
- return await this.generateWithPythonBackend({ kind, backend, runnerBackend: "transformers", prompt, model, duration, steps, seed, playback, start: start2, python: args["python"] });
253247
- }
253248
- if (backend === "audiocraft") {
253249
- return await this.generateWithPythonBackend({ kind, backend, runnerBackend: "audiocraft", prompt, model, duration, steps, seed, playback, start: start2, python: args["python"] });
253250
- }
253251
- if (backend === "stable-audio") {
253252
- return await this.generateWithPythonBackend({ kind, backend, runnerBackend: "stable-audio", prompt, model, duration, steps, seed, playback, start: start2, python: args["python"] });
253253
- }
253254
- return await this.generateWithPythonBackend({ kind, backend: "diffusers", runnerBackend: "diffusers", prompt, model, duration, steps, seed, playback, start: start2, python: args["python"] });
253427
+ return await this.generateCandidateLadder({ kind, candidates, prompt, args, seed, playback, start: start2 });
253255
253428
  } catch (err) {
253256
253429
  return {
253257
253430
  success: false,
@@ -253260,6 +253433,96 @@ if __name__ == "__main__":
253260
253433
  };
253261
253434
  }
253262
253435
  }
253436
+ async prewarmCandidateLadder(args) {
253437
+ const failed = [];
253438
+ for (let index = 0; index < args.candidates.length; index++) {
253439
+ const candidate = args.candidates[index];
253440
+ const duration = numberArg2(args.args["duration"], candidate.preset?.defaultDurationSec ?? (args.kind === "music" ? 20 : 8));
253441
+ this.emitProgress({
253442
+ stage: "setup",
253443
+ message: `Preparing ${args.kind} model ${candidate.model} (${candidate.backend}) [${index + 1}/${args.candidates.length}]`
253444
+ });
253445
+ const result = candidate.backend === "project" ? this.projectProfileResult(args.kind, candidate, args.start) : await this.prewarmPythonBackend({
253446
+ kind: args.kind,
253447
+ backend: candidate.backend,
253448
+ runnerBackend: candidate.backend,
253449
+ model: candidate.model,
253450
+ duration,
253451
+ start: args.start,
253452
+ python: args.args["python"]
253453
+ });
253454
+ if (result.success)
253455
+ return annotateAudioFallbackSuccess(result, failed, candidate);
253456
+ failed.push({ candidate, reason: summarizeToolResult2(result) });
253457
+ if (index < args.candidates.length - 1) {
253458
+ this.emitProgress({
253459
+ stage: "setup",
253460
+ message: `${candidate.model} failed; trying ${args.candidates[index + 1].model}`
253461
+ });
253462
+ }
253463
+ }
253464
+ return {
253465
+ success: false,
253466
+ output: formatAudioFallbackFailure(args.kind, failed),
253467
+ error: formatAudioFallbackFailure(args.kind, failed),
253468
+ durationMs: performance.now() - args.start
253469
+ };
253470
+ }
253471
+ async generateCandidateLadder(args) {
253472
+ const failed = [];
253473
+ for (let index = 0; index < args.candidates.length; index++) {
253474
+ const candidate = args.candidates[index];
253475
+ const duration = numberArg2(args.args["duration"], candidate.preset?.defaultDurationSec ?? (args.kind === "music" ? 20 : 8));
253476
+ const steps = optionalNumberArg2(args.args["steps"]) ?? candidate.preset?.defaultSteps;
253477
+ this.emitProgress({
253478
+ stage: "setup",
253479
+ message: `Using ${args.kind} model ${candidate.model} (${candidate.backend}) [${index + 1}/${args.candidates.length}]`
253480
+ });
253481
+ const result = candidate.backend === "project" ? this.projectProfileResult(args.kind, candidate, args.start) : await this.generateWithPythonBackend({
253482
+ kind: args.kind,
253483
+ backend: candidate.backend,
253484
+ runnerBackend: candidate.backend,
253485
+ prompt: args.prompt,
253486
+ model: candidate.model,
253487
+ duration,
253488
+ steps,
253489
+ seed: args.seed,
253490
+ playback: args.playback,
253491
+ start: args.start,
253492
+ python: args.args["python"]
253493
+ });
253494
+ if (result.success)
253495
+ return annotateAudioFallbackSuccess(result, failed, candidate);
253496
+ failed.push({ candidate, reason: summarizeToolResult2(result) });
253497
+ if (index < args.candidates.length - 1) {
253498
+ this.emitProgress({
253499
+ stage: "setup",
253500
+ message: `${candidate.model} failed; falling back to ${args.candidates[index + 1].model}`
253501
+ });
253502
+ }
253503
+ }
253504
+ return {
253505
+ success: false,
253506
+ output: formatAudioFallbackFailure(args.kind, failed),
253507
+ error: formatAudioFallbackFailure(args.kind, failed),
253508
+ durationMs: performance.now() - args.start
253509
+ };
253510
+ }
253511
+ projectProfileResult(kind, candidate, start2) {
253512
+ const plan = audioGenerationSetupPlan(kind, "project", this.cwd, candidate.model);
253513
+ return {
253514
+ success: false,
253515
+ output: [
253516
+ `${candidate.preset?.label ?? candidate.model} is a project deployment profile, not an automatic generic runner.`,
253517
+ "",
253518
+ "Setup path:",
253519
+ ...plan.commands.map((cmd) => ` ${cmd}`),
253520
+ "",
253521
+ ...plan.notes.map((note) => `- ${note}`)
253522
+ ].join("\n"),
253523
+ durationMs: performance.now() - start2
253524
+ };
253525
+ }
253263
253526
  async generateWithPythonBackend(args) {
253264
253527
  const runner = await ensureAudioRunner(this.cwd, args.runnerBackend);
253265
253528
  await mkdir12(audioOutputDir(this.cwd), { recursive: true });
@@ -255836,8 +256099,8 @@ var init_browser_action = __esm({
255836
256099
  const afterDom = await apiCall("/dom", "GET");
255837
256100
  const afterTitle = (afterDom.dom || "").match(/<title[^>]*>([^<]*)<\/title>/i)?.[1] || "";
255838
256101
  try {
255839
- const { unlinkSync: unlinkSync25 } = await import("node:fs");
255840
- unlinkSync25(imagePath);
256102
+ const { unlinkSync: unlinkSync26 } = await import("node:fs");
256103
+ unlinkSync26(imagePath);
255841
256104
  } catch {
255842
256105
  }
255843
256106
  return {
@@ -507359,6 +507622,18 @@ function supertonicInferScript() {
507359
507622
  function mlxVenvPy() {
507360
507623
  return process.platform === "win32" ? join58(voiceDir(), "mlx-venv", "Scripts", "python.exe") : join58(voiceDir(), "mlx-venv", "bin", "python3");
507361
507624
  }
507625
+ function luxttsVenvDir() {
507626
+ return join58(voiceDir(), "luxtts-venv");
507627
+ }
507628
+ function luxttsVenvPy() {
507629
+ return process.platform === "win32" ? join58(luxttsVenvDir(), "Scripts", "python.exe") : join58(luxttsVenvDir(), "bin", "python3");
507630
+ }
507631
+ function luxttsRepoDir() {
507632
+ return join58(voiceDir(), "LuxTTS");
507633
+ }
507634
+ function luxttsInferScript() {
507635
+ return join58(voiceDir(), "luxtts-infer.py");
507636
+ }
507362
507637
  function piperVenvDir() {
507363
507638
  return join58(voiceDir(), "piper-venv");
507364
507639
  }
@@ -507385,7 +507660,7 @@ function ensureSupertonicInstalled() {
507385
507660
  }
507386
507661
  function ensureMlxInstalled() {
507387
507662
  if (process.platform !== "darwin" || process.arch !== "arm64") {
507388
- throw new Error("MLX TTS requires macOS on Apple Silicon. Use luxtts, supertonic, onnx/piper, or espeak on this machine.");
507663
+ throw new Error("MLX TTS requires macOS on Apple Silicon. Use luxtts, supertonic, onnx/piper, or backend=auto on this machine.");
507389
507664
  }
507390
507665
  const venvPy = mlxVenvPy();
507391
507666
  if (!existsSync40(venvPy)) {
@@ -507402,6 +507677,81 @@ function ensureMlxInstalled() {
507402
507677
  }
507403
507678
  return venvPy;
507404
507679
  }
507680
+ function pythonCanImportLuxTts(venvPy) {
507681
+ try {
507682
+ execFileSync2(venvPy, [
507683
+ "-c",
507684
+ "import sys, os; sys.path.insert(0, os.environ['LUXTTS_REPO_PATH']); from zipvoice.luxvoice import LuxTTS; print('ok')"
507685
+ ], {
507686
+ stdio: "pipe",
507687
+ timeout: 3e4,
507688
+ env: { ...process.env, LUXTTS_REPO_PATH: luxttsRepoDir() }
507689
+ });
507690
+ return true;
507691
+ } catch {
507692
+ return false;
507693
+ }
507694
+ }
507695
+ function pipInstall(venvPy, packages, timeout2 = 9e5) {
507696
+ execFileSync2(venvPy, ["-m", "pip", "install", "--prefer-binary", ...packages], {
507697
+ stdio: "pipe",
507698
+ timeout: timeout2,
507699
+ env: process.env
507700
+ });
507701
+ }
507702
+ function ensureLuxttsInstalled() {
507703
+ const venvPy = luxttsVenvPy();
507704
+ const repoDir = luxttsRepoDir();
507705
+ mkdirSync16(voiceDir(), { recursive: true });
507706
+ if (existsSync40(venvPy) && existsSync40(join58(repoDir, "zipvoice", "luxvoice.py")) && pythonCanImportLuxTts(venvPy)) {
507707
+ writeFileSync16(luxttsInferScript(), LUXTTS_DAEMON_PY, "utf-8");
507708
+ return venvPy;
507709
+ }
507710
+ const py = findPython32();
507711
+ if (!py)
507712
+ throw new Error("python3 is required to set up LuxTTS voice cloning.");
507713
+ if (!existsSync40(venvPy)) {
507714
+ execFileSync2(py, ["-m", "venv", luxttsVenvDir()], { stdio: "pipe", timeout: 18e4 });
507715
+ }
507716
+ execFileSync2(venvPy, ["-m", "pip", "install", "--upgrade", "pip", "wheel", "setuptools<81"], {
507717
+ stdio: "pipe",
507718
+ timeout: 3e5
507719
+ });
507720
+ pipInstall(venvPy, ["torch", "torchaudio"], 12e5);
507721
+ if (!existsSync40(join58(repoDir, "zipvoice", "luxvoice.py"))) {
507722
+ if (!hasCommand3("git"))
507723
+ throw new Error("git is required to set up LuxTTS voice cloning.");
507724
+ execFileSync2("git", ["clone", "--depth", "1", "https://github.com/ysharma3501/LuxTTS.git", repoDir], {
507725
+ stdio: "pipe",
507726
+ timeout: 3e5
507727
+ });
507728
+ }
507729
+ pipInstall(venvPy, [
507730
+ "lhotse",
507731
+ "huggingface_hub",
507732
+ "safetensors",
507733
+ "pydub",
507734
+ "onnxruntime",
507735
+ "librosa",
507736
+ "transformers<=4.57.6",
507737
+ "inflect",
507738
+ "numpy",
507739
+ "vocos",
507740
+ "jieba",
507741
+ "pypinyin",
507742
+ "cn2an"
507743
+ ], 12e5);
507744
+ try {
507745
+ pipInstall(venvPy, ["git+https://github.com/ysharma3501/LinaCodec.git"], 12e5);
507746
+ } catch {
507747
+ }
507748
+ pipInstall(venvPy, ["-e", repoDir], 6e5);
507749
+ writeFileSync16(luxttsInferScript(), LUXTTS_DAEMON_PY, "utf-8");
507750
+ if (!pythonCanImportLuxTts(venvPy)) {
507751
+ throw new Error(`LuxTTS setup completed but import still fails in ${luxttsVenvDir()}.`);
507752
+ }
507753
+ return venvPy;
507754
+ }
507405
507755
  function ensurePiperInstalled() {
507406
507756
  if (hasCommand3("piper"))
507407
507757
  return "piper";
@@ -507435,6 +507785,28 @@ function saveCloneRefFromSample(sample, cloneName) {
507435
507785
  copyFileSync2(source, dest);
507436
507786
  return dest;
507437
507787
  }
507788
+ function cloneSampleArg(args) {
507789
+ for (const key of ["sample", "source_audio", "voice_sample", "reference_audio", "ref_audio", "clone_sample"]) {
507790
+ const value2 = args[key];
507791
+ if (typeof value2 === "string" && value2.trim())
507792
+ return value2.trim();
507793
+ }
507794
+ return "";
507795
+ }
507796
+ function wantsVoiceClone(args) {
507797
+ if (cloneSampleArg(args))
507798
+ return true;
507799
+ if (typeof args["clone_ref"] === "string" && args["clone_ref"].trim())
507800
+ return true;
507801
+ const voice = typeof args["voice"] === "string" ? args["voice"].trim() : "";
507802
+ return /\.(wav|mp3|flac|ogg|m4a)$/i.test(voice) || voice.startsWith("/") || voice.startsWith("./") || voice.startsWith("../") || voice.startsWith("~/");
507803
+ }
507804
+ function cloneRefForSynthesis(args) {
507805
+ const sample = cloneSampleArg(args);
507806
+ if (sample)
507807
+ return saveCloneRefFromSample(sample, typeof args["clone_name"] === "string" ? args["clone_name"] : void 0);
507808
+ return resolveCloneRef(args["clone_ref"] ?? args["voice"]);
507809
+ }
507438
507810
  function ensureLuxttsDaemon() {
507439
507811
  if (_luxttsDaemon && !_luxttsDaemon.killed && _luxttsReady)
507440
507812
  return Promise.resolve(true);
@@ -507448,14 +507820,23 @@ function ensureLuxttsDaemon() {
507448
507820
  }
507449
507821
  if (_luxttsStarting)
507450
507822
  return Promise.resolve(false);
507451
- const venvPy = join58(homedir14(), ".omnius", "voice", "luxtts-venv", "bin", "python3");
507452
- const inferScript = join58(homedir14(), ".omnius", "voice", "luxtts-infer.py");
507453
- const repoDir = join58(homedir14(), ".omnius", "voice", "LuxTTS");
507823
+ const venvPy = luxttsVenvPy();
507824
+ const inferScript = luxttsInferScript();
507825
+ const repoDir = luxttsRepoDir();
507454
507826
  if (!existsSync40(venvPy) || !existsSync40(inferScript))
507455
507827
  return Promise.resolve(false);
507456
507828
  _luxttsStarting = true;
507457
507829
  return new Promise((resolve48) => {
507458
- const timeout2 = setTimeout(() => {
507830
+ let settled = false;
507831
+ let timeout2;
507832
+ const finish = (ready) => {
507833
+ if (settled)
507834
+ return;
507835
+ settled = true;
507836
+ clearTimeout(timeout2);
507837
+ resolve48(ready);
507838
+ };
507839
+ timeout2 = setTimeout(() => {
507459
507840
  _luxttsStarting = false;
507460
507841
  if (_luxttsDaemon && !_luxttsReady) {
507461
507842
  try {
@@ -507464,7 +507845,7 @@ function ensureLuxttsDaemon() {
507464
507845
  }
507465
507846
  _luxttsDaemon = null;
507466
507847
  }
507467
- resolve48(false);
507848
+ finish(false);
507468
507849
  }, 12e4);
507469
507850
  const daemon = spawn16(venvPy, [inferScript], {
507470
507851
  stdio: ["pipe", "pipe", "pipe"],
@@ -507486,8 +507867,7 @@ function ensureLuxttsDaemon() {
507486
507867
  if (msg.type === "ready") {
507487
507868
  _luxttsReady = true;
507488
507869
  _luxttsStarting = false;
507489
- clearTimeout(timeout2);
507490
- resolve48(true);
507870
+ finish(true);
507491
507871
  } else if (msg.type === "result" && msg.id) {
507492
507872
  const pending = _luxttsPending.get(msg.id);
507493
507873
  if (pending) {
@@ -507509,13 +507889,13 @@ function ensureLuxttsDaemon() {
507509
507889
  _luxttsDaemon = null;
507510
507890
  _luxttsReady = false;
507511
507891
  _luxttsStarting = false;
507892
+ finish(false);
507512
507893
  });
507513
507894
  daemon.on("error", () => {
507514
507895
  _luxttsDaemon = null;
507515
507896
  _luxttsReady = false;
507516
507897
  _luxttsStarting = false;
507517
- clearTimeout(timeout2);
507518
- resolve48(false);
507898
+ finish(false);
507519
507899
  });
507520
507900
  });
507521
507901
  }
@@ -507545,7 +507925,7 @@ function luxttsSynthesize(text, cloneRef, outputPath2, speed = 1) {
507545
507925
  _luxttsDaemon.stdin.write(req2 + "\n");
507546
507926
  });
507547
507927
  }
507548
- var _luxttsDaemon, _luxttsReady, _luxttsRequestId, _luxttsPending, _luxttsBuffer, _luxttsStarting, SUPERTONIC_INFER_PY, AudioPlaybackTool, TtsGenerateTool, SoundPlaybackTool;
507928
+ var _luxttsDaemon, _luxttsReady, _luxttsRequestId, _luxttsPending, _luxttsBuffer, _luxttsStarting, SUPERTONIC_INFER_PY, LUXTTS_DAEMON_PY, AudioPlaybackTool, TtsGenerateTool, SoundPlaybackTool;
507549
507929
  var init_audio_playback = __esm({
507550
507930
  "packages/execution/dist/tools/audio-playback.js"() {
507551
507931
  "use strict";
@@ -507585,10 +507965,45 @@ try:
507585
507965
  except Exception as exc:
507586
507966
  print(json.dumps({"ok": False, "error": str(exc), "trace": traceback.format_exc(limit=3)}))
507587
507967
  sys.exit(1)
507968
+ `;
507969
+ LUXTTS_DAEMON_PY = String.raw`
507970
+ import json, os, sys, traceback, wave
507971
+ import numpy as np
507972
+ import torch
507973
+ repo = os.environ.get("LUXTTS_REPO_PATH") or ""
507974
+ if repo:
507975
+ sys.path.insert(0, repo)
507976
+ from zipvoice.luxvoice import LuxTTS
507977
+ device = "cuda" if torch.cuda.is_available() else "cpu"
507978
+ tts = LuxTTS(model_path="YatharthS/LuxTTS", device=device, threads=4)
507979
+ print(json.dumps({"type": "ready", "device": device}), flush=True)
507980
+ for line in sys.stdin:
507981
+ if not line.strip():
507982
+ continue
507983
+ req = json.loads(line)
507984
+ if req.get("action") == "quit":
507985
+ break
507986
+ rid = req.get("id")
507987
+ try:
507988
+ text = str(req.get("text") or "").strip()
507989
+ clone_ref = str(req.get("clone_ref") or "")
507990
+ output = str(req.get("output_path") or "")
507991
+ speed = float(req.get("speed") or 1.0)
507992
+ enc = tts.encode_prompt(clone_ref, duration=5, rms=0.001)
507993
+ wav = tts.generate_speech(text, enc, num_steps=4, guidance_scale=3.0, t_shift=0.5, speed=speed)
507994
+ data = (np.clip(wav.cpu().numpy().squeeze(), -1, 1) * 32767).astype(np.int16)
507995
+ with wave.open(output, "wb") as f:
507996
+ f.setnchannels(1)
507997
+ f.setsampwidth(2)
507998
+ f.setframerate(48000)
507999
+ f.writeframes(data.tobytes())
508000
+ print(json.dumps({"type": "result", "id": rid, "path": output}), flush=True)
508001
+ except Exception as exc:
508002
+ print(json.dumps({"type": "error", "id": rid, "error": str(exc), "trace": traceback.format_exc(limit=3)}), flush=True)
507588
508003
  `;
507589
508004
  AudioPlaybackTool = class {
507590
508005
  name = "audio_playback";
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.";
508006
+ 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 source clip, 'list_voices' to inspect available clone refs/backends, 'volume' to get or set system volume, 'list' to enumerate audio output devices. TTS backends include auto, LuxTTS voice cloning, Supertonic, MLX, ONNX/Piper, and a local fallback. Neural TTS backends self-provision into ~/.omnius/voice on first use where supported. For cloned speech from a source clip, call generate_tts or audio_playback action=synthesize with sample/source_audio/voice_sample and backend=auto or luxtts. 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.";
507592
508007
  parameters = {
507593
508008
  type: "object",
507594
508009
  properties: {
@@ -507615,8 +508030,8 @@ except Exception as exc:
507615
508030
  },
507616
508031
  backend: {
507617
508032
  type: "string",
507618
- enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper", "espeak"],
507619
- description: "TTS backend. auto tries LuxTTS clone, Supertonic, MLX on Apple Silicon, Piper/ONNX, then espeak."
508033
+ enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper"],
508034
+ description: "TTS backend. auto tries LuxTTS clone, Supertonic, MLX on Apple Silicon, Piper/ONNX, then a local fallback."
507620
508035
  },
507621
508036
  output: {
507622
508037
  type: "string",
@@ -507632,11 +508047,31 @@ except Exception as exc:
507632
508047
  },
507633
508048
  sample: {
507634
508049
  type: "string",
507635
- description: "Audio sample path to register as a clone voice for action=clone."
508050
+ description: "Audio source clip path to register or use as a LuxTTS clone voice."
508051
+ },
508052
+ source_audio: {
508053
+ type: "string",
508054
+ description: "Alias for sample. Use this for cloned speech from a source voice clip."
508055
+ },
508056
+ voice_sample: {
508057
+ type: "string",
508058
+ description: "Alias for sample/source_audio."
508059
+ },
508060
+ reference_audio: {
508061
+ type: "string",
508062
+ description: "Alias for sample/source_audio."
508063
+ },
508064
+ ref_audio: {
508065
+ type: "string",
508066
+ description: "Alias for sample/source_audio."
508067
+ },
508068
+ clone_sample: {
508069
+ type: "string",
508070
+ description: "Alias for sample/source_audio."
507636
508071
  },
507637
508072
  clone_name: {
507638
508073
  type: "string",
507639
- description: "Friendly filename stem for action=clone."
508074
+ description: "Friendly filename stem for action=clone or for registering a source clip during synthesis."
507640
508075
  },
507641
508076
  model: {
507642
508077
  type: "string",
@@ -507652,11 +508087,11 @@ except Exception as exc:
507652
508087
  },
507653
508088
  speed: {
507654
508089
  type: "number",
507655
- description: "Speech speed. espeak uses words per minute; neural backends use a multiplier."
508090
+ description: "Speech speed. Neural backends use a multiplier; local fallback uses its backend-specific rate."
507656
508091
  },
507657
508092
  voice: {
507658
508093
  type: "string",
507659
- description: "Voice id/name. Examples: Supertonic voice M4, MLX voice af_heart, espeak voice en-us, or Piper/ONNX model path."
508094
+ description: "Voice id/name. Examples: Supertonic voice M4, MLX voice af_heart, a source audio path for cloning, or Piper/ONNX model path."
507660
508095
  },
507661
508096
  lang: {
507662
508097
  type: "string",
@@ -507720,9 +508155,9 @@ except Exception as exc:
507720
508155
  return await this.synthesizeText(args, start2, true);
507721
508156
  }
507722
508157
  cloneVoice(args, start2) {
507723
- const sample = typeof args["sample"] === "string" ? args["sample"] : typeof args["file"] === "string" ? args["file"] : "";
508158
+ const sample = cloneSampleArg(args) || (typeof args["file"] === "string" ? args["file"] : "");
507724
508159
  if (!sample.trim()) {
507725
- return { success: false, output: "", error: "Missing 'sample' parameter. Provide a local audio sample to register as a clone voice.", durationMs: performance.now() - start2 };
508160
+ return { success: false, output: "", error: "Missing source audio. Provide sample=<file> or source_audio=<file> to register as a clone voice.", durationMs: performance.now() - start2 };
507726
508161
  }
507727
508162
  const saved = saveCloneRefFromSample(sample, typeof args["clone_name"] === "string" ? args["clone_name"] : void 0);
507728
508163
  return {
@@ -507739,10 +508174,11 @@ except Exception as exc:
507739
508174
  const lines = [
507740
508175
  "TTS backends:",
507741
508176
  ` luxtts: ${existsSync40(join58(voiceDir(), "luxtts-venv", "bin", "python3")) ? "installed" : "not installed"}; clone refs: ${refs.length}`,
508177
+ " clone from source clip: generate_tts text=<words> source_audio=<wav/mp3/flac/ogg/m4a> backend=auto",
507742
508178
  ` supertonic: ${existsSync40(supertonicVenvPy()) ? "installed" : "not installed"}; voices include M1, M2, M3, M4 when package assets are available`,
507743
508179
  ` mlx: ${existsSync40(mlxVenvPy()) ? "installed" : "not installed"}; Apple Silicon only; default model mlx-community/Kokoro-82M-bf16`,
507744
508180
  ` piper/onnx: ${hasCommand3("piper") || existsSync40(piperVenvBin()) ? "available" : "not installed"}; first use installs piper-tts into ${piperVenvDir()}; pass model=<path.onnx> for raw ONNX voices`,
507745
- ` espeak: ${hasCommand3("espeak-ng") ? "available" : "not found"}`,
508181
+ ` local fallback: ${hasCommand3("espeak-ng") ? "available" : "not found"}`,
507746
508182
  "",
507747
508183
  "Registered clone refs:",
507748
508184
  ...refs.length ? refs.map((ref) => ` ${ref}`) : [" none"]
@@ -507756,11 +508192,20 @@ except Exception as exc:
507756
508192
  }
507757
508193
  const requestedBackend = normalizeTtsBackend(args["backend"]);
507758
508194
  const strictBackend = boolArg(args["strict_backend"] ?? args["strictBackend"], false);
508195
+ const cloneRequested = wantsVoiceClone(args);
508196
+ if (cloneRequested && requestedBackend !== "auto" && requestedBackend !== "luxtts") {
508197
+ return {
508198
+ success: false,
508199
+ output: "",
508200
+ error: "Voice cloning from a source clip requires backend=auto or backend=luxtts.",
508201
+ durationMs: performance.now() - start2
508202
+ };
508203
+ }
507759
508204
  const playback = playbackArg(args, speakDefault);
507760
508205
  const outputPath2 = ttsOutputPath(args, requestedBackend);
507761
508206
  const device = typeof args["device"] === "string" ? args["device"] : "default";
507762
508207
  const tried = [];
507763
- const autoCandidates = ["luxtts", "supertonic", ...process.platform === "darwin" && process.arch === "arm64" ? ["mlx"] : [], "piper", "espeak"];
508208
+ const autoCandidates = cloneRequested ? ["luxtts"] : ["luxtts", "supertonic", ...process.platform === "darwin" && process.arch === "arm64" ? ["mlx"] : [], "piper", "espeak"];
507764
508209
  const candidates = requestedBackend === "auto" ? autoCandidates : strictBackend ? [requestedBackend] : [requestedBackend, ...autoCandidates.filter((backend) => backend !== requestedBackend)];
507765
508210
  let usedBackend = "";
507766
508211
  let voiceSummary = "";
@@ -507823,21 +508268,19 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
507823
508268
  };
507824
508269
  }
507825
508270
  async synthesizeLuxtts(text, outputPath2, args) {
507826
- const cloneRef = resolveCloneRef(args["clone_ref"] ?? args["voice"] ?? args["sample"]);
508271
+ const cloneRef = cloneRefForSynthesis(args);
507827
508272
  if (!cloneRef)
507828
- throw new Error(`No LuxTTS clone reference found. Register one with audio_playback action=clone sample=<file>.`);
508273
+ throw new Error(`No LuxTTS clone source found. Provide source_audio=<voice clip> or clone_ref=<registered clip>.`);
507829
508274
  const speed = numberArg3(args["speed"], 1);
508275
+ ensureLuxttsInstalled();
507830
508276
  const daemonReady = await ensureLuxttsDaemon();
507831
508277
  if (daemonReady) {
507832
508278
  await luxttsSynthesize(text, cloneRef, outputPath2, speed);
507833
508279
  if (existsSync40(outputPath2))
507834
508280
  return `${basename12(cloneRef)} (LuxTTS daemon)`;
507835
508281
  }
507836
- const venvPy = join58(voiceDir(), "luxtts-venv", "bin", "python3");
507837
- const repoDir = join58(voiceDir(), "LuxTTS");
507838
- if (!existsSync40(venvPy) || !existsSync40(repoDir)) {
507839
- throw new Error("LuxTTS is not installed in the managed voice environment yet.");
507840
- }
508282
+ const venvPy = luxttsVenvPy();
508283
+ const repoDir = luxttsRepoDir();
507841
508284
  const pyScript = [
507842
508285
  "import json, sys, wave",
507843
508286
  "import numpy as np, torch",
@@ -507913,7 +508356,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
507913
508356
  }
507914
508357
  synthesizeEspeak(text, outputPath2, args) {
507915
508358
  if (!hasCommand3("espeak-ng"))
507916
- throw new Error("espeak-ng command not found.");
508359
+ throw new Error("Local fallback TTS command not found.");
507917
508360
  const voice = typeof args["voice"] === "string" ? args["voice"] : "en";
507918
508361
  const speed = Math.round(numberArg3(args["speed"], 160));
507919
508362
  execFileSync2("espeak-ng", ["-v", voice, "-s", String(speed), "-w", outputPath2, text], {
@@ -507995,20 +508438,27 @@ ${devices.join("\n")}`,
507995
508438
  };
507996
508439
  TtsGenerateTool = class {
507997
508440
  name = "generate_tts";
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.";
508441
+ 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 local fallback. Neural TTS backends self-provision into ~/.omnius/voice on first use where supported. For voice cloning, pass source_audio/sample/voice_sample with the reference clip and backend=auto or luxtts; clone_name can register it for reuse. Use clone_ref to select a registered LuxTTS voice 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.";
507999
508442
  parameters = {
508000
508443
  type: "object",
508001
508444
  properties: {
508002
508445
  text: { type: "string", description: "Text to synthesize" },
508003
508446
  input: { type: "string", description: "Alias for text." },
508004
508447
  prompt: { type: "string", description: "Alias for text." },
508005
- backend: { type: "string", enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper", "espeak"] },
508448
+ backend: { type: "string", enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper"] },
508006
508449
  output: { type: "string", description: "Output WAV path. Defaults to ~/.omnius/voice/generated/tts-*.wav." },
508007
508450
  path: { type: "string", description: "Alias for output." },
508008
508451
  playback: { type: "boolean", description: "Whether to play after generating. Defaults false for generate_tts." },
508009
508452
  strict_backend: { type: "boolean", description: "When true, fail instead of falling back if the requested backend is unavailable. Defaults false." },
508010
508453
  voice: { type: "string", description: "Voice id/name, or raw Piper/ONNX path when backend=onnx/piper." },
508011
508454
  clone_ref: { type: "string", description: "LuxTTS clone reference path, filename, or registered clone name." },
508455
+ sample: { type: "string", description: "Voice source clip path for cloned speech. Alias: source_audio." },
508456
+ source_audio: { type: "string", description: "Voice source clip path for cloned speech." },
508457
+ voice_sample: { type: "string", description: "Alias for source_audio." },
508458
+ reference_audio: { type: "string", description: "Alias for source_audio." },
508459
+ ref_audio: { type: "string", description: "Alias for source_audio." },
508460
+ clone_sample: { type: "string", description: "Alias for source_audio." },
508461
+ clone_name: { type: "string", description: "Optional name to register the source clip for later reuse." },
508012
508462
  model: { type: "string", description: "Backend model id or raw ONNX/Piper model path." },
508013
508463
  lang: { type: "string", description: "Language code for Supertonic/MLX where supported." },
508014
508464
  speed: { type: "number", description: "Speech speed multiplier or backend-specific rate." },
@@ -542582,7 +543032,7 @@ ${result}`
542582
543032
  let resizedBase64 = null;
542583
543033
  try {
542584
543034
  const { execSync: execSync60 } = await import("node:child_process");
542585
- const { writeFileSync: writeFileSync71, readFileSync: readFileSync102, unlinkSync: unlinkSync25 } = await import("node:fs");
543035
+ const { writeFileSync: writeFileSync71, readFileSync: readFileSync102, unlinkSync: unlinkSync26 } = await import("node:fs");
542586
543036
  const { join: join142 } = await import("node:path");
542587
543037
  const { tmpdir: tmpdir23 } = await import("node:os");
542588
543038
  const tmpIn = join142(tmpdir23(), `omnius_img_in_${Date.now()}.png`);
@@ -542595,11 +543045,11 @@ ${result}`
542595
543045
  const resizedBuf = readFileSync102(tmpOut);
542596
543046
  resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
542597
543047
  try {
542598
- unlinkSync25(tmpIn);
543048
+ unlinkSync26(tmpIn);
542599
543049
  } catch {
542600
543050
  }
542601
543051
  try {
542602
- unlinkSync25(tmpOut);
543052
+ unlinkSync26(tmpOut);
542603
543053
  } catch {
542604
543054
  }
542605
543055
  } catch {
@@ -551117,7 +551567,7 @@ var require_websocket3 = __commonJS({
551117
551567
  var http6 = __require("http");
551118
551568
  var net5 = __require("net");
551119
551569
  var tls2 = __require("tls");
551120
- var { randomBytes: randomBytes25, createHash: createHash24 } = __require("crypto");
551570
+ var { randomBytes: randomBytes26, createHash: createHash24 } = __require("crypto");
551121
551571
  var { Duplex: Duplex3, Readable } = __require("stream");
551122
551572
  var { URL: URL3 } = __require("url");
551123
551573
  var PerMessageDeflate3 = require_permessage_deflate3();
@@ -551647,7 +552097,7 @@ var require_websocket3 = __commonJS({
551647
552097
  }
551648
552098
  }
551649
552099
  const defaultPort = isSecure ? 443 : 80;
551650
- const key = randomBytes25(16).toString("base64");
552100
+ const key = randomBytes26(16).toString("base64");
551651
552101
  const request = isSecure ? https4.request : http6.request;
551652
552102
  const protocolSet = /* @__PURE__ */ new Set();
551653
552103
  let perMessageDeflate;
@@ -575357,19 +575807,19 @@ function modelOnnxPath(id) {
575357
575807
  function modelConfigPath(id) {
575358
575808
  return join109(modelDir(id), "config.json");
575359
575809
  }
575360
- function luxttsVenvDir() {
575810
+ function luxttsVenvDir2() {
575361
575811
  return join109(voiceDir2(), "luxtts-venv");
575362
575812
  }
575363
- function luxttsVenvPy() {
575364
- return platform5() === "win32" ? join109(luxttsVenvDir(), "Scripts", "python.exe") : join109(luxttsVenvDir(), "bin", "python3");
575813
+ function luxttsVenvPy2() {
575814
+ return platform5() === "win32" ? join109(luxttsVenvDir2(), "Scripts", "python.exe") : join109(luxttsVenvDir2(), "bin", "python3");
575365
575815
  }
575366
- function luxttsRepoDir() {
575816
+ function luxttsRepoDir2() {
575367
575817
  return join109(voiceDir2(), "LuxTTS");
575368
575818
  }
575369
575819
  function luxttsCloneRefsDir() {
575370
575820
  return join109(voiceDir2(), "clone-refs");
575371
575821
  }
575372
- function luxttsInferScript() {
575822
+ function luxttsInferScript2() {
575373
575823
  return join109(voiceDir2(), "luxtts-infer.py");
575374
575824
  }
575375
575825
  function supertonicVenvDir() {
@@ -577936,12 +578386,12 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
577936
578386
  "python3 not found. LuxTTS requires Python 3.10+. Try: apt install python3 / brew install python3"
577937
578387
  );
577938
578388
  }
577939
- const venvDir = luxttsVenvDir();
577940
- const venvPy = luxttsVenvPy();
578389
+ const venvDir = luxttsVenvDir2();
578390
+ const venvPy = luxttsVenvPy2();
577941
578391
  if (existsSync95(venvPy)) {
577942
578392
  try {
577943
578393
  const quotedPy = `"${venvPy}"`;
577944
- const repoPath = luxttsRepoDir().replace(/\\/g, "/");
578394
+ const repoPath = luxttsRepoDir2().replace(/\\/g, "/");
577945
578395
  await this.asyncShell(
577946
578396
  `${quotedPy} -c "import sys; sys.path.insert(0, '${repoPath}'); from zipvoice.luxvoice import LuxTTS; print('ok')"`,
577947
578397
  3e4
@@ -578055,7 +578505,7 @@ Error: ${err2 instanceof Error ? err2.message : String(err2)}`
578055
578505
  }
578056
578506
  }
578057
578507
  }
578058
- const repoDir = luxttsRepoDir();
578508
+ const repoDir = luxttsRepoDir2();
578059
578509
  if (!existsSync95(join109(repoDir, "zipvoice", "luxvoice.py"))) {
578060
578510
  renderInfo(" Cloning LuxTTS repository...");
578061
578511
  try {
@@ -578479,18 +578929,18 @@ def main():
578479
578929
  if __name__ == '__main__':
578480
578930
  main()
578481
578931
  `;
578482
- const scriptPath2 = luxttsInferScript();
578932
+ const scriptPath2 = luxttsInferScript2();
578483
578933
  mkdirSync52(voiceDir2(), { recursive: true });
578484
578934
  writeFileSync49(scriptPath2, script);
578485
578935
  }
578486
578936
  /** Ensure the LuxTTS daemon is running, spawn if needed */
578487
578937
  async ensureLuxttsDaemon() {
578488
578938
  if (this._luxttsDaemon && !this._luxttsDaemon.killed) return true;
578489
- const venvPy = luxttsVenvPy();
578939
+ const venvPy = luxttsVenvPy2();
578490
578940
  if (!existsSync95(venvPy)) return false;
578491
578941
  return new Promise((resolve48) => {
578492
- const env2 = { ...process.env, LUXTTS_REPO_PATH: luxttsRepoDir() };
578493
- const daemon = nodeSpawn(venvPy, [luxttsInferScript()], {
578942
+ const env2 = { ...process.env, LUXTTS_REPO_PATH: luxttsRepoDir2() };
578943
+ const daemon = nodeSpawn(venvPy, [luxttsInferScript2()], {
578494
578944
  stdio: ["pipe", "pipe", "pipe"],
578495
578945
  cwd: tmpdir20(),
578496
578946
  env: env2
@@ -579688,11 +580138,11 @@ async function handleSlashCommand(input, ctx3) {
579688
580138
  }
579689
580139
  if (action === "new") {
579690
580140
  try {
579691
- const { randomBytes: randomBytes25 } = await import("node:crypto");
580141
+ const { randomBytes: randomBytes26 } = await import("node:crypto");
579692
580142
  const { homedir: homedir48 } = await import("node:os");
579693
580143
  const { mkdirSync: mkdirSync76, writeFileSync: writeFileSync71 } = await import("node:fs");
579694
580144
  const { join: join142 } = await import("node:path");
579695
- const newKey = randomBytes25(16).toString("hex");
580145
+ const newKey = randomBytes26(16).toString("hex");
579696
580146
  process.env["OMNIUS_API_KEY"] = newKey;
579697
580147
  const dir = join142(homedir48(), ".omnius");
579698
580148
  mkdirSync76(dir, { recursive: true });
@@ -579949,8 +580399,8 @@ async function handleSlashCommand(input, ctx3) {
579949
580399
  }
579950
580400
  process.env["OMNIUS_ACCESS"] = val2;
579951
580401
  if (val2 === "any" && !process.env["OMNIUS_API_KEY"]) {
579952
- const { randomBytes: randomBytes25 } = await import("node:crypto");
579953
- const apiKey = randomBytes25(16).toString("hex");
580402
+ const { randomBytes: randomBytes26 } = await import("node:crypto");
580403
+ const apiKey = randomBytes26(16).toString("hex");
579954
580404
  process.env["OMNIUS_API_KEY"] = apiKey;
579955
580405
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
579956
580406
  renderInfo(
@@ -580069,8 +580519,8 @@ async function handleSlashCommand(input, ctx3) {
580069
580519
  }
580070
580520
  process.env["OMNIUS_ACCESS"] = val;
580071
580521
  if (val === "any" && !process.env["OMNIUS_API_KEY"]) {
580072
- const { randomBytes: randomBytes25 } = await import("node:crypto");
580073
- const apiKey = randomBytes25(16).toString("hex");
580522
+ const { randomBytes: randomBytes26 } = await import("node:crypto");
580523
+ const apiKey = randomBytes26(16).toString("hex");
580074
580524
  process.env["OMNIUS_API_KEY"] = apiKey;
580075
580525
  renderInfo(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
580076
580526
  renderInfo(
@@ -590377,11 +590827,11 @@ var init_commands = __esm({
590377
590827
  process.env["OMNIUS_ACCESS"] = val;
590378
590828
  if (val === "any" && !process.env["OMNIUS_API_KEY"]) {
590379
590829
  try {
590380
- const { randomBytes: randomBytes25 } = await import("node:crypto");
590830
+ const { randomBytes: randomBytes26 } = await import("node:crypto");
590381
590831
  const { homedir: homedir48 } = await import("node:os");
590382
590832
  const { mkdirSync: mkdirSync76, writeFileSync: writeFileSync71 } = await import("node:fs");
590383
590833
  const { join: join142 } = await import("node:path");
590384
- const apiKey = randomBytes25(16).toString("hex");
590834
+ const apiKey = randomBytes26(16).toString("hex");
590385
590835
  process.env["OMNIUS_API_KEY"] = apiKey;
590386
590836
  const dir = join142(homedir48(), ".omnius");
590387
590837
  mkdirSync76(dir, { recursive: true });
@@ -596404,11 +596854,13 @@ var init_tool_policy = __esm({
596404
596854
  });
596405
596855
 
596406
596856
  // packages/cli/src/tui/telegram-creative-tools.ts
596857
+ import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes21 } from "node:crypto";
596407
596858
  import {
596408
596859
  existsSync as existsSync104,
596409
596860
  mkdirSync as mkdirSync59,
596410
596861
  readFileSync as readFileSync85,
596411
596862
  statSync as statSync35,
596863
+ unlinkSync as unlinkSync19,
596412
596864
  writeFileSync as writeFileSync55
596413
596865
  } from "node:fs";
596414
596866
  import { mkdir as mkdir17 } from "node:fs/promises";
@@ -596435,6 +596887,7 @@ function formatTelegramCreativeWorkspacePrompt(root) {
596435
596887
  `Workspace root: ${root}`,
596436
596888
  "Creative file tools are scoped to that folder only.",
596437
596889
  "Allowed: create and send non-executable creative artifacts in this workspace.",
596890
+ "At rest, artifacts are stored as random internal blobs with random header bytes; requested filenames are logical names restored only during Telegram upload.",
596438
596891
  "Forbidden: delete files, create scripts/executables, access paths outside this workspace, mutate the project tree, run shell/Python/code commands, or touch system state.",
596439
596892
  "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."
596440
596893
  ].join("\n");
@@ -596461,7 +596914,7 @@ function collectGeneratedArtifactPathsFromText(text, root) {
596461
596914
  const value2 = match[1];
596462
596915
  if (!value2) continue;
596463
596916
  const guarded = guardPath(rootAbs, value2);
596464
- if (guarded.ok && existsSync104(guarded.path.abs) && safeStatFile(guarded.path.abs)) {
596917
+ if (guarded.ok && (isManifestArtifact(rootAbs, guarded.path.rel) || existsSync104(guarded.path.abs) && safeStatFile(guarded.path.abs))) {
596465
596918
  paths.add(guarded.path.abs);
596466
596919
  }
596467
596920
  }
@@ -596470,7 +596923,7 @@ function collectGeneratedArtifactPathsFromText(text, root) {
596470
596923
  const value2 = marker?.[1]?.trim().replace(/^["']|["']$/g, "");
596471
596924
  if (!value2) continue;
596472
596925
  const guarded = guardPath(rootAbs, value2);
596473
- if (guarded.ok && existsSync104(guarded.path.abs) && safeStatFile(guarded.path.abs)) {
596926
+ if (guarded.ok && (isManifestArtifact(rootAbs, guarded.path.rel) || existsSync104(guarded.path.abs) && safeStatFile(guarded.path.abs))) {
596474
596927
  paths.add(guarded.path.abs);
596475
596928
  }
596476
596929
  }
@@ -596480,11 +596933,7 @@ function buildTelegramCreativeTools(repoRoot, chatId, backendUrl, imageDefaults
596480
596933
  const root = telegramCreativeWorkspaceRoot(repoRoot, chatId);
596481
596934
  ensureManifest(root);
596482
596935
  return [
596483
- scopedTool(new FileReadTool(root), root, "read"),
596484
- scopedTool(new ListDirectoryTool(root), root, "list"),
596485
596936
  scopedTool(new FileWriteTool(root), root, "create"),
596486
- scopedTool(new FileEditTool(root), root, "edit"),
596487
- scopedTool(new FilePatchTool(root), root, "edit"),
596488
596937
  scopedTool(new StructuredFileTool(root), root, "create"),
596489
596938
  scopedTool(new ImageGenerateTool(root, backendUrl, imageDefaults), root, "generate"),
596490
596939
  scopedTool(new AudioGenerateTool(root, audioDefaults), root, "generate"),
@@ -596501,6 +596950,7 @@ function scopedTool(base3, root, mode) {
596501
596950
  async execute(args) {
596502
596951
  const next = { ...args };
596503
596952
  if (base3.name === "generate_image" || base3.name === "generate_audio" || base3.name === "generate_tts") {
596953
+ const cleanup = [];
596504
596954
  const localModel = typeof next["model_path"] === "string" ? String(next["model_path"]) : typeof next["model"] === "string" && looksLikeLocalPath(String(next["model"])) ? String(next["model"]) : "";
596505
596955
  if (localModel) {
596506
596956
  const guarded = guardPath(rootAbs, localModel);
@@ -596509,6 +596959,22 @@ function scopedTool(base3, root, mode) {
596509
596959
  else next["model"] = guarded.path.abs;
596510
596960
  }
596511
596961
  if (base3.name === "generate_tts") {
596962
+ for (const key of TTS_CLONE_SOURCE_KEYS) {
596963
+ const value2 = next[key];
596964
+ if (typeof value2 !== "string" || !value2.trim()) continue;
596965
+ const materialized = materializeTelegramCreativeArtifactForSend(rootAbs, value2.trim());
596966
+ if (!materialized.ok) return denied(materialized.error);
596967
+ next[key] = materialized.path;
596968
+ if (materialized.cleanup) cleanup.push(materialized.cleanup);
596969
+ }
596970
+ for (const key of ["clone_ref", "voice"]) {
596971
+ const value2 = next[key];
596972
+ if (typeof value2 !== "string" || !value2.trim() || !looksLikeAudioPath(value2.trim())) continue;
596973
+ const materialized = materializeTelegramCreativeArtifactForSend(rootAbs, value2.trim());
596974
+ if (!materialized.ok) return denied(materialized.error);
596975
+ next[key] = materialized.path;
596976
+ if (materialized.cleanup) cleanup.push(materialized.cleanup);
596977
+ }
596512
596978
  const rawOutput = typeof next["output"] === "string" && String(next["output"]).trim() ? String(next["output"]) : typeof next["output_path"] === "string" && String(next["output_path"]).trim() ? String(next["output_path"]) : `tts-${Date.now()}.wav`;
596513
596979
  const guardedOutput = guardPath(rootAbs, rawOutput);
596514
596980
  if (!guardedOutput.ok) return denied(guardedOutput.error);
@@ -596518,16 +596984,20 @@ function scopedTool(base3, root, mode) {
596518
596984
  next["output"] = guardedOutput.path.abs;
596519
596985
  next["playback"] = false;
596520
596986
  }
596521
- const result2 = await base3.execute(next);
596522
- if (result2.success) {
596523
- if (base3.name === "generate_tts" && typeof next["output"] === "string") {
596524
- rememberCreated(rootAbs, String(next["output"]));
596525
- }
596526
- for (const path11 of collectGeneratedArtifactPathsFromText(result2.output, rootAbs)) {
596527
- rememberCreated(rootAbs, path11);
596987
+ try {
596988
+ const result2 = await base3.execute(next);
596989
+ if (result2.success) {
596990
+ if (base3.name === "generate_tts" && typeof next["output"] === "string") {
596991
+ rememberCreated(rootAbs, String(next["output"]));
596992
+ }
596993
+ for (const path11 of collectGeneratedArtifactPathsFromText(result2.output, rootAbs)) {
596994
+ rememberCreated(rootAbs, path11);
596995
+ }
596528
596996
  }
596997
+ return result2;
596998
+ } finally {
596999
+ for (const fn of cleanup) fn();
596529
597000
  }
596530
- return result2;
596531
597001
  }
596532
597002
  const pathKey = PATH_KEYS.find((key) => typeof next[key] === "string" && String(next[key]).trim());
596533
597003
  if (pathKey) {
@@ -596592,6 +597062,9 @@ function isInside(root, path11) {
596592
597062
  function looksLikeLocalPath(value2) {
596593
597063
  return value2.startsWith("/") || value2.startsWith("./") || value2.startsWith("../");
596594
597064
  }
597065
+ function looksLikeAudioPath(value2) {
597066
+ return looksLikeLocalPath(value2) || value2.startsWith("~/") || /\.(wav|mp3|flac|ogg|m4a)$/i.test(value2);
597067
+ }
596595
597068
  function manifestPath(root) {
596596
597069
  return join119(root, MANIFEST_FILE);
596597
597070
  }
@@ -596606,12 +597079,21 @@ function readManifest(root) {
596606
597079
  ensureManifest(root);
596607
597080
  try {
596608
597081
  const parsed = JSON.parse(readFileSync85(manifestPath(root), "utf8"));
597082
+ const objects = parsed.objects && typeof parsed.objects === "object" ? Object.fromEntries(
597083
+ Object.entries(parsed.objects).filter((entry) => {
597084
+ const value2 = entry[1];
597085
+ return Boolean(
597086
+ value2 && typeof value2.logicalRel === "string" && typeof value2.storedRel === "string" && typeof value2.originalName === "string" && typeof value2.prefixBytes === "number" && typeof value2.size === "number"
597087
+ );
597088
+ })
597089
+ ) : {};
596609
597090
  return {
596610
597091
  files: Array.isArray(parsed.files) ? parsed.files.filter((file) => typeof file === "string") : [],
597092
+ objects,
596611
597093
  updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString()
596612
597094
  };
596613
597095
  } catch {
596614
- return { files: [], updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
597096
+ return { files: [], objects: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
596615
597097
  }
596616
597098
  }
596617
597099
  function writeManifest(root, manifest) {
@@ -596620,18 +597102,114 @@ function writeManifest(root, manifest) {
596620
597102
  }
596621
597103
  function manifestHas(root, relPath) {
596622
597104
  const rel = relPath.replace(/\\/g, "/");
596623
- return readManifest(root).files.includes(rel);
597105
+ const manifest = readManifest(root);
597106
+ return manifest.files.includes(rel) || Boolean(manifest.objects?.[rel]);
596624
597107
  }
596625
597108
  function rememberCreated(root, absPath) {
596626
597109
  const guarded = guardPath(root, absPath);
596627
597110
  if (!guarded.ok || guarded.path.rel === ".") return;
596628
597111
  const manifest = readManifest(root);
596629
597112
  const rel = guarded.path.rel.replace(/\\/g, "/");
597113
+ if (publicCreativeArtifactPolicyError(guarded.path.abs)) return;
597114
+ if (existsSync104(guarded.path.abs) && safeStatFile(guarded.path.abs)) {
597115
+ const previous = manifest.objects?.[rel];
597116
+ if (previous) {
597117
+ const previousPath = resolve38(root, previous.storedRel);
597118
+ if (isInside(resolve38(root), previousPath) && existsSync104(previousPath)) {
597119
+ try {
597120
+ unlinkSync19(previousPath);
597121
+ } catch {
597122
+ }
597123
+ }
597124
+ }
597125
+ mkdirSync59(join119(root, OBJECTS_DIR), { recursive: true });
597126
+ const data = readFileSync85(guarded.path.abs);
597127
+ const prefix = randomBytes21(48);
597128
+ const key = randomBytes21(32);
597129
+ const iv = randomBytes21(12);
597130
+ const cipher = createCipheriv4("aes-256-gcm", key, iv);
597131
+ const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
597132
+ const tag = cipher.getAuthTag();
597133
+ const storedRel = join119(OBJECTS_DIR, `${Date.now()}-${randomBytes21(12).toString("hex")}.blob`).replace(/\\/g, "/");
597134
+ const storedAbs = join119(root, storedRel);
597135
+ writeFileSync55(storedAbs, Buffer.concat([prefix, encrypted]));
597136
+ try {
597137
+ unlinkSync19(guarded.path.abs);
597138
+ } catch {
597139
+ }
597140
+ manifest.objects = manifest.objects || {};
597141
+ manifest.objects[rel] = {
597142
+ logicalRel: rel,
597143
+ storedRel,
597144
+ originalName: basename22(guarded.path.abs),
597145
+ prefixBytes: prefix.length,
597146
+ encrypted: true,
597147
+ key: key.toString("base64"),
597148
+ iv: iv.toString("base64"),
597149
+ tag: tag.toString("base64"),
597150
+ size: data.length,
597151
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
597152
+ };
597153
+ }
596630
597154
  if (!manifest.files.includes(rel)) manifest.files.push(rel);
596631
597155
  manifest.files.sort();
596632
597156
  manifest.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
596633
597157
  writeManifest(root, manifest);
596634
597158
  }
597159
+ function isManifestArtifact(root, relPath) {
597160
+ return manifestHas(root, relPath);
597161
+ }
597162
+ function materializeTelegramCreativeArtifactForSend(root, rawPath) {
597163
+ const rootAbs = resolve38(root);
597164
+ const guarded = guardPath(rootAbs, rawPath);
597165
+ if (!guarded.ok) return { ok: false, error: guarded.error };
597166
+ const policyError = publicCreativeArtifactPolicyError(guarded.path.abs);
597167
+ if (policyError) return { ok: false, error: policyError };
597168
+ const manifest = readManifest(rootAbs);
597169
+ const rel = guarded.path.rel.replace(/\\/g, "/");
597170
+ const object = manifest.objects?.[rel];
597171
+ if (object) {
597172
+ const storedAbs = resolve38(rootAbs, object.storedRel);
597173
+ if (!isInside(rootAbs, storedAbs) || !existsSync104(storedAbs) || !safeStatFile(storedAbs)) {
597174
+ return { ok: false, error: `Scoped artifact storage is missing for ${rel}.` };
597175
+ }
597176
+ const blob = readFileSync85(storedAbs);
597177
+ if (blob.length < object.prefixBytes) {
597178
+ return { ok: false, error: `Scoped artifact storage is corrupt for ${rel}.` };
597179
+ }
597180
+ let payload = blob.subarray(object.prefixBytes);
597181
+ if (object.encrypted) {
597182
+ if (!object.key || !object.iv || !object.tag) {
597183
+ return { ok: false, error: `Scoped artifact encryption metadata is missing for ${rel}.` };
597184
+ }
597185
+ const decipher = createDecipheriv4(
597186
+ "aes-256-gcm",
597187
+ Buffer.from(object.key, "base64"),
597188
+ Buffer.from(object.iv, "base64")
597189
+ );
597190
+ decipher.setAuthTag(Buffer.from(object.tag, "base64"));
597191
+ payload = Buffer.concat([decipher.update(payload), decipher.final()]);
597192
+ }
597193
+ const stageDir = join119(rootAbs, SEND_DIR, `${Date.now()}-${randomBytes21(8).toString("hex")}`);
597194
+ mkdirSync59(stageDir, { recursive: true });
597195
+ const staged = join119(stageDir, object.originalName || basename22(rel));
597196
+ writeFileSync55(staged, payload);
597197
+ return {
597198
+ ok: true,
597199
+ path: staged,
597200
+ cleanup: () => {
597201
+ try {
597202
+ unlinkSync19(staged);
597203
+ } catch {
597204
+ }
597205
+ }
597206
+ };
597207
+ }
597208
+ if (existsSync104(guarded.path.abs) && safeStatFile(guarded.path.abs)) {
597209
+ return { ok: true, path: guarded.path.abs };
597210
+ }
597211
+ return { ok: false, error: `Scoped artifact not found in manifest: ${rawPath}` };
597212
+ }
596635
597213
  function safeStatFile(path11) {
596636
597214
  try {
596637
597215
  return statSync35(path11).isFile();
@@ -596649,14 +597227,16 @@ function denied(error) {
596649
597227
  mutatedFiles: []
596650
597228
  };
596651
597229
  }
596652
- var MANIFEST_FILE, PATH_KEYS, MEDIA_PATH_RE, PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS, CreativeAudioFileTool;
597230
+ var MANIFEST_FILE, OBJECTS_DIR, SEND_DIR, PATH_KEYS, TTS_CLONE_SOURCE_KEYS, MEDIA_PATH_RE, PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS, CreativeAudioFileTool;
596653
597231
  var init_telegram_creative_tools = __esm({
596654
597232
  "packages/cli/src/tui/telegram-creative-tools.ts"() {
596655
597233
  "use strict";
596656
- init_typed_node_events();
596657
597234
  init_dist5();
596658
597235
  MANIFEST_FILE = ".omnius-creative-manifest.json";
597236
+ OBJECTS_DIR = ".objects";
597237
+ SEND_DIR = ".send";
596659
597238
  PATH_KEYS = ["path", "file", "file_path", "filename", "filepath", "filePath"];
597239
+ TTS_CLONE_SOURCE_KEYS = ["sample", "source_audio", "voice_sample", "reference_audio", "ref_audio", "clone_sample"];
596660
597240
  MEDIA_PATH_RE = /(?:^|[\s([])(\/[^\s<>"')\]]+\.[A-Za-z0-9]{1,12})(?:$|[\s),.\]])/g;
596661
597241
  PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS = /* @__PURE__ */ new Set([
596662
597242
  ".sh",
@@ -596731,9 +597311,16 @@ var init_telegram_creative_tools = __esm({
596731
597311
  input: { type: "string", description: "Alias for text" },
596732
597312
  prompt: { type: "string", description: "Alias for text" },
596733
597313
  path: { type: "string", description: "Output .wav path inside the creative workspace" },
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" },
597314
+ backend: { type: "string", enum: ["auto", "luxtts", "supertonic", "mlx", "onnx", "piper"], description: "TTS backend. Defaults to auto." },
597315
+ voice: { type: "string", description: "Voice id/name for the selected TTS backend, or a scoped source audio path for cloning" },
596736
597316
  clone_ref: { type: "string", description: "Optional LuxTTS clone reference" },
597317
+ sample: { type: "string", description: "Voice source clip path inside the creative workspace" },
597318
+ source_audio: { type: "string", description: "Alias for sample" },
597319
+ voice_sample: { type: "string", description: "Alias for sample" },
597320
+ reference_audio: { type: "string", description: "Alias for sample" },
597321
+ ref_audio: { type: "string", description: "Alias for sample" },
597322
+ clone_sample: { type: "string", description: "Alias for sample" },
597323
+ clone_name: { type: "string", description: "Optional name to register the source clip for later reuse" },
596737
597324
  model: { type: "string", description: "Optional backend model id or raw Piper/ONNX path" },
596738
597325
  speed: { type: "number", description: "Speech speed multiplier or backend-specific rate" }
596739
597326
  },
@@ -596752,26 +597339,57 @@ var init_telegram_creative_tools = __esm({
596752
597339
  if (!guarded.path.abs.toLowerCase().endsWith(".wav")) {
596753
597340
  return denied("create_audio_file currently writes WAV files; use a .wav output path.");
596754
597341
  }
596755
- await mkdir17(dirname33(guarded.path.abs), { recursive: true });
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)) {
596768
- return {
596769
- success: false,
596770
- output: "",
596771
- error: `Audio synthesis failed through generate_tts.
597342
+ const cloneArgs = {};
597343
+ const cleanup = [];
597344
+ for (const key of TTS_CLONE_SOURCE_KEYS) {
597345
+ const value2 = args[key];
597346
+ if (typeof value2 !== "string" || !value2.trim()) continue;
597347
+ const materialized = materializeTelegramCreativeArtifactForSend(this.root, value2.trim());
597348
+ if (!materialized.ok) return denied(materialized.error);
597349
+ cloneArgs[key] = materialized.path;
597350
+ if (materialized.cleanup) cleanup.push(materialized.cleanup);
597351
+ }
597352
+ for (const key of ["clone_ref", "voice"]) {
597353
+ const value2 = args[key];
597354
+ if (typeof value2 !== "string" || !value2.trim() || !looksLikeAudioPath(value2.trim())) continue;
597355
+ const materialized = materializeTelegramCreativeArtifactForSend(this.root, value2.trim());
597356
+ if (!materialized.ok) return denied(materialized.error);
597357
+ cloneArgs[key] = materialized.path;
597358
+ if (materialized.cleanup) cleanup.push(materialized.cleanup);
597359
+ }
597360
+ let result;
597361
+ try {
597362
+ await mkdir17(dirname33(guarded.path.abs), { recursive: true });
597363
+ const tts = new TtsGenerateTool();
597364
+ result = await tts.execute({
597365
+ text,
597366
+ output: guarded.path.abs,
597367
+ playback: false,
597368
+ backend: args["backend"],
597369
+ voice: cloneArgs["voice"] ?? args["voice"],
597370
+ clone_ref: cloneArgs["clone_ref"] ?? args["clone_ref"],
597371
+ ...cloneArgs,
597372
+ sample: cloneArgs["sample"],
597373
+ source_audio: cloneArgs["source_audio"],
597374
+ voice_sample: cloneArgs["voice_sample"],
597375
+ reference_audio: cloneArgs["reference_audio"],
597376
+ ref_audio: cloneArgs["ref_audio"],
597377
+ clone_sample: cloneArgs["clone_sample"],
597378
+ clone_name: args["clone_name"],
597379
+ model: args["model"],
597380
+ speed: args["speed"]
597381
+ });
597382
+ if (!result.success || !existsSync104(guarded.path.abs)) {
597383
+ return {
597384
+ success: false,
597385
+ output: "",
597386
+ error: `Audio synthesis failed through generate_tts.
596772
597387
  ${(result.error || result.output || "").slice(0, 1200)}`,
596773
- durationMs: performance.now() - start2
596774
- };
597388
+ durationMs: performance.now() - start2
597389
+ };
597390
+ }
597391
+ } finally {
597392
+ for (const fn of cleanup) fn();
596775
597393
  }
596776
597394
  rememberCreated(this.root, guarded.path.abs);
596777
597395
  const sizeKB = Math.round(statSync35(guarded.path.abs).size / 1024);
@@ -596800,7 +597418,7 @@ __export(vision_ingress_exports, {
596800
597418
  runVisionIngress: () => runVisionIngress
596801
597419
  });
596802
597420
  import { execFileSync as execFileSync4 } from "node:child_process";
596803
- import { existsSync as existsSync105, readFileSync as readFileSync86, unlinkSync as unlinkSync19 } from "node:fs";
597421
+ import { existsSync as existsSync105, readFileSync as readFileSync86, unlinkSync as unlinkSync20 } from "node:fs";
596804
597422
  import { join as join120 } from "node:path";
596805
597423
  function isTesseractAvailable() {
596806
597424
  try {
@@ -596858,7 +597476,7 @@ function advancedOcr(imagePath) {
596858
597476
  const text = readFileSync86(txtFile, "utf-8").trim();
596859
597477
  if (text.length > 0) results.push(text);
596860
597478
  try {
596861
- unlinkSync19(txtFile);
597479
+ unlinkSync20(txtFile);
596862
597480
  } catch {
596863
597481
  }
596864
597482
  }
@@ -596943,7 +597561,7 @@ var init_vision_ingress = __esm({
596943
597561
  });
596944
597562
 
596945
597563
  // packages/cli/src/tui/telegram-bridge.ts
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";
597564
+ import { mkdirSync as mkdirSync60, existsSync as existsSync106, unlinkSync as unlinkSync21, readdirSync as readdirSync36, statSync as statSync36, readFileSync as readFileSync87, writeFileSync as writeFileSync57 } from "node:fs";
596947
597565
  import { join as join121, resolve as resolve39, basename as basename23, relative as relative13, isAbsolute as isAbsolute7 } from "node:path";
596948
597566
  import { writeFile as writeFileAsync } from "node:fs/promises";
596949
597567
  import { createHash as createHash19, randomInt } from "node:crypto";
@@ -600325,6 +600943,8 @@ Scoped workspace: ${scopedRoot}`,
600325
600943
  error: `Telegram file upload failed: ${err instanceof Error ? err.message : String(err)}`,
600326
600944
  durationMs: performance.now() - start2
600327
600945
  };
600946
+ } finally {
600947
+ file.cleanup?.();
600328
600948
  }
600329
600949
  }
600330
600950
  };
@@ -600378,14 +600998,12 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
600378
600998
  resolveTelegramFilePath(rawPath, repoRoot, scopedRoot) {
600379
600999
  const base3 = scopedRoot ? resolve39(scopedRoot) : resolve39(repoRoot);
600380
601000
  const trimmed = rawPath.trim().replace(/^["']|["']$/g, "");
600381
- const abs = isAbsolute7(trimmed) ? resolve39(trimmed) : resolve39(base3, trimmed);
600382
- if (scopedRoot && !isPathInside(base3, abs)) {
600383
- return { ok: false, error: `Public/group telegram_send_file can only send files inside ${base3}.` };
600384
- }
600385
601001
  if (scopedRoot) {
600386
- const policyError = publicCreativeArtifactPolicyError(abs);
600387
- if (policyError) return { ok: false, error: policyError };
601002
+ const materialized = materializeTelegramCreativeArtifactForSend(base3, trimmed);
601003
+ if (!materialized.ok) return materialized;
601004
+ return materialized;
600388
601005
  }
601006
+ const abs = isAbsolute7(trimmed) ? resolve39(trimmed) : resolve39(base3, trimmed);
600389
601007
  if (!existsSync106(abs)) return { ok: false, error: `File does not exist: ${trimmed}` };
600390
601008
  if (!statSync36(abs).isFile()) return { ok: false, error: `Path is not a file: ${trimmed}` };
600391
601009
  return { ok: true, path: abs };
@@ -600546,7 +601164,7 @@ ${visionContext}]`;
600546
601164
  for (const [key, entry] of this.mediaCache) {
600547
601165
  if (now - entry.cachedAt > MEDIA_CACHE_TTL_MS) {
600548
601166
  try {
600549
- unlinkSync20(entry.localPath);
601167
+ unlinkSync21(entry.localPath);
600550
601168
  } catch {
600551
601169
  }
600552
601170
  this.mediaCache.delete(key);
@@ -600713,15 +601331,22 @@ Content-Type: ${contentType}\r
600713
601331
  const abs = resolve39(path11);
600714
601332
  if (!isPathInside(rootAbs, abs)) continue;
600715
601333
  if (!includeMentioned && alreadySentByText.has(abs)) continue;
600716
- if (!existsSync106(abs) || !statSync36(abs).isFile()) continue;
600717
- const kind = classifyMedia(abs) ?? "document";
601334
+ const materialized = materializeTelegramCreativeArtifactForSend(rootAbs, abs);
601335
+ if (!materialized.ok) continue;
601336
+ if (!existsSync106(materialized.path) || !statSync36(materialized.path).isFile()) {
601337
+ materialized.cleanup?.();
601338
+ continue;
601339
+ }
601340
+ const kind = classifyMedia(materialized.path) ?? "document";
600718
601341
  await this.sendMediaReference(msg.chatId, {
600719
- original: abs,
600720
- value: abs,
601342
+ original: materialized.path,
601343
+ value: materialized.path,
600721
601344
  kind,
600722
601345
  source: "file",
600723
601346
  audioAsVoice: kind === "voice"
600724
- }).catch(() => null);
601347
+ }).catch(() => null).finally(() => {
601348
+ materialized.cleanup?.();
601349
+ });
600725
601350
  }
600726
601351
  }
600727
601352
  async uploadTelegramFiles(method, fields, files) {
@@ -601163,7 +601788,7 @@ import {
601163
601788
  writeFileSync as writeFileSync58,
601164
601789
  renameSync as renameSync5,
601165
601790
  mkdirSync as mkdirSync61,
601166
- unlinkSync as unlinkSync21
601791
+ unlinkSync as unlinkSync22
601167
601792
  } from "node:fs";
601168
601793
  import { join as join122 } from "node:path";
601169
601794
  import { homedir as homedir36 } from "node:os";
@@ -601201,7 +601826,7 @@ function persistInFlight(j) {
601201
601826
  function deleteInFlightFile(id) {
601202
601827
  try {
601203
601828
  const p2 = inFlightPath(id);
601204
- if (existsSync107(p2)) unlinkSync21(p2);
601829
+ if (existsSync107(p2)) unlinkSync22(p2);
601205
601830
  } catch {
601206
601831
  }
601207
601832
  }
@@ -601422,7 +602047,7 @@ function drainCheckins(sessionId) {
601422
602047
  try {
601423
602048
  const raw = readFileSync88(fp, "utf-8");
601424
602049
  try {
601425
- unlinkSync21(fp);
602050
+ unlinkSync22(fp);
601426
602051
  } catch {
601427
602052
  }
601428
602053
  if (!raw.trim()) return [];
@@ -601460,7 +602085,7 @@ function listSessions2() {
601460
602085
  function deleteSession2(id) {
601461
602086
  try {
601462
602087
  const p2 = sessionPath(id);
601463
- if (existsSync107(p2)) unlinkSync21(p2);
602088
+ if (existsSync107(p2)) unlinkSync22(p2);
601464
602089
  } catch {
601465
602090
  }
601466
602091
  deleteInFlightFile(id);
@@ -602396,7 +603021,7 @@ var init_access_policy = __esm({
602396
603021
 
602397
603022
  // packages/cli/src/api/project-preferences.ts
602398
603023
  import { createHash as createHash20 } from "node:crypto";
602399
- import { existsSync as existsSync109, mkdirSync as mkdirSync63, readFileSync as readFileSync90, renameSync as renameSync7, writeFileSync as writeFileSync60, unlinkSync as unlinkSync22 } from "node:fs";
603024
+ import { existsSync as existsSync109, mkdirSync as mkdirSync63, readFileSync as readFileSync90, renameSync as renameSync7, writeFileSync as writeFileSync60, unlinkSync as unlinkSync23 } from "node:fs";
602400
603025
  import { homedir as homedir38 } from "node:os";
602401
603026
  import { join as join124, resolve as resolve41 } from "node:path";
602402
603027
  import { randomUUID as randomUUID15 } from "node:crypto";
@@ -602457,7 +603082,7 @@ function writeProjectPreferences(root, partial) {
602457
603082
  } catch {
602458
603083
  }
602459
603084
  try {
602460
- unlinkSync22(tmp);
603085
+ unlinkSync23(tmp);
602461
603086
  } catch {
602462
603087
  }
602463
603088
  throw err;
@@ -602468,7 +603093,7 @@ function deleteProjectPreferences(root) {
602468
603093
  try {
602469
603094
  const file = prefsPath(root);
602470
603095
  if (!existsSync109(file)) return false;
602471
- unlinkSync22(file);
603096
+ unlinkSync23(file);
602472
603097
  return true;
602473
603098
  } catch {
602474
603099
  return false;
@@ -604515,7 +605140,7 @@ __export(runtime_keys_exports, {
604515
605140
  import { existsSync as existsSync113, readFileSync as readFileSync93, writeFileSync as writeFileSync61, mkdirSync as mkdirSync66, chmodSync } from "node:fs";
604516
605141
  import { join as join127 } from "node:path";
604517
605142
  import { homedir as homedir40 } from "node:os";
604518
- import { randomBytes as randomBytes21 } from "node:crypto";
605143
+ import { randomBytes as randomBytes22 } from "node:crypto";
604519
605144
  function ensureDir2() {
604520
605145
  const dir = join127(homedir40(), ".omnius");
604521
605146
  if (!existsSync113(dir)) mkdirSync66(dir, { recursive: true });
@@ -604541,7 +605166,7 @@ function persistAll(records) {
604541
605166
  }
604542
605167
  function mintKey(args) {
604543
605168
  const records = loadAll();
604544
- const key = `omnius_${randomBytes21(32).toString("hex")}`;
605169
+ const key = `omnius_${randomBytes22(32).toString("hex")}`;
604545
605170
  const record = {
604546
605171
  key,
604547
605172
  scope: args.scope,
@@ -607849,11 +608474,11 @@ async function handleAimsIncidentPost(ctx3) {
607849
608474
  }));
607850
608475
  return true;
607851
608476
  }
607852
- const { randomBytes: randomBytes25 } = await import("node:crypto");
608477
+ const { randomBytes: randomBytes26 } = await import("node:crypto");
607853
608478
  const record = await withAimsLock("incidents.json", () => {
607854
608479
  const existing = readAimsFile("incidents.json", []);
607855
608480
  const rec = {
607856
- id: `INC-${Date.now()}-${randomBytes25(4).toString("hex")}`,
608481
+ id: `INC-${Date.now()}-${randomBytes26(4).toString("hex")}`,
607857
608482
  ts: (/* @__PURE__ */ new Date()).toISOString(),
607858
608483
  raised_by: user ?? "anonymous",
607859
608484
  status: "open",
@@ -617014,10 +617639,10 @@ var init_usage_tracker = __esm({
617014
617639
  });
617015
617640
 
617016
617641
  // packages/cli/src/api/profiles.ts
617017
- import { existsSync as existsSync118, readFileSync as readFileSync97, writeFileSync as writeFileSync64, mkdirSync as mkdirSync69, readdirSync as readdirSync40, unlinkSync as unlinkSync23 } from "node:fs";
617642
+ import { existsSync as existsSync118, readFileSync as readFileSync97, writeFileSync as writeFileSync64, mkdirSync as mkdirSync69, readdirSync as readdirSync40, unlinkSync as unlinkSync24 } from "node:fs";
617018
617643
  import { join as join132 } from "node:path";
617019
617644
  import { homedir as homedir43 } from "node:os";
617020
- import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes22, scryptSync as scryptSync3 } from "node:crypto";
617645
+ import { createCipheriv as createCipheriv5, createDecipheriv as createDecipheriv5, randomBytes as randomBytes23, scryptSync as scryptSync3 } from "node:crypto";
617021
617646
  function globalProfileDir() {
617022
617647
  return join132(homedir43(), ".omnius", "profiles");
617023
617648
  }
@@ -617095,16 +617720,16 @@ function deleteProfile(name10, scope = "global", projectDir2) {
617095
617720
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
617096
617721
  const filePath = join132(dir, `${sanitized}.json`);
617097
617722
  if (existsSync118(filePath)) {
617098
- unlinkSync23(filePath);
617723
+ unlinkSync24(filePath);
617099
617724
  return true;
617100
617725
  }
617101
617726
  return false;
617102
617727
  }
617103
617728
  function encryptProfile(profile, password) {
617104
- const salt = randomBytes22(32);
617729
+ const salt = randomBytes23(32);
617105
617730
  const key = scryptSync3(password, salt, 32);
617106
- const iv = randomBytes22(16);
617107
- const cipher = createCipheriv4("aes-256-gcm", key, iv);
617731
+ const iv = randomBytes23(16);
617732
+ const cipher = createCipheriv5("aes-256-gcm", key, iv);
617108
617733
  const plaintext = JSON.stringify(profile);
617109
617734
  const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
617110
617735
  const tag = cipher.getAuthTag();
@@ -617123,7 +617748,7 @@ function decryptProfile(enc, password) {
617123
617748
  const salt = Buffer.from(enc.salt, "hex");
617124
617749
  const key = scryptSync3(password, salt, 32);
617125
617750
  const iv = Buffer.from(enc.iv, "hex");
617126
- const decipher = createDecipheriv4("aes-256-gcm", key, iv);
617751
+ const decipher = createDecipheriv5("aes-256-gcm", key, iv);
617127
617752
  decipher.setAuthTag(Buffer.from(enc.tag, "hex"));
617128
617753
  const decrypted = Buffer.concat([
617129
617754
  decipher.update(Buffer.from(enc.data, "hex")),
@@ -617755,8 +618380,8 @@ import { fileURLToPath as fileURLToPath17 } from "node:url";
617755
618380
  import { dirname as dirname37, join as join135, resolve as resolve43 } from "node:path";
617756
618381
  import { homedir as homedir45 } from "node:os";
617757
618382
  import { spawn as spawn29, execSync as execSync57 } from "node:child_process";
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";
617759
- import { randomBytes as randomBytes23, randomUUID as randomUUID16 } from "node:crypto";
618383
+ import { mkdirSync as mkdirSync71, writeFileSync as writeFileSync66, readFileSync as readFileSync98, readdirSync as readdirSync41, existsSync as existsSync120, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync25 } from "node:fs";
618384
+ import { randomBytes as randomBytes24, randomUUID as randomUUID16 } from "node:crypto";
617760
618385
  import { createHash as createHash23 } from "node:crypto";
617761
618386
  function getVersion3() {
617762
618387
  try {
@@ -618505,13 +619130,13 @@ function pruneOldJobs() {
618505
619130
  const ts = ageRef ? Date.parse(ageRef) : NaN;
618506
619131
  if (Number.isFinite(ts) && ts < cutoffMs) {
618507
619132
  try {
618508
- unlinkSync24(path11);
619133
+ unlinkSync25(path11);
618509
619134
  } catch {
618510
619135
  }
618511
619136
  const outFile = path11.replace(/\.json$/, ".output");
618512
619137
  if (existsSync120(outFile)) {
618513
619138
  try {
618514
- unlinkSync24(outFile);
619139
+ unlinkSync25(outFile);
618515
619140
  } catch {
618516
619141
  }
618517
619142
  }
@@ -618521,7 +619146,7 @@ function pruneOldJobs() {
618521
619146
  }
618522
619147
  } catch {
618523
619148
  try {
618524
- unlinkSync24(path11);
619149
+ unlinkSync25(path11);
618525
619150
  pruned++;
618526
619151
  } catch {
618527
619152
  }
@@ -618813,7 +619438,7 @@ function atomicJobWrite(dir, id, job) {
618813
619438
  } catch {
618814
619439
  }
618815
619440
  try {
618816
- unlinkSync24(tmpPath);
619441
+ unlinkSync25(tmpPath);
618817
619442
  } catch {
618818
619443
  }
618819
619444
  }
@@ -619378,7 +620003,7 @@ ${messages2[firstSystemIdx].content}`
619378
620003
  messages2.unshift({ role: "system", content: SYSTEM_FACTUAL_FIRST });
619379
620004
  }
619380
620005
  }
619381
- const chatId = `chatcmpl-${randomBytes23(12).toString("hex")}`;
620006
+ const chatId = `chatcmpl-${randomBytes24(12).toString("hex")}`;
619382
620007
  const turnsLog = [];
619383
620008
  for (let turn = 1; turn <= maxTurns; turn++) {
619384
620009
  if (Date.now() > totalDeadline) {
@@ -619643,7 +620268,7 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
619643
620268
  "Cache-Control": "no-cache",
619644
620269
  "Connection": "keep-alive"
619645
620270
  });
619646
- const chatId = `chatcmpl-${randomBytes23(12).toString("hex")}`;
620271
+ const chatId = `chatcmpl-${randomBytes24(12).toString("hex")}`;
619647
620272
  let buffer2 = "";
619648
620273
  ollamaStream(
619649
620274
  targetUrl,
@@ -619685,7 +620310,7 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
619685
620310
  if (ollamaChunk.message.content) delta.content = ollamaChunk.message.content;
619686
620311
  if (ollamaChunk.message.tool_calls) {
619687
620312
  delta.tool_calls = ollamaChunk.message.tool_calls.map((tc, idx) => ({
619688
- id: tc.id || `call_${randomBytes23(8).toString("hex")}`,
620313
+ id: tc.id || `call_${randomBytes24(8).toString("hex")}`,
619689
620314
  type: "function",
619690
620315
  function: {
619691
620316
  name: tc?.function?.name ?? tc?.name ?? "",
@@ -619764,14 +620389,14 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
619764
620389
  if (ollamaResp.eval_count) metrics.totalTokensOut += ollamaResp.eval_count;
619765
620390
  if (ollamaResp.prompt_eval_count) metrics.totalTokensIn += ollamaResp.prompt_eval_count;
619766
620391
  trackTokens("local", ollamaResp.prompt_eval_count ?? 0, ollamaResp.eval_count ?? 0);
619767
- const chatId = `chatcmpl-${randomBytes23(12).toString("hex")}`;
620392
+ const chatId = `chatcmpl-${randomBytes24(12).toString("hex")}`;
619768
620393
  const responseMessage = {
619769
620394
  role: ollamaResp.message?.role ?? "assistant",
619770
620395
  content: ollamaResp.message?.content ?? ""
619771
620396
  };
619772
620397
  if (ollamaResp.message?.tool_calls && ollamaResp.message.tool_calls.length > 0) {
619773
620398
  responseMessage.tool_calls = ollamaResp.message.tool_calls.map((tc, idx) => ({
619774
- id: tc.id || `call_${randomBytes23(8).toString("hex")}`,
620399
+ id: tc.id || `call_${randomBytes24(8).toString("hex")}`,
619775
620400
  type: "function",
619776
620401
  function: {
619777
620402
  name: tc?.function?.name ?? tc?.name ?? "",
@@ -620556,7 +621181,7 @@ async function handleV1Run(req2, res) {
620556
621181
  return;
620557
621182
  }
620558
621183
  }
620559
- const id = `job-${randomBytes23(8).toString("hex")}`;
621184
+ const id = `job-${randomBytes24(8).toString("hex")}`;
620560
621185
  const dir = jobsDir();
620561
621186
  const workingDir = requestBody["working_directory"] || req2.headers["x-working-directory"];
620562
621187
  const isolate = requestBody["isolate"] === true;
@@ -621723,7 +622348,7 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
621723
622348
  return;
621724
622349
  }
621725
622350
  const { tmpdir: tmpdir23 } = await import("node:os");
621726
- const { writeFileSync: writeFileSync71, unlinkSync: unlinkSync25 } = await import("node:fs");
622351
+ const { writeFileSync: writeFileSync71, unlinkSync: unlinkSync26 } = await import("node:fs");
621727
622352
  const { join: pjoin } = await import("node:path");
621728
622353
  const tmpPath = pjoin(tmpdir23(), `omnius-clone-upload-${Date.now()}-${safeName2}`);
621729
622354
  writeFileSync71(tmpPath, buf);
@@ -621738,7 +622363,7 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
621738
622363
  });
621739
622364
  } finally {
621740
622365
  try {
621741
- unlinkSync25(tmpPath);
622366
+ unlinkSync26(tmpPath);
621742
622367
  } catch {
621743
622368
  }
621744
622369
  }
@@ -624191,8 +624816,8 @@ function startApiServer(options2 = {}) {
624191
624816
  const job = JSON.parse(readFileSync98(jobPath, "utf-8"));
624192
624817
  const jobTime = new Date(job.startedAt ?? job.completedAt ?? 0).getTime();
624193
624818
  if (jobTime > 0 && jobTime < cutoff && job.status !== "running") {
624194
- const { unlinkSync: unlinkSync25 } = require4("node:fs");
624195
- unlinkSync25(jobPath);
624819
+ const { unlinkSync: unlinkSync26 } = require4("node:fs");
624820
+ unlinkSync26(jobPath);
624196
624821
  }
624197
624822
  } catch {
624198
624823
  }
@@ -633676,7 +634301,7 @@ __export(run_exports, {
633676
634301
  import { resolve as resolve45 } from "node:path";
633677
634302
  import { spawn as spawn30 } from "node:child_process";
633678
634303
  import { mkdirSync as mkdirSync74, writeFileSync as writeFileSync69, readFileSync as readFileSync101, readdirSync as readdirSync43, existsSync as existsSync122 } from "node:fs";
633679
- import { randomBytes as randomBytes24 } from "node:crypto";
634304
+ import { randomBytes as randomBytes25 } from "node:crypto";
633680
634305
  import { join as join138 } from "node:path";
633681
634306
  function jobsDir2(repoPath) {
633682
634307
  const root = resolve45(repoPath ?? process.cwd());
@@ -633774,7 +634399,7 @@ function extractSummary(captured) {
633774
634399
  return lines.slice(-3).join(" ").slice(0, 200);
633775
634400
  }
633776
634401
  async function runBackground(task, config, opts) {
633777
- const id = `job-${randomBytes24(3).toString("hex")}`;
634402
+ const id = `job-${randomBytes25(3).toString("hex")}`;
633778
634403
  const dir = jobsDir2(opts.repoPath);
633779
634404
  const repoRoot = resolve45(opts.repoPath ?? process.cwd());
633780
634405
  const job = {