omnius 1.0.408 → 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 +654 -155
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -41519,7 +41519,7 @@ function formatTime(seconds) {
|
|
|
41519
41519
|
const s2 = Math.floor(seconds % 60);
|
|
41520
41520
|
return `${String(m2).padStart(2, "0")}:${String(s2).padStart(2, "0")}`;
|
|
41521
41521
|
}
|
|
41522
|
-
var AUDIO_EXTS, VIDEO_EXTS, MAX_TRANSCRIBE_URL_BYTES, _tcModule, _tcChecked, TranscribeFileTool, YT_DLP_VENV, TranscribeUrlTool, YouTubeDownloadTool;
|
|
41522
|
+
var AUDIO_EXTS, VIDEO_EXTS, MAX_TRANSCRIBE_URL_BYTES, MANAGED_ASR_VENV, _tcModule, _tcChecked, _managedAsrReady, TranscribeFileTool, YT_DLP_VENV, TranscribeUrlTool, YouTubeDownloadTool;
|
|
41523
41523
|
var init_transcribe_tool = __esm({
|
|
41524
41524
|
"packages/execution/dist/tools/transcribe-tool.js"() {
|
|
41525
41525
|
"use strict";
|
|
@@ -41550,8 +41550,10 @@ var init_transcribe_tool = __esm({
|
|
|
41550
41550
|
".ts"
|
|
41551
41551
|
]);
|
|
41552
41552
|
MAX_TRANSCRIBE_URL_BYTES = 100 * 1024 * 1024;
|
|
41553
|
+
MANAGED_ASR_VENV = join40(homedir11(), ".omnius", "runtimes", "asr", ".venv-whisper");
|
|
41553
41554
|
_tcModule = null;
|
|
41554
41555
|
_tcChecked = false;
|
|
41556
|
+
_managedAsrReady = false;
|
|
41555
41557
|
TranscribeFileTool = class {
|
|
41556
41558
|
name = "transcribe_file";
|
|
41557
41559
|
description = "Transcribe a local audio or video file to text using Whisper (faster-whisper). Supports MP3, WAV, FLAC, AAC, M4A, OGG, MP4, MKV, AVI, MOV, WebM. Returns the full transcription text with optional speaker diarization. Transcription is 100% local — no API keys needed.";
|
|
@@ -41625,102 +41627,131 @@ var init_transcribe_tool = __esm({
|
|
|
41625
41627
|
}
|
|
41626
41628
|
if (effectiveModel !== askedModel)
|
|
41627
41629
|
model = effectiveModel;
|
|
41630
|
+
const failures = [];
|
|
41628
41631
|
const tc = await loadTranscribeCli();
|
|
41629
|
-
if (
|
|
41630
|
-
|
|
41631
|
-
|
|
41632
|
+
if (tc) {
|
|
41633
|
+
try {
|
|
41634
|
+
const result = await withProcessEnv(transcriptionPythonEnv(), () => tc.transcribe(filePath, {
|
|
41635
|
+
model,
|
|
41636
|
+
format: "json",
|
|
41637
|
+
diarize,
|
|
41638
|
+
wordTimestamps: true
|
|
41639
|
+
}));
|
|
41640
|
+
return this.formatTranscriptionResult(filePath, model, result, start2, "transcribe-cli");
|
|
41641
|
+
} catch (err) {
|
|
41642
|
+
failures.push(`transcribe-cli module: ${err instanceof Error ? err.message : String(err)}`);
|
|
41643
|
+
}
|
|
41644
|
+
}
|
|
41645
|
+
const cli = await this.execViaCli(filePath, model, diarize, start2);
|
|
41646
|
+
if (cli.success)
|
|
41647
|
+
return cli;
|
|
41648
|
+
if (cli.error)
|
|
41649
|
+
failures.push(cli.error);
|
|
41650
|
+
const managed = await this.execViaManagedWhisper(filePath, model, start2);
|
|
41651
|
+
if (managed.success)
|
|
41652
|
+
return managed;
|
|
41653
|
+
if (managed.error)
|
|
41654
|
+
failures.push(managed.error);
|
|
41655
|
+
return {
|
|
41656
|
+
success: false,
|
|
41657
|
+
output: "",
|
|
41658
|
+
error: `Transcription failed after all ASR backends. ${failures.join(" | ").slice(0, 1200)}`,
|
|
41659
|
+
durationMs: performance.now() - start2
|
|
41660
|
+
};
|
|
41661
|
+
}
|
|
41662
|
+
formatTranscriptionResult(filePath, model, result, start2, backend) {
|
|
41663
|
+
const segments = Array.isArray(result.segments) ? result.segments : [];
|
|
41664
|
+
const text2 = String(result.text ?? result.fullText ?? "").trim();
|
|
41665
|
+
const speakers = Array.isArray(result.speakers) ? result.speakers : [];
|
|
41666
|
+
const wordCount2 = Number(result.wordCount ?? (text2 ? text2.split(/\s+/).filter(Boolean).length : 0));
|
|
41667
|
+
const transcriptDir = join40(this.workingDir, ".omnius", "transcripts");
|
|
41668
|
+
mkdirSync22(transcriptDir, { recursive: true });
|
|
41669
|
+
const fileBase = basename7(filePath).replace(/\.[^.]+$/, "");
|
|
41670
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
41671
|
+
const txtFile = join40(transcriptDir, `${fileBase}-${timestamp}.txt`);
|
|
41672
|
+
const jsonFile = join40(transcriptDir, `${fileBase}-${timestamp}.json`);
|
|
41673
|
+
writeFileSync21(txtFile, text2, "utf-8");
|
|
41674
|
+
const structured = {
|
|
41675
|
+
source: filePath,
|
|
41676
|
+
model,
|
|
41677
|
+
backend,
|
|
41678
|
+
language: result.language,
|
|
41679
|
+
duration: result.duration,
|
|
41680
|
+
wordCount: wordCount2,
|
|
41681
|
+
speakers,
|
|
41682
|
+
transcribedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41683
|
+
segments: segments.map((seg) => ({
|
|
41684
|
+
start: Number(seg.start ?? 0),
|
|
41685
|
+
end: Number(seg.end ?? seg.start ?? 0),
|
|
41686
|
+
text: String(seg.text ?? ""),
|
|
41687
|
+
speaker: seg.speaker || void 0
|
|
41688
|
+
})),
|
|
41689
|
+
fullText: text2
|
|
41690
|
+
};
|
|
41691
|
+
writeFileSync21(jsonFile, JSON.stringify(structured, null, 2), "utf-8");
|
|
41632
41692
|
try {
|
|
41633
|
-
const
|
|
41634
|
-
|
|
41635
|
-
|
|
41636
|
-
|
|
41637
|
-
wordTimestamps: true
|
|
41638
|
-
// Always get timestamps for structured output
|
|
41639
|
-
}));
|
|
41640
|
-
const transcriptDir = join40(this.workingDir, ".omnius", "transcripts");
|
|
41641
|
-
mkdirSync22(transcriptDir, { recursive: true });
|
|
41642
|
-
const fileBase = basename7(filePath).replace(/\.[^.]+$/, "");
|
|
41643
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
41644
|
-
const txtFile = join40(transcriptDir, `${fileBase}-${timestamp}.txt`);
|
|
41645
|
-
writeFileSync21(txtFile, result.text, "utf-8");
|
|
41646
|
-
const jsonFile = join40(transcriptDir, `${fileBase}-${timestamp}.json`);
|
|
41647
|
-
const structured = {
|
|
41648
|
-
source: filePath,
|
|
41649
|
-
model,
|
|
41650
|
-
language: result.language,
|
|
41651
|
-
duration: result.duration,
|
|
41652
|
-
wordCount: result.wordCount,
|
|
41653
|
-
speakers: result.speakers,
|
|
41654
|
-
transcribedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41655
|
-
segments: result.segments.map((seg) => ({
|
|
41656
|
-
start: seg.start,
|
|
41657
|
-
end: seg.end,
|
|
41658
|
-
text: seg.text,
|
|
41659
|
-
speaker: seg.speaker || void 0
|
|
41660
|
-
})),
|
|
41661
|
-
fullText: result.text
|
|
41662
|
-
};
|
|
41663
|
-
writeFileSync21(jsonFile, JSON.stringify(structured, null, 2), "utf-8");
|
|
41693
|
+
const memDir = join40(this.workingDir, ".omnius", "memory");
|
|
41694
|
+
mkdirSync22(memDir, { recursive: true });
|
|
41695
|
+
const memFile = join40(memDir, "transcripts.json");
|
|
41696
|
+
let memEntries = [];
|
|
41664
41697
|
try {
|
|
41665
|
-
|
|
41666
|
-
mkdirSync22(memDir, { recursive: true });
|
|
41667
|
-
const memFile = join40(memDir, "transcripts.json");
|
|
41668
|
-
let memEntries = [];
|
|
41669
|
-
try {
|
|
41670
|
-
memEntries = JSON.parse(readFileSync30(memFile, "utf-8"));
|
|
41671
|
-
} catch {
|
|
41672
|
-
}
|
|
41673
|
-
memEntries.push({
|
|
41674
|
-
source: basename7(filePath),
|
|
41675
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41676
|
-
duration: result.duration,
|
|
41677
|
-
wordCount: result.wordCount,
|
|
41678
|
-
language: result.language,
|
|
41679
|
-
summary: result.text.slice(0, 200),
|
|
41680
|
-
jsonPath: jsonFile,
|
|
41681
|
-
txtPath: txtFile
|
|
41682
|
-
});
|
|
41683
|
-
if (memEntries.length > 50)
|
|
41684
|
-
memEntries = memEntries.slice(-50);
|
|
41685
|
-
writeFileSync21(memFile, JSON.stringify(memEntries, null, 2), "utf-8");
|
|
41698
|
+
memEntries = JSON.parse(readFileSync30(memFile, "utf-8"));
|
|
41686
41699
|
} catch {
|
|
41687
41700
|
}
|
|
41688
|
-
|
|
41689
|
-
|
|
41690
|
-
|
|
41691
|
-
|
|
41692
|
-
|
|
41693
|
-
|
|
41694
|
-
|
|
41695
|
-
|
|
41696
|
-
|
|
41697
|
-
|
|
41698
|
-
}
|
|
41699
|
-
if (
|
|
41700
|
-
|
|
41701
|
-
|
|
41702
|
-
|
|
41703
|
-
|
|
41704
|
-
|
|
41705
|
-
|
|
41706
|
-
|
|
41701
|
+
memEntries.push({
|
|
41702
|
+
source: basename7(filePath),
|
|
41703
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41704
|
+
duration: result.duration,
|
|
41705
|
+
wordCount: wordCount2,
|
|
41706
|
+
language: result.language,
|
|
41707
|
+
backend,
|
|
41708
|
+
summary: text2.slice(0, 200),
|
|
41709
|
+
jsonPath: jsonFile,
|
|
41710
|
+
txtPath: txtFile
|
|
41711
|
+
});
|
|
41712
|
+
if (memEntries.length > 50)
|
|
41713
|
+
memEntries = memEntries.slice(-50);
|
|
41714
|
+
writeFileSync21(memFile, JSON.stringify(memEntries, null, 2), "utf-8");
|
|
41715
|
+
} catch {
|
|
41716
|
+
}
|
|
41717
|
+
const lines = [
|
|
41718
|
+
`Transcription of: ${basename7(filePath)}`,
|
|
41719
|
+
`Backend: ${backend}`,
|
|
41720
|
+
`Model: ${model} | Language: ${result.language ?? "unknown"} | Duration: ${result.duration ? `${Number(result.duration).toFixed(1)}s` : "unknown"}`,
|
|
41721
|
+
`Words: ${wordCount2} | Segments: ${segments.length}`,
|
|
41722
|
+
`Saved: ${txtFile} (text) + ${jsonFile} (structured with timestamps)`,
|
|
41723
|
+
""
|
|
41724
|
+
];
|
|
41725
|
+
if (speakers.length > 1) {
|
|
41726
|
+
lines.push(`Speakers: ${speakers.join(", ")}`);
|
|
41727
|
+
lines.push("");
|
|
41728
|
+
}
|
|
41729
|
+
if (segments.length > 0) {
|
|
41730
|
+
for (const seg of segments) {
|
|
41731
|
+
const ts = `[${formatTime(Number(seg.start ?? 0))} → ${formatTime(Number(seg.end ?? seg.start ?? 0))}]`;
|
|
41732
|
+
const speaker = seg.speaker ? `${seg.speaker}: ` : "";
|
|
41733
|
+
lines.push(`${ts} ${speaker}${String(seg.text ?? "").trim()}`);
|
|
41707
41734
|
}
|
|
41708
|
-
|
|
41709
|
-
|
|
41710
|
-
|
|
41711
|
-
|
|
41712
|
-
|
|
41713
|
-
|
|
41735
|
+
} else {
|
|
41736
|
+
lines.push(text2 || "[silent — no speech detected]");
|
|
41737
|
+
}
|
|
41738
|
+
return {
|
|
41739
|
+
success: true,
|
|
41740
|
+
output: lines.join("\n"),
|
|
41741
|
+
llmContent: text2,
|
|
41742
|
+
durationMs: performance.now() - start2
|
|
41743
|
+
};
|
|
41744
|
+
}
|
|
41745
|
+
/** Fallback: invoke transcribe-cli as a subprocess. */
|
|
41746
|
+
async execViaCli(filePath, model, diarize, start2) {
|
|
41747
|
+
if (!await commandExists("transcribe-cli", 1500)) {
|
|
41714
41748
|
return {
|
|
41715
41749
|
success: false,
|
|
41716
41750
|
output: "",
|
|
41717
|
-
error:
|
|
41751
|
+
error: "transcribe-cli command unavailable",
|
|
41718
41752
|
durationMs: performance.now() - start2
|
|
41719
41753
|
};
|
|
41720
41754
|
}
|
|
41721
|
-
}
|
|
41722
|
-
/** Fallback: invoke transcribe-cli as a subprocess. */
|
|
41723
|
-
async execViaCli(filePath, model, diarize, start2) {
|
|
41724
41755
|
try {
|
|
41725
41756
|
const args = [filePath, "-m", model, "-f", "txt"];
|
|
41726
41757
|
if (diarize)
|
|
@@ -41734,6 +41765,7 @@ var init_transcribe_tool = __esm({
|
|
|
41734
41765
|
return {
|
|
41735
41766
|
success: true,
|
|
41736
41767
|
output: output.trim() || "(empty transcription)",
|
|
41768
|
+
llmContent: output.trim() || "",
|
|
41737
41769
|
durationMs: performance.now() - start2
|
|
41738
41770
|
};
|
|
41739
41771
|
} catch (err) {
|
|
@@ -41747,6 +41779,112 @@ var init_transcribe_tool = __esm({
|
|
|
41747
41779
|
};
|
|
41748
41780
|
}
|
|
41749
41781
|
}
|
|
41782
|
+
async ensureManagedWhisperRuntime() {
|
|
41783
|
+
const python = venvPython(MANAGED_ASR_VENV);
|
|
41784
|
+
if (!existsSync38(python)) {
|
|
41785
|
+
mkdirSync22(join40(homedir11(), ".omnius", "runtimes", "asr"), { recursive: true });
|
|
41786
|
+
await execFileText("python3", ["-m", "venv", MANAGED_ASR_VENV], {
|
|
41787
|
+
timeout: 6e4,
|
|
41788
|
+
env: transcriptionPythonEnv()
|
|
41789
|
+
});
|
|
41790
|
+
}
|
|
41791
|
+
if (_managedAsrReady)
|
|
41792
|
+
return python;
|
|
41793
|
+
try {
|
|
41794
|
+
await execFileText(python, ["-c", "import whisper, numpy"], {
|
|
41795
|
+
timeout: 1e4,
|
|
41796
|
+
env: transcriptionPythonEnv()
|
|
41797
|
+
});
|
|
41798
|
+
_managedAsrReady = true;
|
|
41799
|
+
return python;
|
|
41800
|
+
} catch {
|
|
41801
|
+
await execFileText(python, [
|
|
41802
|
+
"-m",
|
|
41803
|
+
"pip",
|
|
41804
|
+
"install",
|
|
41805
|
+
"-U",
|
|
41806
|
+
"pip",
|
|
41807
|
+
"wheel",
|
|
41808
|
+
"setuptools",
|
|
41809
|
+
"numpy",
|
|
41810
|
+
"openai-whisper"
|
|
41811
|
+
], {
|
|
41812
|
+
timeout: 9e5,
|
|
41813
|
+
env: transcriptionPythonEnv()
|
|
41814
|
+
});
|
|
41815
|
+
_managedAsrReady = true;
|
|
41816
|
+
return python;
|
|
41817
|
+
}
|
|
41818
|
+
}
|
|
41819
|
+
async execViaManagedWhisper(filePath, model, start2) {
|
|
41820
|
+
const scriptPath2 = join40(this.workingDir, ".omnius", "tmp", `managed-whisper-${Date.now()}.py`);
|
|
41821
|
+
mkdirSync22(join40(this.workingDir, ".omnius", "tmp"), { recursive: true });
|
|
41822
|
+
const script = `
|
|
41823
|
+
import json, os, warnings
|
|
41824
|
+
warnings.filterwarnings("ignore")
|
|
41825
|
+
os.environ.setdefault("TF_CPP_MIN_LOG_LEVEL", "3")
|
|
41826
|
+
audio_file = ${JSON.stringify(filePath)}
|
|
41827
|
+
model_name = ${JSON.stringify(model)}
|
|
41828
|
+
try:
|
|
41829
|
+
import whisper
|
|
41830
|
+
device = "cpu"
|
|
41831
|
+
try:
|
|
41832
|
+
import torch
|
|
41833
|
+
if torch.cuda.is_available():
|
|
41834
|
+
device = "cuda"
|
|
41835
|
+
except Exception:
|
|
41836
|
+
pass
|
|
41837
|
+
m = whisper.load_model(model_name, device=device)
|
|
41838
|
+
result = m.transcribe(audio_file, fp16=(device == "cuda"), condition_on_previous_text=False)
|
|
41839
|
+
segments = []
|
|
41840
|
+
for seg in result.get("segments", []) or []:
|
|
41841
|
+
segments.append({"start": float(seg.get("start", 0)), "end": float(seg.get("end", seg.get("start", 0))), "text": str(seg.get("text", "")).strip()})
|
|
41842
|
+
text = str(result.get("text", "")).strip()
|
|
41843
|
+
print(json.dumps({
|
|
41844
|
+
"ok": True,
|
|
41845
|
+
"text": text,
|
|
41846
|
+
"language": result.get("language"),
|
|
41847
|
+
"duration": segments[-1]["end"] if segments else None,
|
|
41848
|
+
"wordCount": len([w for w in text.split() if w]),
|
|
41849
|
+
"speakers": [],
|
|
41850
|
+
"segments": segments,
|
|
41851
|
+
}))
|
|
41852
|
+
except Exception as exc:
|
|
41853
|
+
print(json.dumps({"ok": False, "error": str(exc)}))
|
|
41854
|
+
`;
|
|
41855
|
+
try {
|
|
41856
|
+
writeFileSync21(scriptPath2, script, "utf-8");
|
|
41857
|
+
const python = await this.ensureManagedWhisperRuntime();
|
|
41858
|
+
const raw = await execFileText(python, [scriptPath2], {
|
|
41859
|
+
timeout: 6e5,
|
|
41860
|
+
cwd: this.workingDir,
|
|
41861
|
+
env: transcriptionPythonEnv({ PYTHONUNBUFFERED: "1" }),
|
|
41862
|
+
maxBuffer: 16 * 1024 * 1024
|
|
41863
|
+
});
|
|
41864
|
+
const parsed = JSON.parse(raw.trim().split("\n").reverse().find((line) => line.trim().startsWith("{")) ?? "{}");
|
|
41865
|
+
if (!parsed.ok) {
|
|
41866
|
+
return {
|
|
41867
|
+
success: false,
|
|
41868
|
+
output: "",
|
|
41869
|
+
error: `managed openai-whisper failed: ${parsed.error || raw.slice(-500)}`,
|
|
41870
|
+
durationMs: performance.now() - start2
|
|
41871
|
+
};
|
|
41872
|
+
}
|
|
41873
|
+
return this.formatTranscriptionResult(filePath, model, parsed, start2, "managed openai-whisper");
|
|
41874
|
+
} catch (err) {
|
|
41875
|
+
return {
|
|
41876
|
+
success: false,
|
|
41877
|
+
output: "",
|
|
41878
|
+
error: `managed openai-whisper failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
41879
|
+
durationMs: performance.now() - start2
|
|
41880
|
+
};
|
|
41881
|
+
} finally {
|
|
41882
|
+
try {
|
|
41883
|
+
unlinkSync5(scriptPath2);
|
|
41884
|
+
} catch {
|
|
41885
|
+
}
|
|
41886
|
+
}
|
|
41887
|
+
}
|
|
41750
41888
|
};
|
|
41751
41889
|
YT_DLP_VENV = join40(homedir11(), ".omnius", "runtimes", "media-tools", ".venv-ytdlp");
|
|
41752
41890
|
TranscribeUrlTool = class {
|
|
@@ -547804,17 +547942,23 @@ except Exception as e:
|
|
|
547804
547942
|
const audioFile = await this.ensureAudioFile(args);
|
|
547805
547943
|
if (!audioFile)
|
|
547806
547944
|
return { success: false, output: "", error: "No audio available.", durationMs: performance.now() - start2 };
|
|
547807
|
-
await this.ensureVenv(["torch", "
|
|
547945
|
+
await this.ensureVenv(["torch", "numpy", "soundfile", "resampy"]);
|
|
547808
547946
|
const audioLiteral = JSON.stringify(audioFile);
|
|
547809
547947
|
const script = `
|
|
547810
547948
|
import sys, json, numpy as np
|
|
547811
547949
|
try:
|
|
547812
|
-
import torch,
|
|
547950
|
+
import torch, soundfile as sf, resampy
|
|
547813
547951
|
|
|
547814
547952
|
model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad', model='silero_vad', force_reload=False, trust_repo=True)
|
|
547815
|
-
(get_speech_timestamps,
|
|
547953
|
+
(get_speech_timestamps, *_) = utils
|
|
547816
547954
|
|
|
547817
|
-
|
|
547955
|
+
audio, sr = sf.read(${audioLiteral})
|
|
547956
|
+
if len(getattr(audio, "shape", [])) > 1:
|
|
547957
|
+
audio = audio.mean(axis=1)
|
|
547958
|
+
if sr != 16000:
|
|
547959
|
+
audio = resampy.resample(audio, sr, 16000)
|
|
547960
|
+
audio = audio.astype(np.float32)
|
|
547961
|
+
wav = torch.from_numpy(audio)
|
|
547818
547962
|
speech_timestamps = get_speech_timestamps(wav, model, sampling_rate=16000, threshold=0.5)
|
|
547819
547963
|
|
|
547820
547964
|
total_samples = len(wav)
|
|
@@ -547825,6 +547969,7 @@ try:
|
|
|
547825
547969
|
|
|
547826
547970
|
print(json.dumps({
|
|
547827
547971
|
"success": True,
|
|
547972
|
+
"backend": "silero-vad",
|
|
547828
547973
|
"voice_detected": len(speech_timestamps) > 0,
|
|
547829
547974
|
"speech_percentage": round(speech_pct, 1),
|
|
547830
547975
|
"speech_segments": len(speech_timestamps),
|
|
@@ -547832,7 +547977,57 @@ try:
|
|
|
547832
547977
|
"segments": segments[:10]
|
|
547833
547978
|
}))
|
|
547834
547979
|
except Exception as e:
|
|
547835
|
-
|
|
547980
|
+
try:
|
|
547981
|
+
import soundfile as sf
|
|
547982
|
+
import resampy
|
|
547983
|
+
audio, sr = sf.read(${audioLiteral})
|
|
547984
|
+
if len(getattr(audio, "shape", [])) > 1:
|
|
547985
|
+
audio = audio.mean(axis=1)
|
|
547986
|
+
if sr != 16000:
|
|
547987
|
+
audio = resampy.resample(audio, sr, 16000)
|
|
547988
|
+
audio = audio.astype(np.float32)
|
|
547989
|
+
frame = max(1, int(0.03 * 16000))
|
|
547990
|
+
hop = max(1, int(0.01 * 16000))
|
|
547991
|
+
rms = []
|
|
547992
|
+
for i in range(0, max(1, len(audio) - frame + 1), hop):
|
|
547993
|
+
chunk = audio[i:i+frame]
|
|
547994
|
+
if len(chunk) == 0:
|
|
547995
|
+
continue
|
|
547996
|
+
rms.append(float(np.sqrt(np.mean(chunk * chunk))))
|
|
547997
|
+
arr = np.array(rms, dtype=np.float32)
|
|
547998
|
+
if len(arr) == 0:
|
|
547999
|
+
active = np.array([], dtype=bool)
|
|
548000
|
+
else:
|
|
548001
|
+
floor = float(np.percentile(arr, 35))
|
|
548002
|
+
peak = float(np.max(arr))
|
|
548003
|
+
threshold = max(0.008, floor * 2.5, peak * 0.12)
|
|
548004
|
+
active = arr > threshold
|
|
548005
|
+
segments = []
|
|
548006
|
+
start_idx = None
|
|
548007
|
+
for idx, flag in enumerate(active.tolist()):
|
|
548008
|
+
if flag and start_idx is None:
|
|
548009
|
+
start_idx = idx
|
|
548010
|
+
if (not flag or idx == len(active) - 1) and start_idx is not None:
|
|
548011
|
+
end_idx = idx if not flag else idx + 1
|
|
548012
|
+
start_s = start_idx * hop / 16000.0
|
|
548013
|
+
end_s = min(len(audio) / 16000.0, (end_idx * hop + frame) / 16000.0)
|
|
548014
|
+
if end_s - start_s >= 0.12:
|
|
548015
|
+
segments.append({"start_s": start_s, "end_s": end_s, "duration_s": end_s - start_s})
|
|
548016
|
+
start_idx = None
|
|
548017
|
+
speech_duration = sum(seg["duration_s"] for seg in segments)
|
|
548018
|
+
total_duration = len(audio) / 16000.0 if len(audio) else 0
|
|
548019
|
+
print(json.dumps({
|
|
548020
|
+
"success": True,
|
|
548021
|
+
"backend": "energy-vad-fallback",
|
|
548022
|
+
"warning": "silero unavailable: " + str(e),
|
|
548023
|
+
"voice_detected": len(segments) > 0,
|
|
548024
|
+
"speech_percentage": round((speech_duration / total_duration * 100) if total_duration else 0, 1),
|
|
548025
|
+
"speech_segments": len(segments),
|
|
548026
|
+
"total_duration_s": round(total_duration, 2),
|
|
548027
|
+
"segments": segments[:10]
|
|
548028
|
+
}))
|
|
548029
|
+
except Exception as fallback:
|
|
548030
|
+
print(json.dumps({"success": False, "error": str(e) + "; fallback failed: " + str(fallback)}))
|
|
547836
548031
|
`;
|
|
547837
548032
|
return await this.runPythonScript(script, start2, "Voice activity detection");
|
|
547838
548033
|
}
|
|
@@ -555516,6 +555711,9 @@ function summarizeObjects(objects) {
|
|
|
555516
555711
|
}
|
|
555517
555712
|
return [...counts.entries()].sort((a2, b) => b[1].count - a2[1].count).slice(0, 12).map(([label, value2]) => `${label} x${value2.count} mean_conf=${(value2.confidence / Math.max(1, value2.count)).toFixed(2)}`);
|
|
555518
555713
|
}
|
|
555714
|
+
function isDiagnosticAudioEvidence(text2) {
|
|
555715
|
+
return /^(?:ASR|VAD|Audio comprehension)\s+(?:failed|unavailable|completed)|No module named ['"]?transcribe_cli|torchcodec|torchaudio version|no speech detected|empty transcription/i.test(text2.trim());
|
|
555716
|
+
}
|
|
555519
555717
|
function observationsFromResult(result, extraText) {
|
|
555520
555718
|
const observations = [];
|
|
555521
555719
|
for (const obj of result.objects) {
|
|
@@ -555541,6 +555739,8 @@ function observationsFromResult(result, extraText) {
|
|
|
555541
555739
|
});
|
|
555542
555740
|
}
|
|
555543
555741
|
for (const text2 of extraText) {
|
|
555742
|
+
if (isDiagnosticAudioEvidence(text2))
|
|
555743
|
+
continue;
|
|
555544
555744
|
observations.push({
|
|
555545
555745
|
type: "transcript",
|
|
555546
555746
|
confidence: 0.75,
|
|
@@ -555958,7 +556158,12 @@ var init_live_media_loop = __esm({
|
|
|
555958
556158
|
try {
|
|
555959
556159
|
const transcribe = new TranscribeFileTool(this.workingDir);
|
|
555960
556160
|
const asr = await transcribe.execute({ path: result.media_path, model: "tiny" });
|
|
555961
|
-
|
|
556161
|
+
if (asr.success) {
|
|
556162
|
+
const transcript = typeof asr.llmContent === "string" ? asr.llmContent.trim() : "";
|
|
556163
|
+
audioEvidence.push(transcript ? transcript.slice(0, 3e3) : "ASR completed: no speech detected");
|
|
556164
|
+
} else {
|
|
556165
|
+
audioEvidence.push(`ASR unavailable: ${asr.error}`);
|
|
556166
|
+
}
|
|
555962
556167
|
} catch (err) {
|
|
555963
556168
|
audioEvidence.push(`ASR failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
555964
556169
|
}
|
|
@@ -601681,7 +601886,8 @@ __export(image_ascii_preview_exports, {
|
|
|
601681
601886
|
buildImageAsciiPreview: () => buildImageAsciiPreview,
|
|
601682
601887
|
defaultAsciiPreviewSize: () => defaultAsciiPreviewSize,
|
|
601683
601888
|
extractSavedImagePath: () => extractSavedImagePath,
|
|
601684
|
-
formatImageAsciiContext: () => formatImageAsciiContext
|
|
601889
|
+
formatImageAsciiContext: () => formatImageAsciiContext,
|
|
601890
|
+
isLowInformationAsciiPreview: () => isLowInformationAsciiPreview
|
|
601685
601891
|
});
|
|
601686
601892
|
import { createRequire as createRequire5 } from "node:module";
|
|
601687
601893
|
import { existsSync as existsSync108, readFileSync as readFileSync87, statSync as statSync42 } from "node:fs";
|
|
@@ -601694,10 +601900,13 @@ function defaultAsciiPreviewSize(dimensions) {
|
|
|
601694
601900
|
const columns = process.stdout.columns || 100;
|
|
601695
601901
|
const rows = process.stdout.rows || 32;
|
|
601696
601902
|
const width = clamp7(columns - 14, 42, 96);
|
|
601697
|
-
const
|
|
601698
|
-
const height = clamp7(Math.min(Math.round(width * imageAspect * TERMINAL_CELL_ASPECT), rows - 10), 8, 42);
|
|
601903
|
+
const height = defaultAsciiPreviewHeight(width, dimensions, rows);
|
|
601699
601904
|
return { width, height };
|
|
601700
601905
|
}
|
|
601906
|
+
function defaultAsciiPreviewHeight(width, dimensions, rows = process.stdout.rows || 32) {
|
|
601907
|
+
const imageAspect = dimensions && dimensions.width > 0 && dimensions.height > 0 ? dimensions.height / dimensions.width : 1;
|
|
601908
|
+
return clamp7(Math.min(Math.round(width * imageAspect * TERMINAL_CELL_ASPECT), rows - 10), 8, 42);
|
|
601909
|
+
}
|
|
601701
601910
|
function readImageDimensions2(imagePath) {
|
|
601702
601911
|
try {
|
|
601703
601912
|
const buf = readFileSync87(imagePath);
|
|
@@ -601846,6 +602055,15 @@ function normalizeImageToAsciiResult(converted, width, height) {
|
|
|
601846
602055
|
if (!ascii2) return { ascii: null, error: "empty normalized renderer output" };
|
|
601847
602056
|
return { ascii: ascii2, plainAscii: plainAscii || void 0 };
|
|
601848
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
|
+
}
|
|
601849
602067
|
function previewFailure(message2, width) {
|
|
601850
602068
|
const clean5 = message2.replace(/\s+/g, " ").trim();
|
|
601851
602069
|
const text2 = `[image-to-ascii preview failed: ${clean5 || "unknown error"}]`;
|
|
@@ -601959,15 +602177,19 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
|
|
|
601959
602177
|
const dimensions = readImageDimensions2(imagePath);
|
|
601960
602178
|
const defaults3 = defaultAsciiPreviewSize(dimensions);
|
|
601961
602179
|
const width = clamp7(options2.width ?? defaults3.width, 24, 140);
|
|
601962
|
-
const height = clamp7(options2.height ??
|
|
602180
|
+
const height = clamp7(options2.height ?? defaultAsciiPreviewHeight(width, dimensions), 6, 60);
|
|
601963
602181
|
const timeoutMs = clamp7(options2.timeoutMs ?? 5e3, 500, 3e4);
|
|
601964
602182
|
if (options2.preferPackage !== false) {
|
|
601965
602183
|
const result = await convertWithImageToAscii(imagePath, width, height, timeoutMs);
|
|
601966
602184
|
if (result.ascii) {
|
|
602185
|
+
let plainAscii = result.plainAscii;
|
|
602186
|
+
if (isLowInformationAsciiPreview(plainAscii)) {
|
|
602187
|
+
plainAscii = await convertWithFfmpeg(imagePath, width, height, timeoutMs) ?? plainAscii;
|
|
602188
|
+
}
|
|
601967
602189
|
return {
|
|
601968
602190
|
path: imagePath,
|
|
601969
602191
|
ascii: result.ascii,
|
|
601970
|
-
plainAscii
|
|
602192
|
+
plainAscii,
|
|
601971
602193
|
renderer: "image-to-ascii",
|
|
601972
602194
|
width,
|
|
601973
602195
|
height
|
|
@@ -602189,9 +602411,238 @@ function compactSourceId(source) {
|
|
|
602189
602411
|
return source.replace(/^\/dev\//, "");
|
|
602190
602412
|
}
|
|
602191
602413
|
function truncateCell(value2, width) {
|
|
602414
|
+
value2 = stripDashboardAnsi(String(value2 ?? "")).replace(/\r?\n/g, " ");
|
|
602192
602415
|
if (width <= 1) return value2.slice(0, Math.max(0, width));
|
|
602193
602416
|
return value2.length <= width ? value2.padEnd(width, " ") : `${value2.slice(0, Math.max(1, width - 1))}…`;
|
|
602194
602417
|
}
|
|
602418
|
+
function stripDashboardAnsi(value2) {
|
|
602419
|
+
return value2.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
|
|
602420
|
+
}
|
|
602421
|
+
function dashboardPreviewLines(camera) {
|
|
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);
|
|
602424
|
+
}
|
|
602425
|
+
function dashboardPreviewAspect(lines) {
|
|
602426
|
+
const widths = lines.map((line) => stripDashboardAnsi(line).length).filter((n2) => n2 > 0);
|
|
602427
|
+
const maxWidth = Math.max(1, ...widths);
|
|
602428
|
+
return Math.max(0.12, Math.min(1.6, lines.length / maxWidth));
|
|
602429
|
+
}
|
|
602430
|
+
function fitDashboardPane(lines, paneWidth, paneHeight, fallback) {
|
|
602431
|
+
const source = lines.length > 0 ? lines : [fallback];
|
|
602432
|
+
const normalized = [];
|
|
602433
|
+
for (let i2 = 0; i2 < paneHeight; i2++) {
|
|
602434
|
+
const sourceIndex = source.length <= paneHeight ? i2 : Math.min(source.length - 1, Math.floor(i2 * source.length / paneHeight));
|
|
602435
|
+
normalized.push(truncateCell(source[sourceIndex] ?? "", paneWidth));
|
|
602436
|
+
}
|
|
602437
|
+
while (normalized.length < paneHeight) normalized.push(" ".repeat(paneWidth));
|
|
602438
|
+
return normalized;
|
|
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
|
+
}
|
|
602465
|
+
function pushDashboardWrapped(lines, value2, width) {
|
|
602466
|
+
const contentWidth = Math.max(10, width - 2);
|
|
602467
|
+
const words = stripDashboardAnsi(value2).replace(/\s+/g, " ").trim().split(" ").filter(Boolean);
|
|
602468
|
+
if (words.length === 0) {
|
|
602469
|
+
lines.push(`│${" ".repeat(contentWidth)}│`);
|
|
602470
|
+
return;
|
|
602471
|
+
}
|
|
602472
|
+
let current = "";
|
|
602473
|
+
for (const word2 of words) {
|
|
602474
|
+
if (!current) {
|
|
602475
|
+
current = word2.length > contentWidth ? word2.slice(0, contentWidth) : word2;
|
|
602476
|
+
continue;
|
|
602477
|
+
}
|
|
602478
|
+
if (current.length + 1 + word2.length <= contentWidth) {
|
|
602479
|
+
current += ` ${word2}`;
|
|
602480
|
+
} else {
|
|
602481
|
+
lines.push(`│${truncateCell(` ${current}`, contentWidth)}│`);
|
|
602482
|
+
current = word2.length > contentWidth ? word2.slice(0, contentWidth) : word2;
|
|
602483
|
+
}
|
|
602484
|
+
}
|
|
602485
|
+
if (current) lines.push(`│${truncateCell(` ${current}`, contentWidth)}│`);
|
|
602486
|
+
}
|
|
602487
|
+
function isAudioFailureText(value2) {
|
|
602488
|
+
const text2 = String(value2 ?? "").trim();
|
|
602489
|
+
if (!text2) return false;
|
|
602490
|
+
return /^(?:ASR|VAD|Transcription)\s+(?:failed|unavailable)|No module named ['"]?transcribe_cli|torchcodec|torchaudio version/i.test(text2);
|
|
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
|
+
}
|
|
602195
602646
|
function summarizeInference(value2) {
|
|
602196
602647
|
if (!value2) return "objects: pending";
|
|
602197
602648
|
const lines = value2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
@@ -602224,41 +602675,53 @@ function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
|
602224
602675
|
if (cameras.length === 0) {
|
|
602225
602676
|
lines.push(`│${truncateCell(" no camera frames captured yet", width - 2)}│`);
|
|
602226
602677
|
}
|
|
602678
|
+
if (cameras.length > 0) {
|
|
602679
|
+
lines.push(`├${horizontal}┤`);
|
|
602680
|
+
const { innerWidth, paneCount, paneWidth, paneContentWidth } = liveDashboardPaneLayout(width, cameras.length);
|
|
602681
|
+
for (let start2 = 0; start2 < cameras.length; start2 += paneCount) {
|
|
602682
|
+
const group = cameras.slice(start2, start2 + paneCount);
|
|
602683
|
+
const sourceLines = group.map((camera) => dashboardPreviewLines(camera));
|
|
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++) {
|
|
602689
|
+
const body = panes.map((pane) => pane[row] ?? " ".repeat(paneWidth)).join(" ");
|
|
602690
|
+
lines.push(`│${truncateCell(body, innerWidth)}│`);
|
|
602691
|
+
}
|
|
602692
|
+
}
|
|
602693
|
+
}
|
|
602694
|
+
if (cameras.length > 0) {
|
|
602695
|
+
lines.push(`├${horizontal}┤`);
|
|
602696
|
+
lines.push(`│${truncateCell(" camera observations", width - 2)}│`);
|
|
602697
|
+
}
|
|
602227
602698
|
for (const camera of cameras) {
|
|
602228
602699
|
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602229
602700
|
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
602230
|
-
lines.push(`├${horizontal}┤`);
|
|
602231
|
-
lines.push(`│${truncateCell(` ${compactSourceId(camera.source)} age=${age}s rotation=${rotation}`, width - 2)}│`);
|
|
602232
|
-
const asciiLines = (camera.ascii ?? "").split("\n").filter((line) => line.length > 0).slice(0, 10);
|
|
602233
602701
|
const detailLines = [
|
|
602702
|
+
`${compactSourceId(camera.source)} age=${age}s rotation=${rotation}`,
|
|
602234
602703
|
cameraSnapshotSummary(camera),
|
|
602235
602704
|
camera.framePath ? `frame: ${camera.displayPath || camera.framePath}` : "",
|
|
602236
602705
|
camera.orientation?.evidence ? `orientation: ${trimOneLine(camera.orientation.evidence, 120)}` : ""
|
|
602237
602706
|
].filter(Boolean);
|
|
602238
|
-
|
|
602239
|
-
|
|
602240
|
-
lines.push(`│${truncateCell(` ${detail}`, width - 2)}│`);
|
|
602241
|
-
}
|
|
602242
|
-
continue;
|
|
602243
|
-
}
|
|
602244
|
-
const imageWidth = Math.max(24, Math.min(52, Math.floor((width - 5) * 0.58)));
|
|
602245
|
-
const detailWidth = Math.max(18, width - imageWidth - 5);
|
|
602246
|
-
const rowCount = Math.max(asciiLines.length, detailLines.length);
|
|
602247
|
-
for (let i2 = 0; i2 < rowCount; i2++) {
|
|
602248
|
-
const image = truncateCell(asciiLines[i2] ?? "", imageWidth);
|
|
602249
|
-
const detail = truncateCell(detailLines[i2] ?? "", detailWidth);
|
|
602250
|
-
lines.push(`│ ${image} │ ${detail}│`);
|
|
602707
|
+
for (const detail of detailLines.length ? detailLines : ["preview pending"]) {
|
|
602708
|
+
pushDashboardWrapped(lines, detail, width);
|
|
602251
602709
|
}
|
|
602252
602710
|
}
|
|
602253
602711
|
if (snapshot.audio) {
|
|
602254
602712
|
lines.push(`├${horizontal}┤`);
|
|
602713
|
+
const transcriptFailure = isAudioFailureText(snapshot.audio.transcript) ? snapshot.audio.transcript : "";
|
|
602714
|
+
const transcript = transcriptFailure ? "" : snapshot.audio.transcript;
|
|
602715
|
+
const audioError = [snapshot.audio.error, transcriptFailure].filter(Boolean).join(" | ");
|
|
602255
602716
|
const audioBits = [
|
|
602256
602717
|
`audio input=${snapshot.audio.input || snapshot.config.selectedAudioInput || "none"}`,
|
|
602257
|
-
snapshot.audio.
|
|
602258
|
-
|
|
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)}%` : "",
|
|
602719
|
+
audioError ? `error=${trimOneLine(audioError, 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}` : "",
|
|
602259
602722
|
snapshot.audio.analysis ? `sound=${trimOneLine(snapshot.audio.analysis, 120)}` : ""
|
|
602260
602723
|
].filter(Boolean);
|
|
602261
|
-
for (const item of audioBits.slice(0,
|
|
602724
|
+
for (const item of audioBits.slice(0, 6)) lines.push(`│${truncateCell(` ${item}`, width - 2)}│`);
|
|
602262
602725
|
}
|
|
602263
602726
|
lines.push(`╰${horizontal}╯`);
|
|
602264
602727
|
return lines;
|
|
@@ -602607,6 +603070,9 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602607
603070
|
}
|
|
602608
603071
|
}
|
|
602609
603072
|
if (snapshot.audio) {
|
|
603073
|
+
const transcriptFailure = isAudioFailureText(snapshot.audio.transcript) ? snapshot.audio.transcript : "";
|
|
603074
|
+
const transcript = transcriptFailure ? "" : snapshot.audio.transcript;
|
|
603075
|
+
const audioError = [snapshot.audio.error, transcriptFailure].filter(Boolean).join(" | ");
|
|
602610
603076
|
const audioEvents = (snapshot.events ?? []).filter((event) => event.kind === "audio_transcript" || event.kind === "audio_sound").sort((a2, b) => b.observedAt - a2.observedAt).slice(0, 8);
|
|
602611
603077
|
if (audioEvents.length > 0) {
|
|
602612
603078
|
lines.push("");
|
|
@@ -602623,11 +603089,18 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602623
603089
|
lines.push("");
|
|
602624
603090
|
lines.push("## LIVE AUDIO LATEST SAMPLE");
|
|
602625
603091
|
lines.push(`timestamp: ${nowIso3(snapshot.audio.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s${age > maxAgeMs ? " STALE" : ""}`);
|
|
602626
|
-
if (
|
|
603092
|
+
if (audioError) lines.push(`error: ${audioError}`);
|
|
602627
603093
|
if (snapshot.audio.recordingPath) lines.push(`recording: ${snapshot.audio.recordingPath}`);
|
|
602628
|
-
if (snapshot.audio.
|
|
602629
|
-
lines.push(
|
|
602630
|
-
|
|
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
|
+
}
|
|
603097
|
+
if (transcript) {
|
|
603098
|
+
lines.push(`Live ASR${snapshot.audio.asrBackend ? ` (${snapshot.audio.asrBackend})` : ""}:`);
|
|
603099
|
+
lines.push(cleanPreview(transcript, 1200));
|
|
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}`);
|
|
602631
603104
|
}
|
|
602632
603105
|
if (snapshot.audio.analysis) {
|
|
602633
603106
|
lines.push("Live sound analysis:");
|
|
@@ -602890,6 +603363,7 @@ var init_live_sensors = __esm({
|
|
|
602890
603363
|
lastAgentReviewAt = 0;
|
|
602891
603364
|
autoOrientationInFlight = /* @__PURE__ */ new Set();
|
|
602892
603365
|
lastFeedbackAt = /* @__PURE__ */ new Map();
|
|
603366
|
+
intakeAgentConfig;
|
|
602893
603367
|
setStatusSink(sink) {
|
|
602894
603368
|
this.statusSink = sink ?? null;
|
|
602895
603369
|
this.emitStatus();
|
|
@@ -602900,6 +603374,9 @@ var init_live_sensors = __esm({
|
|
|
602900
603374
|
setAgentActionSink(sink) {
|
|
602901
603375
|
this.agentActionSink = sink ?? null;
|
|
602902
603376
|
}
|
|
603377
|
+
setAudioIntakeAgentConfig(config) {
|
|
603378
|
+
this.intakeAgentConfig = config && config.backendUrl && config.model ? { backendUrl: config.backendUrl, model: config.model, apiKey: config.apiKey } : void 0;
|
|
603379
|
+
}
|
|
602903
603380
|
getConfig() {
|
|
602904
603381
|
return { ...this.config };
|
|
602905
603382
|
}
|
|
@@ -603195,7 +603672,8 @@ var init_live_sensors = __esm({
|
|
|
603195
603672
|
const framePath = extractSavedImagePath(result.output, this.repoRoot);
|
|
603196
603673
|
if (!framePath) return { ok: false, message: "Camera capture succeeded but no saved image path was returned." };
|
|
603197
603674
|
const displayPath = relative11(this.repoRoot, framePath).startsWith("..") ? framePath : relative11(this.repoRoot, framePath);
|
|
603198
|
-
const
|
|
603675
|
+
const previewWidth = Math.max(42, Math.min(120, Math.floor(Number(options2.previewWidth ?? (process.stdout.columns || 100) - 14))));
|
|
603676
|
+
const preview = await buildImageAsciiPreview(framePath, { width: previewWidth });
|
|
603199
603677
|
const asciiContext = preview ? formatImageAsciiContext(preview, displayPath) : void 0;
|
|
603200
603678
|
const observedAt = Date.now();
|
|
603201
603679
|
const cameraView = {
|
|
@@ -603204,6 +603682,7 @@ var init_live_sensors = __esm({
|
|
|
603204
603682
|
framePath,
|
|
603205
603683
|
displayPath,
|
|
603206
603684
|
ascii: preview?.ascii,
|
|
603685
|
+
plainAscii: preview?.plainAscii,
|
|
603207
603686
|
renderer: preview?.renderer,
|
|
603208
603687
|
asciiContext,
|
|
603209
603688
|
rotation,
|
|
@@ -603237,6 +603716,7 @@ var init_live_sensors = __esm({
|
|
|
603237
603716
|
imagePath: framePath,
|
|
603238
603717
|
displayPath,
|
|
603239
603718
|
ascii: preview?.ascii,
|
|
603719
|
+
plainAscii: preview?.plainAscii,
|
|
603240
603720
|
renderer: preview?.renderer
|
|
603241
603721
|
});
|
|
603242
603722
|
}
|
|
@@ -603246,6 +603726,7 @@ var init_live_sensors = __esm({
|
|
|
603246
603726
|
framePath,
|
|
603247
603727
|
displayPath,
|
|
603248
603728
|
ascii: preview?.ascii,
|
|
603729
|
+
plainAscii: preview?.plainAscii,
|
|
603249
603730
|
renderer: preview?.renderer
|
|
603250
603731
|
};
|
|
603251
603732
|
}
|
|
@@ -603291,8 +603772,8 @@ var init_live_sensors = __esm({
|
|
|
603291
603772
|
return message2;
|
|
603292
603773
|
}
|
|
603293
603774
|
}
|
|
603294
|
-
async sampleSingleVideoNow(source, forceInfer) {
|
|
603295
|
-
const preview = await this.previewCamera(source, { emit: false });
|
|
603775
|
+
async sampleSingleVideoNow(source, forceInfer, previewWidth) {
|
|
603776
|
+
const preview = await this.previewCamera(source, { emit: false, previewWidth });
|
|
603296
603777
|
let inference = "";
|
|
603297
603778
|
let clipSummary = "";
|
|
603298
603779
|
const orientation = this.getCameraOrientation(source);
|
|
@@ -603381,8 +603862,9 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603381
603862
|
const sources = this.activeVideoSources();
|
|
603382
603863
|
if (sources.length === 0) return "No camera selected. Use /live refresh after connecting hardware.";
|
|
603383
603864
|
const outputs = [];
|
|
603865
|
+
const previewWidth = liveDashboardPreviewWidthForSources(sources.length);
|
|
603384
603866
|
for (const source of sources) {
|
|
603385
|
-
outputs.push(await this.sampleSingleVideoNow(source, forceInfer));
|
|
603867
|
+
outputs.push(await this.sampleSingleVideoNow(source, forceInfer, previewWidth));
|
|
603386
603868
|
}
|
|
603387
603869
|
this.emitDashboard();
|
|
603388
603870
|
return outputs.map((output, index) => `## Camera ${sources[index]}
|
|
@@ -603400,7 +603882,10 @@ ${output}`).join("\n\n");
|
|
|
603400
603882
|
ensureLiveDir(this.repoRoot);
|
|
603401
603883
|
const recordingPath = join124(outputDir2, `audio-${Date.now()}.wav`);
|
|
603402
603884
|
let transcript = "";
|
|
603885
|
+
let asrBackend = "";
|
|
603403
603886
|
let analysis = "";
|
|
603887
|
+
let intake;
|
|
603888
|
+
const audioErrors = [];
|
|
603404
603889
|
const capture = await recordAudioSampleWithRecovery(input, this.devices.audioInputs, 3, recordingPath);
|
|
603405
603890
|
if (!capture.ok) {
|
|
603406
603891
|
const message2 = `Audio capture failed from ${input}; tried fallbacks: ${capture.errors.slice(0, 5).join(" | ")}`;
|
|
@@ -603423,12 +603908,18 @@ ${output}`).join("\n\n");
|
|
|
603423
603908
|
if (capture.device !== this.config.selectedAudioInput) {
|
|
603424
603909
|
this.config.selectedAudioInput = capture.device;
|
|
603425
603910
|
}
|
|
603911
|
+
const activity = readPcm16WavActivity(recordingPath);
|
|
603426
603912
|
if (this.config.asrEnabled) {
|
|
603427
603913
|
try {
|
|
603428
603914
|
const asr = await new TranscribeFileTool(this.repoRoot).execute({ path: recordingPath, model: "tiny" });
|
|
603429
|
-
|
|
603915
|
+
if (asr.success) {
|
|
603916
|
+
transcript = cleanPreview(typeof asr.llmContent === "string" ? asr.llmContent : "", 1600);
|
|
603917
|
+
asrBackend = extractAsrBackend(asr.output) ?? "whisper";
|
|
603918
|
+
} else {
|
|
603919
|
+
audioErrors.push(`ASR unavailable: ${asr.error || asr.output || "unknown error"}`);
|
|
603920
|
+
}
|
|
603430
603921
|
} catch (err) {
|
|
603431
|
-
|
|
603922
|
+
audioErrors.push(`ASR unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
603432
603923
|
}
|
|
603433
603924
|
}
|
|
603434
603925
|
if (this.config.audioAnalysisEnabled) {
|
|
@@ -603442,19 +603933,27 @@ ${output}`).join("\n\n");
|
|
|
603442
603933
|
}
|
|
603443
603934
|
try {
|
|
603444
603935
|
const vad = await analyzer.execute({ action: "vad", file: recordingPath });
|
|
603445
|
-
|
|
603936
|
+
if (vad.success) parts.push(cleanPreview(vad.output, 700));
|
|
603937
|
+
else audioErrors.push(`VAD unavailable: ${vad.error || vad.output || "unknown error"}`);
|
|
603446
603938
|
} catch (err) {
|
|
603447
|
-
|
|
603939
|
+
audioErrors.push(`VAD unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
603448
603940
|
}
|
|
603449
603941
|
analysis = parts.filter(Boolean).join("\n");
|
|
603450
603942
|
}
|
|
603943
|
+
if (transcript) {
|
|
603944
|
+
intake = await classifyLiveAudioIntake(transcript, this.intakeAgentConfig);
|
|
603945
|
+
}
|
|
603451
603946
|
this.snapshot.audio = {
|
|
603452
603947
|
updatedAt: Date.now(),
|
|
603453
603948
|
input: capture.device,
|
|
603454
603949
|
output: this.config.selectedAudioOutput,
|
|
603455
603950
|
recordingPath,
|
|
603456
603951
|
transcript,
|
|
603457
|
-
|
|
603952
|
+
asrBackend: asrBackend || void 0,
|
|
603953
|
+
analysis,
|
|
603954
|
+
activity,
|
|
603955
|
+
intake,
|
|
603956
|
+
error: audioErrors.length > 0 ? audioErrors.join("\n") : void 0
|
|
603458
603957
|
};
|
|
603459
603958
|
const audioEvents = [];
|
|
603460
603959
|
if (transcript) {
|
|
@@ -603481,12 +603980,20 @@ ${output}`).join("\n\n");
|
|
|
603481
603980
|
}
|
|
603482
603981
|
this.snapshot.events = mergeRecentById(this.snapshot.events, audioEvents, 50);
|
|
603483
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
|
+
}
|
|
603484
603989
|
this.emitDashboard();
|
|
603485
603990
|
return [
|
|
603486
603991
|
`Audio sample captured from ${capture.device}${capture.device !== input ? ` (recovered from ${input})` : ""}: ${recordingPath}`,
|
|
603487
603992
|
transcript ? `
|
|
603488
|
-
ASR:
|
|
603993
|
+
ASR${asrBackend ? ` (${asrBackend})` : ""}:
|
|
603489
603994
|
${transcript}` : "",
|
|
603995
|
+
intake ? `
|
|
603996
|
+
Intake: ${intake.intendedForAgent ? "intended for agent" : "ambient/monitor"} confidence=${intake.confidence.toFixed(2)} (${intake.source})` : "",
|
|
603490
603997
|
analysis ? `
|
|
603491
603998
|
Sound analysis:
|
|
603492
603999
|
${analysis}` : ""
|
|
@@ -624102,15 +624609,6 @@ var init_status_bar = __esm({
|
|
|
624102
624609
|
const cmdPrefix = cmd.startsWith("view:") ? "omnius-view:" + cmd.slice(5) : "omnius-cmd:" + cmd;
|
|
624103
624610
|
return `\x1B]8;;${cmdPrefix}\x07${label}\x1B]8;;\x07`;
|
|
624104
624611
|
};
|
|
624105
|
-
const buttonFg = (cmd) => {
|
|
624106
|
-
let fg2 = TEXT_DIM;
|
|
624107
|
-
if (cmd === "voice" && this._voiceActive) fg2 = 82;
|
|
624108
|
-
if (cmd.startsWith("live") && (this._liveMediaStatus.audio || this._liveMediaStatus.video)) fg2 = 82;
|
|
624109
|
-
if (cmd === "nexus") {
|
|
624110
|
-
fg2 = this._nexusStatus === "connected" ? 82 : this._nexusStatus === "connecting" ? 208 : 196;
|
|
624111
|
-
}
|
|
624112
|
-
return fg2;
|
|
624113
|
-
};
|
|
624114
624612
|
const decorateMenuButton = (cmd, label) => {
|
|
624115
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}`;
|
|
624116
624614
|
};
|
|
@@ -624121,14 +624619,7 @@ var init_status_bar = __esm({
|
|
|
624121
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}`;
|
|
624122
624620
|
};
|
|
624123
624621
|
const renderBtn = (cmd, label) => {
|
|
624124
|
-
|
|
624125
|
-
const btnW = label.length + 2;
|
|
624126
|
-
const availW2 = getTermWidth() - identity3.width - 1;
|
|
624127
|
-
if (btnW > availW2) return linkify(cmd, `\x1B[38;5;${fg2}m${label}`);
|
|
624128
|
-
return linkify(
|
|
624129
|
-
cmd,
|
|
624130
|
-
`${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}`
|
|
624131
|
-
);
|
|
624622
|
+
return linkify(cmd, decorateMenuButton(cmd, label));
|
|
624132
624623
|
};
|
|
624133
624624
|
const identity3 = this.buildHeaderIdentityRender();
|
|
624134
624625
|
const modelLabel = this.summarizeHeaderModelName() || "model";
|
|
@@ -624201,10 +624692,9 @@ var init_status_bar = __esm({
|
|
|
624201
624692
|
};
|
|
624202
624693
|
if (this._activeViewId !== "main") {
|
|
624203
624694
|
const mainLabel = ` ↩ main `;
|
|
624204
|
-
const mainColored = `\x1B[38;5;110m${mainLabel}\x1B[0m`;
|
|
624205
624695
|
sysItems.push({
|
|
624206
|
-
render: () =>
|
|
624207
|
-
w: mainLabel.length + 1
|
|
624696
|
+
render: () => linkify("view:main", decorateMenuButton("view:main", mainLabel)) + " ",
|
|
624697
|
+
w: mainLabel.length + 2 + 1
|
|
624208
624698
|
});
|
|
624209
624699
|
}
|
|
624210
624700
|
if (this._agentViews.size > 1) {
|
|
@@ -624242,10 +624732,7 @@ var init_status_bar = __esm({
|
|
|
624242
624732
|
const telegramDot = this._telegramStatus.active ? "●" : "○";
|
|
624243
624733
|
const telegramLabel = this._telegramStatus.activeSubAgents > 0 ? ` ✈ tg ${this._telegramStatus.activeSubAgents} ` : " ✈ tg ";
|
|
624244
624734
|
sysItems.push({
|
|
624245
|
-
render: () => renderBtn(
|
|
624246
|
-
"telegram",
|
|
624247
|
-
`${HEADER_TELEGRAM_FG}${telegramDot}${telegramLabel}\x1B[0m`
|
|
624248
|
-
) + " ",
|
|
624735
|
+
render: () => renderBtn("telegram", `${telegramDot}${telegramLabel}`) + " ",
|
|
624249
624736
|
w: telegramLabel.length + 2
|
|
624250
624737
|
});
|
|
624251
624738
|
const liveMediaActive = this._liveMediaStatus.audio || this._liveMediaStatus.video;
|
|
@@ -624390,7 +624877,7 @@ var init_status_bar = __esm({
|
|
|
624390
624877
|
const trunc3 = (s2) => s2.trim().split(/\s+/).slice(0, 3).join(" ");
|
|
624391
624878
|
if (this._activeViewId !== "main") {
|
|
624392
624879
|
const mainLabel = ` ↩ main `;
|
|
624393
|
-
zones.push({ w: mainLabel.length + 1, id: "main", render: () => "" });
|
|
624880
|
+
zones.push({ w: mainLabel.length + 2 + 1, id: "main", render: () => "" });
|
|
624394
624881
|
}
|
|
624395
624882
|
if (this._agentViews.size > 1) {
|
|
624396
624883
|
for (const view of this._agentViews.values()) {
|
|
@@ -655292,12 +655779,17 @@ function renderLiveFeedback(ctx3, manager, feedback) {
|
|
|
655292
655779
|
return;
|
|
655293
655780
|
}
|
|
655294
655781
|
const stamp = new Date(feedback.observedAt).toISOString();
|
|
655782
|
+
const explicitImagePreview = (feedback.kind === "camera-frame" || feedback.kind === "face-crop") && feedback.ascii && feedback.displayPath && feedback.renderer;
|
|
655783
|
+
if (ctx3.liveDynamicBlockHost && !explicitImagePreview) {
|
|
655784
|
+
renderLiveDashboard(ctx3, manager);
|
|
655785
|
+
return;
|
|
655786
|
+
}
|
|
655295
655787
|
if (feedback.kind === "error") {
|
|
655296
655788
|
renderWarning(`[live ${stamp}] ${feedback.title}
|
|
655297
655789
|
${feedback.message}`);
|
|
655298
655790
|
return;
|
|
655299
655791
|
}
|
|
655300
|
-
if (
|
|
655792
|
+
if (explicitImagePreview && feedback.displayPath && feedback.ascii && feedback.renderer) {
|
|
655301
655793
|
renderInfo(`[live ${stamp}] ${feedback.message}`);
|
|
655302
655794
|
renderImageAsciiPreview(feedback.title, feedback.displayPath, feedback.ascii, feedback.renderer);
|
|
655303
655795
|
return;
|
|
@@ -655308,6 +655800,11 @@ ${feedback.message}`);
|
|
|
655308
655800
|
function attachLiveSinks(ctx3, manager, agentEnabled = false) {
|
|
655309
655801
|
manager.setStatusSink(ctx3.setLiveMediaStatus);
|
|
655310
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
|
+
});
|
|
655311
655808
|
if (agentEnabled && ctx3.startBackgroundPrompt) {
|
|
655312
655809
|
manager.setAgentActionSink((prompt) => {
|
|
655313
655810
|
const id2 = ctx3.startBackgroundPrompt?.(prompt);
|
|
@@ -733836,6 +734333,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
733836
734333
|
} else {
|
|
733837
734334
|
parts.push("active: no");
|
|
733838
734335
|
}
|
|
734336
|
+
const liveContext = formatLiveSensorContext(repoRoot);
|
|
734337
|
+
if (liveContext) parts.push(liveContext);
|
|
733839
734338
|
return parts.join("\n");
|
|
733840
734339
|
}
|
|
733841
734340
|
},
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
9
|
+
"version": "1.0.410",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED