omnius 1.0.190 → 1.0.191

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
@@ -2859,7 +2859,7 @@ var init_security_classifier = __esm({
2859
2859
  // ── Network reads (safe)
2860
2860
  { match: /^(web_search|web_fetch)$/, info: NETWORK_READ },
2861
2861
  // ── Network outbound (mutating or remote inference)
2862
- { match: /^(image_generate|generate_image|generate_audio|generate_tts|create_audio_file|vision|video_understand|telegram_send_file)$/, info: NETWORK_OUTBOUND },
2862
+ { match: /^(image_generate|generate_image|generate_audio|generate_model|generate_tts|create_audio_file|vision|video_understand|telegram_send_file)$/, info: NETWORK_OUTBOUND },
2863
2863
  { match: /^(transcribe_file|transcribe_url|youtube_download)$/, info: NETWORK_OUTBOUND },
2864
2864
  { match: /^(fortemi_bridge)$/, info: NETWORK_OUTBOUND },
2865
2865
  // ── Memory tools
@@ -9996,8 +9996,11 @@ var init_explore_tools = __esm({
9996
9996
  desktop_describe: "Describe a region of the desktop",
9997
9997
  transcribe_file: "Transcribe audio/video files to text",
9998
9998
  telegram_media_recent: "List recent Telegram media available in the current chat scope",
9999
+ hf_model_discover: "Search Hugging Face for image/video/audio/3D/CAD model adapters",
10000
+ hf_model_intake: "Inspect, validate, save, list, or delete Hugging Face media model adapters",
9999
10001
  generate_audio: "Generate sound effects or music with local model backends",
10000
10002
  generate_tts: "Generate speech from text with configured voice/TTS backends",
10003
+ generate_model: "Generate or check 3D/CAD model requests using the /models Hugging Face catalog",
10001
10004
  create_tool: "Create a new custom tool from a workflow",
10002
10005
  manage_tools: "List, inspect, or remove custom tools",
10003
10006
  skill_list: "List available AIWG skills",
@@ -267026,6 +267029,423 @@ ${llmAnnotation}` : result.llmContent;
267026
267029
  }
267027
267030
  });
267028
267031
 
267032
+ // packages/execution/dist/tools/model-generate.js
267033
+ function normalizeAction(value2) {
267034
+ const raw = String(value2 ?? "generate").trim().toLowerCase().replace(/-/g, "_");
267035
+ if (raw === "list" || raw === "models" || raw === "list_models")
267036
+ return "list_models";
267037
+ if (raw === "check" || raw === "setup" || raw === "prepare" || raw === "select")
267038
+ return "check";
267039
+ return "generate";
267040
+ }
267041
+ function rankCatalogEntries(kind) {
267042
+ return listMediaModelCatalog(kind).sort((a2, b) => {
267043
+ const exact = Number(a2.spec.modality !== kind) - Number(b.spec.modality !== kind);
267044
+ if (exact !== 0)
267045
+ return exact;
267046
+ const active = statusRank(a2.spec.status) - statusRank(b.spec.status);
267047
+ if (active !== 0)
267048
+ return active;
267049
+ const runtime = Number(!a2.spec.deployment.runtimeCompatible) - Number(!b.spec.deployment.runtimeCompatible);
267050
+ if (runtime !== 0)
267051
+ return runtime;
267052
+ const download = (a2.spec.resources.approxDownloadGB ?? 9999) - (b.spec.resources.approxDownloadGB ?? 9999);
267053
+ if (download !== 0)
267054
+ return download;
267055
+ return a2.spec.id.localeCompare(b.spec.id);
267056
+ });
267057
+ }
267058
+ function statusRank(status) {
267059
+ if (status === "active")
267060
+ return 0;
267061
+ if (status === "metadata-only")
267062
+ return 1;
267063
+ if (status === "draft")
267064
+ return 2;
267065
+ return 3;
267066
+ }
267067
+ function renderCatalogForKind(kind, entries) {
267068
+ if (entries.length === 0)
267069
+ return `No ${kindLabel(kind)} model adapters are available.`;
267070
+ const lines = [`Available ${kindLabel(kind)} model adapters:`];
267071
+ for (const entry of entries) {
267072
+ const spec = entry.spec;
267073
+ const resources = resourceSummary(spec);
267074
+ lines.push(`- ${spec.id} [${spec.modality}/${spec.backend}/${spec.status}${resources ? `, ${resources}` : ""}] - ${spec.label}`);
267075
+ }
267076
+ lines.push("");
267077
+ lines.push("Call generate_model with action='check' to see disk/resource readiness, or action='generate' to attempt generation when a runtime adapter is wired.");
267078
+ return lines.join("\n");
267079
+ }
267080
+ function renderReadiness(kind, spec, readiness) {
267081
+ const lines = [
267082
+ `${kindLabel(kind)} generation model: ${spec.id}`,
267083
+ `Status: ${readiness.ok ? "ready" : "blocked"}`,
267084
+ `Backend: ${spec.backend}`,
267085
+ `Runner: ${spec.runner}`,
267086
+ `Resources: ${resourceSummary(spec) || "unknown"}`,
267087
+ `Disk: ${readiness.disk.reason}`
267088
+ ];
267089
+ if (readiness.notes.length > 0) {
267090
+ lines.push("Notes:");
267091
+ for (const note of readiness.notes)
267092
+ lines.push(`- ${note}`);
267093
+ }
267094
+ if (readiness.warnings.length > 0) {
267095
+ lines.push("Warnings:");
267096
+ for (const warning of readiness.warnings)
267097
+ lines.push(`- ${warning}`);
267098
+ }
267099
+ if (readiness.blockers.length > 0) {
267100
+ lines.push("Blockers:");
267101
+ for (const blocker of readiness.blockers)
267102
+ lines.push(`- ${blocker}`);
267103
+ }
267104
+ if (spec.deployment.notes.length > 0) {
267105
+ lines.push("Deployment notes:");
267106
+ for (const note of spec.deployment.notes.slice(0, 4))
267107
+ lines.push(`- ${note}`);
267108
+ }
267109
+ return lines.join("\n");
267110
+ }
267111
+ function modelSummary(entry) {
267112
+ const spec = entry.spec;
267113
+ return {
267114
+ id: spec.id,
267115
+ repoId: spec.repoId,
267116
+ label: spec.label,
267117
+ modality: spec.modality,
267118
+ modalities: spec.modalities,
267119
+ backend: spec.backend,
267120
+ runner: spec.runner,
267121
+ status: spec.status,
267122
+ resources: spec.resources,
267123
+ requiresToken: spec.deployment.requiresToken,
267124
+ runtimeCompatible: spec.deployment.runtimeCompatible,
267125
+ safeRunner: spec.deployment.safeRunner,
267126
+ outputKind: spec.smokeTest.outputKind
267127
+ };
267128
+ }
267129
+ function checkDisk(spec) {
267130
+ const disk = getModelStoreDiskInfo();
267131
+ const approxGb = spec.resources.approxDownloadGB ?? spec.smokeTest.maxDownloadGB;
267132
+ if (!approxGb || approxGb <= 0) {
267133
+ return {
267134
+ ok: true,
267135
+ reason: `${formatBytes(disk.freeBytes)} free on ${disk.path}; model download size is unknown.`,
267136
+ freeBytes: disk.freeBytes,
267137
+ totalBytes: disk.totalBytes
267138
+ };
267139
+ }
267140
+ const neededBytes = gigabytesToBytes(approxGb);
267141
+ const safetyMargin = 1 * 1024 ** 3;
267142
+ const ok3 = disk.freeBytes >= neededBytes + safetyMargin;
267143
+ return {
267144
+ ok: ok3,
267145
+ reason: ok3 ? `${formatBytes(disk.freeBytes)} free; ${spec.id} needs about ${formatBytes(neededBytes)} plus 1.0GB headroom.` : `${formatBytes(disk.freeBytes)} free; ${spec.id} needs about ${formatBytes(neededBytes)} plus 1.0GB headroom.`,
267146
+ neededBytes,
267147
+ freeBytes: disk.freeBytes,
267148
+ totalBytes: disk.totalBytes
267149
+ };
267150
+ }
267151
+ function modelLoadSpecFor(spec, kind) {
267152
+ return {
267153
+ name: spec.id,
267154
+ domain: domainForKind(kind),
267155
+ host: hostForBackend(spec.backend),
267156
+ owner: "generate_model",
267157
+ estimatedVramMB: gbToMb(spec.resources.minVramGB),
267158
+ estimatedRamMB: gbToMb(spec.resources.minRamGB),
267159
+ priority: 5
267160
+ };
267161
+ }
267162
+ function domainForKind(kind) {
267163
+ return kind === "cad" ? "cad" : "3d-gen";
267164
+ }
267165
+ function hostForBackend(backend) {
267166
+ if (backend === "diffusers")
267167
+ return "diffusers-py";
267168
+ if (backend === "comfyui")
267169
+ return "comfyui";
267170
+ if (backend === "project" || backend === "generated")
267171
+ return "subprocess";
267172
+ return "hf-local";
267173
+ }
267174
+ function gbToMb(gb) {
267175
+ if (typeof gb !== "number" || !Number.isFinite(gb))
267176
+ return void 0;
267177
+ return Math.max(0, Math.round(gb * 1024));
267178
+ }
267179
+ function resourceSummary(spec) {
267180
+ const parts = [];
267181
+ if (spec.resources.approxDownloadGB !== void 0)
267182
+ parts.push(`~${spec.resources.approxDownloadGB}GB download`);
267183
+ if (spec.resources.minVramGB !== void 0)
267184
+ parts.push(`min ${spec.resources.minVramGB}GB VRAM`);
267185
+ if (spec.resources.recommendedVramGB !== void 0)
267186
+ parts.push(`rec ${spec.resources.recommendedVramGB}GB VRAM`);
267187
+ if (spec.resources.minRamGB !== void 0)
267188
+ parts.push(`min ${spec.resources.minRamGB}GB RAM`);
267189
+ if (spec.deployment.requiresToken)
267190
+ parts.push("HF token/license required");
267191
+ return parts.join(", ");
267192
+ }
267193
+ function kindLabel(kind) {
267194
+ return kind === "cad" ? "CAD" : "3D model";
267195
+ }
267196
+ var RUNTIME_BLOCKER, ModelGenerateTool;
267197
+ var init_model_generate = __esm({
267198
+ "packages/execution/dist/tools/model-generate.js"() {
267199
+ "use strict";
267200
+ init_model_broker();
267201
+ init_hf_media_models();
267202
+ init_model_store();
267203
+ RUNTIME_BLOCKER = "No 3D/CAD generation runtime is wired yet for this catalog adapter, so no artifact was created.";
267204
+ ModelGenerateTool = class {
267205
+ cwd;
267206
+ name = "generate_model";
267207
+ description = "Generate or prepare 3D/CAD models from prompts or input images. Use this for text-to-CAD, text-to-3D, image-to-3D, mesh, GLB, OBJ, STL, PLY, SCAD, or STEP requests. It exposes the same Hugging Face 3D/CAD catalog used by /models, checks disk/resource fit, and returns explicit runtime blockers instead of silently failing.";
267208
+ parameters = {
267209
+ type: "object",
267210
+ properties: {
267211
+ action: {
267212
+ type: "string",
267213
+ enum: ["generate", "list_models", "check", "setup"],
267214
+ description: "generate attempts a model-generation run; list_models shows selectable 3D/CAD adapters; check reports model, disk, and resource readiness."
267215
+ },
267216
+ kind: {
267217
+ type: "string",
267218
+ enum: ["3d", "cad"],
267219
+ description: "Use '3d' for mesh/asset/world generation and 'cad' for parametric/mechanical CAD."
267220
+ },
267221
+ prompt: {
267222
+ type: "string",
267223
+ description: "Text prompt describing the desired 3D asset or CAD part."
267224
+ },
267225
+ model: {
267226
+ type: "string",
267227
+ description: "Optional Hugging Face repo/model id. Defaults to the configured /models selection or the smallest fitting catalog candidate."
267228
+ },
267229
+ backend: {
267230
+ type: "string",
267231
+ description: "Optional backend filter such as diffusers, transformers, onnx, project, or generated."
267232
+ },
267233
+ input_image: {
267234
+ type: "string",
267235
+ description: "Optional local image path for image-to-3D reconstruction models."
267236
+ },
267237
+ input: {
267238
+ type: "string",
267239
+ description: "Optional local image/video path or upstream artifact path for reconstruction models."
267240
+ },
267241
+ output_format: {
267242
+ type: "string",
267243
+ enum: ["glb", "obj", "stl", "ply", "scad", "step", "png", "json"],
267244
+ description: "Desired output artifact format when the selected runtime supports it."
267245
+ },
267246
+ output_path: {
267247
+ type: "string",
267248
+ description: "Optional output file path for the generated artifact."
267249
+ }
267250
+ },
267251
+ required: []
267252
+ };
267253
+ progressCallback = null;
267254
+ defaults;
267255
+ constructor(cwd4 = process.cwd(), defaults3 = {}) {
267256
+ this.cwd = cwd4;
267257
+ this.defaults = defaults3;
267258
+ }
267259
+ setDefaults(defaults3) {
267260
+ this.defaults = defaults3;
267261
+ }
267262
+ setProgressCallback(handler) {
267263
+ this.progressCallback = handler;
267264
+ }
267265
+ async execute(args) {
267266
+ const start2 = performance.now();
267267
+ const action = normalizeAction(args["action"]);
267268
+ const kind = this.normalizeKind(args);
267269
+ try {
267270
+ if (action === "list_models") {
267271
+ const entries = rankCatalogEntries(kind);
267272
+ return {
267273
+ success: true,
267274
+ output: renderCatalogForKind(kind, entries),
267275
+ llmContent: JSON.stringify(entries.map((entry) => modelSummary(entry)), null, 2),
267276
+ durationMs: performance.now() - start2
267277
+ };
267278
+ }
267279
+ const selected = this.selectModel(kind, args);
267280
+ if (!selected) {
267281
+ return {
267282
+ success: false,
267283
+ output: "",
267284
+ error: `No ${kindLabel(kind)} model adapter is available. Use hf_model_discover or hf_model_intake to add one.`,
267285
+ durationMs: performance.now() - start2
267286
+ };
267287
+ }
267288
+ const spec = selected.spec;
267289
+ this.emit(start2, "setup", `Selected ${spec.id} (${spec.modality}/${spec.backend})`, 10);
267290
+ const readiness = await this.checkReadiness(spec, kind);
267291
+ const body = renderReadiness(kind, spec, readiness);
267292
+ if (action === "check") {
267293
+ return {
267294
+ success: true,
267295
+ output: body,
267296
+ llmContent: JSON.stringify({ model: modelSummary(selected), readiness }, null, 2),
267297
+ durationMs: performance.now() - start2
267298
+ };
267299
+ }
267300
+ const prompt = String(args["prompt"] ?? "").trim();
267301
+ const input = String(args["input_image"] ?? args["input"] ?? "").trim();
267302
+ if (!prompt && !input) {
267303
+ return {
267304
+ success: false,
267305
+ output: `${body}
267306
+
267307
+ No artifact created: provide prompt and/or input_image for ${kindLabel(kind)} generation.`,
267308
+ error: `prompt or input_image is required for ${kindLabel(kind)} generation`,
267309
+ llmContent: JSON.stringify({ model: modelSummary(selected), readiness, created: false, reason: "missing_prompt_or_input" }, null, 2),
267310
+ durationMs: performance.now() - start2
267311
+ };
267312
+ }
267313
+ this.emit(start2, "load", RUNTIME_BLOCKER, 100);
267314
+ const output = [
267315
+ body,
267316
+ "",
267317
+ RUNTIME_BLOCKER,
267318
+ "Use this tool result as the user-facing blocker. Do not claim that a GLB/OBJ/STL/SCAD/STEP file was generated."
267319
+ ].join("\n");
267320
+ return {
267321
+ success: false,
267322
+ output,
267323
+ error: RUNTIME_BLOCKER,
267324
+ llmContent: JSON.stringify({
267325
+ model: modelSummary(selected),
267326
+ readiness,
267327
+ created: false,
267328
+ reason: "runtime_not_wired",
267329
+ prompt,
267330
+ input,
267331
+ cwd: this.cwd
267332
+ }, null, 2),
267333
+ durationMs: performance.now() - start2
267334
+ };
267335
+ } catch (err) {
267336
+ return {
267337
+ success: false,
267338
+ output: "",
267339
+ error: err instanceof Error ? err.message : String(err),
267340
+ durationMs: performance.now() - start2
267341
+ };
267342
+ }
267343
+ }
267344
+ normalizeKind(args) {
267345
+ const rawKind = String(args["kind"] ?? "").trim().toLowerCase();
267346
+ if (rawKind === "cad" || rawKind.includes("cad") || rawKind.includes("step") || rawKind.includes("scad"))
267347
+ return "cad";
267348
+ if (rawKind === "3d" || rawKind.includes("mesh") || rawKind.includes("model") || rawKind.includes("world"))
267349
+ return "3d";
267350
+ const outputFormat = String(args["output_format"] ?? "").trim().toLowerCase();
267351
+ if (outputFormat === "scad" || outputFormat === "step")
267352
+ return "cad";
267353
+ if (outputFormat === "glb" || outputFormat === "obj" || outputFormat === "stl" || outputFormat === "ply")
267354
+ return "3d";
267355
+ const modelId = String(args["model"] ?? "").trim();
267356
+ if (modelId) {
267357
+ const entry = resolveMediaModel(modelId);
267358
+ if (entry?.spec.modality === "cad")
267359
+ return "cad";
267360
+ if (entry?.spec.modality === "3d")
267361
+ return "3d";
267362
+ }
267363
+ return "3d";
267364
+ }
267365
+ selectModel(kind, args) {
267366
+ const requested = String(args["model"] ?? "").trim();
267367
+ const configured = kind === "cad" ? this.defaults.cadModel : this.defaults.model3dModel;
267368
+ const model = requested || configured || "";
267369
+ if (model)
267370
+ return resolveMediaModel(model, kind);
267371
+ const requestedBackend = String(args["backend"] ?? "").trim();
267372
+ const backend = requestedBackend || (kind === "cad" ? this.defaults.cadBackend : this.defaults.model3dBackend);
267373
+ const entries = rankCatalogEntries(kind);
267374
+ if (backend && backend !== "auto") {
267375
+ const wantedBackend = backend.toLowerCase();
267376
+ const matching = entries.find((entry) => entry.spec.backend === wantedBackend);
267377
+ if (matching)
267378
+ return matching;
267379
+ }
267380
+ return entries[0];
267381
+ }
267382
+ async checkReadiness(spec, kind) {
267383
+ const blockers = [];
267384
+ const warnings = [];
267385
+ const notes2 = [];
267386
+ const disk = checkDisk(spec);
267387
+ if (!disk.ok)
267388
+ blockers.push(disk.reason);
267389
+ else
267390
+ notes2.push(disk.reason);
267391
+ const broker = getModelBroker();
267392
+ const loadSpec = modelLoadSpecFor(spec, kind);
267393
+ let brokerDecision = { kind: "unknown" };
267394
+ let clearInflight = false;
267395
+ try {
267396
+ const decision2 = await broker.ensureModelLoadable(loadSpec);
267397
+ clearInflight = decision2.kind === "ok";
267398
+ if (decision2.kind === "wait-for-inflight") {
267399
+ brokerDecision = { kind: "wait-for-inflight" };
267400
+ warnings.push(`Another load for ${spec.id} is already in flight.`);
267401
+ } else if (decision2.kind === "ok") {
267402
+ brokerDecision = { kind: "ok", gpuIndex: decision2.gpuIndex ?? null, note: decision2.note };
267403
+ notes2.push(decision2.note ? `Resource broker accepted ${spec.id}: ${decision2.note}.` : `Resource broker accepted ${spec.id}.`);
267404
+ } else if (decision2.kind === "evict") {
267405
+ brokerDecision = { kind: "evict", evictTargets: decision2.evictTargets.map((m2) => `${m2.host}:${m2.name}`), gpuIndex: decision2.gpuIndex ?? null };
267406
+ warnings.push(`Resource broker would need to evict ${decision2.evictTargets.length} loaded model(s) before loading ${spec.id}.`);
267407
+ } else if (decision2.kind === "degrade") {
267408
+ brokerDecision = { kind: "degrade", fallback: decision2.fallback.name, reason: decision2.reason };
267409
+ warnings.push(`Resource broker would degrade ${spec.id} to ${decision2.fallback.name}: ${decision2.reason}.`);
267410
+ } else {
267411
+ brokerDecision = { kind: "reject", reason: decision2.reason };
267412
+ blockers.push(`Resource broker rejected ${spec.id}: ${decision2.reason}.`);
267413
+ }
267414
+ } catch (err) {
267415
+ const message2 = err instanceof Error ? err.message : String(err);
267416
+ warnings.push(`Resource broker check failed: ${message2}`);
267417
+ } finally {
267418
+ if (clearInflight)
267419
+ broker.clearInflight(loadSpec.host, loadSpec.name);
267420
+ }
267421
+ if (spec.deployment.requiresToken) {
267422
+ blockers.push(`${spec.id} requires a Hugging Face token/license acceptance before download.`);
267423
+ }
267424
+ if (!spec.deployment.runtimeCompatible || !spec.deployment.safeRunner) {
267425
+ blockers.push(`${spec.id} has catalog metadata but no trusted built-in runtime adapter.`);
267426
+ }
267427
+ blockers.push(RUNTIME_BLOCKER);
267428
+ return {
267429
+ ok: blockers.length === 0,
267430
+ blockers,
267431
+ warnings,
267432
+ notes: notes2,
267433
+ disk,
267434
+ brokerDecision
267435
+ };
267436
+ }
267437
+ emit(start2, stage, message2, percent) {
267438
+ this.progressCallback?.({
267439
+ stage,
267440
+ message: message2,
267441
+ percent,
267442
+ elapsedMs: performance.now() - start2
267443
+ });
267444
+ }
267445
+ };
267446
+ }
267447
+ });
267448
+
267029
267449
  // packages/execution/dist/tools/sponsor-media.js
267030
267450
  import { createHash as createHash8 } from "node:crypto";
267031
267451
  function normalizeSponsorMediaConfig(value2) {
@@ -532003,6 +532423,7 @@ __export(dist_exports, {
532003
532423
  MemoryWriteTool: () => MemoryWriteTool,
532004
532424
  MeshtasticTool: () => MeshtasticTool,
532005
532425
  ModelBroker: () => ModelBroker,
532426
+ ModelGenerateTool: () => ModelGenerateTool,
532006
532427
  MultimodalMemoryTool: () => MultimodalMemoryTool,
532007
532428
  NetworkEgressPolicyError: () => NetworkEgressPolicyError,
532008
532429
  NexusTool: () => NexusTool,
@@ -532372,6 +532793,7 @@ var init_dist5 = __esm({
532372
532793
  init_cuda_device_filter();
532373
532794
  init_model_store();
532374
532795
  init_video_generate();
532796
+ init_model_generate();
532375
532797
  init_sponsor_media();
532376
532798
  init_structured_read();
532377
532799
  init_vision();
@@ -551450,7 +551872,8 @@ var init_agenticRunner = __esm({
551450
551872
  "visual_memory",
551451
551873
  "desktop_describe",
551452
551874
  "ocr_pdf",
551453
- "generate_image"
551875
+ "generate_image",
551876
+ "generate_model"
551454
551877
  ]);
551455
551878
  AUDIO_TOOLS = /* @__PURE__ */ new Set([
551456
551879
  "audio_capture",
@@ -561180,7 +561603,7 @@ ${marker}` : marker);
561180
561603
  */
561181
561604
  toolResultEventContent(toolName, output) {
561182
561605
  const displayOutput = this.unwrapToolOutputForDisplay(output);
561183
- if (toolName === "generate_image" || toolName === "generate_audio" || toolName === "generate_video" || toolName === "generate_tts" || toolName === "create_audio_file" || toolName === "screenshot" || toolName === "camera_capture" || /(?:Image generated|Music generated|Sound generated|Video generated|Screenshot saved|Saved to|Output saved to):?\s+/i.test(displayOutput)) {
561606
+ if (toolName === "generate_image" || toolName === "generate_audio" || toolName === "generate_video" || toolName === "generate_model" || toolName === "generate_tts" || toolName === "create_audio_file" || toolName === "screenshot" || toolName === "camera_capture" || /(?:Image generated|Music generated|Sound generated|Video generated|3D model generated|CAD generated|Model generated|Screenshot saved|Saved to|Output saved to):?\s+/i.test(displayOutput)) {
561184
561607
  return displayOutput.slice(0, 2e3);
561185
561608
  }
561186
561609
  return displayOutput.slice(0, 200);
@@ -569853,12 +570276,13 @@ function generationKindForToolName(toolName) {
569853
570276
  if (toolName === "generate_image") return "image";
569854
570277
  if (toolName === "generate_audio") return "audio";
569855
570278
  if (toolName === "generate_video") return "video";
570279
+ if (toolName === "generate_model") return "model";
569856
570280
  if (toolName === "generate_tts" || toolName === "create_audio_file") return "tts";
569857
570281
  return null;
569858
570282
  }
569859
570283
  function formatGenerativeProgress(kind, event, options2 = {}) {
569860
570284
  const width = Math.max(8, Math.min(32, options2.width ?? (options2.surface === "telegram" ? 12 : 20)));
569861
- const label = kindLabel(kind);
570285
+ const label = kindLabel2(kind);
569862
570286
  const stage = stageLabel(event.stage);
569863
570287
  const pct = finitePercent(event.percent);
569864
570288
  const bytes = formatProgressBytes(event);
@@ -569871,8 +570295,9 @@ function formatGenerativeProgress(kind, event, options2 = {}) {
569871
570295
  }
569872
570296
  return `${label} ${stage}: ${message2}${bytes}${elapsed}`;
569873
570297
  }
569874
- function kindLabel(kind) {
570298
+ function kindLabel2(kind) {
569875
570299
  if (kind === "tts") return "TTS";
570300
+ if (kind === "model") return "3D/CAD";
569876
570301
  return kind.slice(0, 1).toUpperCase() + kind.slice(1);
569877
570302
  }
569878
570303
  function stageLabel(stage) {
@@ -626471,6 +626896,7 @@ var init_tool_policy = __esm({
626471
626896
  "telegram",
626472
626897
  "generate_image",
626473
626898
  "generate_audio",
626899
+ "generate_model",
626474
626900
  "generate_tts",
626475
626901
  "create_audio_file",
626476
626902
  "telegram_send_file",
@@ -626503,6 +626929,7 @@ var init_tool_policy = __esm({
626503
626929
  "telegram",
626504
626930
  "generate_image",
626505
626931
  "generate_audio",
626932
+ "generate_model",
626506
626933
  "generate_tts",
626507
626934
  "create_audio_file",
626508
626935
  "telegram_send_file",
@@ -627484,7 +627911,7 @@ function formatTelegramCreativeWorkspacePrompt(root) {
627484
627911
  "Allowed: create and send non-executable creative artifacts in this workspace.",
627485
627912
  "At rest, artifacts are stored as random internal blobs with random header bytes; requested filenames are logical names restored only during Telegram upload.",
627486
627913
  "Forbidden: delete files, create scripts/executables, access paths outside this workspace, mutate the project tree, run shell/Python/code commands, or touch system state.",
627487
- "Freshly generated/created artifacts are recorded for automatic Telegram attachment when the turn completes. Do not call telegram_send_file for the same fresh artifact unless the user asked for a specific caption, existing/unrecorded file, or non-default target. Refer to the attachment naturally; do not expose filesystem paths unless the admin explicitly asks."
627914
+ "Freshly generated/created artifacts, including image/audio/video/3D/CAD outputs, are recorded for automatic Telegram attachment when the turn completes. Do not call telegram_send_file for the same fresh artifact unless the user asked for a specific caption, existing/unrecorded file, or non-default target. Refer to the attachment naturally; do not expose filesystem paths unless the admin explicitly asks."
627488
627915
  ].join("\n");
627489
627916
  }
627490
627917
  function publicCreativeArtifactPolicyError(path12) {
@@ -627514,7 +627941,7 @@ function collectGeneratedArtifactPathsFromText(text, root) {
627514
627941
  }
627515
627942
  }
627516
627943
  for (const line of text.split(/\r?\n/)) {
627517
- const marker = line.match(/(?:Image generated|Sound generated|Music generated|Video generated|TTS generated|Created [A-Z]+ file|Created|Overwrote|Saved to):\s*([^\n\r(]+)/i);
627944
+ const marker = line.match(/(?:Image generated|Sound generated|Music generated|Video generated|3D model generated|CAD generated|Model generated|TTS generated|Created [A-Z]+ file|Created|Overwrote|Saved to):\s*([^\n\r(]+)/i);
627518
627945
  const value2 = marker?.[1]?.trim().replace(/^["']|["']$/g, "");
627519
627946
  if (!value2) continue;
627520
627947
  const guarded = guardPath(rootAbs, value2);
@@ -627524,7 +627951,7 @@ function collectGeneratedArtifactPathsFromText(text, root) {
627524
627951
  }
627525
627952
  return [...paths];
627526
627953
  }
627527
- function buildTelegramCreativeTools(repoRoot, chatId, backendUrl2, imageDefaults = {}, audioDefaults = {}, videoDefaults = {}) {
627954
+ function buildTelegramCreativeTools(repoRoot, chatId, backendUrl2, imageDefaults = {}, audioDefaults = {}, videoDefaults = {}, modelDefaults = {}) {
627528
627955
  const root = telegramCreativeWorkspaceRoot(repoRoot, chatId);
627529
627956
  ensureManifest(root);
627530
627957
  return [
@@ -627535,6 +627962,7 @@ function buildTelegramCreativeTools(repoRoot, chatId, backendUrl2, imageDefaults
627535
627962
  scopedTool(new AudioGenerateTool(root, audioDefaults), root, "generate"),
627536
627963
  scopedTool(new TtsGenerateTool(), root, "generate"),
627537
627964
  scopedTool(new VideoGenerateTool(root, videoDefaults), root, "generate"),
627965
+ scopedTool(new ModelGenerateTool(root, modelDefaults), root, "generate"),
627538
627966
  new CreativeAudioFileTool(root)
627539
627967
  ];
627540
627968
  }
@@ -627546,7 +627974,7 @@ function scopedTool(base3, root, mode) {
627546
627974
  parameters: base3.parameters,
627547
627975
  async execute(args) {
627548
627976
  const next = { ...args };
627549
- if (base3.name === "generate_image" || base3.name === "generate_audio" || base3.name === "generate_tts" || base3.name === "generate_video") {
627977
+ if (base3.name === "generate_image" || base3.name === "generate_audio" || base3.name === "generate_tts" || base3.name === "generate_video" || base3.name === "generate_model") {
627550
627978
  const cleanup = [];
627551
627979
  const localModel = typeof next["model_path"] === "string" ? String(next["model_path"]) : typeof next["model"] === "string" && looksLikeLocalPath(String(next["model"])) ? String(next["model"]) : "";
627552
627980
  if (localModel) {
@@ -627566,6 +627994,26 @@ function scopedTool(base3, root, mode) {
627566
627994
  if (materialized.cleanup) cleanup.push(materialized.cleanup);
627567
627995
  }
627568
627996
  }
627997
+ if (base3.name === "generate_model") {
627998
+ for (const key of MODEL_INPUT_PATH_KEYS) {
627999
+ const value2 = next[key];
628000
+ if (typeof value2 !== "string" || !value2.trim()) continue;
628001
+ if (/^https?:\/\//i.test(value2.trim())) continue;
628002
+ const materialized = materializeTelegramCreativeArtifactForSend(rootAbs, value2.trim());
628003
+ if (!materialized.ok) return denied(materialized.error);
628004
+ next[key] = materialized.path;
628005
+ if (materialized.cleanup) cleanup.push(materialized.cleanup);
628006
+ }
628007
+ const output = typeof next["output_path"] === "string" && String(next["output_path"]).trim() ? String(next["output_path"]) : typeof next["output"] === "string" && String(next["output"]).trim() ? String(next["output"]) : "";
628008
+ if (output) {
628009
+ const guardedOutput = guardPath(rootAbs, output);
628010
+ if (!guardedOutput.ok) return denied(guardedOutput.error);
628011
+ if (existsSync121(guardedOutput.path.abs) && !manifestHas(rootAbs, guardedOutput.path.rel)) {
628012
+ return denied(`Refusing to overwrite a file that is not owned by this chat workspace manifest: ${guardedOutput.path.rel}`);
628013
+ }
628014
+ next["output_path"] = guardedOutput.path.abs;
628015
+ }
628016
+ }
627569
628017
  if (base3.name === "generate_tts") {
627570
628018
  for (const key of TTS_CLONE_SOURCE_KEYS) {
627571
628019
  const value2 = next[key];
@@ -627876,7 +628324,7 @@ function denied(error) {
627876
628324
  mutatedFiles: []
627877
628325
  };
627878
628326
  }
627879
- var MANIFEST_FILE, OBJECTS_DIR, SEND_DIR, PATH_KEYS, TTS_CLONE_SOURCE_KEYS, VIDEO_IMAGE_INPUT_KEYS, MEDIA_PATH_RE, PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS, CreativeAudioFileTool;
628327
+ var MANIFEST_FILE, OBJECTS_DIR, SEND_DIR, PATH_KEYS, TTS_CLONE_SOURCE_KEYS, VIDEO_IMAGE_INPUT_KEYS, MODEL_INPUT_PATH_KEYS, MEDIA_PATH_RE, PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS, CreativeAudioFileTool;
627880
628328
  var init_telegram_creative_tools = __esm({
627881
628329
  "packages/cli/src/tui/telegram-creative-tools.ts"() {
627882
628330
  "use strict";
@@ -627887,6 +628335,7 @@ var init_telegram_creative_tools = __esm({
627887
628335
  PATH_KEYS = ["path", "file", "file_path", "filename", "filepath", "filePath"];
627888
628336
  TTS_CLONE_SOURCE_KEYS = ["sample", "source_audio", "voice_sample", "reference_audio", "ref_audio", "clone_sample"];
627889
628337
  VIDEO_IMAGE_INPUT_KEYS = ["image", "image_path", "init_image", "source_image", "reference_image"];
628338
+ MODEL_INPUT_PATH_KEYS = ["input", "input_image", "image", "image_path", "source_image", "reference_image"];
627890
628339
  MEDIA_PATH_RE = /(?:^|[\s([])(\/[^\s<>"')\]]+\.[A-Za-z0-9]{1,12})(?:$|[\s),.\]])/g;
627891
628340
  PUBLIC_EXECUTABLE_ARTIFACT_EXTENSIONS = /* @__PURE__ */ new Set([
627892
628341
  ".sh",
@@ -633007,6 +633456,10 @@ When asked to generate speech, narration, or TTS, use generate_tts or audio_play
633007
633456
  Those tools handle first-use backend setup where supported. Do not fall back to shell
633008
633457
  commands or generic audio generation for speech synthesis while those tools are available.
633009
633458
 
633459
+ When asked to generate a 3D asset, mesh, printable model, or CAD part, use
633460
+ generate_model. Use hf_model_discover or hf_model_intake only when the admin is
633461
+ asking to add, inspect, validate, or save model adapters.
633462
+
633010
633463
  Keep responses concise for Telegram but don't withhold information from the admin.
633011
633464
  `.trim();
633012
633465
  ADMIN_GROUP_PROMPT = `
@@ -640797,9 +641250,10 @@ ${currentTelegramPrompt}`;
640797
641250
  "You have the full scoped Telegram media-analysis stack by default: telegram_media_recent, image_read, ocr, ocr_image_advanced, vision, pdf_to_text, ocr_pdf, transcribe_file, video_understand, audio_analyze, and identity_memory. For complex textual imagery, screenshots, forms, scans, or dense labels, prefer ocr_image_advanced after resolving media with path='reply' or path='latest'.",
640798
641251
  formatIdentityMemoryContext(chatLabel || "Telegram private chat"),
640799
641252
  reminderToolContract,
640800
- "If the user asks you to create an image, audio file, video, or document artifact, create it with the scoped creative tools. Freshly generated artifacts are recorded and automatically attached to this Telegram chat when the turn completes, so do not call telegram_send_file for those same artifacts unless the user asked for a specific caption, existing/unrecorded file, or non-default target.",
641253
+ "If the user asks you to create an image, audio file, video, 3D/CAD model, or document artifact, create it with the scoped creative tools. Freshly generated artifacts are recorded and automatically attached to this Telegram chat when the turn completes, so do not call telegram_send_file for those same artifacts unless the user asked for a specific caption, existing/unrecorded file, or non-default target.",
640801
641254
  "For image generation requests, decide from the conversation whether generate_image is appropriate; do not ask the user to use a hardcoded shortcut when the request is clear.",
640802
641255
  "For video generation requests, decide whether generate_video is appropriate. Use mode='i2v' (image-to-video) when the user references an attached or already-generated image; otherwise mode='t2v'. Warn the user up front that video generation typically takes 2-10 minutes on consumer GPUs, and that the public Telegram video-generation quota is tight (2/hour/user).",
641256
+ "For 3D asset, mesh, printable model, or CAD generation requests, use generate_model. Use kind='cad' for parametric/mechanical parts and kind='3d' for mesh/asset/world requests. If the tool reports a catalog/runtime blocker, explain that blocker plainly.",
640803
641257
  creativeWorkspace
640804
641258
  ].filter(Boolean).join("\n\n");
640805
641259
  userPrompt = `${systemPrompt}${discretionPrompt}
@@ -641487,6 +641941,7 @@ Scoped workspace: ${scopedRoot}`,
641487
641941
  const imageDefaults = this.imageGenerationDefaultsForRepo(repoRoot);
641488
641942
  const audioDefaults = this.audioGenerationDefaultsForRepo(repoRoot);
641489
641943
  const videoDefaults = this.videoGenerationDefaultsForRepo(repoRoot);
641944
+ const modelDefaults = this.modelGenerationDefaultsForRepo(repoRoot);
641490
641945
  const generativeProgress = this.createTelegramGenerativeProgressBridge(chatId, msg);
641491
641946
  const taskComplete = {
641492
641947
  name: "task_complete",
@@ -641605,6 +642060,7 @@ Scoped workspace: ${scopedRoot}`,
641605
642060
  new ImageGenerateTool(repoRoot, this.agentConfig?.backendUrl, imageDefaults),
641606
642061
  new AudioGenerateTool(repoRoot, audioDefaults),
641607
642062
  new VideoGenerateTool(repoRoot, videoDefaults),
642063
+ new ModelGenerateTool(repoRoot, modelDefaults),
641608
642064
  new HfModelIntakeTool(),
641609
642065
  new HfModelDiscoverTool(),
641610
642066
  new TtsGenerateTool(),
@@ -641648,7 +642104,8 @@ Scoped workspace: ${scopedRoot}`,
641648
642104
  this.agentConfig?.backendUrl,
641649
642105
  imageDefaults,
641650
642106
  audioDefaults,
641651
- videoDefaults
642107
+ videoDefaults,
642108
+ modelDefaults
641652
642109
  ).map((tool) => adaptTool5(tool, todoSessionId, generativeProgress));
641653
642110
  adaptedTools.push(...creativeTools);
641654
642111
  adaptedTools.push(adaptTool5(this.buildTelegramSendFileTool(context2, repoRoot, chatId, msg), todoSessionId, generativeProgress));
@@ -641706,7 +642163,7 @@ Scoped workspace: ${scopedRoot}`,
641706
642163
  if (toolName === "web_fetch") return "web";
641707
642164
  if (/^(image_read|ocr|ocr_image_advanced|ocr_pdf|pdf_to_text|vision|transcribe_file|video_understand|audio_analyze)$/.test(toolName)) return "media";
641708
642165
  if (toolName === "generate_video") return "video-generation";
641709
- if (/^(generate_image|generate_audio|generate_tts|create_audio_file)$/.test(toolName)) return "generation";
642166
+ if (/^(generate_image|generate_audio|generate_model|generate_tts|create_audio_file)$/.test(toolName)) return "generation";
641710
642167
  if (toolName === "telegram_send_file") return "upload";
641711
642168
  if (/^(reminder|remind|reminders)$/.test(toolName)) return "reminder";
641712
642169
  return null;
@@ -643061,6 +643518,15 @@ Scoped workspace: ${scopedRoot}`,
643061
643518
  defaultKind: settings.videoKind
643062
643519
  };
643063
643520
  }
643521
+ modelGenerationDefaultsForRepo(repoRoot) {
643522
+ const settings = resolveSettings(repoRoot);
643523
+ return {
643524
+ cadModel: typeof settings.cadModel === "string" && settings.cadModel.trim() ? settings.cadModel : void 0,
643525
+ cadBackend: settings.cadBackend,
643526
+ model3dModel: typeof settings.model3dModel === "string" && settings.model3dModel.trim() ? settings.model3dModel : void 0,
643527
+ model3dBackend: settings.model3dBackend
643528
+ };
643529
+ }
643064
643530
  buildTelegramSendFileTool(context2, repoRoot, currentChatId, currentMsg) {
643065
643531
  const bridge = this;
643066
643532
  const adminDm = context2 === "telegram-admin-dm";
@@ -670267,6 +670733,18 @@ function videoGenerationDefaultsForRepo(repoRoot) {
670267
670733
  function createConfiguredVideoGenerateTool(repoRoot) {
670268
670734
  return new VideoGenerateTool(repoRoot, videoGenerationDefaultsForRepo(repoRoot));
670269
670735
  }
670736
+ function modelGenerationDefaultsForRepo(repoRoot) {
670737
+ const settings = resolveSettings(repoRoot);
670738
+ return {
670739
+ cadModel: typeof settings.cadModel === "string" && settings.cadModel.trim() ? settings.cadModel : void 0,
670740
+ cadBackend: settings.cadBackend,
670741
+ model3dModel: typeof settings.model3dModel === "string" && settings.model3dModel.trim() ? settings.model3dModel : void 0,
670742
+ model3dBackend: settings.model3dBackend
670743
+ };
670744
+ }
670745
+ function createConfiguredModelGenerateTool(repoRoot) {
670746
+ return new ModelGenerateTool(repoRoot, modelGenerationDefaultsForRepo(repoRoot));
670747
+ }
670270
670748
  function buildSubAgentTools(repoRoot, config) {
670271
670749
  return [
670272
670750
  // File + search
@@ -670366,6 +670844,7 @@ function buildSubAgentTools(repoRoot, config) {
670366
670844
  createConfiguredImageGenerateTool(repoRoot, config.backendUrl),
670367
670845
  createConfiguredAudioGenerateTool(repoRoot),
670368
670846
  createConfiguredVideoGenerateTool(repoRoot),
670847
+ createConfiguredModelGenerateTool(repoRoot),
670369
670848
  // Hardware sensors + radios (read-only scans)
670370
670849
  new GpsLocationTool(),
670371
670850
  new WifiControlTool(),
@@ -670455,6 +670934,8 @@ function buildTools(repoRoot, config, contextWindowSize, modelTier) {
670455
670934
  createConfiguredAudioGenerateTool(repoRoot),
670456
670935
  // Video Generation — local Diffusers/ComfyUI video pipelines
670457
670936
  createConfiguredVideoGenerateTool(repoRoot),
670937
+ // 3D/CAD Generation — Hugging Face catalog-backed model readiness surface
670938
+ createConfiguredModelGenerateTool(repoRoot),
670458
670939
  // Structured file reading (CSV, JSON, Markdown, binary detection)
670459
670940
  new StructuredReadTool(repoRoot),
670460
670941
  // Vision tools (Moondream — desktop awareness + point-and-click)
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.190",
3
+ "version": "1.0.191",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.190",
9
+ "version": "1.0.191",
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.190",
3
+ "version": "1.0.191",
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",