omnius 1.0.193 → 1.0.195
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/README.md +2 -2
- package/dist/index.js +238 -34
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -158,8 +158,8 @@ pnpm -r build
|
|
|
158
158
|
node scripts/build-publish.mjs
|
|
159
159
|
cd publish
|
|
160
160
|
mkdir -p .npm-cache
|
|
161
|
-
NPM_CONFIG_CACHE=$(pwd)/.npm-cache npm pack
|
|
162
|
-
NPM_CONFIG_CACHE=$(pwd)/.npm-cache npm publish --access public
|
|
161
|
+
NPM_CONFIG_CACHE=$(pwd)/.npm-cache npm pack --prefer-online --cache-min=0 --registry https://registry.npmjs.org/
|
|
162
|
+
NPM_CONFIG_CACHE=$(pwd)/.npm-cache npm publish --access public --prefer-online --cache-min=0 --registry https://registry.npmjs.org/
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
Before publishing, verify `README.md`, `package.json`, `dist/index.js`, and `dist/launcher.cjs` are in the tarball, and that `package.json` includes `readmeFilename: "README.md"` plus a string `readme`.
|
package/dist/index.js
CHANGED
|
@@ -23930,7 +23930,25 @@ function listMediaModelCatalog(modality) {
|
|
|
23930
23930
|
function resolveMediaModel(id, modality) {
|
|
23931
23931
|
const wanted = normalizeModality(modality);
|
|
23932
23932
|
const normalized = normalizeRepoId(id);
|
|
23933
|
-
|
|
23933
|
+
const normalizedLower = normalized.toLowerCase();
|
|
23934
|
+
const normalizedSlug = mediaModelSlug(normalized).toLowerCase();
|
|
23935
|
+
return listMediaModelCatalog(wanted).find((entry) => {
|
|
23936
|
+
const spec = entry.spec;
|
|
23937
|
+
const keys = [
|
|
23938
|
+
spec.id,
|
|
23939
|
+
spec.repoId,
|
|
23940
|
+
basename4(spec.repoId),
|
|
23941
|
+
spec.label,
|
|
23942
|
+
mediaModelSlug(spec.id),
|
|
23943
|
+
mediaModelSlug(spec.repoId),
|
|
23944
|
+
mediaModelSlug(basename4(spec.repoId)),
|
|
23945
|
+
mediaModelSlug(spec.label)
|
|
23946
|
+
];
|
|
23947
|
+
return keys.some((key) => {
|
|
23948
|
+
const candidate = String(key ?? "").trim();
|
|
23949
|
+
return candidate.toLowerCase() === normalizedLower || mediaModelSlug(candidate).toLowerCase() === normalizedSlug;
|
|
23950
|
+
});
|
|
23951
|
+
});
|
|
23934
23952
|
}
|
|
23935
23953
|
function listRuntimeMediaModelSpecs(modality) {
|
|
23936
23954
|
return listMediaModelCatalog(modality).map((entry) => entry.spec).filter((spec) => (spec.status === "active" || spec.status === "metadata-only") && mediaBackendCompatibleWithModality(spec.backend, modality));
|
|
@@ -267033,7 +267051,9 @@ ${llmAnnotation}` : result.llmContent;
|
|
|
267033
267051
|
import { mkdir as mkdir15, stat as stat5, writeFile as writeFile20 } from "node:fs/promises";
|
|
267034
267052
|
import { dirname as dirname9, extname as extname5, isAbsolute as isAbsolute2, join as join44, resolve as resolvePath } from "node:path";
|
|
267035
267053
|
function builtInRuntimeFor(spec, kind) {
|
|
267036
|
-
if (
|
|
267054
|
+
if (spec.modality !== "cad")
|
|
267055
|
+
return null;
|
|
267056
|
+
if (kind !== "cad" && !spec.modalities.includes("3d"))
|
|
267037
267057
|
return null;
|
|
267038
267058
|
const id = spec.id.toLowerCase();
|
|
267039
267059
|
if (id === "campedersen/cad0" || id === "campedersen/cad0-mini")
|
|
@@ -267061,9 +267081,9 @@ function resolveModelOutputPath(cwd4, outputPath3, ext) {
|
|
|
267061
267081
|
}
|
|
267062
267082
|
return resolved;
|
|
267063
267083
|
}
|
|
267064
|
-
function formatCadSuccessOutput(args) {
|
|
267084
|
+
function formatCadSuccessOutput(args, warnings = []) {
|
|
267065
267085
|
const prompt = args.prompt.length > 140 ? args.prompt.slice(0, 137) + "..." : args.prompt;
|
|
267066
|
-
|
|
267086
|
+
const lines = [
|
|
267067
267087
|
`CAD generated: ${args.filepath}`,
|
|
267068
267088
|
` Model: ${args.model}`,
|
|
267069
267089
|
` Backend: ${args.backend}`,
|
|
@@ -267072,7 +267092,13 @@ function formatCadSuccessOutput(args) {
|
|
|
267072
267092
|
` Prompt: "${prompt}"`,
|
|
267073
267093
|
" Compact IR:",
|
|
267074
267094
|
...args.compactIr.split("\n").map((line) => ` ${line}`)
|
|
267075
|
-
]
|
|
267095
|
+
];
|
|
267096
|
+
if (warnings.length > 0) {
|
|
267097
|
+
lines.push(" Notes:");
|
|
267098
|
+
for (const warning of warnings)
|
|
267099
|
+
lines.push(` - ${warning}`);
|
|
267100
|
+
}
|
|
267101
|
+
return lines.join("\n");
|
|
267076
267102
|
}
|
|
267077
267103
|
function buildCadScadArtifact(prompt, modelId) {
|
|
267078
267104
|
const profile = inferCadProfile(prompt);
|
|
@@ -267104,7 +267130,7 @@ function inferCadProfile(prompt) {
|
|
|
267104
267130
|
const lower = prompt.toLowerCase();
|
|
267105
267131
|
const dims = inferDimensions(prompt);
|
|
267106
267132
|
const holeCount = inferHoleCount(lower);
|
|
267107
|
-
const family = /gear|tooth|teeth|cog/.test(lower) ? "gear" : /flange|bolt circle|bcd/.test(lower) ? "flange" : /standoff|spacer|bushing|sleeve/.test(lower) ? "standoff" : /enclosure|box|case|shell/.test(lower) ? "enclosure" : /l-bracket|bracket|angle/.test(lower) ? "bracket" : "plate";
|
|
267133
|
+
const family = /sphere|ball|orb|globe|planet/.test(lower) ? "sphere" : /cone|conical/.test(lower) ? "cone" : /cylinder|tube|pipe|rod/.test(lower) ? "cylinder" : /cube|boxy block|block/.test(lower) && !/enclosure|case|shell/.test(lower) ? "cube" : /gear|tooth|teeth|cog/.test(lower) ? "gear" : /flange|bolt circle|bcd/.test(lower) ? "flange" : /standoff|spacer|bushing|sleeve/.test(lower) ? "standoff" : /enclosure|box|case|shell/.test(lower) ? "enclosure" : /l-bracket|bracket|angle/.test(lower) ? "bracket" : "plate";
|
|
267108
267134
|
const holeRadius = inferHoleRadius(lower);
|
|
267109
267135
|
return {
|
|
267110
267136
|
family,
|
|
@@ -267201,7 +267227,11 @@ function holePositions(count, width, depth) {
|
|
|
267201
267227
|
}
|
|
267202
267228
|
function compactIrForProfile(profile) {
|
|
267203
267229
|
const lines = [];
|
|
267204
|
-
if (profile.family === "
|
|
267230
|
+
if (profile.family === "sphere") {
|
|
267231
|
+
lines.push(`S ${num(profile.width / 2)}`);
|
|
267232
|
+
} else if (profile.family === "cone") {
|
|
267233
|
+
lines.push(`CN ${num(profile.width / 2)} 0 ${num(profile.height)}`);
|
|
267234
|
+
} else if (profile.family === "standoff" || profile.family === "flange" || profile.family === "gear" || profile.family === "cylinder") {
|
|
267205
267235
|
lines.push(`Y ${num(profile.width / 2)} ${num(profile.height)}`);
|
|
267206
267236
|
} else {
|
|
267207
267237
|
lines.push(`C ${num(profile.width)} ${num(profile.depth)} ${num(profile.height)}`);
|
|
@@ -267223,6 +267253,38 @@ function compactIrForProfile(profile) {
|
|
|
267223
267253
|
return lines.join("\n");
|
|
267224
267254
|
}
|
|
267225
267255
|
function scadBodyForProfile(profile) {
|
|
267256
|
+
if (profile.family === "sphere") {
|
|
267257
|
+
return [
|
|
267258
|
+
"difference() {",
|
|
267259
|
+
" sphere(r = width / 2);",
|
|
267260
|
+
" for (p = hole_positions) translate([p[0], p[1], -width / 2 - 0.2]) cylinder(h = width + 0.4, r = hole_r);",
|
|
267261
|
+
"}"
|
|
267262
|
+
].join("\n");
|
|
267263
|
+
}
|
|
267264
|
+
if (profile.family === "cube") {
|
|
267265
|
+
return [
|
|
267266
|
+
"difference() {",
|
|
267267
|
+
" translate([-width / 2, -width / 2, -width / 2]) cube([width, width, width]);",
|
|
267268
|
+
" for (p = hole_positions) translate([p[0], p[1], -width / 2 - 0.2]) cylinder(h = width + 0.4, r = hole_r);",
|
|
267269
|
+
"}"
|
|
267270
|
+
].join("\n");
|
|
267271
|
+
}
|
|
267272
|
+
if (profile.family === "cylinder") {
|
|
267273
|
+
return [
|
|
267274
|
+
"difference() {",
|
|
267275
|
+
" cylinder(h = height, r = width / 2);",
|
|
267276
|
+
" for (p = hole_positions) translate([p[0], p[1], -0.2]) cylinder(h = height + 0.4, r = hole_r);",
|
|
267277
|
+
"}"
|
|
267278
|
+
].join("\n");
|
|
267279
|
+
}
|
|
267280
|
+
if (profile.family === "cone") {
|
|
267281
|
+
return [
|
|
267282
|
+
"difference() {",
|
|
267283
|
+
" cylinder(h = height, r1 = width / 2, r2 = 0);",
|
|
267284
|
+
" for (p = hole_positions) translate([p[0], p[1], -0.2]) cylinder(h = height + 0.4, r = hole_r);",
|
|
267285
|
+
"}"
|
|
267286
|
+
].join("\n");
|
|
267287
|
+
}
|
|
267226
267288
|
if (profile.family === "standoff") {
|
|
267227
267289
|
return [
|
|
267228
267290
|
"difference() {",
|
|
@@ -267333,10 +267395,62 @@ function statusRank(status) {
|
|
|
267333
267395
|
return 2;
|
|
267334
267396
|
return 3;
|
|
267335
267397
|
}
|
|
267398
|
+
function isGenericModelSelector(value2, kind) {
|
|
267399
|
+
const raw = value2.trim().toLowerCase();
|
|
267400
|
+
if (!raw)
|
|
267401
|
+
return true;
|
|
267402
|
+
if (raw === "auto" || raw === "default" || raw === "best" || raw === "smallest" || raw === "recommended")
|
|
267403
|
+
return true;
|
|
267404
|
+
if (kind === "3d") {
|
|
267405
|
+
return /^(3d|3d model|3d-model|model|mesh|asset|object|world|glb|obj|stl|ply)$/.test(raw);
|
|
267406
|
+
}
|
|
267407
|
+
return /^(cad|cad model|text-to-cad|parametric cad|scad|step|openscad)$/.test(raw);
|
|
267408
|
+
}
|
|
267409
|
+
function looksLikeExplicitModelAdapter(value2) {
|
|
267410
|
+
const raw = value2.trim();
|
|
267411
|
+
if (!raw)
|
|
267412
|
+
return false;
|
|
267413
|
+
if (/^https?:\/\/huggingface\.co\//i.test(raw))
|
|
267414
|
+
return true;
|
|
267415
|
+
if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+(?:\/.*)?$/.test(raw))
|
|
267416
|
+
return true;
|
|
267417
|
+
if (/^(hf|huggingface):/i.test(raw))
|
|
267418
|
+
return true;
|
|
267419
|
+
return false;
|
|
267420
|
+
}
|
|
267421
|
+
function shouldUseTextCadFallbackFor3d(kind, prompt, input, args) {
|
|
267422
|
+
if (kind !== "3d")
|
|
267423
|
+
return false;
|
|
267424
|
+
if (!prompt || input)
|
|
267425
|
+
return false;
|
|
267426
|
+
const outputFormat = String(args["output_format"] ?? "").trim().toLowerCase().replace(/^\./, "");
|
|
267427
|
+
if (outputFormat && outputFormat !== "scad" && outputFormat !== "step")
|
|
267428
|
+
return false;
|
|
267429
|
+
const outputPath3 = String(args["output_path"] ?? "").trim().toLowerCase();
|
|
267430
|
+
if (/\.(glb|obj|stl|ply|png|json)$/.test(outputPath3))
|
|
267431
|
+
return false;
|
|
267432
|
+
return true;
|
|
267433
|
+
}
|
|
267434
|
+
function renderSelectionError(kind, selection) {
|
|
267435
|
+
const label = adapterLabel(kind);
|
|
267436
|
+
const available = selection.available.slice(0, 8).map((entry) => entry.spec.id);
|
|
267437
|
+
if (selection.missingRequested) {
|
|
267438
|
+
return [
|
|
267439
|
+
`Requested ${label} "${selection.missingRequested}" was not found in the /models catalog.`,
|
|
267440
|
+
available.length > 0 ? `Available ${label}s include: ${available.join(", ")}.` : `No ${label}s are currently available.`,
|
|
267441
|
+
`Call generate_model with action='list_models' and kind='${kind}' to inspect selectable adapters.`
|
|
267442
|
+
].join("\n");
|
|
267443
|
+
}
|
|
267444
|
+
return [
|
|
267445
|
+
`No ${label} is available in the /models catalog.`,
|
|
267446
|
+
"Built-in CAD/3D adapters may be disabled by OMNIUS_MEDIA_MODEL_DISABLE_BUILTINS=1.",
|
|
267447
|
+
"Use hf_model_discover or hf_model_intake to add a Hugging Face media model adapter."
|
|
267448
|
+
].join("\n");
|
|
267449
|
+
}
|
|
267336
267450
|
function renderCatalogForKind(kind, entries) {
|
|
267337
267451
|
if (entries.length === 0)
|
|
267338
|
-
return `No ${
|
|
267339
|
-
const lines = [`Available ${
|
|
267452
|
+
return `No ${adapterLabel(kind)}s are available.`;
|
|
267453
|
+
const lines = [`Available ${adapterLabel(kind)}s:`];
|
|
267340
267454
|
for (const entry of entries) {
|
|
267341
267455
|
const spec = entry.spec;
|
|
267342
267456
|
const resources = resourceSummary(spec);
|
|
@@ -267474,6 +267588,9 @@ function resourceSummary(spec) {
|
|
|
267474
267588
|
function kindLabel(kind) {
|
|
267475
267589
|
return kind === "cad" ? "CAD" : "3D model";
|
|
267476
267590
|
}
|
|
267591
|
+
function adapterLabel(kind) {
|
|
267592
|
+
return kind === "cad" ? "CAD model adapter" : "3D model adapter";
|
|
267593
|
+
}
|
|
267477
267594
|
var RUNTIME_BLOCKER, ModelGenerateTool;
|
|
267478
267595
|
var init_model_generate = __esm({
|
|
267479
267596
|
"packages/execution/dist/tools/model-generate.js"() {
|
|
@@ -267557,18 +267674,28 @@ var init_model_generate = __esm({
|
|
|
267557
267674
|
durationMs: performance.now() - start2
|
|
267558
267675
|
};
|
|
267559
267676
|
}
|
|
267560
|
-
const
|
|
267561
|
-
if (!
|
|
267677
|
+
const selection = this.selectModel(kind, args);
|
|
267678
|
+
if (!selection.entry) {
|
|
267679
|
+
const error = renderSelectionError(kind, selection);
|
|
267562
267680
|
return {
|
|
267563
267681
|
success: false,
|
|
267564
|
-
output:
|
|
267565
|
-
error
|
|
267682
|
+
output: error,
|
|
267683
|
+
error,
|
|
267684
|
+
llmContent: JSON.stringify({
|
|
267685
|
+
kind,
|
|
267686
|
+
created: false,
|
|
267687
|
+
reason: selection.missingRequested ? "model_adapter_not_found" : "no_model_adapters",
|
|
267688
|
+
requestedModel: selection.missingRequested,
|
|
267689
|
+
available: selection.available.map((entry) => modelSummary(entry))
|
|
267690
|
+
}, null, 2),
|
|
267566
267691
|
durationMs: performance.now() - start2
|
|
267567
267692
|
};
|
|
267568
267693
|
}
|
|
267694
|
+
const selected = selection.entry;
|
|
267569
267695
|
const spec = selected.spec;
|
|
267570
267696
|
this.emit(start2, "setup", `Selected ${spec.id} (${spec.modality}/${spec.backend})`, 10);
|
|
267571
267697
|
const readiness = await this.checkReadiness(spec, kind);
|
|
267698
|
+
readiness.warnings.unshift(...selection.warnings);
|
|
267572
267699
|
const body = renderReadiness(kind, spec, readiness);
|
|
267573
267700
|
if (action === "check") {
|
|
267574
267701
|
return {
|
|
@@ -267590,7 +267717,7 @@ No artifact created: ${readiness.blockers[0] ?? `${kindLabel(kind)} generation i
|
|
|
267590
267717
|
durationMs: performance.now() - start2
|
|
267591
267718
|
};
|
|
267592
267719
|
}
|
|
267593
|
-
const prompt = String(args["prompt"] ?? "").trim();
|
|
267720
|
+
const prompt = String(args["prompt"] ?? "").trim() || selection.promptFromModelValue || "";
|
|
267594
267721
|
const input = String(args["input_image"] ?? args["input"] ?? "").trim();
|
|
267595
267722
|
if (!prompt && !input) {
|
|
267596
267723
|
return {
|
|
@@ -267667,21 +267794,46 @@ No artifact created: provide prompt and/or input_image for ${kindLabel(kind)} ge
|
|
|
267667
267794
|
return "3d";
|
|
267668
267795
|
}
|
|
267669
267796
|
selectModel(kind, args) {
|
|
267797
|
+
const warnings = [];
|
|
267798
|
+
const entries = rankCatalogEntries(kind);
|
|
267670
267799
|
const requested = String(args["model"] ?? "").trim();
|
|
267671
267800
|
const configured = kind === "cad" ? this.defaults.cadModel : this.defaults.model3dModel;
|
|
267672
|
-
|
|
267673
|
-
if (
|
|
267674
|
-
|
|
267801
|
+
let promptFromModelValue;
|
|
267802
|
+
if (requested && !isGenericModelSelector(requested, kind)) {
|
|
267803
|
+
const resolved = resolveMediaModel(requested, kind);
|
|
267804
|
+
if (resolved)
|
|
267805
|
+
return { entry: resolved, available: entries, warnings };
|
|
267806
|
+
if (looksLikeExplicitModelAdapter(requested)) {
|
|
267807
|
+
return { available: entries, warnings, missingRequested: requested };
|
|
267808
|
+
}
|
|
267809
|
+
promptFromModelValue = requested;
|
|
267810
|
+
warnings.push(`Treating model="${requested}" as the requested object description, not a /models adapter id; using automatic selection.`);
|
|
267811
|
+
}
|
|
267812
|
+
if (configured && !isGenericModelSelector(configured, kind)) {
|
|
267813
|
+
const resolved = resolveMediaModel(configured, kind);
|
|
267814
|
+
if (resolved)
|
|
267815
|
+
return { entry: resolved, available: entries, warnings };
|
|
267816
|
+
warnings.push(`Configured /models ${kind} adapter "${configured}" is not available; falling back to automatic selection.`);
|
|
267817
|
+
}
|
|
267675
267818
|
const requestedBackend = String(args["backend"] ?? "").trim();
|
|
267676
267819
|
const backend = requestedBackend || (kind === "cad" ? this.defaults.cadBackend : this.defaults.model3dBackend);
|
|
267677
|
-
const entries = rankCatalogEntries(kind);
|
|
267678
267820
|
if (backend && backend !== "auto") {
|
|
267679
267821
|
const wantedBackend = backend.toLowerCase();
|
|
267680
267822
|
const matching = entries.find((entry) => entry.spec.backend === wantedBackend);
|
|
267681
267823
|
if (matching)
|
|
267682
|
-
return matching;
|
|
267824
|
+
return { entry: matching, available: entries, warnings, promptFromModelValue };
|
|
267825
|
+
warnings.push(`Requested ${kindLabel(kind)} backend "${backend}" is not available; falling back to automatic selection.`);
|
|
267826
|
+
}
|
|
267827
|
+
const prompt = String(args["prompt"] ?? "").trim() || promptFromModelValue || "";
|
|
267828
|
+
const input = String(args["input_image"] ?? args["input"] ?? "").trim();
|
|
267829
|
+
if (shouldUseTextCadFallbackFor3d(kind, prompt, input, args)) {
|
|
267830
|
+
const cadFallback = rankCatalogEntries("cad").find((entry) => builtInRuntimeFor(entry.spec, "cad") === "cad0-scad");
|
|
267831
|
+
if (cadFallback) {
|
|
267832
|
+
warnings.push("Text-only 3D generation has no wired mesh runtime yet; using the built-in CAD/SCAD adapter to create a real 3D artifact.");
|
|
267833
|
+
return { entry: cadFallback, available: entries, warnings, promptFromModelValue };
|
|
267834
|
+
}
|
|
267683
267835
|
}
|
|
267684
|
-
return entries[0];
|
|
267836
|
+
return { entry: entries[0], available: entries, warnings, promptFromModelValue };
|
|
267685
267837
|
}
|
|
267686
267838
|
async checkReadiness(spec, kind) {
|
|
267687
267839
|
const blockers = [];
|
|
@@ -267789,7 +267941,7 @@ No artifact created: ${msg}`,
|
|
|
267789
267941
|
prompt: args.prompt,
|
|
267790
267942
|
sizeKB: Math.max(1, Math.round(info.size / 1024)),
|
|
267791
267943
|
compactIr: artifact.compactIr
|
|
267792
|
-
});
|
|
267944
|
+
}, args.readiness.warnings);
|
|
267793
267945
|
return {
|
|
267794
267946
|
success: true,
|
|
267795
267947
|
output,
|
|
@@ -551987,6 +552139,14 @@ function normalizeProviderToolMessage(msg, model) {
|
|
|
551987
552139
|
toolCalls: toolCalls.length > 0 ? toolCalls : void 0
|
|
551988
552140
|
};
|
|
551989
552141
|
}
|
|
552142
|
+
function isGenerationToolName(toolName) {
|
|
552143
|
+
return /^(generate_image|generate_audio|generate_video|generate_model|generate_tts|create_audio_file)$/.test(toolName);
|
|
552144
|
+
}
|
|
552145
|
+
function isGenerationArtifactSuccess(toolName, output) {
|
|
552146
|
+
if (!isGenerationToolName(toolName))
|
|
552147
|
+
return false;
|
|
552148
|
+
return /(?:Image generated|Music generated|Sound generated|Video generated|3D model generated|CAD generated|Model generated|TTS generated|Created [A-Z]+ file|Created|Saved to|Output saved to):?\s+/i.test(output);
|
|
552149
|
+
}
|
|
551990
552150
|
function inferEpisodeModality(toolName) {
|
|
551991
552151
|
if (VISUAL_TOOLS.has(toolName))
|
|
551992
552152
|
return "visual";
|
|
@@ -553486,6 +553646,8 @@ ${result.output ?? ""}`;
|
|
|
553486
553646
|
* no todo list, returns an empty array to allow task completion.
|
|
553487
553647
|
*/
|
|
553488
553648
|
getOpenTodoItems() {
|
|
553649
|
+
if (this.options.disableTodoCompletionGuard)
|
|
553650
|
+
return [];
|
|
553489
553651
|
const todos = this.readSessionTodos();
|
|
553490
553652
|
if (!todos || todos.length === 0)
|
|
553491
553653
|
return [];
|
|
@@ -557651,7 +557813,7 @@ ${_staleSamples.join("\n")}` : ``,
|
|
|
557651
557813
|
const userMsg = this.pendingUserMessages.shift();
|
|
557652
557814
|
await this.appendInjectedUserMessage(userMsg, messages2, turn);
|
|
557653
557815
|
}
|
|
557654
|
-
{
|
|
557816
|
+
if (!this.options.disableTodoPlanningNudges) {
|
|
557655
557817
|
const maybeReminder = this.getTodoReminderContent(turn);
|
|
557656
557818
|
if (maybeReminder) {
|
|
557657
557819
|
messages2.push({ role: "user", content: maybeReminder });
|
|
@@ -557663,7 +557825,7 @@ ${_staleSamples.join("\n")}` : ``,
|
|
|
557663
557825
|
}
|
|
557664
557826
|
}
|
|
557665
557827
|
const turnTier = this.options.modelTier ?? "large";
|
|
557666
|
-
if (turn === 0 && (turnTier === "small" || turnTier === "medium")) {
|
|
557828
|
+
if (turn === 0 && !this.options.disableTodoPlanningNudges && (turnTier === "small" || turnTier === "medium")) {
|
|
557667
557829
|
const goal = this._taskState.goal || "";
|
|
557668
557830
|
const wordCount2 = goal.split(/\s+/).length;
|
|
557669
557831
|
const hasMultipleActions = /\band\b.*\band\b|then.*then|also.*also/i.test(goal);
|
|
@@ -558775,6 +558937,9 @@ ${criticDecision.cachedResult.slice(0, 500)}` : `[BLOCKED — the observer confi
|
|
|
558775
558937
|
mode: "step_repetition",
|
|
558776
558938
|
rationale: `force_progress_block on ${tc.name} after ${criticDecision.hitNumber} identical calls`
|
|
558777
558939
|
});
|
|
558940
|
+
const generationCompletionHint = isGenerationArtifactSuccess(tc.name, criticDecision.cachedResult) ? `
|
|
558941
|
+
|
|
558942
|
+
[GENERATION ALREADY COMPLETE] This exact ${tc.name} call already succeeded. Do not call it again. Use the cached artifact/path above; if delivery is needed, send it, otherwise call task_complete.` : "";
|
|
558778
558943
|
const header = criticDecision.compacted ? `[RE-SERVED FROM CACHE — the original result was compacted from context. Here is the data again. Do not retry this exact call.]
|
|
558779
558944
|
|
|
558780
558945
|
` : `[SKIPPED DUPLICATE — exact ${tc.name} call not re-run. The cached result below is from the prior successful call. Do not retry this exact call.]
|
|
@@ -558789,7 +558954,7 @@ ${truncatedCache}`);
|
|
|
558789
558954
|
tc,
|
|
558790
558955
|
output: `${criticDecision.blockMessage}
|
|
558791
558956
|
|
|
558792
|
-
${header}${truncatedCache}`,
|
|
558957
|
+
${header}${truncatedCache}${generationCompletionHint}`,
|
|
558793
558958
|
success: true
|
|
558794
558959
|
};
|
|
558795
558960
|
}
|
|
@@ -558807,6 +558972,9 @@ ${header}${truncatedCache}`,
|
|
|
558807
558972
|
turn,
|
|
558808
558973
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
558809
558974
|
});
|
|
558975
|
+
const generationCompletionHint = isGenerationArtifactSuccess(tc.name, criticDecision.cachedResult) ? `
|
|
558976
|
+
|
|
558977
|
+
[GENERATION ALREADY COMPLETE] This exact ${tc.name} call already succeeded. Do not call it again. Use the cached artifact/path above; if delivery is needed, send it, otherwise call task_complete.` : "";
|
|
558810
558978
|
const header = criticDecision.compacted ? `[RE-SERVED FROM CACHE — the original result was compacted from context. Here is the data again. No need to call this tool again.]
|
|
558811
558979
|
|
|
558812
558980
|
` : `[DUPLICATE CALL #${criticDecision.hitNumber} — you already called ${tc.name} with these exact arguments. The result is identical. Do NOT call this again. Use the data you already have to make progress. One more identical call will trigger a hard progress block.]
|
|
@@ -558814,7 +558982,7 @@ ${header}${truncatedCache}`,
|
|
|
558814
558982
|
`;
|
|
558815
558983
|
const truncatedCache = criticDecision.cachedResult.length > 500 ? criticDecision.cachedResult.slice(0, 500) + `
|
|
558816
558984
|
... [${criticDecision.cachedResult.length - 500} chars omitted — same as before]` : criticDecision.cachedResult;
|
|
558817
|
-
const dedupOutput = header + truncatedCache;
|
|
558985
|
+
const dedupOutput = header + truncatedCache + generationCompletionHint;
|
|
558818
558986
|
markSyntheticToolLog(dedupOutput);
|
|
558819
558987
|
this.emit({
|
|
558820
558988
|
type: "tool_result",
|
|
@@ -560008,6 +560176,9 @@ Do NOT retry ${tc.name} with similar arguments.`);
|
|
|
560008
560176
|
} else if (result.success && tc.name !== "task_complete") {
|
|
560009
560177
|
sameToolFailStreak = 0;
|
|
560010
560178
|
sameToolFailName = null;
|
|
560179
|
+
if (isGenerationArtifactSuccess(tc.name, result.output ?? "")) {
|
|
560180
|
+
this.pendingUserMessages.push(`[GENERATION COMPLETE] ${tc.name} succeeded. Do not call the same generation tool again for the same request. Use the artifact/path from the tool result; if delivery is needed, send it, otherwise call task_complete.`);
|
|
560181
|
+
}
|
|
560011
560182
|
}
|
|
560012
560183
|
if (filePath && (tc.name === "file_read" || tc.name === "file_write" || tc.name === "file_edit" || tc.name === "batch_edit" || tc.name === "file_patch")) {
|
|
560013
560184
|
const isModify = tc.name !== "file_read";
|
|
@@ -564537,6 +564708,8 @@ Example: ${tool.name}(${JSON.stringify(meta.examples[0].args ?? {})})` : "";
|
|
|
564537
564708
|
"batch_edit"
|
|
564538
564709
|
]);
|
|
564539
564710
|
const taskText = (this._taskState.originalGoal || this._taskState.goal || "").toLowerCase();
|
|
564711
|
+
const wants3dModelGeneration = /\b(?:make|create|generate|build|produce|render|give)\b/.test(taskText) && /\b(?:3d|three[-\s]?d|mesh|glb|obj|stl|ply|cad|scad|step|printable|model)\b/.test(taskText) && !/\b(?:image|picture|photo|rendering|screenshot)\b/.test(taskText);
|
|
564712
|
+
const wantsModelCatalogManagement = /\b(?:discover|search|find|add|install|intake|inspect|validate|save|catalog|adapter|hugging\s*face|hf)\b/.test(taskText) && /\b(?:model|adapter|hugging\s*face|hf)\b/.test(taskText);
|
|
564540
564713
|
const taskWords = new Set(taskText.split(/\s+/).filter((w) => w.length > 2));
|
|
564541
564714
|
const scored = [];
|
|
564542
564715
|
for (const tool of allTools) {
|
|
@@ -564557,6 +564730,15 @@ Example: ${tool.name}(${JSON.stringify(meta.examples[0].args ?? {})})` : "";
|
|
|
564557
564730
|
if (taskText.includes(tool.name.replace(/_/g, " ")) || taskText.includes(tool.name)) {
|
|
564558
564731
|
score += customMeta ? 16 : 10;
|
|
564559
564732
|
}
|
|
564733
|
+
if (wants3dModelGeneration) {
|
|
564734
|
+
if (tool.name === "generate_model")
|
|
564735
|
+
score += 40;
|
|
564736
|
+
if (tool.name === "generate_image")
|
|
564737
|
+
score -= 12;
|
|
564738
|
+
if ((tool.name === "hf_model_discover" || tool.name === "hf_model_intake") && !wantsModelCatalogManagement) {
|
|
564739
|
+
score -= 18;
|
|
564740
|
+
}
|
|
564741
|
+
}
|
|
564560
564742
|
if (customMeta) {
|
|
564561
564743
|
const lastStatus = customMeta.qualityGate?.lastTest?.status;
|
|
564562
564744
|
if (lastStatus === "passed")
|
|
@@ -633935,6 +634117,9 @@ file access, and code analysis. Respond thoroughly and helpfully.
|
|
|
633935
634117
|
When asked to send a generated or existing file to Telegram, call telegram_send_file with
|
|
633936
634118
|
path and target. Do not search for Telegram bot tokens, environment secrets, or Bot API
|
|
633937
634119
|
credentials; upload authorization is encapsulated by telegram_send_file.
|
|
634120
|
+
For admin-DM artifact generation requests, send the generated file with
|
|
634121
|
+
telegram_send_file after the generation tool succeeds unless the admin
|
|
634122
|
+
explicitly asked only for a local path.
|
|
633938
634123
|
|
|
633939
634124
|
When asked to generate speech, narration, or TTS, use generate_tts or audio_playback.
|
|
633940
634125
|
Those tools handle first-use backend setup where supported. Do not fall back to shell
|
|
@@ -633943,6 +634128,9 @@ commands or generic audio generation for speech synthesis while those tools are
|
|
|
633943
634128
|
When asked to generate a 3D asset, mesh, printable model, or CAD part, use
|
|
633944
634129
|
generate_model. Use hf_model_discover or hf_model_intake only when the admin is
|
|
633945
634130
|
asking to add, inspect, validate, or save model adapters.
|
|
634131
|
+
For text-only 3D requests, pass the requested object in prompt and do not put
|
|
634132
|
+
the object name in model; model is only for exact /models adapter ids. Do not
|
|
634133
|
+
use generate_image as a substitute for a 3D/CAD artifact.
|
|
633946
634134
|
|
|
633947
634135
|
Keep responses concise for Telegram but don't withhold information from the admin.
|
|
633948
634136
|
`.trim();
|
|
@@ -634057,6 +634245,7 @@ Telegram response contract:
|
|
|
634057
634245
|
- Do not summarize the fact that you answered; the visible assistant text must be the answer itself.
|
|
634058
634246
|
- If you delegated long-running work, include the sub-agent id/status and what the admin should expect next.
|
|
634059
634247
|
- Do not narrate retry strategy ("let me try one more approach", "actually", "wait, let me try") in assistant text. If you are stuck, send a single concise blocker sentence as the reply instead of streaming deliberation.
|
|
634248
|
+
- Treat todo_write as optional scratch state in Telegram runs. Do not create or maintain todos for simple Q&A, capability checks, or single artifact-generation requests.
|
|
634060
634249
|
`.trim();
|
|
634061
634250
|
TELEGRAM_EXTERNAL_ACQUISITION_CONTRACT = `
|
|
634062
634251
|
External acquisition contract:
|
|
@@ -634243,10 +634432,6 @@ Telegram link integrity contract:
|
|
|
634243
634432
|
TELEGRAM_ALLOWED_UPDATES = ["message", "guest_message", "callback_query", "poll", "message_reaction", "message_reaction_count"];
|
|
634244
634433
|
TELEGRAM_DEFAULT_LONG_POLL_TIMEOUT_SECONDS = 50;
|
|
634245
634434
|
TELEGRAM_DEFAULT_ROUTER_MODEL_CANDIDATES = [
|
|
634246
|
-
"qwen3:0.6b",
|
|
634247
|
-
"qwen3:1.7b",
|
|
634248
|
-
"qwen3:4b",
|
|
634249
|
-
"qwen3:8b",
|
|
634250
634435
|
"qwen2.5:3b",
|
|
634251
634436
|
"qwen2.5:7b",
|
|
634252
634437
|
"llama3.2:1b",
|
|
@@ -634254,7 +634439,11 @@ Telegram link integrity contract:
|
|
|
634254
634439
|
"gemma3:1b",
|
|
634255
634440
|
"gemma3:4b",
|
|
634256
634441
|
"phi3:mini",
|
|
634257
|
-
"phi4-mini:latest"
|
|
634442
|
+
"phi4-mini:latest",
|
|
634443
|
+
"qwen3:0.6b",
|
|
634444
|
+
"qwen3:1.7b",
|
|
634445
|
+
"qwen3:4b",
|
|
634446
|
+
"qwen3:8b"
|
|
634258
634447
|
];
|
|
634259
634448
|
TELEGRAM_PUBLIC_TOOL_QUOTAS = {
|
|
634260
634449
|
web: { limit: 20, windowMs: 60 * 6e4 },
|
|
@@ -639205,6 +639394,19 @@ ${retryText}`,
|
|
|
639205
639394
|
const candidates = raw ? raw.split(/[,\s]+/).map((part) => part.trim()).filter(Boolean) : TELEGRAM_DEFAULT_ROUTER_MODEL_CANDIDATES;
|
|
639206
639395
|
return Array.from(new Set(candidates));
|
|
639207
639396
|
}
|
|
639397
|
+
telegramRouterAllowThinkHeavyAutoModels() {
|
|
639398
|
+
const raw = (process.env["OMNIUS_TG_ROUTER_ALLOW_THINK_MODELS"] ?? "").trim().toLowerCase();
|
|
639399
|
+
return raw === "1" || raw === "true" || raw === "on";
|
|
639400
|
+
}
|
|
639401
|
+
telegramRouterModelLooksThinkHeavy(name10) {
|
|
639402
|
+
return /\b(?:qwen3|qwq|deepseek-r1|r1-|reasoning)\b/i.test(name10);
|
|
639403
|
+
}
|
|
639404
|
+
orderTelegramRouterCandidates(candidates) {
|
|
639405
|
+
if (this.telegramRouterAllowThinkHeavyAutoModels()) return candidates;
|
|
639406
|
+
const stable = candidates.filter((candidate) => !this.telegramRouterModelLooksThinkHeavy(candidate));
|
|
639407
|
+
const thinkHeavy = candidates.filter((candidate) => this.telegramRouterModelLooksThinkHeavy(candidate));
|
|
639408
|
+
return [...stable, ...thinkHeavy];
|
|
639409
|
+
}
|
|
639208
639410
|
normalizeOllamaModelNameForMatch(name10) {
|
|
639209
639411
|
return name10.trim().toLowerCase().replace(/:latest$/, "");
|
|
639210
639412
|
}
|
|
@@ -639235,7 +639437,7 @@ ${retryText}`,
|
|
|
639235
639437
|
source: "main"
|
|
639236
639438
|
};
|
|
639237
639439
|
}
|
|
639238
|
-
const candidates = this.telegramRouterCandidateModels();
|
|
639440
|
+
const candidates = this.orderTelegramRouterCandidates(this.telegramRouterCandidateModels());
|
|
639239
639441
|
const cacheKey = `${config.backendUrl}
|
|
639240
639442
|
${config.model}
|
|
639241
639443
|
${candidates.join(",")}`;
|
|
@@ -639263,7 +639465,7 @@ ${candidates.join(",")}`;
|
|
|
639263
639465
|
atMs: now,
|
|
639264
639466
|
model: selected,
|
|
639265
639467
|
source: "auto-small",
|
|
639266
|
-
detail: "selected first installed
|
|
639468
|
+
detail: "selected first installed Telegram router candidate from Ollama /api/tags; think-heavy models are tried last unless OMNIUS_TG_ROUTER_ALLOW_THINK_MODELS=1"
|
|
639267
639469
|
};
|
|
639268
639470
|
this.telegramRouterModelCache = resolved;
|
|
639269
639471
|
return {
|
|
@@ -640425,7 +640627,7 @@ ${TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT}`);
|
|
|
640425
640627
|
return result.result?.message_id ?? null;
|
|
640426
640628
|
} catch (err) {
|
|
640427
640629
|
const errStr = err instanceof Error ? err.message : String(err);
|
|
640428
|
-
if (this.shouldLogTelegramSendFailure(errStr)) {
|
|
640630
|
+
if (process.env["OMNIUS_TELEGRAM_DEBUG_SEND_FAILURES"] === "1" && this.shouldLogTelegramSendFailure(errStr)) {
|
|
640429
640631
|
this.tuiWrite(() => renderWarning(`Failed to send Telegram live message: ${errStr}`));
|
|
640430
640632
|
}
|
|
640431
640633
|
this.updateTelegramTextDeliveryCapability(chatId, {
|
|
@@ -641851,6 +642053,8 @@ ${conversationStream}`
|
|
|
641851
642053
|
compactionThreshold: this.telegramFallbackCompactionThreshold(modelTier),
|
|
641852
642054
|
contextWindowSize,
|
|
641853
642055
|
modelTier,
|
|
642056
|
+
disableTodoCompletionGuard: true,
|
|
642057
|
+
disableTodoPlanningNudges: true,
|
|
641854
642058
|
streamEnabled: true,
|
|
641855
642059
|
dynamicContext: sessionContext.context,
|
|
641856
642060
|
captureContextFrame: true,
|
|
@@ -642044,7 +642248,7 @@ ${currentTelegramPrompt}`;
|
|
|
642044
642248
|
"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.",
|
|
642045
642249
|
"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.",
|
|
642046
642250
|
"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).",
|
|
642047
|
-
"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.",
|
|
642251
|
+
"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. For text-only 3D requests, put the object description in prompt and omit model unless selecting an exact /models adapter id. Do not use generate_image as a substitute for a 3D/CAD artifact. If the tool reports a catalog/runtime blocker, explain that blocker plainly.",
|
|
642048
642252
|
creativeWorkspace
|
|
642049
642253
|
].filter(Boolean).join("\n\n");
|
|
642050
642254
|
userPrompt = `${systemPrompt}${discretionPrompt}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.195",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omnius",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.195",
|
|
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.
|
|
3
|
+
"version": "1.0.195",
|
|
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",
|
|
@@ -142,5 +142,5 @@
|
|
|
142
142
|
"transcribe-cli": "^2.0.1",
|
|
143
143
|
"viem": "2.47.4"
|
|
144
144
|
},
|
|
145
|
-
"readme": "# Omnius\n\nOmnius is a local-first agentic coding runtime: terminal UI, autonomous coding loop, REST daemon, model router, memory layer, media tools, Telegram bridge, and peer-to-peer inference mesh in one CLI.\n\nIt is designed for open-weight and user-controlled models first, while still routing cleanly through Ollama, vLLM, OpenAI-compatible endpoints, OpenRouter, Groq, Chutes, sponsor peers, COHERE peers, and other configured providers.\n\n[](https://www.npmjs.com/package/omnius)\n[](https://nodejs.org/)\n[](LICENSE)\n\n## Install\n\n```bash\nnpm install -g omnius\nomnius\n```\n\nRequirements:\n\n- Node.js 22 or newer\n- npm 10 or newer for published CLI use\n- pnpm 9 or newer for workspace development\n- A local model or configured remote endpoint\n\nStart the REST daemon:\n\n```bash\nomnius serve\n```\n\nThe daemon defaults to `http://127.0.0.1:11435`. Open the interactive API docs at `http://127.0.0.1:11435/docs`.\n\n## What Omnius Does\n\n- Runs autonomous coding tasks, edits files, executes tools, tests changes, and iterates on failures.\n- Provides a dense terminal UI for model selection, endpoint routing, task control, shell output, voice, sponsors, Telegram, and system telemetry.\n- Exposes a REST daemon with OpenAI/Ollama-compatible inference, agentic task execution, memory, skills, tools, MCP, events, voice, projects, and governance endpoints.\n- Routes models through local, cloud, sponsor, and peer-to-peer endpoints without assuming local Ollama is the only source.\n- Supports realtime spoken conversation for ASR/TTS clients through `/realtime` and REST `realtime: true`.\n- Supports image, video, sound, music, TTS, ASR, voice clone references, Telegram media workflows, and sponsor-provided media generation.\n- Keeps project runtime state in `.omnius/`, which is intentionally ignored by git.\n\n## Common Workflows\n\n```bash\nomnius \"inspect this repo and summarize the main entrypoints\"\nomnius serve\n```\n\n```text\n/help command help\n/model select or inspect the active model\n/endpoint select or configure local, cloud, sponsor, or peer endpoints\n/realtime toggle short ASR/TTS-oriented conversation mode\n/broker inspect model broker, RAM/VRAM thresholds, and loaded models\n/sponsor expose local or upstream capacity to peers\n/cohere participate in distributed COHERE inference\n/telegram configure or toggle the Telegram bridge\n/skills list explorable skills and docs memories\n/pause pause after the current turn boundary\n/stop interrupt the active run\n/resume resume saved state\n```\n\n## Current Feature Areas\n\n| Area | What to read |\n| --- | --- |\n| Install and setup | [Install](docs/getting-started/install.md), [First run](docs/getting-started/first-run.md), [Model providers](docs/getting-started/model-providers.md) |\n| Terminal workflows | [TUI workflows](docs/guides/tui-workflows.md), [Slash commands](docs/reference/slash-commands.md) |\n| REST daemon | [REST reference](docs/reference/rest-api.md), [REST quickref](docs/rest/QUICKREF.md), [OpenAPI source](docs/rest/openapi-source.md) |\n| Realtime voice chat | [Realtime guide](docs/guides/realtime.md) |\n| Sponsor and COHERE mesh | [Sponsor and COHERE guide](docs/guides/sponsor-and-cohere.md) |\n| Telegram bridge | [Telegram guide](docs/guides/telegram.md) |\n| Media generation | [Media guide](docs/guides/media-generation.md) |\n| Operations | [Runtime hygiene](docs/operations/runtime-hygiene.md), [Security and remote access](docs/operations/security-and-remote-access.md) |\n| Architecture | [Architecture overview](docs/architecture/overview.md) |\n| Agent-explorable docs | [Agent memory docs index](docs/agent-memory/INDEX.md) |\n\n## Recent Highlights\n\n- `/realtime` and REST `realtime: true` provide short, natural, SOUL.md-aware conversation for ASR/TTS clients.\n- Endpoint setup and sponsor setup aggregate models from all enabled endpoints, including external OpenAI-compatible routers.\n- `/sponsor` can expose text inference and media generation for image, video, sound, and music with per-modality limits.\n- Sponsor and COHERE status surfaces now use shared telemetry concepts: concurrency, request rate, daily tokens, peer usage, model usage, and remote system metrics.\n- The TUI reports token production rate as `t/s`, supports Shift+Enter multiline input, and renders dynamic shell output inside bounded Unicode cards.\n- Telegram state is scoped by user and group, supports durable reply preferences, and feeds raw platform/tool failures back into the agent loop.\n- Ollama pool cleanup now accounts for process groups and orphan runner processes that can keep VRAM pinned.\n- REST documentation is available both as human docs and as Omnius-discoverable docs skills.\n\n## REST API\n\nStart:\n\n```bash\nomnius serve\n```\n\nUseful entrypoints:\n\n```text\nGET /docs\nGET /openapi.json\nPOST /v1/chat\nPOST /v1/chat/completions\nPOST /v1/run\nGET /v1/events\nGET /v1/skills\nPOST /v1/tools/{name}/call\nWS /v1/voicechat/ws\n```\n\nFor shared deployments, use bearer keys:\n\n```bash\nOMNIUS_API_KEYS=\"read-key:read:grafana,run-key:run:ci:60:100000:3,admin-key:admin:ops\" omnius serve\n```\n\nSee [docs/reference/rest-api.md](docs/reference/rest-api.md) for the maintained endpoint inventory. The canonical machine contract is generated from [packages/cli/src/api/openapi.ts](packages/cli/src/api/openapi.ts).\n\n## Agent-Explorable Documentation\n\nOmnius discovers project-local docs skills from `.aiwg/addons/*/skills`. The docs bundles in this repo expose high-signal entrypoints for agents:\n\n```text\n/skills omnius docs\nskill_execute name=\"omnius-docs\"\nskill_execute name=\"omnius-rest-docs\"\nskill_extract name=\"omnius-realtime-docs\" query=\"How does realtime REST mode work?\"\n```\n\nThe intended pattern is index first, targeted document second, not loading the whole manual into the active context.\n\n## Development\n\n```bash\npnpm install\npnpm -r build\npnpm docs:check\n```\n\nFocused checks used for the docs skill surface:\n\n```bash\npnpm --filter @omnius/execution exec vitest run tests/skill-discovery.test.ts\npnpm --filter omnius exec vitest run tests/realtime-mode.test.ts tests/command-registry.test.ts\n```\n\n## Publishing\n\nPublish only from `publish/`.\n\n```bash\ncd omnius\npnpm -r clean || true\nfind . -name 'tsconfig.tsbuildinfo' -not -path '*/node_modules/*' -delete\npnpm -r build\nnode scripts/build-publish.mjs\ncd publish\nmkdir -p .npm-cache\nNPM_CONFIG_CACHE=$(pwd)/.npm-cache npm pack
|
|
145
|
+
"readme": "# Omnius\n\nOmnius is a local-first agentic coding runtime: terminal UI, autonomous coding loop, REST daemon, model router, memory layer, media tools, Telegram bridge, and peer-to-peer inference mesh in one CLI.\n\nIt is designed for open-weight and user-controlled models first, while still routing cleanly through Ollama, vLLM, OpenAI-compatible endpoints, OpenRouter, Groq, Chutes, sponsor peers, COHERE peers, and other configured providers.\n\n[](https://www.npmjs.com/package/omnius)\n[](https://nodejs.org/)\n[](LICENSE)\n\n## Install\n\n```bash\nnpm install -g omnius\nomnius\n```\n\nRequirements:\n\n- Node.js 22 or newer\n- npm 10 or newer for published CLI use\n- pnpm 9 or newer for workspace development\n- A local model or configured remote endpoint\n\nStart the REST daemon:\n\n```bash\nomnius serve\n```\n\nThe daemon defaults to `http://127.0.0.1:11435`. Open the interactive API docs at `http://127.0.0.1:11435/docs`.\n\n## What Omnius Does\n\n- Runs autonomous coding tasks, edits files, executes tools, tests changes, and iterates on failures.\n- Provides a dense terminal UI for model selection, endpoint routing, task control, shell output, voice, sponsors, Telegram, and system telemetry.\n- Exposes a REST daemon with OpenAI/Ollama-compatible inference, agentic task execution, memory, skills, tools, MCP, events, voice, projects, and governance endpoints.\n- Routes models through local, cloud, sponsor, and peer-to-peer endpoints without assuming local Ollama is the only source.\n- Supports realtime spoken conversation for ASR/TTS clients through `/realtime` and REST `realtime: true`.\n- Supports image, video, sound, music, TTS, ASR, voice clone references, Telegram media workflows, and sponsor-provided media generation.\n- Keeps project runtime state in `.omnius/`, which is intentionally ignored by git.\n\n## Common Workflows\n\n```bash\nomnius \"inspect this repo and summarize the main entrypoints\"\nomnius serve\n```\n\n```text\n/help command help\n/model select or inspect the active model\n/endpoint select or configure local, cloud, sponsor, or peer endpoints\n/realtime toggle short ASR/TTS-oriented conversation mode\n/broker inspect model broker, RAM/VRAM thresholds, and loaded models\n/sponsor expose local or upstream capacity to peers\n/cohere participate in distributed COHERE inference\n/telegram configure or toggle the Telegram bridge\n/skills list explorable skills and docs memories\n/pause pause after the current turn boundary\n/stop interrupt the active run\n/resume resume saved state\n```\n\n## Current Feature Areas\n\n| Area | What to read |\n| --- | --- |\n| Install and setup | [Install](docs/getting-started/install.md), [First run](docs/getting-started/first-run.md), [Model providers](docs/getting-started/model-providers.md) |\n| Terminal workflows | [TUI workflows](docs/guides/tui-workflows.md), [Slash commands](docs/reference/slash-commands.md) |\n| REST daemon | [REST reference](docs/reference/rest-api.md), [REST quickref](docs/rest/QUICKREF.md), [OpenAPI source](docs/rest/openapi-source.md) |\n| Realtime voice chat | [Realtime guide](docs/guides/realtime.md) |\n| Sponsor and COHERE mesh | [Sponsor and COHERE guide](docs/guides/sponsor-and-cohere.md) |\n| Telegram bridge | [Telegram guide](docs/guides/telegram.md) |\n| Media generation | [Media guide](docs/guides/media-generation.md) |\n| Operations | [Runtime hygiene](docs/operations/runtime-hygiene.md), [Security and remote access](docs/operations/security-and-remote-access.md) |\n| Architecture | [Architecture overview](docs/architecture/overview.md) |\n| Agent-explorable docs | [Agent memory docs index](docs/agent-memory/INDEX.md) |\n\n## Recent Highlights\n\n- `/realtime` and REST `realtime: true` provide short, natural, SOUL.md-aware conversation for ASR/TTS clients.\n- Endpoint setup and sponsor setup aggregate models from all enabled endpoints, including external OpenAI-compatible routers.\n- `/sponsor` can expose text inference and media generation for image, video, sound, and music with per-modality limits.\n- Sponsor and COHERE status surfaces now use shared telemetry concepts: concurrency, request rate, daily tokens, peer usage, model usage, and remote system metrics.\n- The TUI reports token production rate as `t/s`, supports Shift+Enter multiline input, and renders dynamic shell output inside bounded Unicode cards.\n- Telegram state is scoped by user and group, supports durable reply preferences, and feeds raw platform/tool failures back into the agent loop.\n- Ollama pool cleanup now accounts for process groups and orphan runner processes that can keep VRAM pinned.\n- REST documentation is available both as human docs and as Omnius-discoverable docs skills.\n\n## REST API\n\nStart:\n\n```bash\nomnius serve\n```\n\nUseful entrypoints:\n\n```text\nGET /docs\nGET /openapi.json\nPOST /v1/chat\nPOST /v1/chat/completions\nPOST /v1/run\nGET /v1/events\nGET /v1/skills\nPOST /v1/tools/{name}/call\nWS /v1/voicechat/ws\n```\n\nFor shared deployments, use bearer keys:\n\n```bash\nOMNIUS_API_KEYS=\"read-key:read:grafana,run-key:run:ci:60:100000:3,admin-key:admin:ops\" omnius serve\n```\n\nSee [docs/reference/rest-api.md](docs/reference/rest-api.md) for the maintained endpoint inventory. The canonical machine contract is generated from [packages/cli/src/api/openapi.ts](packages/cli/src/api/openapi.ts).\n\n## Agent-Explorable Documentation\n\nOmnius discovers project-local docs skills from `.aiwg/addons/*/skills`. The docs bundles in this repo expose high-signal entrypoints for agents:\n\n```text\n/skills omnius docs\nskill_execute name=\"omnius-docs\"\nskill_execute name=\"omnius-rest-docs\"\nskill_extract name=\"omnius-realtime-docs\" query=\"How does realtime REST mode work?\"\n```\n\nThe intended pattern is index first, targeted document second, not loading the whole manual into the active context.\n\n## Development\n\n```bash\npnpm install\npnpm -r build\npnpm docs:check\n```\n\nFocused checks used for the docs skill surface:\n\n```bash\npnpm --filter @omnius/execution exec vitest run tests/skill-discovery.test.ts\npnpm --filter omnius exec vitest run tests/realtime-mode.test.ts tests/command-registry.test.ts\n```\n\n## Publishing\n\nPublish only from `publish/`.\n\n```bash\ncd omnius\npnpm -r clean || true\nfind . -name 'tsconfig.tsbuildinfo' -not -path '*/node_modules/*' -delete\npnpm -r build\nnode scripts/build-publish.mjs\ncd publish\nmkdir -p .npm-cache\nNPM_CONFIG_CACHE=$(pwd)/.npm-cache npm pack --prefer-online --cache-min=0 --registry https://registry.npmjs.org/\nNPM_CONFIG_CACHE=$(pwd)/.npm-cache npm publish --access public --prefer-online --cache-min=0 --registry https://registry.npmjs.org/\n```\n\nBefore publishing, verify `README.md`, `package.json`, `dist/index.js`, and `dist/launcher.cjs` are in the tarball, and that `package.json` includes `readmeFilename: \"README.md\"` plus a string `readme`.\n\n## License\n\nOmnius is released under [CC-BY-NC-4.0](LICENSE) for non-commercial use. Commercial use, redistribution, hosted services, and enterprise deployment require a commercial license.\n"
|
|
146
146
|
}
|