omnius 1.0.198 → 1.0.199

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
@@ -260097,6 +260097,31 @@ function approxImageDownloadBytes(preset) {
260097
260097
  }
260098
260098
  return gigabytesToBytes(4);
260099
260099
  }
260100
+ function imagePresetDependencyModels(preset, selectedModel) {
260101
+ const models = [
260102
+ selectedModel,
260103
+ preset?.diffusersBaseModel,
260104
+ preset?.textEncoderModel,
260105
+ ...preset?.loraAdapters?.map((adapter) => adapter.repoId) ?? []
260106
+ ].filter((value2) => Boolean(value2 && value2.trim()));
260107
+ return [...new Set(models)];
260108
+ }
260109
+ function diffusersRunnerModelArgs(model) {
260110
+ const preset = getImageGenerationPreset(model);
260111
+ const runnerModel = preset?.diffusersBaseModel ?? model;
260112
+ const argv = ["--model", runnerModel];
260113
+ if (runnerModel !== model)
260114
+ argv.push("--display-model", model);
260115
+ if (preset?.textEncoderModel) {
260116
+ argv.push("--text-encoder", preset.textEncoderModel);
260117
+ if (preset.textEncoderTarget)
260118
+ argv.push("--text-encoder-target", preset.textEncoderTarget);
260119
+ }
260120
+ for (const adapter of preset?.loraAdapters ?? []) {
260121
+ argv.push("--lora", JSON.stringify(adapter));
260122
+ }
260123
+ return argv;
260124
+ }
260100
260125
  async function ensureImageGenerationCacheDirs(repoRoot) {
260101
260126
  const env2 = imageGenerationPythonEnv(repoRoot);
260102
260127
  await Promise.all([
@@ -260228,7 +260253,7 @@ function parseRunnerJson(stdout) {
260228
260253
  }
260229
260254
  return null;
260230
260255
  }
260231
- var DEFAULT_DIFFUSERS_IMAGE_MODEL, DEFAULT_OLLAMA_IMAGE_MODEL, LEGACY_SDXL_TURBO_MODEL, SANA_1_5_1_6B_MODEL, SANA_1_5_4_8B_MODEL, SANA_1_6B_MULTILING_MODEL, SANA_1_6B_2K_MODEL, SANA_1_6B_4K_MODEL, SANA_SPRINT_0_6B_MODEL, SECONDARY_FLUX_DEV_MODEL, SECONDARY_FLUX_DEV_MIRROR_MODEL, SECONDARY_FLUX_DEV_COMFY_MODEL, SECONDARY_FLUX_FILL_MODEL, SECONDARY_FLUX_FILL_FP8_MODEL, SECONDARY_FLUX2_MODEL, OFFICIAL_BFL_ORG, IMAGE_GENERATION_MODEL_REPLACEMENTS, DIFFUSERS_PYTHON_PACKAGES, SDCPP_PYTHON_PACKAGES, IMAGE_GENERATION_MODEL_PRESETS, IMAGE_GENERATION_QUALITY_LADDER, OLLAMA_IMAGE_MODELS, DIFFUSERS_RUNNER, SDCPP_RUNNER, ImageGenerateTool;
260256
+ var DEFAULT_DIFFUSERS_IMAGE_MODEL, DEFAULT_OLLAMA_IMAGE_MODEL, LEGACY_SDXL_TURBO_MODEL, SANA_1_5_1_6B_MODEL, SANA_1_5_4_8B_MODEL, SANA_1_6B_MULTILING_MODEL, SANA_1_6B_2K_MODEL, SANA_1_6B_4K_MODEL, SANA_SPRINT_0_6B_MODEL, SECONDARY_FLUX_DEV_MODEL, SECONDARY_FLUX_DEV_MIRROR_MODEL, SECONDARY_FLUX_DEV_COMFY_MODEL, SECONDARY_FLUX_FILL_MODEL, SECONDARY_FLUX_FILL_FP8_MODEL, SECONDARY_FLUX2_MODEL, OFFICIAL_FLUX1_DEV_MODEL, OFFICIAL_FLUX2_KLEIN_9B_MODEL, PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL, LUSTLY_FLUX_UNCENSORED_LORA_MODEL, KENERATE_FLUX_UNCENSORED_LORA_MODEL, OFFICIAL_BFL_ORG, IMAGE_GENERATION_MODEL_REPLACEMENTS, DIFFUSERS_PYTHON_PACKAGES, SDCPP_PYTHON_PACKAGES, IMAGE_GENERATION_MODEL_PRESETS, IMAGE_GENERATION_QUALITY_LADDER, OLLAMA_IMAGE_MODELS, DIFFUSERS_RUNNER, SDCPP_RUNNER, ImageGenerateTool;
260232
260257
  var init_image_generate = __esm({
260233
260258
  "packages/execution/dist/tools/image-generate.js"() {
260234
260259
  "use strict";
@@ -260252,6 +260277,11 @@ var init_image_generate = __esm({
260252
260277
  SECONDARY_FLUX_FILL_MODEL = "diffusers/FLUX.1-Fill-dev-nf4";
260253
260278
  SECONDARY_FLUX_FILL_FP8_MODEL = "boricuapab/flux1-fill-dev-fp8";
260254
260279
  SECONDARY_FLUX2_MODEL = "x/flux2-klein";
260280
+ OFFICIAL_FLUX1_DEV_MODEL = "black-forest-labs/FLUX.1-dev";
260281
+ OFFICIAL_FLUX2_KLEIN_9B_MODEL = "black-forest-labs/FLUX.2-klein-9B";
260282
+ PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL = "ponpoke/flux2-klein-9b-uncensored-text-encoder";
260283
+ LUSTLY_FLUX_UNCENSORED_LORA_MODEL = "lustlyai/Flux_Lustly.ai_Uncensored_nsfw_v1";
260284
+ KENERATE_FLUX_UNCENSORED_LORA_MODEL = "kenerateai/Flux-uncensored";
260255
260285
  OFFICIAL_BFL_ORG = "black-forest-labs";
260256
260286
  IMAGE_GENERATION_MODEL_REPLACEMENTS = /* @__PURE__ */ new Map([
260257
260287
  [officialBflModel("FLUX.1-dev"), SECONDARY_FLUX_DEV_MODEL],
@@ -260274,6 +260304,7 @@ var init_image_generate = __esm({
260274
260304
  "transformers",
260275
260305
  "accelerate",
260276
260306
  "safetensors",
260307
+ "peft",
260277
260308
  "pillow",
260278
260309
  "sentencepiece",
260279
260310
  "protobuf"
@@ -260387,6 +260418,76 @@ var init_image_generate = __esm({
260387
260418
  fallbackFor: [SECONDARY_FLUX_FILL_MODEL],
260388
260419
  note: "Traceable FP8 fallback for FLUX.1 Fill dev from the research package."
260389
260420
  },
260421
+ {
260422
+ id: PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL,
260423
+ label: "FLUX.2 Klein 9B uncensored text encoder",
260424
+ backend: "diffusers",
260425
+ install: 'python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.2-klein-9B --text-encoder ponpoke/flux2-klein-9b-uncensored-text-encoder --steps 4 --guidance 1 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
260426
+ category: "Adult-capable FLUX adapter",
260427
+ sizeClass: "FLUX.2 Klein 9B text-encoder override",
260428
+ quality: "Uncensored FLUX.2 Klein text-encoder override; uses the official FLUX.2 Klein 9B base pipeline and replaces the prompt encoder when the installed Diffusers stack supports that component layout.",
260429
+ minVramGB: 24,
260430
+ recommendedVramGB: 32,
260431
+ deployment: "Diffusers Flux2KleinPipeline over black-forest-labs/FLUX.2-klein-9B with a replacement text encoder. Requires Hugging Face access/license acceptance for the BFL base and the text-encoder repo.",
260432
+ steps: 4,
260433
+ guidance: 1,
260434
+ width: 1024,
260435
+ height: 1024,
260436
+ diffusersBaseModel: OFFICIAL_FLUX2_KLEIN_9B_MODEL,
260437
+ textEncoderModel: PONPOKE_FLUX2_UNCENSORED_TEXT_ENCODER_MODEL,
260438
+ textEncoderTarget: "auto",
260439
+ approxDownloadGB: 36,
260440
+ note: "Adapter-style preset: loads the gated FLUX.2 Klein 9B base and swaps in the ponpoke text encoder. Not in the automatic fallback ladder."
260441
+ },
260442
+ {
260443
+ id: LUSTLY_FLUX_UNCENSORED_LORA_MODEL,
260444
+ label: "Flux Lustly uncensored LoRA",
260445
+ backend: "diffusers",
260446
+ install: `python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.1-dev --lora '{"repoId":"lustlyai/Flux_Lustly.ai_Uncensored_nsfw_v1","weightName":"flux_lustly-ai_v1.safetensors","adapterName":"v1","adapterWeight":1}' --steps 20 --guidance 4 --width 768 --height 768 --prompt "..." --output .omnius/images/out.png`,
260447
+ category: "Adult-capable FLUX adapter",
260448
+ sizeClass: "FLUX.1-dev LoRA adapter",
260449
+ quality: "Adult-capable FLUX.1-dev LoRA tested by its publisher on full FLUX dev and schnell. Listed as an explicit opt-in model, not part of automatic fallback.",
260450
+ minVramGB: 16,
260451
+ recommendedVramGB: 24,
260452
+ deployment: "Diffusers LoRA over black-forest-labs/FLUX.1-dev. Requires Hugging Face access/license acceptance for the gated BFL base model and the adapter terms.",
260453
+ steps: 20,
260454
+ guidance: 4,
260455
+ width: 768,
260456
+ height: 768,
260457
+ diffusersBaseModel: OFFICIAL_FLUX1_DEV_MODEL,
260458
+ loraAdapters: [{
260459
+ repoId: LUSTLY_FLUX_UNCENSORED_LORA_MODEL,
260460
+ weightName: "flux_lustly-ai_v1.safetensors",
260461
+ adapterName: "v1",
260462
+ adapterWeight: 1
260463
+ }],
260464
+ approxDownloadGB: 24,
260465
+ note: "Adapter-style preset: loads FLUX.1-dev then applies the Lustly LoRA. Explicit selection only."
260466
+ },
260467
+ {
260468
+ id: KENERATE_FLUX_UNCENSORED_LORA_MODEL,
260469
+ label: "Kenerate Flux uncensored LoRA",
260470
+ backend: "diffusers",
260471
+ install: `python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.1-dev --lora '{"repoId":"kenerateai/Flux-uncensored","adapterName":"kenerate","adapterWeight":1}' --steps 20 --guidance 3.5 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png`,
260472
+ category: "Adult-capable FLUX adapter",
260473
+ sizeClass: "FLUX.1-dev LoRA adapter",
260474
+ quality: "Community FLUX.1-dev LoRA. Hugging Face currently marks the model card as removed, so availability may fail at download time; kept as a selectable explicit model because the repo still advertises a Diffusers LoRA load path.",
260475
+ minVramGB: 16,
260476
+ recommendedVramGB: 24,
260477
+ deployment: "Diffusers LoRA over black-forest-labs/FLUX.1-dev. Requires Hugging Face access/license acceptance for the gated BFL base model; adapter availability may vary.",
260478
+ steps: 20,
260479
+ guidance: 3.5,
260480
+ width: 1024,
260481
+ height: 1024,
260482
+ diffusersBaseModel: OFFICIAL_FLUX1_DEV_MODEL,
260483
+ loraAdapters: [{
260484
+ repoId: KENERATE_FLUX_UNCENSORED_LORA_MODEL,
260485
+ adapterName: "kenerate",
260486
+ adapterWeight: 1
260487
+ }],
260488
+ approxDownloadGB: 24,
260489
+ note: "Adapter-style preset: loads FLUX.1-dev then applies the Kenerate LoRA. The upstream card says the LoRA has been removed, so failures should be surfaced directly."
260490
+ },
260390
260491
  {
260391
260492
  id: "stabilityai/stable-diffusion-3.5-large",
260392
260493
  label: "Stable Diffusion 3.5 Large",
@@ -260723,6 +260824,13 @@ def _device():
260723
260824
 
260724
260825
  def _pipeline_class(model):
260725
260826
  lowered = model.lower()
260827
+ if "flux.2" in lowered or "flux2" in lowered or "flux-2" in lowered:
260828
+ try:
260829
+ from diffusers import Flux2KleinPipeline
260830
+ return Flux2KleinPipeline
260831
+ except Exception:
260832
+ from diffusers import DiffusionPipeline
260833
+ return DiffusionPipeline
260726
260834
  if "flux" in lowered:
260727
260835
  from diffusers import FluxPipeline
260728
260836
  return FluxPipeline
@@ -260749,13 +260857,85 @@ def _pipeline_class(model):
260749
260857
  def _large_model(model):
260750
260858
  lowered = model.lower()
260751
260859
  return any(token in lowered for token in [
260752
- "flux.1", "flux.2", "stable-diffusion-3.5", "hunyuan", "janus",
260860
+ "flux.1", "flux.2", "flux2", "stable-diffusion-3.5", "hunyuan", "janus",
260753
260861
  "sana1.5_4.8b", "sana_1600m_2kpx", "sana_1600m_4kpx",
260754
260862
  ])
260755
260863
 
260864
+ def _parse_lora_specs(raw_specs):
260865
+ specs = []
260866
+ for raw in raw_specs or []:
260867
+ if not raw:
260868
+ continue
260869
+ try:
260870
+ parsed = json.loads(raw)
260871
+ if isinstance(parsed, str):
260872
+ parsed = {"repoId": parsed}
260873
+ if isinstance(parsed, dict) and parsed.get("repoId"):
260874
+ specs.append(parsed)
260875
+ except Exception as exc:
260876
+ raise ValueError(f"Invalid --lora JSON {raw!r}: {exc}")
260877
+ return specs
260878
+
260879
+ def _load_lora_adapters(pipe, specs):
260880
+ adapter_names = []
260881
+ adapter_weights = []
260882
+ for index, spec in enumerate(specs):
260883
+ repo_id = str(spec.get("repoId") or "").strip()
260884
+ if not repo_id:
260885
+ continue
260886
+ kwargs = {}
260887
+ weight_name = str(spec.get("weightName") or "").strip()
260888
+ adapter_name = str(spec.get("adapterName") or f"adapter_{index}").strip()
260889
+ if weight_name:
260890
+ kwargs["weight_name"] = weight_name
260891
+ if adapter_name:
260892
+ kwargs["adapter_name"] = adapter_name
260893
+ _progress("load", f"loading LoRA adapter {repo_id}")
260894
+ pipe.load_lora_weights(repo_id, **kwargs)
260895
+ if adapter_name:
260896
+ adapter_names.append(adapter_name)
260897
+ adapter_weights.append(float(spec.get("adapterWeight", 1.0)))
260898
+ if adapter_names and hasattr(pipe, "set_adapters"):
260899
+ _progress("load", f"activating {len(adapter_names)} LoRA adapter(s)")
260900
+ pipe.set_adapters(adapter_names, adapter_weights=adapter_weights)
260901
+
260902
+ def _target_text_encoder_attrs(pipe, target, repo_id):
260903
+ explicit = str(target or "").strip()
260904
+ if explicit and explicit != "auto":
260905
+ return [explicit]
260906
+ lowered = repo_id.lower()
260907
+ preferred = ["text_encoder_3", "text_encoder_2", "text_encoder"] if ("qwen" in lowered or "flux2" in lowered or "klein" in lowered) else ["text_encoder", "text_encoder_2", "text_encoder_3"]
260908
+ return [name for name in preferred if hasattr(pipe, name)]
260909
+
260910
+ def _tokenizer_attr_for_text_encoder(attr):
260911
+ if attr == "text_encoder":
260912
+ return "tokenizer"
260913
+ if attr.startswith("text_encoder_"):
260914
+ return "tokenizer_" + attr.split("_")[-1]
260915
+ return "tokenizer"
260916
+
260917
+ def _replace_text_encoder(pipe, repo_id, target, dtype):
260918
+ if not repo_id:
260919
+ return
260920
+ from transformers import AutoModel, AutoTokenizer
260921
+ attrs = _target_text_encoder_attrs(pipe, target, repo_id)
260922
+ if not attrs:
260923
+ raise ValueError(f"Pipeline has no text_encoder component compatible with text encoder override {repo_id}")
260924
+ attr = attrs[0]
260925
+ tokenizer_attr = _tokenizer_attr_for_text_encoder(attr)
260926
+ _progress("load", f"loading replacement text encoder {repo_id} into {attr}")
260927
+ tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
260928
+ text_encoder = AutoModel.from_pretrained(repo_id, torch_dtype=dtype, trust_remote_code=True)
260929
+ setattr(pipe, attr, text_encoder)
260930
+ if hasattr(pipe, tokenizer_attr):
260931
+ setattr(pipe, tokenizer_attr, tokenizer)
260932
+ else:
260933
+ _progress("load", f"pipeline has no {tokenizer_attr}; replaced {attr} only")
260934
+
260756
260935
  def main():
260757
260936
  parser = argparse.ArgumentParser()
260758
260937
  parser.add_argument("--model", required=True)
260938
+ parser.add_argument("--display-model", default="")
260759
260939
  parser.add_argument("--prompt", required=True)
260760
260940
  parser.add_argument("--output", required=True)
260761
260941
  parser.add_argument("--width", type=int, default=512)
@@ -260765,6 +260945,9 @@ def main():
260765
260945
  parser.add_argument("--seed", type=int, default=None)
260766
260946
  parser.add_argument("--device", default="auto")
260767
260947
  parser.add_argument("--variant", default="")
260948
+ parser.add_argument("--lora", action="append", default=[])
260949
+ parser.add_argument("--text-encoder", default="")
260950
+ parser.add_argument("--text-encoder-target", default="auto")
260768
260951
  parser.add_argument("--prewarm", action="store_true")
260769
260952
  args = parser.parse_args()
260770
260953
 
@@ -260793,12 +260976,19 @@ def main():
260793
260976
  pipe = pipeline_cls.from_pretrained(args.model, **kwargs)
260794
260977
  _progress("load", f"model loaded on {device}")
260795
260978
 
260979
+ if args.text_encoder:
260980
+ _replace_text_encoder(pipe, args.text_encoder, args.text_encoder_target, dtype)
260981
+
260796
260982
  if "sana" in lowered_model and hasattr(pipe, "text_encoder") and pipe.text_encoder is not None:
260797
260983
  try:
260798
260984
  pipe.text_encoder.to(torch.bfloat16)
260799
260985
  except Exception:
260800
260986
  pass
260801
260987
 
260988
+ lora_specs = _parse_lora_specs(args.lora)
260989
+ if lora_specs:
260990
+ _load_lora_adapters(pipe, lora_specs)
260991
+
260802
260992
  if hasattr(pipe, "enable_attention_slicing"):
260803
260993
  try:
260804
260994
  pipe.enable_attention_slicing()
@@ -260817,7 +261007,10 @@ def main():
260817
261007
  print(json.dumps({
260818
261008
  "ok": True,
260819
261009
  "path": "",
260820
- "model": args.model,
261010
+ "model": args.display_model or args.model,
261011
+ "base_model": args.model,
261012
+ "lora_adapters": [spec.get("repoId") for spec in lora_specs],
261013
+ "text_encoder": args.text_encoder or None,
260821
261014
  "backend": "diffusers",
260822
261015
  "device": device,
260823
261016
  "prewarm": True,
@@ -260853,7 +261046,10 @@ def main():
260853
261046
  print(json.dumps({
260854
261047
  "ok": True,
260855
261048
  "path": str(out),
260856
- "model": args.model,
261049
+ "model": args.display_model or args.model,
261050
+ "base_model": args.model,
261051
+ "lora_adapters": [spec.get("repoId") for spec in lora_specs],
261052
+ "text_encoder": args.text_encoder or None,
260857
261053
  "backend": "diffusers",
260858
261054
  "device": device,
260859
261055
  "seconds": round(time.perf_counter() - t0, 3),
@@ -260913,7 +261109,7 @@ if __name__ == "__main__":
260913
261109
  `;
260914
261110
  ImageGenerateTool = class {
260915
261111
  name = "generate_image";
260916
- description = `Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/flux2-klein), Python Diffusers models (Sana 1.5 1.6B default, Sana 1.5 4.8B, Sana multilingual/2K/4K, FLUX.1 dev, SD3.5 Large, SDXL Turbo, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first (Sana 1.5 above FLUX so we avoid HF gating), including community FLUX mirrors, and then falls back to smaller models if setup, download, or generation fails. Aspect ratio and resolution are model-controllable: pass aspect_ratio (e.g. "16:9", "9:16", "4:3", "3:4", "1:1", "21:9", "2:3", "3:2") to derive width/height around the selected model's preferred base resolution, or pass explicit width/height (in pixels, both rounded to a multiple of 8) when a specific size is required. A preliminary prompt-expansion stage rewrites the user's prompt into a richer, model-tuned version before generation when an LLM expander is wired; pass expand_prompt=false to skip. Saves a PNG under .omnius/images and returns the file path.`;
261112
+ description = `Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/flux2-klein), Python Diffusers models (Sana 1.5 1.6B default, Sana 1.5 4.8B, Sana multilingual/2K/4K, FLUX.1 dev, FLUX adapter/LoRA presets, SD3.5 Large, SDXL Turbo, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first (Sana 1.5 above FLUX so we avoid HF gating), including community FLUX mirrors, and then falls back to smaller models if setup, download, or generation fails. Aspect ratio and resolution are model-controllable: pass aspect_ratio (e.g. "16:9", "9:16", "4:3", "3:4", "1:1", "21:9", "2:3", "3:2") to derive width/height around the selected model's preferred base resolution, or pass explicit width/height (in pixels, both rounded to a multiple of 8) when a specific size is required. A preliminary prompt-expansion stage rewrites the user's prompt into a richer, model-tuned version before generation when an LLM expander is wired; pass expand_prompt=false to skip. Saves a PNG under .omnius/images and returns the file path.`;
260917
261113
  parameters = {
260918
261114
  type: "object",
260919
261115
  properties: {
@@ -261407,7 +261603,7 @@ ${errText.slice(0, 1200)}`,
261407
261603
  try {
261408
261604
  const space = ensureDiskSpaceForDownload({
261409
261605
  approxDownloadBytes: approxBytes,
261410
- keepRepos: [args.model]
261606
+ keepRepos: imagePresetDependencyModels(preset, args.model)
261411
261607
  });
261412
261608
  if (space.evicted.length > 0) {
261413
261609
  this.emitProgress({
@@ -261442,8 +261638,7 @@ ${errText.slice(0, 1200)}`,
261442
261638
  }
261443
261639
  const result = await runProcess2(python.command, [
261444
261640
  runner,
261445
- "--model",
261446
- args.model,
261641
+ ...diffusersRunnerModelArgs(args.model),
261447
261642
  "--prompt",
261448
261643
  "omnius prewarm",
261449
261644
  "--output",
@@ -261617,8 +261812,7 @@ ${errText.slice(0, 800)}`,
261617
261812
  }
261618
261813
  const argv = [
261619
261814
  runner,
261620
- "--model",
261621
- args.model,
261815
+ ...diffusersRunnerModelArgs(args.model),
261622
261816
  "--prompt",
261623
261817
  args.prompt,
261624
261818
  "--output",
@@ -261639,7 +261833,7 @@ ${errText.slice(0, 800)}`,
261639
261833
  try {
261640
261834
  const space = ensureDiskSpaceForDownload({
261641
261835
  approxDownloadBytes: approxBytes,
261642
- keepRepos: [args.model]
261836
+ keepRepos: imagePresetDependencyModels(preset, args.model)
261643
261837
  });
261644
261838
  if (space.evicted.length > 0) {
261645
261839
  this.emitProgress({
@@ -534733,6 +534927,7 @@ __export(dist_exports, {
534733
534927
  imageGenerationDir: () => imageGenerationDir,
534734
534928
  imageGenerationModelPresets: () => imageGenerationModelPresets,
534735
534929
  imageGenerationSetupPlan: () => imageGenerationSetupPlan,
534930
+ imagePresetDependencyModels: () => imagePresetDependencyModels,
534736
534931
  inferAudioGenerationBackend: () => inferAudioGenerationBackend,
534737
534932
  inferImageGenerationBackend: () => inferImageGenerationBackend,
534738
534933
  inferMediaBackend: () => inferMediaBackend,
@@ -586916,9 +587111,9 @@ var init_profiles = __esm({
586916
587111
  encrypted: false,
586917
587112
  created: "2026-03-31T00:00:00Z"
586918
587113
  },
586919
- "cygnus-regi-tracking": {
586920
- name: "cygnus-regi-tracking",
586921
- description: "REGI bookkeeping only — todos, working notes, and completion markers. No filesystem, search, shell, network, or model-generation tools.",
587114
+ "bookkeeping-tracking": {
587115
+ name: "bookkeeping-tracking",
587116
+ description: "Bookkeeping only — todos, working notes, and completion markers. No filesystem, search, shell, network, or model-generation tools.",
586922
587117
  tools: {
586923
587118
  allow: ["todo_write", "todo_read", "working_notes", "task_complete"],
586924
587119
  deny: [
@@ -616212,7 +616407,13 @@ function ollamaModelDiskStats(model, sizes) {
616212
616407
  }
616213
616408
  function imageModelDiskStats(ctx3, preset, ollamaSizes) {
616214
616409
  if (preset.backend === "ollama") return ollamaModelDiskStats(preset.id, ollamaSizes);
616215
- if (preset.backend === "diffusers") return cachedModelDiskStats(imageGenerationDir(ctx3.repoRoot), preset.id);
616410
+ if (preset.backend === "diffusers") {
616411
+ const root = imageGenerationDir(ctx3.repoRoot);
616412
+ const parts = imagePresetDependencyModels(preset, preset.id).map((model) => cachedModelDiskStats(root, model));
616413
+ const paths = [...new Set(parts.flatMap((part) => part.paths))];
616414
+ const bytes = parts.reduce((sum, part) => sum + part.bytes, 0);
616415
+ return { downloaded: paths.length > 0, bytes, paths };
616416
+ }
616216
616417
  return { downloaded: false, bytes: 0, paths: [] };
616217
616418
  }
616218
616419
  function audioModelDiskStats(ctx3, preset) {
@@ -616238,7 +616439,8 @@ async function deleteImageModelWeights(ctx3, preset) {
616238
616439
  if (preset.backend === "ollama") {
616239
616440
  messages2.push(await deleteOllamaWeights(ctx3, preset.id));
616240
616441
  } else if (preset.backend === "diffusers") {
616241
- const removed = removeCachedModelPaths(imageGenerationDir(ctx3.repoRoot), preset.id);
616442
+ const root = imageGenerationDir(ctx3.repoRoot);
616443
+ const removed = imagePresetDependencyModels(preset, preset.id).flatMap((model) => removeCachedModelPaths(root, model));
616242
616444
  messages2.push(removed.length > 0 ? `Deleted ${removed.length} cached image model path(s) for ${preset.id}.` : `No cached image weights found for ${preset.id}.`);
616243
616445
  } else {
616244
616446
  messages2.push("stable-diffusion.cpp uses explicit local checkpoint paths; remove the chosen checkpoint file directly if needed.");
@@ -616261,7 +616463,7 @@ async function showImageModelsMenu(ctx3, hasLocal) {
616261
616463
  };
616262
616464
  const items = [
616263
616465
  { key: "setup:ollama", label: "Setup Ollama", detail: "Pull x/z-image-turbo or x/flux2-klein" },
616264
- { key: "setup:diffusers", label: "Setup Diffusers", detail: "Auto-installs SDXL Turbo under .omnius/image-gen/.venv" },
616466
+ { key: "setup:diffusers", label: "Setup Diffusers", detail: "Auto-installs the shared image runtime and selected Diffusers model" },
616265
616467
  { key: "setup:sdcpp", label: "Setup stable-diffusion.cpp", detail: "CPU/GGUF/checkpoint route" },
616266
616468
  { key: "hdr:models", label: selectColors.dim("─── Models ───") },
616267
616469
  ...imageGenerationModelPresets().map(buildModelItem)
@@ -667079,7 +667281,7 @@ function handleHelp(req2, res) {
667079
667281
  override: "Operators can override per-tool classification via OMNIUS_TOOL_OVERRIDES env var (JSON map of name → partial security info).",
667080
667282
  filters: "GET /v1/tools supports ?category=, ?scope=, ?risk=, ?off_device=true|false (Q9). E.g. /v1/tools?scope=read&off_device=true returns the safe-to-expose set.",
667081
667283
  profiles: "Tool profiles are enforced before tool exposure and again at execution. Resolution order: preset, working_directory/.omnius/profiles, ~/.omnius/profiles. Missing named profiles fail closed.",
667082
- bookkeeping: "For deterministic tracking, direct-call todo_write, todo_read, working_notes, and task_complete with profile:'cygnus-regi-tracking' and a stable session_id. Do not use /v1/run for bookkeeping-only mutations."
667284
+ bookkeeping: "For deterministic tracking, direct-call todo_write, todo_read, working_notes, and task_complete with profile:'bookkeeping-tracking' and a stable session_id. Do not use /v1/run for bookkeeping-only mutations."
667083
667285
  },
667084
667286
  runtime_keys: {
667085
667287
  "GET /v1/keys": "List runtime keys (admin scope). Secrets masked.",
@@ -110,8 +110,8 @@ Deterministic bookkeeping without an agent run:
110
110
  ```bash
111
111
  curl -s -X POST http://127.0.0.1:11435/v1/tools/todo_write/call \
112
112
  -H 'content-type: application/json' \
113
- -H 'x-omnius-session-id: regi-turn-123' \
114
- -d '{"profile":"cygnus-regi-tracking","args":{"todos":[{"content":"Create NC report","status":"in_progress"}]}}'
113
+ -H 'x-omnius-session-id: tracking-turn-123' \
114
+ -d '{"profile":"bookkeeping-tracking","args":{"todos":[{"content":"Create report","status":"in_progress"}]}}'
115
115
  ```
116
116
 
117
117
  ## Voice TTS
@@ -88,7 +88,7 @@ Mint body:
88
88
 
89
89
  Tool calls are additionally gated by each tool's security metadata. Scope alone is not the only control.
90
90
 
91
- Runtime keys may bind a tool profile. A run-scope key with profile `cygnus-regi-tracking` can update todos and notes but cannot expose or execute filesystem/search/shell tools.
91
+ Runtime keys may bind a tool profile. A run-scope key with profile `bookkeeping-tracking` can update todos and notes but cannot expose or execute filesystem/search/shell tools.
92
92
 
93
93
  ## Request Header
94
94
 
@@ -47,8 +47,8 @@ For bookkeeping integrations, prefer direct calls instead of `/v1/run`:
47
47
  ```bash
48
48
  curl -s -X POST "$OMNIUS/v1/tools/todo_write/call" \
49
49
  -H 'content-type: application/json' \
50
- -H 'x-omnius-session-id: regi-turn-123' \
51
- -d '{"profile":"cygnus-regi-tracking","args":{"todos":[{"content":"Create NC report","status":"in_progress"}]}}'
50
+ -H 'x-omnius-session-id: tracking-turn-123' \
51
+ -d '{"profile":"bookkeeping-tracking","args":{"todos":[{"content":"Create report","status":"in_progress"}]}}'
52
52
  ```
53
53
 
54
54
  ## Abort
@@ -39,7 +39,7 @@ Filters include:
39
39
  ?scope=read|run|admin
40
40
  ?risk=low|medium|high|critical
41
41
  ?limit=200&offset=0
42
- ?profile=cygnus-regi-tracking
42
+ ?profile=bookkeeping-tracking
43
43
  ```
44
44
 
45
45
  Profiles can also be selected with `X-Tool-Profile`. Named profiles resolve in this order: built-in preset, `{working_dir}/.omnius/profiles/{name}.json`, then `~/.omnius/profiles/{name}.json`. Missing profiles fail closed.
@@ -50,10 +50,10 @@ Profiles can also be selected with `X-Tool-Profile`. Named profiles resolve in t
50
50
 
51
51
  ```json
52
52
  {
53
- "session_id": "regi-turn-123",
53
+ "session_id": "tracking-turn-123",
54
54
  "args": {},
55
55
  "working_dir": "/path/to/repo",
56
- "profile": "cygnus-regi-tracking"
56
+ "profile": "bookkeeping-tracking"
57
57
  }
58
58
  ```
59
59
 
@@ -66,8 +66,8 @@ Bookkeeping tools are direct-callable and do not require a model run:
66
66
  ```bash
67
67
  curl -s -X POST "$OMNIUS/v1/tools/todo_write/call" \
68
68
  -H 'content-type: application/json' \
69
- -H 'x-omnius-session-id: regi-turn-123' \
70
- -d '{"profile":"cygnus-regi-tracking","args":{"todos":[{"content":"Create NC report","status":"in_progress"}]}}'
69
+ -H 'x-omnius-session-id: tracking-turn-123' \
70
+ -d '{"profile":"bookkeeping-tracking","args":{"todos":[{"content":"Create report","status":"in_progress"}]}}'
71
71
  ```
72
72
 
73
73
  Use `todo_write`, `todo_read`, `working_notes`, and `task_complete` this way for deterministic tracking. `task_complete` on this endpoint is only a bookkeeping boundary; `/v1/runs/{id}` remains the canonical run terminal state.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.198",
3
+ "version": "1.0.199",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.198",
9
+ "version": "1.0.199",
10
10
  "bundleDependencies": [
11
11
  "image-to-ascii"
12
12
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.198",
3
+ "version": "1.0.199",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",