omnius 1.0.407 → 1.0.408
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 +475 -149
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -602166,6 +602166,103 @@ function describeCameraOrientation(orientation) {
|
|
|
602166
602166
|
if (!orientation) return "upright/no correction (default)";
|
|
602167
602167
|
return `${formatCameraRotation(orientation.rotation)} (${orientation.source}${orientation.confidence !== void 0 ? ` confidence=${orientation.confidence.toFixed(2)}` : ""}, updated=${nowIso3(orientation.updatedAt)})`;
|
|
602168
602168
|
}
|
|
602169
|
+
function chooseCameraOrientationFromScores(scores) {
|
|
602170
|
+
const sorted = [...scores].filter((score) => Number.isFinite(score.score)).sort((a2, b) => b.score - a2.score);
|
|
602171
|
+
const best = sorted[0];
|
|
602172
|
+
if (!best || best.score <= 0 || best.faces <= 0) return null;
|
|
602173
|
+
const second3 = sorted[1];
|
|
602174
|
+
const gap = best.score - (second3?.score ?? 0);
|
|
602175
|
+
const total = sorted.reduce((sum, score) => sum + Math.max(0, score.score), 0);
|
|
602176
|
+
const share = total > 0 ? best.score / total : 1;
|
|
602177
|
+
const confidence2 = Math.max(0.55, Math.min(0.98, share * 0.75 + Math.min(0.23, gap / Math.max(1, best.score))));
|
|
602178
|
+
if (confidence2 < 0.58 && gap < 0.4) return null;
|
|
602179
|
+
return {
|
|
602180
|
+
rotation: best.rotation,
|
|
602181
|
+
confidence: confidence2,
|
|
602182
|
+
reason: `cv-face-orientation faces=${best.faces} score=${best.score.toFixed(2)} next=${(second3?.score ?? 0).toFixed(2)} area=${(best.faceAreaRatio ?? 0).toFixed(3)}`
|
|
602183
|
+
};
|
|
602184
|
+
}
|
|
602185
|
+
function trimOneLine(value2, maxChars) {
|
|
602186
|
+
return String(value2 ?? "").replace(/\r/g, " ").replace(/\n/g, " ").replace(/\s+/g, " ").trim().slice(0, maxChars);
|
|
602187
|
+
}
|
|
602188
|
+
function compactSourceId(source) {
|
|
602189
|
+
return source.replace(/^\/dev\//, "");
|
|
602190
|
+
}
|
|
602191
|
+
function truncateCell(value2, width) {
|
|
602192
|
+
if (width <= 1) return value2.slice(0, Math.max(0, width));
|
|
602193
|
+
return value2.length <= width ? value2.padEnd(width, " ") : `${value2.slice(0, Math.max(1, width - 1))}…`;
|
|
602194
|
+
}
|
|
602195
|
+
function summarizeInference(value2) {
|
|
602196
|
+
if (!value2) return "objects: pending";
|
|
602197
|
+
const lines = value2.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
602198
|
+
const objectsIndex = lines.findIndex((line) => /^##\s+Objects/i.test(line));
|
|
602199
|
+
if (objectsIndex >= 0) {
|
|
602200
|
+
const objects = lines.slice(objectsIndex + 1).filter((line) => line.startsWith("- ")).slice(0, 4).map((line) => line.replace(/^-\s*/, ""));
|
|
602201
|
+
if (objects.length > 0) return `objects: ${objects.join("; ")}`;
|
|
602202
|
+
}
|
|
602203
|
+
const sampled = lines.find((line) => /^objects:/i.test(line));
|
|
602204
|
+
return sampled ? trimOneLine(sampled, 160) : trimOneLine(lines.slice(0, 4).join("; "), 160);
|
|
602205
|
+
}
|
|
602206
|
+
function cameraSnapshotSummary(camera) {
|
|
602207
|
+
if (camera.error) return `error: ${trimOneLine(camera.error, 160)}`;
|
|
602208
|
+
return camera.summary || summarizeInference(camera.inference);
|
|
602209
|
+
}
|
|
602210
|
+
function formatLiveDashboardFromSnapshot(snapshot, opts = {}) {
|
|
602211
|
+
const width = Math.max(60, opts.width ?? 100);
|
|
602212
|
+
const now2 = opts.now ?? Date.now();
|
|
602213
|
+
const maxCameras = opts.maxCameras ?? 4;
|
|
602214
|
+
const cameras = Object.values(snapshot.cameras ?? (snapshot.video ? { [snapshot.video.source]: snapshot.video } : {})).sort((a2, b) => {
|
|
602215
|
+
if (a2.source === snapshot.config.selectedCamera) return -1;
|
|
602216
|
+
if (b.source === snapshot.config.selectedCamera) return 1;
|
|
602217
|
+
return a2.source.localeCompare(b.source);
|
|
602218
|
+
}).slice(0, maxCameras);
|
|
602219
|
+
const audioAge = snapshot.audio?.updatedAt ? `${Math.max(0, Math.round((now2 - snapshot.audio.updatedAt) / 1e3))}s` : "none";
|
|
602220
|
+
const title = `live audio/video cameras=${cameras.length}/${snapshot.devices.video.length} audio=${snapshot.config.audioEnabled ? "on" : "off"} age=${audioAge}`;
|
|
602221
|
+
const horizontal = "─".repeat(Math.max(0, width - 2));
|
|
602222
|
+
const lines = [`╭${horizontal}╮`];
|
|
602223
|
+
lines.push(`│${truncateCell(` ${title}`, width - 2)}│`);
|
|
602224
|
+
if (cameras.length === 0) {
|
|
602225
|
+
lines.push(`│${truncateCell(" no camera frames captured yet", width - 2)}│`);
|
|
602226
|
+
}
|
|
602227
|
+
for (const camera of cameras) {
|
|
602228
|
+
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602229
|
+
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
|
+
const detailLines = [
|
|
602234
|
+
cameraSnapshotSummary(camera),
|
|
602235
|
+
camera.framePath ? `frame: ${camera.displayPath || camera.framePath}` : "",
|
|
602236
|
+
camera.orientation?.evidence ? `orientation: ${trimOneLine(camera.orientation.evidence, 120)}` : ""
|
|
602237
|
+
].filter(Boolean);
|
|
602238
|
+
if (asciiLines.length === 0) {
|
|
602239
|
+
for (const detail of detailLines.length ? detailLines : ["preview pending"]) {
|
|
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}│`);
|
|
602251
|
+
}
|
|
602252
|
+
}
|
|
602253
|
+
if (snapshot.audio) {
|
|
602254
|
+
lines.push(`├${horizontal}┤`);
|
|
602255
|
+
const audioBits = [
|
|
602256
|
+
`audio input=${snapshot.audio.input || snapshot.config.selectedAudioInput || "none"}`,
|
|
602257
|
+
snapshot.audio.error ? `error=${trimOneLine(snapshot.audio.error, 120)}` : "",
|
|
602258
|
+
snapshot.audio.transcript ? `asr=${trimOneLine(snapshot.audio.transcript, 120)}` : "",
|
|
602259
|
+
snapshot.audio.analysis ? `sound=${trimOneLine(snapshot.audio.analysis, 120)}` : ""
|
|
602260
|
+
].filter(Boolean);
|
|
602261
|
+
for (const item of audioBits.slice(0, 4)) lines.push(`│${truncateCell(` ${item}`, width - 2)}│`);
|
|
602262
|
+
}
|
|
602263
|
+
lines.push(`╰${horizontal}╯`);
|
|
602264
|
+
return lines;
|
|
602265
|
+
}
|
|
602169
602266
|
function parseLiveMediaPayload(value2) {
|
|
602170
602267
|
const parsed = parseJsonObject3(value2);
|
|
602171
602268
|
if (!parsed) return null;
|
|
@@ -602425,6 +602522,21 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602425
602522
|
lines.push(`Selected camera: ${snapshot.config.selectedCamera}`);
|
|
602426
602523
|
lines.push(`Camera orientation correction: ${describeCameraOrientation(orientation)}`);
|
|
602427
602524
|
}
|
|
602525
|
+
const cameraSnapshots = Object.values(snapshot.cameras ?? (snapshot.video ? { [snapshot.video.source]: snapshot.video } : {})).sort((a2, b) => {
|
|
602526
|
+
if (a2.source === snapshot.config.selectedCamera) return -1;
|
|
602527
|
+
if (b.source === snapshot.config.selectedCamera) return 1;
|
|
602528
|
+
return b.updatedAt - a2.updatedAt;
|
|
602529
|
+
});
|
|
602530
|
+
if (cameraSnapshots.length > 0) {
|
|
602531
|
+
lines.push("Camera snapshot set:");
|
|
602532
|
+
for (const camera of cameraSnapshots.slice(0, 6)) {
|
|
602533
|
+
const age = Math.max(0, Math.round((now2 - camera.updatedAt) / 1e3));
|
|
602534
|
+
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
602535
|
+
lines.push(`- ${camera.source} age=${age}s rotation=${rotation}${camera.framePath ? ` frame=${camera.framePath}` : ""}${camera.error ? ` error=${trimOneLine(camera.error, 240)}` : ""}`);
|
|
602536
|
+
const summary = cameraSnapshotSummary(camera);
|
|
602537
|
+
if (summary) lines.push(` summary: ${summary}`);
|
|
602538
|
+
}
|
|
602539
|
+
}
|
|
602428
602540
|
if (snapshot.config.selectedAudioInput) lines.push(`Selected audio input: ${snapshot.config.selectedAudioInput}`);
|
|
602429
602541
|
if (snapshot.config.selectedAudioOutput) lines.push(`Selected audio output: ${snapshot.config.selectedAudioOutput}`);
|
|
602430
602542
|
const cameraEvents = (snapshot.events ?? []).filter((event) => event.kind.startsWith("camera_") || event.kind === "clip_recognition" || event.kind === "visual_trigger").sort((a2, b) => b.observedAt - a2.observedAt).slice(0, 18);
|
|
@@ -602481,6 +602593,19 @@ function formatLiveSensorContextFromSnapshot(snapshot, opts = {}) {
|
|
|
602481
602593
|
lines.push(cleanPreview(snapshot.video.inference, 1800));
|
|
602482
602594
|
}
|
|
602483
602595
|
}
|
|
602596
|
+
if (cameraSnapshots.length > 1) {
|
|
602597
|
+
lines.push("");
|
|
602598
|
+
lines.push("## LIVE CAMERA LATEST FRAMES");
|
|
602599
|
+
for (const camera of cameraSnapshots.slice(0, 6)) {
|
|
602600
|
+
const age = now2 - camera.updatedAt;
|
|
602601
|
+
const rotation = camera.orientation ? formatCameraRotation(camera.orientation.rotation) : "upright/no correction";
|
|
602602
|
+
lines.push(`### ${camera.source}`);
|
|
602603
|
+
lines.push(`timestamp: ${nowIso3(camera.updatedAt)} age=${Math.max(0, Math.round(age / 1e3))}s rotation=${rotation}${age > maxAgeMs ? " STALE" : ""}`);
|
|
602604
|
+
if (camera.error) lines.push(`error: ${camera.error}`);
|
|
602605
|
+
if (camera.framePath) lines.push(`frame: ${camera.framePath}`);
|
|
602606
|
+
lines.push(`summary: ${cameraSnapshotSummary(camera)}`);
|
|
602607
|
+
}
|
|
602608
|
+
}
|
|
602484
602609
|
if (snapshot.audio) {
|
|
602485
602610
|
const audioEvents = (snapshot.events ?? []).filter((event) => event.kind === "audio_transcript" || event.kind === "audio_sound").sort((a2, b) => b.observedAt - a2.observedAt).slice(0, 8);
|
|
602486
602611
|
if (audioEvents.length > 0) {
|
|
@@ -602587,6 +602712,93 @@ async function recordAudioSample(device, durationSec, outputPath3) {
|
|
|
602587
602712
|
outputPath3
|
|
602588
602713
|
], { timeout: (Number(seconds) + 10) * 1e3 });
|
|
602589
602714
|
}
|
|
602715
|
+
async function scoreCameraOrientationWithOpenCv(framePath) {
|
|
602716
|
+
if (!await commandExists2("python3", 1200)) return null;
|
|
602717
|
+
const script = String.raw`
|
|
602718
|
+
import json, sys
|
|
602719
|
+
try:
|
|
602720
|
+
import cv2
|
|
602721
|
+
except Exception as exc:
|
|
602722
|
+
print(json.dumps({"ok": False, "error": str(exc)}))
|
|
602723
|
+
raise SystemExit(0)
|
|
602724
|
+
|
|
602725
|
+
path = sys.argv[1]
|
|
602726
|
+
img = cv2.imread(path)
|
|
602727
|
+
if img is None:
|
|
602728
|
+
print(json.dumps({"ok": False, "error": "could not read image"}))
|
|
602729
|
+
raise SystemExit(0)
|
|
602730
|
+
|
|
602731
|
+
face = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")
|
|
602732
|
+
eye = cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_eye.xml")
|
|
602733
|
+
|
|
602734
|
+
def rotate(mat, deg):
|
|
602735
|
+
if deg == 90:
|
|
602736
|
+
return cv2.rotate(mat, cv2.ROTATE_90_CLOCKWISE)
|
|
602737
|
+
if deg == 180:
|
|
602738
|
+
return cv2.rotate(mat, cv2.ROTATE_180)
|
|
602739
|
+
if deg == 270:
|
|
602740
|
+
return cv2.rotate(mat, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
|
602741
|
+
return mat
|
|
602742
|
+
|
|
602743
|
+
scores = []
|
|
602744
|
+
for deg in [0, 90, 180, 270]:
|
|
602745
|
+
frame = rotate(img, deg)
|
|
602746
|
+
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
|
602747
|
+
h, w = gray.shape[:2]
|
|
602748
|
+
faces = face.detectMultiScale(gray, scaleFactor=1.08, minNeighbors=4, minSize=(32, 32))
|
|
602749
|
+
face_area = 0.0
|
|
602750
|
+
eye_count = 0
|
|
602751
|
+
for (x, y, fw, fh) in faces[:8]:
|
|
602752
|
+
face_area += float(fw * fh) / max(1.0, float(w * h))
|
|
602753
|
+
roi = gray[y:y+fh, x:x+fw]
|
|
602754
|
+
try:
|
|
602755
|
+
eye_count += len(eye.detectMultiScale(roi, scaleFactor=1.1, minNeighbors=3, minSize=(8, 8)))
|
|
602756
|
+
except Exception:
|
|
602757
|
+
pass
|
|
602758
|
+
score = len(faces) * 2.5 + min(4.0, face_area * 35.0) + min(2.0, eye_count * 0.35)
|
|
602759
|
+
scores.append({"rotation": deg, "score": score, "faces": int(len(faces)), "faceAreaRatio": face_area})
|
|
602760
|
+
print(json.dumps({"ok": True, "scores": scores}))
|
|
602761
|
+
`;
|
|
602762
|
+
try {
|
|
602763
|
+
const raw = await execFileText4("python3", ["-c", script, framePath], { timeout: 15e3, maxBuffer: 1024 * 1024 });
|
|
602764
|
+
const parsed = parseJsonObjectFromText(raw);
|
|
602765
|
+
const scores = Array.isArray(parsed?.["scores"]) ? parsed["scores"] : [];
|
|
602766
|
+
if (scores.length === 0) return null;
|
|
602767
|
+
return scores.map((score) => ({
|
|
602768
|
+
rotation: normalizeLiveCameraRotation(score["rotation"]),
|
|
602769
|
+
score: Math.max(0, Number(score["score"] ?? 0)),
|
|
602770
|
+
faces: Math.max(0, Number(score["faces"] ?? 0)),
|
|
602771
|
+
faceAreaRatio: Number.isFinite(Number(score["faceAreaRatio"])) ? Number(score["faceAreaRatio"]) : void 0
|
|
602772
|
+
}));
|
|
602773
|
+
} catch {
|
|
602774
|
+
return null;
|
|
602775
|
+
}
|
|
602776
|
+
}
|
|
602777
|
+
async function tryRecordAudioDevice(device, durationSec, outputPath3) {
|
|
602778
|
+
await recordAudioSample(device, durationSec, outputPath3);
|
|
602779
|
+
}
|
|
602780
|
+
async function recordAudioSampleWithRecovery(preferred, devices, durationSec, outputPath3) {
|
|
602781
|
+
const ordered = [];
|
|
602782
|
+
const add3 = (id2) => {
|
|
602783
|
+
const clean5 = String(id2 ?? "").trim();
|
|
602784
|
+
if (clean5 && !ordered.includes(clean5)) ordered.push(clean5);
|
|
602785
|
+
};
|
|
602786
|
+
add3(preferred);
|
|
602787
|
+
for (const device of devices.filter((entry) => entry.source === "pulse" || entry.source === "pipewire")) add3(device.id);
|
|
602788
|
+
add3("pulse:default");
|
|
602789
|
+
add3("default");
|
|
602790
|
+
for (const device of devices.filter((entry) => entry.source !== "pulse" && entry.source !== "pipewire")) add3(device.id);
|
|
602791
|
+
const errors = [];
|
|
602792
|
+
for (const candidate of ordered) {
|
|
602793
|
+
try {
|
|
602794
|
+
await tryRecordAudioDevice(candidate, durationSec, outputPath3);
|
|
602795
|
+
return { ok: true, device: candidate, method: candidate.startsWith("pulse:") ? "pulse/ffmpeg" : "arecord", errors };
|
|
602796
|
+
} catch (err) {
|
|
602797
|
+
errors.push(`${candidate}: ${err instanceof Error ? err.message : String(err)}`.slice(0, 360));
|
|
602798
|
+
}
|
|
602799
|
+
}
|
|
602800
|
+
return { ok: false, device: preferred || "default", method: "none", errors };
|
|
602801
|
+
}
|
|
602590
602802
|
function firstDeviceId(devices) {
|
|
602591
602803
|
return devices.find((device) => device.id && !/metadata\/non-capture/i.test(device.detail ?? ""))?.id ?? devices[0]?.id;
|
|
602592
602804
|
}
|
|
@@ -602677,6 +602889,7 @@ var init_live_sensors = __esm({
|
|
|
602677
602889
|
agentActionEnabled = false;
|
|
602678
602890
|
lastAgentReviewAt = 0;
|
|
602679
602891
|
autoOrientationInFlight = /* @__PURE__ */ new Set();
|
|
602892
|
+
lastFeedbackAt = /* @__PURE__ */ new Map();
|
|
602680
602893
|
setStatusSink(sink) {
|
|
602681
602894
|
this.statusSink = sink ?? null;
|
|
602682
602895
|
this.emitStatus();
|
|
@@ -602693,6 +602906,20 @@ var init_live_sensors = __esm({
|
|
|
602693
602906
|
getSnapshot() {
|
|
602694
602907
|
return this.snapshot;
|
|
602695
602908
|
}
|
|
602909
|
+
activeVideoSources() {
|
|
602910
|
+
const ordered = [];
|
|
602911
|
+
const add3 = (id2) => {
|
|
602912
|
+
const clean5 = String(id2 ?? "").trim();
|
|
602913
|
+
if (!clean5 || ordered.includes(clean5)) return;
|
|
602914
|
+
const device = this.devices.video.find((entry) => entry.id === clean5);
|
|
602915
|
+
if (device && /metadata\/non-capture/i.test(device.detail ?? "")) return;
|
|
602916
|
+
ordered.push(clean5);
|
|
602917
|
+
};
|
|
602918
|
+
add3(this.config.selectedCamera);
|
|
602919
|
+
for (const device of this.devices.video) add3(device.id);
|
|
602920
|
+
if (ordered.length === 0) add3(firstDeviceId(this.devices.video));
|
|
602921
|
+
return ordered.slice(0, 4);
|
|
602922
|
+
}
|
|
602696
602923
|
getCameraOrientation(device = this.config.selectedCamera) {
|
|
602697
602924
|
return device ? this.config.cameraOrientation?.[device] : void 0;
|
|
602698
602925
|
}
|
|
@@ -602763,6 +602990,12 @@ var init_live_sensors = __esm({
|
|
|
602763
602990
|
}
|
|
602764
602991
|
const framePath = extractSavedImagePath(captured.output, this.repoRoot);
|
|
602765
602992
|
if (!framePath) return `Camera orientation detection captured ${target}, but no frame path was returned.`;
|
|
602993
|
+
const cvScores = await scoreCameraOrientationWithOpenCv(framePath);
|
|
602994
|
+
const cvDecision = cvScores ? chooseCameraOrientationFromScores(cvScores) : null;
|
|
602995
|
+
if (cvDecision && cvDecision.confidence >= 0.68) {
|
|
602996
|
+
const orientation2 = this.setCameraRotation(target, cvDecision.rotation, "auto", cvDecision.reason, cvDecision.confidence);
|
|
602997
|
+
return orientation2 ? `Auto orientation for ${target}: ${describeCameraOrientation(orientation2)}` : `Auto orientation for ${target} could not be persisted.`;
|
|
602998
|
+
}
|
|
602766
602999
|
const prompt = [
|
|
602767
603000
|
"Determine whether this camera frame is upright for a human viewer.",
|
|
602768
603001
|
"Return only JSON with this schema:",
|
|
@@ -602777,14 +603010,24 @@ var init_live_sensors = __esm({
|
|
|
602777
603010
|
model: "moondream3-preview"
|
|
602778
603011
|
});
|
|
602779
603012
|
if (!vision.success) {
|
|
603013
|
+
if (cvDecision) {
|
|
603014
|
+
const orientation2 = this.setCameraRotation(target, cvDecision.rotation, "auto", cvDecision.reason, cvDecision.confidence);
|
|
603015
|
+
return orientation2 ? `Auto orientation for ${target}: ${describeCameraOrientation(orientation2)}` : `Auto orientation for ${target} could not be persisted.`;
|
|
603016
|
+
}
|
|
602780
603017
|
return `Camera orientation detection captured ${target}, but vision inference was unavailable: ${vision.error || vision.output || "unknown error"}`;
|
|
602781
603018
|
}
|
|
602782
603019
|
const decision2 = parseCameraOrientationDecision(vision.llmContent || vision.output);
|
|
602783
603020
|
if (!decision2) {
|
|
603021
|
+
if (cvDecision) {
|
|
603022
|
+
const orientation2 = this.setCameraRotation(target, cvDecision.rotation, "auto", cvDecision.reason, cvDecision.confidence);
|
|
603023
|
+
return orientation2 ? `Auto orientation for ${target}: ${describeCameraOrientation(orientation2)}` : `Auto orientation for ${target} could not be persisted.`;
|
|
603024
|
+
}
|
|
602784
603025
|
return `Camera orientation detection for ${target} was inconclusive. Vision output did not contain a structured rotation decision.`;
|
|
602785
603026
|
}
|
|
602786
|
-
const
|
|
602787
|
-
const
|
|
603027
|
+
const useCv = cvDecision && (cvDecision.confidence > (decision2.confidence ?? 0) + 0.12 || cvDecision.rotation !== decision2.rotation && cvDecision.confidence >= 0.62 && (decision2.confidence ?? 0) < 0.76);
|
|
603028
|
+
const selected = useCv ? cvDecision : decision2;
|
|
603029
|
+
const evidence = useCv ? cvDecision.reason : `${decision2.reason || boundedText(vision.llmContent || vision.output, 320)}${cvDecision ? `; cv=${cvDecision.reason}` : ""}`;
|
|
603030
|
+
const orientation = this.setCameraRotation(target, selected.rotation, "auto", evidence, selected.confidence);
|
|
602788
603031
|
return orientation ? `Auto orientation for ${target}: ${describeCameraOrientation(orientation)}` : `Auto orientation for ${target} could not be persisted.`;
|
|
602789
603032
|
} finally {
|
|
602790
603033
|
this.autoOrientationInFlight.delete(target);
|
|
@@ -602853,22 +603096,25 @@ var init_live_sensors = __esm({
|
|
|
602853
603096
|
videoIntervalMs: options2.lowLatency === false ? this.config.videoIntervalMs : Math.min(this.config.videoIntervalMs, 1500),
|
|
602854
603097
|
audioIntervalMs: options2.lowLatency === false ? this.config.audioIntervalMs : Math.min(this.config.audioIntervalMs, 3e3)
|
|
602855
603098
|
});
|
|
602856
|
-
const source
|
|
602857
|
-
|
|
602858
|
-
|
|
602859
|
-
|
|
603099
|
+
for (const source of this.activeVideoSources()) {
|
|
603100
|
+
const current = this.getCameraOrientation(source);
|
|
603101
|
+
if (current?.source === "manual") continue;
|
|
603102
|
+
if (current && current.confidence !== void 0 && current.confidence >= 0.75 && Date.now() - current.updatedAt < 15 * 6e4) continue;
|
|
603103
|
+
void this.autoOrientCamera(source).then((message2) => {
|
|
603104
|
+
this.emitFeedbackThrottled(`orient:${source}`, {
|
|
602860
603105
|
kind: "orientation",
|
|
602861
603106
|
title: "Camera auto-orientation",
|
|
602862
603107
|
observedAt: Date.now(),
|
|
602863
603108
|
message: message2
|
|
602864
|
-
});
|
|
603109
|
+
}, 3e4);
|
|
603110
|
+
this.emitDashboard();
|
|
602865
603111
|
}).catch((err) => {
|
|
602866
|
-
this.
|
|
603112
|
+
this.emitFeedbackThrottled(`orient-error:${source}`, {
|
|
602867
603113
|
kind: "error",
|
|
602868
603114
|
title: "Camera auto-orientation failed",
|
|
602869
603115
|
observedAt: Date.now(),
|
|
602870
603116
|
message: err instanceof Error ? err.message : String(err)
|
|
602871
|
-
});
|
|
603117
|
+
}, 6e4);
|
|
602872
603118
|
});
|
|
602873
603119
|
}
|
|
602874
603120
|
void this.sampleVideoNow(true).catch((err) => {
|
|
@@ -602895,7 +603141,7 @@ var init_live_sensors = __esm({
|
|
|
602895
603141
|
});
|
|
602896
603142
|
return options2.agent ? "Live PFC run enabled: visual/audio feedback is active and significant events can launch background agent review." : "Live run enabled: visual/audio feedback is active and live context will update each turn.";
|
|
602897
603143
|
}
|
|
602898
|
-
async previewCamera(device = this.config.selectedCamera) {
|
|
603144
|
+
async previewCamera(device = this.config.selectedCamera, options2 = {}) {
|
|
602899
603145
|
const source = device || firstDeviceId(this.devices.video);
|
|
602900
603146
|
if (!source) return { ok: false, message: "No camera selected. Use /live to select a camera after device discovery." };
|
|
602901
603147
|
const orientation = this.getCameraOrientation(source);
|
|
@@ -602910,10 +603156,22 @@ var init_live_sensors = __esm({
|
|
|
602910
603156
|
if (!result.success) {
|
|
602911
603157
|
const error = result.error || result.output || "camera capture failed";
|
|
602912
603158
|
const observedAt2 = Date.now();
|
|
602913
|
-
|
|
603159
|
+
const errorView = {
|
|
602914
603160
|
updatedAt: observedAt2,
|
|
602915
603161
|
source,
|
|
602916
|
-
error
|
|
603162
|
+
error,
|
|
603163
|
+
rotation,
|
|
603164
|
+
orientation
|
|
603165
|
+
};
|
|
603166
|
+
if (source === this.config.selectedCamera || !this.snapshot.video) this.snapshot.video = errorView;
|
|
603167
|
+
this.snapshot.cameras = {
|
|
603168
|
+
...this.snapshot.cameras ?? {},
|
|
603169
|
+
[source]: {
|
|
603170
|
+
...this.snapshot.cameras?.[source] ?? { source, updatedAt: observedAt2 },
|
|
603171
|
+
...errorView,
|
|
603172
|
+
updatedAt: observedAt2,
|
|
603173
|
+
source
|
|
603174
|
+
}
|
|
602917
603175
|
};
|
|
602918
603176
|
this.snapshot.events = mergeRecentById(this.snapshot.events, [{
|
|
602919
603177
|
id: stableEventId("camera_error", source, observedAt2, error.slice(0, 80)),
|
|
@@ -602924,12 +603182,14 @@ var init_live_sensors = __esm({
|
|
|
602924
603182
|
summary: error
|
|
602925
603183
|
}], 50);
|
|
602926
603184
|
this.persist();
|
|
602927
|
-
|
|
602928
|
-
|
|
602929
|
-
|
|
602930
|
-
|
|
602931
|
-
|
|
602932
|
-
|
|
603185
|
+
if (options2.emit !== false) {
|
|
603186
|
+
this.emitFeedbackThrottled(`camera-capture:${source}:${error.slice(0, 80)}`, {
|
|
603187
|
+
kind: "error",
|
|
603188
|
+
title: "Camera capture failed",
|
|
603189
|
+
observedAt: observedAt2,
|
|
603190
|
+
message: error
|
|
603191
|
+
}, 3e4);
|
|
603192
|
+
}
|
|
602933
603193
|
return { ok: false, message: error };
|
|
602934
603194
|
}
|
|
602935
603195
|
const framePath = extractSavedImagePath(result.output, this.repoRoot);
|
|
@@ -602938,15 +603198,26 @@ var init_live_sensors = __esm({
|
|
|
602938
603198
|
const preview = await buildImageAsciiPreview(framePath, { width: 72, height: 22 });
|
|
602939
603199
|
const asciiContext = preview ? formatImageAsciiContext(preview, displayPath) : void 0;
|
|
602940
603200
|
const observedAt = Date.now();
|
|
602941
|
-
|
|
603201
|
+
const cameraView = {
|
|
602942
603202
|
updatedAt: observedAt,
|
|
602943
603203
|
source,
|
|
602944
603204
|
framePath,
|
|
602945
603205
|
displayPath,
|
|
603206
|
+
ascii: preview?.ascii,
|
|
603207
|
+
renderer: preview?.renderer,
|
|
602946
603208
|
asciiContext,
|
|
602947
603209
|
rotation,
|
|
602948
603210
|
orientation
|
|
602949
603211
|
};
|
|
603212
|
+
if (source === this.config.selectedCamera || !this.snapshot.video) this.snapshot.video = cameraView;
|
|
603213
|
+
this.snapshot.cameras = {
|
|
603214
|
+
...this.snapshot.cameras ?? {},
|
|
603215
|
+
[source]: {
|
|
603216
|
+
...cameraView,
|
|
603217
|
+
summary: this.snapshot.cameras?.[source]?.summary,
|
|
603218
|
+
inference: this.snapshot.cameras?.[source]?.inference
|
|
603219
|
+
}
|
|
603220
|
+
};
|
|
602950
603221
|
this.snapshot.events = mergeRecentById(this.snapshot.events, [{
|
|
602951
603222
|
id: stableEventId("camera_frame", source, observedAt, framePath),
|
|
602952
603223
|
kind: "camera_frame",
|
|
@@ -602957,16 +603228,18 @@ var init_live_sensors = __esm({
|
|
|
602957
603228
|
evidencePath: framePath
|
|
602958
603229
|
}], 50);
|
|
602959
603230
|
this.persist();
|
|
602960
|
-
|
|
602961
|
-
|
|
602962
|
-
|
|
602963
|
-
|
|
602964
|
-
|
|
602965
|
-
|
|
602966
|
-
|
|
602967
|
-
|
|
602968
|
-
|
|
602969
|
-
|
|
603231
|
+
if (options2.emit !== false) {
|
|
603232
|
+
this.emitFeedback({
|
|
603233
|
+
kind: "camera-frame",
|
|
603234
|
+
title: "Live camera frame",
|
|
603235
|
+
observedAt,
|
|
603236
|
+
message: `Captured from ${source}${orientation ? ` (${formatCameraRotation(orientation.rotation)})` : ""}: ${framePath}`,
|
|
603237
|
+
imagePath: framePath,
|
|
603238
|
+
displayPath,
|
|
603239
|
+
ascii: preview?.ascii,
|
|
603240
|
+
renderer: preview?.renderer
|
|
603241
|
+
});
|
|
603242
|
+
}
|
|
602970
603243
|
return {
|
|
602971
603244
|
ok: true,
|
|
602972
603245
|
message: `Camera frame captured from ${source}: ${framePath}`,
|
|
@@ -602976,7 +603249,7 @@ var init_live_sensors = __esm({
|
|
|
602976
603249
|
renderer: preview?.renderer
|
|
602977
603250
|
};
|
|
602978
603251
|
}
|
|
602979
|
-
async recognizeFrameObjects(framePath, source, observedAt = Date.now()) {
|
|
603252
|
+
async recognizeFrameObjects(framePath, source, observedAt = Date.now(), options2 = {}) {
|
|
602980
603253
|
try {
|
|
602981
603254
|
const result = await new VisualMemoryTool().execute({
|
|
602982
603255
|
action: "recognize",
|
|
@@ -602996,109 +603269,124 @@ var init_live_sensors = __esm({
|
|
|
602996
603269
|
evidencePath: framePath
|
|
602997
603270
|
}], 50);
|
|
602998
603271
|
this.persist();
|
|
602999
|
-
|
|
603000
|
-
|
|
603001
|
-
|
|
603002
|
-
|
|
603003
|
-
|
|
603004
|
-
|
|
603272
|
+
if (options2.emit !== false) {
|
|
603273
|
+
this.emitFeedback({
|
|
603274
|
+
kind: "clip",
|
|
603275
|
+
title: result.success ? "CLIP visual-memory recognition" : "CLIP visual-memory unavailable",
|
|
603276
|
+
observedAt,
|
|
603277
|
+
message: summary
|
|
603278
|
+
});
|
|
603279
|
+
}
|
|
603005
603280
|
return summary;
|
|
603006
603281
|
} catch (err) {
|
|
603007
603282
|
const message2 = `CLIP/visual-memory failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
603008
|
-
|
|
603009
|
-
|
|
603010
|
-
|
|
603011
|
-
|
|
603012
|
-
|
|
603013
|
-
|
|
603283
|
+
if (options2.emit !== false) {
|
|
603284
|
+
this.emitFeedbackThrottled(`clip:${source}`, {
|
|
603285
|
+
kind: "error",
|
|
603286
|
+
title: "CLIP visual-memory failed",
|
|
603287
|
+
observedAt,
|
|
603288
|
+
message: message2
|
|
603289
|
+
}, 6e4);
|
|
603290
|
+
}
|
|
603014
603291
|
return message2;
|
|
603015
603292
|
}
|
|
603016
603293
|
}
|
|
603017
|
-
async
|
|
603018
|
-
|
|
603019
|
-
|
|
603020
|
-
|
|
603021
|
-
|
|
603022
|
-
|
|
603023
|
-
|
|
603024
|
-
const
|
|
603025
|
-
const
|
|
603026
|
-
|
|
603027
|
-
|
|
603028
|
-
|
|
603029
|
-
|
|
603030
|
-
|
|
603031
|
-
|
|
603032
|
-
|
|
603033
|
-
|
|
603034
|
-
|
|
603035
|
-
|
|
603036
|
-
|
|
603037
|
-
|
|
603038
|
-
|
|
603039
|
-
|
|
603040
|
-
|
|
603041
|
-
|
|
603042
|
-
|
|
603043
|
-
|
|
603044
|
-
|
|
603045
|
-
|
|
603046
|
-
|
|
603047
|
-
|
|
603048
|
-
|
|
603049
|
-
|
|
603050
|
-
|
|
603051
|
-
|
|
603052
|
-
|
|
603053
|
-
|
|
603054
|
-
|
|
603055
|
-
|
|
603056
|
-
|
|
603057
|
-
}
|
|
603058
|
-
|
|
603059
|
-
|
|
603060
|
-
|
|
603061
|
-
|
|
603294
|
+
async sampleSingleVideoNow(source, forceInfer) {
|
|
603295
|
+
const preview = await this.previewCamera(source, { emit: false });
|
|
603296
|
+
let inference = "";
|
|
603297
|
+
let clipSummary = "";
|
|
603298
|
+
const orientation = this.getCameraOrientation(source);
|
|
603299
|
+
const sampledAt = Date.now();
|
|
603300
|
+
if (forceInfer && source) {
|
|
603301
|
+
const loop = new LiveMediaLoopTool(this.repoRoot);
|
|
603302
|
+
const result = await loop.execute({
|
|
603303
|
+
action: "watch",
|
|
603304
|
+
source_kind: "camera",
|
|
603305
|
+
camera: source,
|
|
603306
|
+
duration_sec: 2.5,
|
|
603307
|
+
sample_interval_sec: 1,
|
|
603308
|
+
max_frames: 2,
|
|
603309
|
+
rotate_degrees: orientation?.rotation ?? 0,
|
|
603310
|
+
auto_bootstrap: true,
|
|
603311
|
+
audio_mode: "none",
|
|
603312
|
+
detect_objects: true,
|
|
603313
|
+
track_objects: true,
|
|
603314
|
+
crop_faces: true,
|
|
603315
|
+
recognize_faces: true
|
|
603316
|
+
});
|
|
603317
|
+
inference = result.success ? String(result.output || result.llmContent || "") : `Video inference failed: ${result.error || result.output || "unknown error"}`;
|
|
603318
|
+
const livePayload = result.success ? parseLiveMediaPayload(result.llmContent || result.output) : null;
|
|
603319
|
+
const observations = observationsFromLiveMediaPayload(livePayload, sampledAt, source);
|
|
603320
|
+
this.snapshot.events = mergeRecentById(this.snapshot.events, observations.events, 50);
|
|
603321
|
+
this.snapshot.people = mergeRecentById(this.snapshot.people, observations.people, 24);
|
|
603322
|
+
const existing = this.snapshot.cameras?.[source] ?? this.snapshot.video ?? { updatedAt: Date.now(), source };
|
|
603323
|
+
const camera = {
|
|
603324
|
+
...existing,
|
|
603325
|
+
updatedAt: sampledAt,
|
|
603326
|
+
source,
|
|
603327
|
+
inference,
|
|
603328
|
+
summary: summarizeInference(inference),
|
|
603329
|
+
rotation: orientation?.rotation ?? 0,
|
|
603330
|
+
orientation,
|
|
603331
|
+
...preview.ok ? { error: void 0 } : { error: preview.message }
|
|
603332
|
+
};
|
|
603333
|
+
this.snapshot.cameras = {
|
|
603334
|
+
...this.snapshot.cameras ?? {},
|
|
603335
|
+
[source]: camera
|
|
603336
|
+
};
|
|
603337
|
+
if (source === this.config.selectedCamera || !this.snapshot.video) this.snapshot.video = camera;
|
|
603338
|
+
this.persist();
|
|
603339
|
+
if (!result.success) {
|
|
603340
|
+
this.emitFeedbackThrottled(`video-infer:${source}`, {
|
|
603341
|
+
kind: "error",
|
|
603342
|
+
title: "Live video inference failed",
|
|
603062
603343
|
observedAt: sampledAt,
|
|
603063
|
-
message: boundedText(inference,
|
|
603064
|
-
});
|
|
603065
|
-
|
|
603066
|
-
|
|
603067
|
-
|
|
603068
|
-
|
|
603069
|
-
|
|
603070
|
-
|
|
603071
|
-
|
|
603072
|
-
|
|
603073
|
-
|
|
603074
|
-
|
|
603075
|
-
|
|
603076
|
-
|
|
603077
|
-
|
|
603078
|
-
|
|
603079
|
-
}
|
|
603080
|
-
const unknownPeople = observations.people.filter((entry) => entry.status === "unknown");
|
|
603081
|
-
if (unknownPeople.length > 0) {
|
|
603082
|
-
this.maybeRequestAgentReview(
|
|
603083
|
-
`${unknownPeople.length} unknown individual(s) observed on live camera`,
|
|
603084
|
-
unknownPeople.map((entry) => `${entry.cropPath || entry.trackId || entry.source} ${entry.evidence || ""}`).join("; ")
|
|
603085
|
-
);
|
|
603086
|
-
}
|
|
603087
|
-
const triggerEvents = observations.events.filter((entry) => entry.kind === "visual_trigger");
|
|
603088
|
-
if (triggerEvents.length > 0) {
|
|
603089
|
-
this.maybeRequestAgentReview(
|
|
603090
|
-
"Visual trigger hit in live camera stream",
|
|
603091
|
-
triggerEvents.map((entry) => entry.summary).join("; ")
|
|
603092
|
-
);
|
|
603093
|
-
}
|
|
603344
|
+
message: boundedText(inference, 1200)
|
|
603345
|
+
}, 45e3);
|
|
603346
|
+
}
|
|
603347
|
+
const unknownPeople = observations.people.filter((entry) => entry.status === "unknown");
|
|
603348
|
+
if (unknownPeople.length > 0) {
|
|
603349
|
+
this.maybeRequestAgentReview(
|
|
603350
|
+
`${unknownPeople.length} unknown individual(s) observed on live camera ${source}`,
|
|
603351
|
+
unknownPeople.map((entry) => `${entry.cropPath || entry.trackId || entry.source} ${entry.evidence || ""}`).join("; ")
|
|
603352
|
+
);
|
|
603353
|
+
}
|
|
603354
|
+
const triggerEvents = observations.events.filter((entry) => entry.kind === "visual_trigger");
|
|
603355
|
+
if (triggerEvents.length > 0) {
|
|
603356
|
+
this.maybeRequestAgentReview(
|
|
603357
|
+
`Visual trigger hit in live camera stream ${source}`,
|
|
603358
|
+
triggerEvents.map((entry) => entry.summary).join("; ")
|
|
603359
|
+
);
|
|
603094
603360
|
}
|
|
603095
|
-
|
|
603096
|
-
|
|
603361
|
+
}
|
|
603362
|
+
if (this.config.clipEnabled && preview.ok && preview.framePath && source) {
|
|
603363
|
+
clipSummary = await this.recognizeFrameObjects(preview.framePath, source, sampledAt, { emit: false });
|
|
603364
|
+
const current = this.snapshot.cameras?.[source];
|
|
603365
|
+
if (current) {
|
|
603366
|
+
current.summary = [current.summary, clipSummary ? `clip: ${trimOneLine(clipSummary, 160)}` : ""].filter(Boolean).join(" | ");
|
|
603367
|
+
this.snapshot.cameras = { ...this.snapshot.cameras ?? {}, [source]: current };
|
|
603368
|
+
if (this.snapshot.video?.source === source) this.snapshot.video = current;
|
|
603369
|
+
this.persist();
|
|
603097
603370
|
}
|
|
603098
|
-
|
|
603371
|
+
}
|
|
603372
|
+
return [preview.message, inference ? `
|
|
603099
603373
|
${inference}` : "", clipSummary ? `
|
|
603100
603374
|
CLIP visual memory:
|
|
603101
603375
|
${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
603376
|
+
}
|
|
603377
|
+
async sampleVideoNow(forceInfer = this.config.inferEnabled) {
|
|
603378
|
+
if (this.videoInFlight) return "Video sampler is already running.";
|
|
603379
|
+
this.videoInFlight = true;
|
|
603380
|
+
try {
|
|
603381
|
+
const sources = this.activeVideoSources();
|
|
603382
|
+
if (sources.length === 0) return "No camera selected. Use /live refresh after connecting hardware.";
|
|
603383
|
+
const outputs = [];
|
|
603384
|
+
for (const source of sources) {
|
|
603385
|
+
outputs.push(await this.sampleSingleVideoNow(source, forceInfer));
|
|
603386
|
+
}
|
|
603387
|
+
this.emitDashboard();
|
|
603388
|
+
return outputs.map((output, index) => `## Camera ${sources[index]}
|
|
603389
|
+
${output}`).join("\n\n");
|
|
603102
603390
|
} finally {
|
|
603103
603391
|
this.videoInFlight = false;
|
|
603104
603392
|
}
|
|
@@ -603113,10 +603401,9 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603113
603401
|
const recordingPath = join124(outputDir2, `audio-${Date.now()}.wav`);
|
|
603114
603402
|
let transcript = "";
|
|
603115
603403
|
let analysis = "";
|
|
603116
|
-
|
|
603117
|
-
|
|
603118
|
-
|
|
603119
|
-
const message2 = `Audio capture failed from ${input}: ${err instanceof Error ? err.message : String(err)}`;
|
|
603404
|
+
const capture = await recordAudioSampleWithRecovery(input, this.devices.audioInputs, 3, recordingPath);
|
|
603405
|
+
if (!capture.ok) {
|
|
603406
|
+
const message2 = `Audio capture failed from ${input}; tried fallbacks: ${capture.errors.slice(0, 5).join(" | ")}`;
|
|
603120
603407
|
this.snapshot.audio = {
|
|
603121
603408
|
updatedAt: Date.now(),
|
|
603122
603409
|
input,
|
|
@@ -603124,14 +603411,18 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603124
603411
|
error: message2
|
|
603125
603412
|
};
|
|
603126
603413
|
this.persist();
|
|
603127
|
-
this.
|
|
603414
|
+
this.emitFeedbackThrottled(`audio-capture:${input}`, {
|
|
603128
603415
|
kind: "error",
|
|
603129
603416
|
title: "Live audio capture failed",
|
|
603130
603417
|
observedAt: this.snapshot.audio.updatedAt,
|
|
603131
603418
|
message: message2
|
|
603132
|
-
});
|
|
603419
|
+
}, 6e4);
|
|
603420
|
+
this.emitDashboard();
|
|
603133
603421
|
return message2;
|
|
603134
603422
|
}
|
|
603423
|
+
if (capture.device !== this.config.selectedAudioInput) {
|
|
603424
|
+
this.config.selectedAudioInput = capture.device;
|
|
603425
|
+
}
|
|
603135
603426
|
if (this.config.asrEnabled) {
|
|
603136
603427
|
try {
|
|
603137
603428
|
const asr = await new TranscribeFileTool(this.repoRoot).execute({ path: recordingPath, model: "tiny" });
|
|
@@ -603159,7 +603450,7 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603159
603450
|
}
|
|
603160
603451
|
this.snapshot.audio = {
|
|
603161
603452
|
updatedAt: Date.now(),
|
|
603162
|
-
input,
|
|
603453
|
+
input: capture.device,
|
|
603163
603454
|
output: this.config.selectedAudioOutput,
|
|
603164
603455
|
recordingPath,
|
|
603165
603456
|
transcript,
|
|
@@ -603168,10 +603459,10 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603168
603459
|
const audioEvents = [];
|
|
603169
603460
|
if (transcript) {
|
|
603170
603461
|
audioEvents.push({
|
|
603171
|
-
id: stableEventId("audio_transcript",
|
|
603462
|
+
id: stableEventId("audio_transcript", capture.device, this.snapshot.audio.updatedAt, recordingPath),
|
|
603172
603463
|
kind: "audio_transcript",
|
|
603173
603464
|
observedAt: this.snapshot.audio.updatedAt,
|
|
603174
|
-
source:
|
|
603465
|
+
source: capture.device,
|
|
603175
603466
|
title: "Live ASR transcript",
|
|
603176
603467
|
summary: transcript,
|
|
603177
603468
|
evidencePath: recordingPath
|
|
@@ -603179,10 +603470,10 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603179
603470
|
}
|
|
603180
603471
|
if (analysis) {
|
|
603181
603472
|
audioEvents.push({
|
|
603182
|
-
id: stableEventId("audio_sound",
|
|
603473
|
+
id: stableEventId("audio_sound", capture.device, this.snapshot.audio.updatedAt, recordingPath),
|
|
603183
603474
|
kind: "audio_sound",
|
|
603184
603475
|
observedAt: this.snapshot.audio.updatedAt,
|
|
603185
|
-
source:
|
|
603476
|
+
source: capture.device,
|
|
603186
603477
|
title: "Live sound analysis",
|
|
603187
603478
|
summary: analysis,
|
|
603188
603479
|
evidencePath: recordingPath
|
|
@@ -603190,21 +603481,9 @@ ${clipSummary}` : ""].filter(Boolean).join("\n");
|
|
|
603190
603481
|
}
|
|
603191
603482
|
this.snapshot.events = mergeRecentById(this.snapshot.events, audioEvents, 50);
|
|
603192
603483
|
this.persist();
|
|
603193
|
-
|
|
603194
|
-
this.emitFeedback({
|
|
603195
|
-
kind: "audio",
|
|
603196
|
-
title: "Live audio sample",
|
|
603197
|
-
observedAt: this.snapshot.audio.updatedAt,
|
|
603198
|
-
message: [
|
|
603199
|
-
transcript ? `ASR:
|
|
603200
|
-
${boundedText(transcript, 700)}` : "",
|
|
603201
|
-
analysis ? `Sounds:
|
|
603202
|
-
${boundedText(analysis, 900)}` : ""
|
|
603203
|
-
].filter(Boolean).join("\n\n")
|
|
603204
|
-
});
|
|
603205
|
-
}
|
|
603484
|
+
this.emitDashboard();
|
|
603206
603485
|
return [
|
|
603207
|
-
`Audio sample captured from ${input}: ${recordingPath}`,
|
|
603486
|
+
`Audio sample captured from ${capture.device}${capture.device !== input ? ` (recovered from ${input})` : ""}: ${recordingPath}`,
|
|
603208
603487
|
transcript ? `
|
|
603209
603488
|
ASR:
|
|
603210
603489
|
${transcript}` : "",
|
|
@@ -603256,6 +603535,21 @@ ${analysis}` : ""
|
|
|
603256
603535
|
} catch {
|
|
603257
603536
|
}
|
|
603258
603537
|
}
|
|
603538
|
+
emitFeedbackThrottled(key, feedback, minIntervalMs) {
|
|
603539
|
+
const now2 = Date.now();
|
|
603540
|
+
const last2 = this.lastFeedbackAt.get(key) ?? 0;
|
|
603541
|
+
if (now2 - last2 < minIntervalMs) return;
|
|
603542
|
+
this.lastFeedbackAt.set(key, now2);
|
|
603543
|
+
this.emitFeedback(feedback);
|
|
603544
|
+
}
|
|
603545
|
+
emitDashboard() {
|
|
603546
|
+
this.emitFeedback({
|
|
603547
|
+
kind: "dashboard",
|
|
603548
|
+
title: "Live audio/video monitor",
|
|
603549
|
+
observedAt: Date.now(),
|
|
603550
|
+
message: "live dashboard refresh"
|
|
603551
|
+
});
|
|
603552
|
+
}
|
|
603259
603553
|
maybeRequestAgentReview(reason, evidence) {
|
|
603260
603554
|
if (!this.agentActionEnabled || !this.agentActionSink) return;
|
|
603261
603555
|
const now2 = Date.now();
|
|
@@ -654970,7 +655264,33 @@ function renderLivePreview(preview) {
|
|
|
654970
655264
|
renderWarning("Camera preview was captured, but ASCII rendering was unavailable.");
|
|
654971
655265
|
}
|
|
654972
655266
|
}
|
|
654973
|
-
function
|
|
655267
|
+
function renderLiveDashboard(ctx3, manager) {
|
|
655268
|
+
const host = ctx3.liveDynamicBlockHost;
|
|
655269
|
+
if (!host) {
|
|
655270
|
+
renderInfo(formatLiveDashboardFromSnapshot(manager.getSnapshot(), { width: 100 }).join("\n"));
|
|
655271
|
+
return;
|
|
655272
|
+
}
|
|
655273
|
+
const key = ctx3.repoRoot;
|
|
655274
|
+
let state = liveDashboardBlocks.get(key);
|
|
655275
|
+
if (!state || state.host !== host) {
|
|
655276
|
+
const id2 = `live-dashboard-${Math.random().toString(36).slice(2, 9)}`;
|
|
655277
|
+
state = { host, id: id2, manager };
|
|
655278
|
+
liveDashboardBlocks.set(key, state);
|
|
655279
|
+
host.registerDynamicBlock(
|
|
655280
|
+
id2,
|
|
655281
|
+
(width) => formatLiveDashboardFromSnapshot(manager.getSnapshot(), { width })
|
|
655282
|
+
);
|
|
655283
|
+
host.appendDynamicBlock(id2);
|
|
655284
|
+
return;
|
|
655285
|
+
}
|
|
655286
|
+
state.manager = manager;
|
|
655287
|
+
host.refreshDynamicBlocks?.();
|
|
655288
|
+
}
|
|
655289
|
+
function renderLiveFeedback(ctx3, manager, feedback) {
|
|
655290
|
+
if (feedback.kind === "dashboard") {
|
|
655291
|
+
renderLiveDashboard(ctx3, manager);
|
|
655292
|
+
return;
|
|
655293
|
+
}
|
|
654974
655294
|
const stamp = new Date(feedback.observedAt).toISOString();
|
|
654975
655295
|
if (feedback.kind === "error") {
|
|
654976
655296
|
renderWarning(`[live ${stamp}] ${feedback.title}
|
|
@@ -654987,7 +655307,7 @@ ${feedback.message}`);
|
|
|
654987
655307
|
}
|
|
654988
655308
|
function attachLiveSinks(ctx3, manager, agentEnabled = false) {
|
|
654989
655309
|
manager.setStatusSink(ctx3.setLiveMediaStatus);
|
|
654990
|
-
manager.setFeedbackSink((feedback) => renderLiveFeedback(feedback));
|
|
655310
|
+
manager.setFeedbackSink((feedback) => renderLiveFeedback(ctx3, manager, feedback));
|
|
654991
655311
|
if (agentEnabled && ctx3.startBackgroundPrompt) {
|
|
654992
655312
|
manager.setAgentActionSink((prompt) => {
|
|
654993
655313
|
const id2 = ctx3.startBackgroundPrompt?.(prompt);
|
|
@@ -660485,7 +660805,7 @@ async function showExposeDashboard(gateway, rl, ctx3) {
|
|
|
660485
660805
|
renderInfo("Expose gateway stopped.");
|
|
660486
660806
|
}
|
|
660487
660807
|
}
|
|
660488
|
-
var OMNIUS_PINNED_DEPENDENCY_SPECS, NEXUS_DIRECTORY_ORIGIN, NEXUS_SPONSORS_URL, _sponsorHeartbeatTimer, _lastRegisteredSponsorPayload, __COMMAND_REGISTRY, DASH_INTERNAL, localGpuMetricsCache, localGpuMetricsProbeAt, localGpuMetricsProbeInFlight;
|
|
660808
|
+
var OMNIUS_PINNED_DEPENDENCY_SPECS, NEXUS_DIRECTORY_ORIGIN, NEXUS_SPONSORS_URL, _sponsorHeartbeatTimer, _lastRegisteredSponsorPayload, __COMMAND_REGISTRY, liveDashboardBlocks, DASH_INTERNAL, localGpuMetricsCache, localGpuMetricsProbeAt, localGpuMetricsProbeInFlight;
|
|
660489
660809
|
var init_commands = __esm({
|
|
660490
660810
|
"packages/cli/src/tui/commands.ts"() {
|
|
660491
660811
|
"use strict";
|
|
@@ -660803,6 +661123,7 @@ var init_commands = __esm({
|
|
|
660803
661123
|
}
|
|
660804
661124
|
});
|
|
660805
661125
|
})();
|
|
661126
|
+
liveDashboardBlocks = /* @__PURE__ */ new Map();
|
|
660806
661127
|
DASH_INTERNAL = /* @__PURE__ */ new Set(["system_metrics", "__list_capabilities"]);
|
|
660807
661128
|
localGpuMetricsCache = null;
|
|
660808
661129
|
localGpuMetricsProbeAt = 0;
|
|
@@ -733124,6 +733445,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
|
|
|
733124
733445
|
setLiveMediaStatus(status) {
|
|
733125
733446
|
statusBar.setLiveMediaStatus(status);
|
|
733126
733447
|
},
|
|
733448
|
+
liveDynamicBlockHost: {
|
|
733449
|
+
registerDynamicBlock: (id2, render2) => statusBar.registerDynamicBlock(id2, render2),
|
|
733450
|
+
appendDynamicBlock: (id2) => statusBar.appendDynamicBlock(id2),
|
|
733451
|
+
refreshDynamicBlocks: () => statusBar.refreshDynamicBlocks()
|
|
733452
|
+
},
|
|
733127
733453
|
// Voice call session — standalone cloudflared tunnel for /call
|
|
733128
733454
|
// Each connecting WebSocket client gets a dedicated CallSubAgent.
|
|
733129
733455
|
// Admin callers (matching session key) get full tool access; public callers get read-only.
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.408",
|
|
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.408",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED