omnius 1.0.409 → 1.0.410

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
@@ -601886,7 +601886,8 @@ __export(image_ascii_preview_exports, {
601886
601886
  buildImageAsciiPreview: () => buildImageAsciiPreview,
601887
601887
  defaultAsciiPreviewSize: () => defaultAsciiPreviewSize,
601888
601888
  extractSavedImagePath: () => extractSavedImagePath,
601889
- formatImageAsciiContext: () => formatImageAsciiContext
601889
+ formatImageAsciiContext: () => formatImageAsciiContext,
601890
+ isLowInformationAsciiPreview: () => isLowInformationAsciiPreview
601890
601891
  });
601891
601892
  import { createRequire as createRequire5 } from "node:module";
601892
601893
  import { existsSync as existsSync108, readFileSync as readFileSync87, statSync as statSync42 } from "node:fs";
@@ -602054,6 +602055,15 @@ function normalizeImageToAsciiResult(converted, width, height) {
602054
602055
  if (!ascii2) return { ascii: null, error: "empty normalized renderer output" };
602055
602056
  return { ascii: ascii2, plainAscii: plainAscii || void 0 };
602056
602057
  }
602058
+ function isLowInformationAsciiPreview(ascii2) {
602059
+ const plain = stripAsciiAnsi(String(ascii2 ?? "")).replace(/\r/g, "").split("\n").map((line) => line.trimEnd()).filter((line) => line.trim().length > 0).join("\n");
602060
+ if (!plain) return true;
602061
+ const visible = plain.replace(/\s/g, "");
602062
+ if (visible.length < 12) return true;
602063
+ const unique2 = new Set(Array.from(visible));
602064
+ if (unique2.size <= 2 && /[█▓▒░#@]/u.test(visible)) return true;
602065
+ return false;
602066
+ }
602057
602067
  function previewFailure(message2, width) {
602058
602068
  const clean5 = message2.replace(/\s+/g, " ").trim();
602059
602069
  const text2 = `[image-to-ascii preview failed: ${clean5 || "unknown error"}]`;
@@ -602172,10 +602182,14 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
602172
602182
  if (options2.preferPackage !== false) {
602173
602183
  const result = await convertWithImageToAscii(imagePath, width, height, timeoutMs);
602174
602184
  if (result.ascii) {
602185
+ let plainAscii = result.plainAscii;
602186
+ if (isLowInformationAsciiPreview(plainAscii)) {
602187
+ plainAscii = await convertWithFfmpeg(imagePath, width, height, timeoutMs) ?? plainAscii;
602188
+ }
602175
602189
  return {
602176
602190
  path: imagePath,
602177
602191
  ascii: result.ascii,
602178
- plainAscii: result.plainAscii,
602192
+ plainAscii,
602179
602193
  renderer: "image-to-ascii",
602180
602194
  width,
602181
602195
  height
@@ -602405,23 +602419,16 @@ function stripDashboardAnsi(value2) {
602405
602419
  return value2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
602406
602420
  }
602407
602421
  function dashboardPreviewLines(camera) {
602408
- const raw = camera.plainAscii || camera.ascii || "";
602409
- return raw.replace(/\r/g, "").split("\n").map((line) => stripDashboardAnsi(line).replace(/\s+$/g, "")).filter((line) => line.trim().length > 0).slice(0, 10);
602410
- }
602411
- function centerDashboardCell(value2, width) {
602412
- const text2 = stripDashboardAnsi(String(value2 ?? "")).replace(/\r?\n/g, " ");
602413
- if (width <= 1) return text2.slice(0, Math.max(0, width));
602414
- const clipped = text2.length > width ? `${text2.slice(0, Math.max(1, width - 1))}…` : text2;
602415
- const left = Math.floor((width - clipped.length) / 2);
602416
- return `${" ".repeat(Math.max(0, left))}${clipped}${" ".repeat(Math.max(0, width - clipped.length - left))}`;
602422
+ const raw = camera.plainAscii && !isLowInformationAsciiPreview(camera.plainAscii) ? camera.plainAscii : "";
602423
+ return raw.replace(/\r/g, "").split("\n").map((line) => stripDashboardAnsi(line).replace(/\s+$/g, "")).filter((line) => line.trim().length > 0);
602417
602424
  }
602418
602425
  function dashboardPreviewAspect(lines) {
602419
602426
  const widths = lines.map((line) => stripDashboardAnsi(line).length).filter((n2) => n2 > 0);
602420
602427
  const maxWidth = Math.max(1, ...widths);
602421
602428
  return Math.max(0.12, Math.min(1.6, lines.length / maxWidth));
602422
602429
  }
602423
- function fitDashboardPane(lines, paneWidth, paneHeight) {
602424
- const source = lines.length > 0 ? lines : ["preview pending"];
602430
+ function fitDashboardPane(lines, paneWidth, paneHeight, fallback) {
602431
+ const source = lines.length > 0 ? lines : [fallback];
602425
602432
  const normalized = [];
602426
602433
  for (let i2 = 0; i2 < paneHeight; i2++) {
602427
602434
  const sourceIndex = source.length <= paneHeight ? i2 : Math.min(source.length - 1, Math.floor(i2 * source.length / paneHeight));
@@ -602430,6 +602437,31 @@ function fitDashboardPane(lines, paneWidth, paneHeight) {
602430
602437
  while (normalized.length < paneHeight) normalized.push(" ".repeat(paneWidth));
602431
602438
  return normalized;
602432
602439
  }
602440
+ function liveDashboardPaneLayout(width, cameraCount) {
602441
+ const innerWidth = Math.max(10, width - 2);
602442
+ const paneCount = Math.max(1, Math.min(cameraCount || 1, width >= 92 ? 2 : 1));
602443
+ const gutter = paneCount - 1;
602444
+ const paneWidth = Math.max(28, Math.floor((innerWidth - gutter) / paneCount));
602445
+ return { innerWidth, paneCount, paneWidth, paneContentWidth: Math.max(10, paneWidth - 2) };
602446
+ }
602447
+ function liveDashboardPreviewWidthForSources(sourceCount, termWidth = process.stdout.columns || 100) {
602448
+ const { paneContentWidth } = liveDashboardPaneLayout(Math.max(60, termWidth), sourceCount);
602449
+ return Math.max(42, Math.min(120, paneContentWidth));
602450
+ }
602451
+ function formatCameraPane(camera, paneWidth, paneHeight, now2) {
602452
+ const contentWidth = Math.max(10, paneWidth - 2);
602453
+ const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
602454
+ const title = `${compactSourceId(camera.source)} ${age}s`;
602455
+ const topTitle = ` ${title} `;
602456
+ const left = Math.max(0, Math.floor((contentWidth - topTitle.length) / 2));
602457
+ const right = Math.max(0, contentWidth - topTitle.length - left);
602458
+ const top = `╭${"─".repeat(left)}${topTitle.slice(0, contentWidth)}${"─".repeat(right)}╮`;
602459
+ const bottom = `╰${"─".repeat(contentWidth)}╯`;
602460
+ const previewLines = dashboardPreviewLines(camera);
602461
+ const fallback = camera.framePath ? "preview refreshing" : camera.error ? "capture failed" : "awaiting frame";
602462
+ const body = fitDashboardPane(previewLines, contentWidth, paneHeight, fallback);
602463
+ return [top, ...body.map((line) => `│${truncateCell(line, contentWidth)}│`), bottom];
602464
+ }
602433
602465
  function pushDashboardWrapped(lines, value2, width) {
602434
602466
  const contentWidth = Math.max(10, width - 2);
602435
602467
  const words = stripDashboardAnsi(value2).replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
@@ -602457,6 +602489,160 @@ function isAudioFailureText(value2) {
602457
602489
  if (!text2) return false;
602458
602490
  return /^(?:ASR|VAD|Transcription)\s+(?:failed|unavailable)|No module named ['"]?transcribe_cli|torchcodec|torchaudio version/i.test(text2);
602459
602491
  }
602492
+ function readPcm16WavActivity(filePath, bins = 36) {
602493
+ try {
602494
+ const buf = readFileSync88(filePath);
602495
+ if (buf.length < 44 || buf.toString("ascii", 0, 4) !== "RIFF" || buf.toString("ascii", 8, 12) !== "WAVE") return void 0;
602496
+ let offset = 12;
602497
+ let channels = 1;
602498
+ let bitsPerSample = 16;
602499
+ let dataStart = -1;
602500
+ let dataSize = 0;
602501
+ while (offset + 8 <= buf.length) {
602502
+ const id2 = buf.toString("ascii", offset, offset + 4);
602503
+ const size = buf.readUInt32LE(offset + 4);
602504
+ const start2 = offset + 8;
602505
+ if (id2 === "fmt " && start2 + 16 <= buf.length) {
602506
+ channels = Math.max(1, buf.readUInt16LE(start2 + 2));
602507
+ bitsPerSample = buf.readUInt16LE(start2 + 14);
602508
+ } else if (id2 === "data") {
602509
+ dataStart = start2;
602510
+ dataSize = Math.min(size, buf.length - start2);
602511
+ break;
602512
+ }
602513
+ offset = start2 + size + size % 2;
602514
+ }
602515
+ if (dataStart < 0 || dataSize <= 0 || bitsPerSample !== 16) return void 0;
602516
+ const frameBytes = channels * 2;
602517
+ const frames = Math.floor(dataSize / frameBytes);
602518
+ if (frames <= 0) return void 0;
602519
+ let sumSquares = 0;
602520
+ let peak = 0;
602521
+ let active = 0;
602522
+ const bucketSquares = new Array(Math.max(1, bins)).fill(0);
602523
+ const bucketCounts = new Array(Math.max(1, bins)).fill(0);
602524
+ for (let i2 = 0; i2 < frames; i2++) {
602525
+ let sampleSum = 0;
602526
+ for (let ch = 0; ch < channels; ch++) {
602527
+ sampleSum += buf.readInt16LE(dataStart + i2 * frameBytes + ch * 2) / 32768;
602528
+ }
602529
+ const sample = sampleSum / channels;
602530
+ const abs = Math.abs(sample);
602531
+ peak = Math.max(peak, abs);
602532
+ sumSquares += sample * sample;
602533
+ if (abs > 0.018) active++;
602534
+ const b = Math.min(bucketSquares.length - 1, Math.floor(i2 / frames * bucketSquares.length));
602535
+ bucketSquares[b] += sample * sample;
602536
+ bucketCounts[b] += 1;
602537
+ }
602538
+ const rms = Math.sqrt(sumSquares / frames);
602539
+ const chars = "▁▂▃▄▅▆▇█";
602540
+ const waveform = bucketSquares.map((sum, idx) => {
602541
+ const count = Math.max(1, bucketCounts[idx] ?? 1);
602542
+ const level = Math.sqrt(sum / count);
602543
+ const normalized = Math.max(0, Math.min(1, level / Math.max(0.02, peak || 0.02)));
602544
+ return chars[Math.min(chars.length - 1, Math.round(normalized * (chars.length - 1)))] ?? "▁";
602545
+ }).join("");
602546
+ return {
602547
+ rms: Number(rms.toFixed(4)),
602548
+ peak: Number(peak.toFixed(4)),
602549
+ rmsDb: Number((20 * Math.log10(Math.max(1e-6, rms))).toFixed(1)),
602550
+ activeRatio: Number((active / frames).toFixed(3)),
602551
+ waveform
602552
+ };
602553
+ } catch {
602554
+ return void 0;
602555
+ }
602556
+ }
602557
+ function extractAsrBackend(output) {
602558
+ const match = String(output ?? "").match(/^Backend:\s*(.+)$/m);
602559
+ if (!match?.[1]) return void 0;
602560
+ const backend = match[1].trim();
602561
+ if (/managed openai-whisper|transcribe-cli|whisper/i.test(backend)) return backend;
602562
+ return backend.slice(0, 80);
602563
+ }
602564
+ function parseLiveAudioIntakeDecision(value2) {
602565
+ const parsed = parseJsonObjectFromText(value2);
602566
+ if (!parsed) return null;
602567
+ const intended = parsed["intended_for_agent"] ?? parsed["intendedForAgent"] ?? parsed["addressed_to_agent"];
602568
+ const confidence2 = Math.max(0, Math.min(1, Number(parsed["confidence"] ?? 0)));
602569
+ if (typeof intended !== "boolean" || !Number.isFinite(confidence2)) return null;
602570
+ const actionRaw = String(parsed["action"] ?? (intended ? "respond" : "ignore")).toLowerCase();
602571
+ const action = actionRaw === "respond" || actionRaw === "monitor" || actionRaw === "ignore" ? actionRaw : intended ? "respond" : "ignore";
602572
+ return {
602573
+ intendedForAgent: intended,
602574
+ confidence: confidence2,
602575
+ action,
602576
+ reason: trimOneLine(parsed["reason"] ?? "", 220) || "intake classifier decision"
602577
+ };
602578
+ }
602579
+ function fallbackLiveAudioIntakeDecision(transcript) {
602580
+ const text2 = transcript.toLowerCase();
602581
+ const addressed = /\b(omnius|hey\s+omnius|assistant|agent|computer)\b/.test(text2) || /\?$/.test(transcript.trim());
602582
+ return {
602583
+ intendedForAgent: addressed,
602584
+ confidence: addressed ? 0.58 : 0.35,
602585
+ action: addressed ? "respond" : "monitor",
602586
+ reason: addressed ? "fallback detected direct address or question shape" : "fallback could not establish that ambient speech was addressed to Omnius"
602587
+ };
602588
+ }
602589
+ async function classifyLiveAudioIntake(transcript, config) {
602590
+ const now2 = Date.now();
602591
+ const clean5 = transcript.trim();
602592
+ if (!clean5) {
602593
+ return {
602594
+ intendedForAgent: false,
602595
+ confidence: 1,
602596
+ action: "ignore",
602597
+ reason: "empty transcript",
602598
+ source: "fallback",
602599
+ updatedAt: now2
602600
+ };
602601
+ }
602602
+ if (config?.backendUrl && config.model) {
602603
+ const url = `${config.backendUrl.replace(/\/+$/, "")}/v1/chat/completions`;
602604
+ const body = {
602605
+ model: config.model,
602606
+ temperature: 0,
602607
+ max_tokens: 180,
602608
+ think: false,
602609
+ messages: [
602610
+ {
602611
+ role: "system",
602612
+ content: [
602613
+ "You are a live-audio intake classifier for Omnius.",
602614
+ "Decide whether a transcribed utterance is intended for the agent or is ambient/background speech.",
602615
+ 'Return only JSON: {"intended_for_agent": boolean, "confidence": 0.0-1.0, "action": "respond"|"ignore"|"monitor", "reason": "brief evidence"}.',
602616
+ "Do not answer the utterance. Do not treat all room speech as addressed to the agent."
602617
+ ].join("\n")
602618
+ },
602619
+ { role: "user", content: clean5.slice(0, 1200) }
602620
+ ]
602621
+ };
602622
+ try {
602623
+ const controller = new AbortController();
602624
+ const timer = setTimeout(() => controller.abort(), 6e3).unref();
602625
+ const resp = await fetch(url, {
602626
+ method: "POST",
602627
+ headers: {
602628
+ "Content-Type": "application/json",
602629
+ ...config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}
602630
+ },
602631
+ body: JSON.stringify(body),
602632
+ signal: controller.signal
602633
+ });
602634
+ clearTimeout(timer);
602635
+ if (resp.ok) {
602636
+ const json = await resp.json();
602637
+ const content = json.choices?.[0]?.message?.content ?? "";
602638
+ const parsed = parseLiveAudioIntakeDecision(content);
602639
+ if (parsed) return { ...parsed, source: "intake-agent", updatedAt: now2 };
602640
+ }
602641
+ } catch {
602642
+ }
602643
+ }
602644
+ return { ...fallbackLiveAudioIntakeDecision(clean5), source: "fallback", updatedAt: now2 };
602645
+ }
602460
602646
  function summarizeInference(value2) {
602461
602647
  if (!value2) return "objects: pending";
602462
602648
  const lines = value2.split("\n").map((line) => line.trim()).filter(Boolean);
@@ -602491,24 +602677,18 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
602491
602677
  }
602492
602678
  if (cameras.length > 0) {
602493
602679
  lines.push(`├${horizontal}┤`);
602494
- const paneCount = Math.min(cameras.length, width >= 116 ? 3 : width >= 78 ? 2 : 1);
602495
- const innerWidth = width - 2;
602496
- const gutter = paneCount - 1;
602497
- const paneWidth = Math.max(24, Math.floor((innerWidth - gutter) / paneCount));
602680
+ const { innerWidth, paneCount, paneWidth, paneContentWidth } = liveDashboardPaneLayout(width, cameras.length);
602498
602681
  for (let start2 = 0; start2 < cameras.length; start2 += paneCount) {
602499
602682
  const group = cameras.slice(start2, start2 + paneCount);
602500
602683
  const sourceLines = group.map((camera) => dashboardPreviewLines(camera));
602501
- const paneHeight = Math.max(7, Math.min(22, Math.round(paneWidth * Math.max(...sourceLines.map(dashboardPreviewAspect)))));
602502
- const panes = sourceLines.map((previewLines) => fitDashboardPane(previewLines, paneWidth, paneHeight));
602503
- for (let row = 0; row < paneHeight; row++) {
602684
+ const aspect = Math.max(0.22, Math.min(0.7, Math.max(...sourceLines.map(dashboardPreviewAspect))));
602685
+ const paneHeight = Math.max(10, Math.min(32, Math.round(paneContentWidth * aspect)));
602686
+ const panes = group.map((camera) => formatCameraPane(camera, paneWidth, paneHeight, now2));
602687
+ const rowCount = Math.max(...panes.map((pane) => pane.length));
602688
+ for (let row = 0; row < rowCount; row++) {
602504
602689
  const body = panes.map((pane) => pane[row] ?? " ".repeat(paneWidth)).join(" ");
602505
602690
  lines.push(`│${truncateCell(body, innerWidth)}│`);
602506
602691
  }
602507
- const labels = group.map((camera) => {
602508
- const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
602509
- return centerDashboardCell(`${compactSourceId(camera.source)} age=${age}s`, paneWidth);
602510
- }).join(" ");
602511
- lines.push(`│${truncateCell(labels, innerWidth)}│`);
602512
602692
  }
602513
602693
  }
602514
602694
  if (cameras.length > 0) {
@@ -602535,11 +602715,13 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
602535
602715
  const audioError = [snapshot.audio.error, transcriptFailure].filter(Boolean).join(" | ");
602536
602716
  const audioBits = [
602537
602717
  `audio input=${snapshot.audio.input || snapshot.config.selectedAudioInput || "none"}`,
602718
+ snapshot.audio.activity ? `activity ${snapshot.audio.activity.waveform} rms=${snapshot.audio.activity.rmsDb.toFixed(1)}dB active=${Math.round(snapshot.audio.activity.activeRatio * 100)}%` : "",
602538
602719
  audioError ? `error=${trimOneLine(audioError, 120)}` : "",
602539
- transcript ? `asr=${trimOneLine(transcript, 120)}` : "",
602720
+ transcript ? `asr${snapshot.audio.asrBackend ? `(${snapshot.audio.asrBackend})` : ""}=${trimOneLine(transcript, 120)}` : "",
602721
+ snapshot.audio.intake ? `intake=${snapshot.audio.intake.intendedForAgent ? "for-agent" : "ambient"} conf=${snapshot.audio.intake.confidence.toFixed(2)} ${snapshot.audio.intake.action}` : "",
602540
602722
  snapshot.audio.analysis ? `sound=${trimOneLine(snapshot.audio.analysis, 120)}` : ""
602541
602723
  ].filter(Boolean);
602542
- for (const item of audioBits.slice(0, 4)) lines.push(`│${truncateCell(` ${item}`, width - 2)}│`);
602724
+ for (const item of audioBits.slice(0, 6)) lines.push(`│${truncateCell(` ${item}`, width - 2)}│`);
602543
602725
  }
602544
602726
  lines.push(`╰${horizontal}╯`);
602545
602727
  return lines;
@@ -602909,10 +603091,17 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
602909
603091
  lines.push(`timestamp: ${nowIso3(snapshot.audio.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s${age > maxAgeMs ? " STALE" : ""}`);
602910
603092
  if (audioError) lines.push(`error: ${audioError}`);
602911
603093
  if (snapshot.audio.recordingPath) lines.push(`recording: ${snapshot.audio.recordingPath}`);
603094
+ if (snapshot.audio.activity) {
603095
+ lines.push(`activity: ${snapshot.audio.activity.waveform} rms_db=${snapshot.audio.activity.rmsDb.toFixed(1)} peak=${snapshot.audio.activity.peak.toFixed(3)} active=${Math.round(snapshot.audio.activity.activeRatio * 100)}%`);
603096
+ }
602912
603097
  if (transcript) {
602913
- lines.push("Live ASR:");
603098
+ lines.push(`Live ASR${snapshot.audio.asrBackend ? ` (${snapshot.audio.asrBackend})` : ""}:`);
602914
603099
  lines.push(cleanPreview(transcript, 1200));
602915
603100
  }
603101
+ if (snapshot.audio.intake) {
603102
+ lines.push(`Live audio intake: intended_for_agent=${snapshot.audio.intake.intendedForAgent ? "true" : "false"} confidence=${snapshot.audio.intake.confidence.toFixed(2)} action=${snapshot.audio.intake.action} source=${snapshot.audio.intake.source}`);
603103
+ lines.push(`intake_reason: ${snapshot.audio.intake.reason}`);
603104
+ }
602916
603105
  if (snapshot.audio.analysis) {
602917
603106
  lines.push("Live sound analysis:");
602918
603107
  lines.push(cleanPreview(snapshot.audio.analysis, 1600));
@@ -603174,6 +603363,7 @@ var init_live_sensors = __esm({
603174
603363
  lastAgentReviewAt = 0;
603175
603364
  autoOrientationInFlight = /* @__PURE__ */ new Set();
603176
603365
  lastFeedbackAt = /* @__PURE__ */ new Map();
603366
+ intakeAgentConfig;
603177
603367
  setStatusSink(sink) {
603178
603368
  this.statusSink = sink ?? null;
603179
603369
  this.emitStatus();
@@ -603184,6 +603374,9 @@ var init_live_sensors = __esm({
603184
603374
  setAgentActionSink(sink) {
603185
603375
  this.agentActionSink = sink ?? null;
603186
603376
  }
603377
+ setAudioIntakeAgentConfig(config) {
603378
+ this.intakeAgentConfig = config && config.backendUrl && config.model ? { backendUrl: config.backendUrl, model: config.model, apiKey: config.apiKey } : void 0;
603379
+ }
603187
603380
  getConfig() {
603188
603381
  return { ...this.config };
603189
603382
  }
@@ -603479,7 +603672,7 @@ var init_live_sensors = __esm({
603479
603672
  const framePath = extractSavedImagePath(result.output, this.repoRoot);
603480
603673
  if (!framePath) return { ok: false, message: "Camera capture succeeded but no saved image path was returned." };
603481
603674
  const displayPath = relative11(this.repoRoot, framePath).startsWith("..") ? framePath : relative11(this.repoRoot, framePath);
603482
- const previewWidth = Math.max(48, Math.min(96, (process.stdout.columns || 100) - 14));
603675
+ const previewWidth = Math.max(42, Math.min(120, Math.floor(Number(options2.previewWidth ?? (process.stdout.columns || 100) - 14))));
603483
603676
  const preview = await buildImageAsciiPreview(framePath, { width: previewWidth });
603484
603677
  const asciiContext = preview ? formatImageAsciiContext(preview, displayPath) : void 0;
603485
603678
  const observedAt = Date.now();
@@ -603523,6 +603716,7 @@ var init_live_sensors = __esm({
603523
603716
  imagePath: framePath,
603524
603717
  displayPath,
603525
603718
  ascii: preview?.ascii,
603719
+ plainAscii: preview?.plainAscii,
603526
603720
  renderer: preview?.renderer
603527
603721
  });
603528
603722
  }
@@ -603532,6 +603726,7 @@ var init_live_sensors = __esm({
603532
603726
  framePath,
603533
603727
  displayPath,
603534
603728
  ascii: preview?.ascii,
603729
+ plainAscii: preview?.plainAscii,
603535
603730
  renderer: preview?.renderer
603536
603731
  };
603537
603732
  }
@@ -603577,8 +603772,8 @@ var init_live_sensors = __esm({
603577
603772
  return message2;
603578
603773
  }
603579
603774
  }
603580
- async sampleSingleVideoNow(source, forceInfer) {
603581
- const preview = await this.previewCamera(source, { emit: false });
603775
+ async sampleSingleVideoNow(source, forceInfer, previewWidth) {
603776
+ const preview = await this.previewCamera(source, { emit: false, previewWidth });
603582
603777
  let inference = "";
603583
603778
  let clipSummary = "";
603584
603779
  const orientation = this.getCameraOrientation(source);
@@ -603667,8 +603862,9 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
603667
603862
  const sources = this.activeVideoSources();
603668
603863
  if (sources.length === 0) return "No camera selected. Use /live refresh after connecting hardware.";
603669
603864
  const outputs = [];
603865
+ const previewWidth = liveDashboardPreviewWidthForSources(sources.length);
603670
603866
  for (const source of sources) {
603671
- outputs.push(await this.sampleSingleVideoNow(source, forceInfer));
603867
+ outputs.push(await this.sampleSingleVideoNow(source, forceInfer, previewWidth));
603672
603868
  }
603673
603869
  this.emitDashboard();
603674
603870
  return outputs.map((output, index) => `## Camera ${sources[index]}
@@ -603686,7 +603882,9 @@ ${output}`).join("\n\n");
603686
603882
  ensureLiveDir(this.repoRoot);
603687
603883
  const recordingPath = join124(outputDir2, `audio-${Date.now()}.wav`);
603688
603884
  let transcript = "";
603885
+ let asrBackend = "";
603689
603886
  let analysis = "";
603887
+ let intake;
603690
603888
  const audioErrors = [];
603691
603889
  const capture = await recordAudioSampleWithRecovery(input, this.devices.audioInputs, 3, recordingPath);
603692
603890
  if (!capture.ok) {
@@ -603710,11 +603908,13 @@ ${output}`).join("\n\n");
603710
603908
  if (capture.device !== this.config.selectedAudioInput) {
603711
603909
  this.config.selectedAudioInput = capture.device;
603712
603910
  }
603911
+ const activity = readPcm16WavActivity(recordingPath);
603713
603912
  if (this.config.asrEnabled) {
603714
603913
  try {
603715
603914
  const asr = await new TranscribeFileTool(this.repoRoot).execute({ path: recordingPath, model: "tiny" });
603716
603915
  if (asr.success) {
603717
603916
  transcript = cleanPreview(typeof asr.llmContent === "string" ? asr.llmContent : "", 1600);
603917
+ asrBackend = extractAsrBackend(asr.output) ?? "whisper";
603718
603918
  } else {
603719
603919
  audioErrors.push(`ASR unavailable: ${asr.error || asr.output || "unknown error"}`);
603720
603920
  }
@@ -603740,13 +603940,19 @@ ${output}`).join("\n\n");
603740
603940
  }
603741
603941
  analysis = parts.filter(Boolean).join("\n");
603742
603942
  }
603943
+ if (transcript) {
603944
+ intake = await classifyLiveAudioIntake(transcript, this.intakeAgentConfig);
603945
+ }
603743
603946
  this.snapshot.audio = {
603744
603947
  updatedAt: Date.now(),
603745
603948
  input: capture.device,
603746
603949
  output: this.config.selectedAudioOutput,
603747
603950
  recordingPath,
603748
603951
  transcript,
603952
+ asrBackend: asrBackend || void 0,
603749
603953
  analysis,
603954
+ activity,
603955
+ intake,
603750
603956
  error: audioErrors.length > 0 ? audioErrors.join("\n") : void 0
603751
603957
  };
603752
603958
  const audioEvents = [];
@@ -603774,12 +603980,20 @@ ${output}`).join("\n\n");
603774
603980
  }
603775
603981
  this.snapshot.events = mergeRecentById(this.snapshot.events, audioEvents, 50);
603776
603982
  this.persist();
603983
+ if (transcript && intake?.intendedForAgent) {
603984
+ this.maybeRequestAgentReview(
603985
+ "Live audio intake classified speech as intended for Omnius",
603986
+ `transcript=${transcript}; confidence=${intake.confidence.toFixed(2)}; reason=${intake.reason}`
603987
+ );
603988
+ }
603777
603989
  this.emitDashboard();
603778
603990
  return [
603779
603991
  `Audio sample captured from ${capture.device}${capture.device !== input ? ` (recovered from ${input})` : ""}: ${recordingPath}`,
603780
603992
  transcript ? `
603781
- ASR:
603993
+ ASR${asrBackend ? ` (${asrBackend})` : ""}:
603782
603994
  ${transcript}` : "",
603995
+ intake ? `
603996
+ Intake: ${intake.intendedForAgent ? "intended for agent" : "ambient/monitor"} confidence=${intake.confidence.toFixed(2)} (${intake.source})` : "",
603783
603997
  analysis ? `
603784
603998
  Sound analysis:
603785
603999
  ${analysis}` : ""
@@ -624395,15 +624609,6 @@ var init_status_bar = __esm({
624395
624609
  const cmdPrefix = cmd.startsWith("view:") ? "omnius-view:" + cmd.slice(5) : "omnius-cmd:" + cmd;
624396
624610
  return `\x1B]8;;${cmdPrefix}\x07${label}\x1B]8;;\x07`;
624397
624611
  };
624398
- const buttonFg = (cmd) => {
624399
- let fg2 = TEXT_DIM;
624400
- if (cmd === "voice" && this._voiceActive) fg2 = 82;
624401
- if (cmd.startsWith("live") && (this._liveMediaStatus.audio || this._liveMediaStatus.video)) fg2 = 82;
624402
- if (cmd === "nexus") {
624403
- fg2 = this._nexusStatus === "connected" ? 82 : this._nexusStatus === "connecting" ? 208 : 196;
624404
- }
624405
- return fg2;
624406
- };
624407
624612
  const decorateMenuButton = (cmd, label) => {
624408
624613
  return `${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_LEFT}\x1B[0m${HEADER_BUTTON_BG}${HEADER_BUTTON_FG}${label}\x1B[0m${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`;
624409
624614
  };
@@ -624414,14 +624619,7 @@ var init_status_bar = __esm({
624414
624619
  return `\x1B[38;5;${color}m${HEADER_BUTTON_LEFT}\x1B[0m${weight}${fg2}${bg}${content}\x1B[0m${PANEL_BG_SEQ}\x1B[38;5;${color}m${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`;
624415
624620
  };
624416
624621
  const renderBtn = (cmd, label) => {
624417
- const fg2 = buttonFg(cmd);
624418
- const btnW = label.length + 2;
624419
- const availW2 = getTermWidth() - identity3.width - 1;
624420
- if (btnW > availW2) return linkify(cmd, `\x1B[38;5;${fg2}m${label}`);
624421
- return linkify(
624422
- cmd,
624423
- `${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_LEFT}\x1B[0m${PANEL_BG_SEQ}\x1B[38;5;${fg2}m${label}\x1B[0m${PANEL_BG_SEQ}${HEADER_BUTTON_GLYPH_FG}${HEADER_BUTTON_RIGHT}\x1B[0m${PANEL_BG_SEQ}`
624424
- );
624622
+ return linkify(cmd, decorateMenuButton(cmd, label));
624425
624623
  };
624426
624624
  const identity3 = this.buildHeaderIdentityRender();
624427
624625
  const modelLabel = this.summarizeHeaderModelName() || "model";
@@ -624494,10 +624692,9 @@ var init_status_bar = __esm({
624494
624692
  };
624495
624693
  if (this._activeViewId !== "main") {
624496
624694
  const mainLabel = ` ↩ main `;
624497
- const mainColored = `\x1B[38;5;110m${mainLabel}\x1B[0m`;
624498
624695
  sysItems.push({
624499
- render: () => mainColored + " ",
624500
- w: mainLabel.length + 1
624696
+ render: () => linkify("view:main", decorateMenuButton("view:main", mainLabel)) + " ",
624697
+ w: mainLabel.length + 2 + 1
624501
624698
  });
624502
624699
  }
624503
624700
  if (this._agentViews.size > 1) {
@@ -624535,10 +624732,7 @@ var init_status_bar = __esm({
624535
624732
  const telegramDot = this._telegramStatus.active ? "●" : "○";
624536
624733
  const telegramLabel = this._telegramStatus.activeSubAgents > 0 ? ` ✈ tg ${this._telegramStatus.activeSubAgents} ` : " ✈ tg ";
624537
624734
  sysItems.push({
624538
- render: () => renderBtn(
624539
- "telegram",
624540
- `${HEADER_TELEGRAM_FG}${telegramDot}${telegramLabel}\x1B[0m`
624541
- ) + " ",
624735
+ render: () => renderBtn("telegram", `${telegramDot}${telegramLabel}`) + " ",
624542
624736
  w: telegramLabel.length + 2
624543
624737
  });
624544
624738
  const liveMediaActive = this._liveMediaStatus.audio || this._liveMediaStatus.video;
@@ -624683,7 +624877,7 @@ var init_status_bar = __esm({
624683
624877
  const trunc3 = (s2) => s2.trim().split(/\s+/).slice(0, 3).join(" ");
624684
624878
  if (this._activeViewId !== "main") {
624685
624879
  const mainLabel = ` ↩ main `;
624686
- zones.push({ w: mainLabel.length + 1, id: "main", render: () => "" });
624880
+ zones.push({ w: mainLabel.length + 2 + 1, id: "main", render: () => "" });
624687
624881
  }
624688
624882
  if (this._agentViews.size > 1) {
624689
624883
  for (const view of this._agentViews.values()) {
@@ -655606,6 +655800,11 @@ ${feedback.message}`);
655606
655800
  function attachLiveSinks(ctx3, manager, agentEnabled = false) {
655607
655801
  manager.setStatusSink(ctx3.setLiveMediaStatus);
655608
655802
  manager.setFeedbackSink((feedback) => renderLiveFeedback(ctx3, manager, feedback));
655803
+ manager.setAudioIntakeAgentConfig({
655804
+ backendUrl: ctx3.config.backendUrl,
655805
+ model: ctx3.config.model,
655806
+ apiKey: ctx3.config.apiKey
655807
+ });
655609
655808
  if (agentEnabled && ctx3.startBackgroundPrompt) {
655610
655809
  manager.setAgentActionSink((prompt) => {
655611
655810
  const id2 = ctx3.startBackgroundPrompt?.(prompt);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "omnius",
3
- "version": "1.0.409",
3
+ "version": "1.0.410",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "omnius",
9
- "version": "1.0.409",
9
+ "version": "1.0.410",
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.409",
3
+ "version": "1.0.410",
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",