@camstack/addon-post-analysis 1.2.653 → 1.2.655
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/CHANGELOG.md +15 -0
- package/dist/{clip-model-registry-Bz1uQvQ8.js → clip-model-registry-C-en5vSD.js} +68371 -66865
- package/dist/{clip-model-registry-BrazZJvr.mjs → clip-model-registry-_b3KHYFm.mjs} +68490 -67020
- package/dist/embedding-encoder/index.js +1 -1
- package/dist/embedding-encoder/index.mjs +1 -1
- package/dist/pipeline-analytics/_stub.js +2 -2
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-D6DnF6Vk.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CF-Mts9j.mjs} +2 -2
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C7OJ7wek.mjs +26 -0
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DXQUFp7P.mjs +26 -0
- package/dist/pipeline-analytics/{hostInit-DtrcY1yz.mjs → hostInit-BSfz5s98.mjs} +2 -2
- package/dist/pipeline-analytics/index.js +478 -81
- package/dist/pipeline-analytics/index.mjs +477 -80
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BKMr4IqL.mjs +0 -26
- package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CtNyIYsM.mjs +0 -26
|
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
-
const require_clip_model_registry = require("../clip-model-registry-
|
|
5
|
+
const require_clip_model_registry = require("../clip-model-registry-C-en5vSD.js");
|
|
6
6
|
let node_fs = require("node:fs");
|
|
7
7
|
node_fs = require_clip_model_registry.__toESM(node_fs, 1);
|
|
8
8
|
let node_path = require("node:path");
|
|
@@ -895,7 +895,9 @@ function describeGrant(grant) {
|
|
|
895
895
|
return grant.action === "disarm" ? "Alarm disarmed" : `Alarm armed${grant.mode !== void 0 ? ` (${grant.mode})` : ""}`;
|
|
896
896
|
case "addon-update": return `${grant.packages.length} addon update(s) on ${grant.nodeId}`;
|
|
897
897
|
case "server-update": return `Server update on ${grant.nodeId}`;
|
|
898
|
-
case "detection-verdict":
|
|
898
|
+
case "detection-verdict":
|
|
899
|
+
if (grant.verdict === "wrong-label") return "Marked label wrong, class right — kept for retraining";
|
|
900
|
+
return grant.verdict === "correct" ? "Marked correct — kept for retraining" : grant.suggestedClass === void 0 ? "Marked wrong — kept for retraining" : `Marked wrong, it was a ${grant.suggestedClass} — kept for retraining`;
|
|
899
901
|
}
|
|
900
902
|
}
|
|
901
903
|
/** The audit line's `meta`, per family. A snooze that logged a `sequence` it
|
|
@@ -969,7 +971,7 @@ var FAILURE_COPY = {
|
|
|
969
971
|
/** The question a link-only channel lands on, answered by a POST form. */
|
|
970
972
|
function confirmPage(url, grant) {
|
|
971
973
|
const action = `${url.pathname}?exp=${encodeURIComponent(url.searchParams.get("exp") ?? "")}&sig=${encodeURIComponent(url.searchParams.get("sig") ?? "")}&confirm=1`;
|
|
972
|
-
return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Confirm</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#111;color:#eee}main{max-width:24rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 1rem}button{font:inherit;padding:.6rem 1.4rem;border-radius:.5rem;border:0;background:#3b82f6;color:#fff}</style><main><h1>Confirm</h1><p>${grant.verdict === "correct" ? "Mark this detection as correct?" : grant.suggestedClass === void 0 ? "Mark this detection as wrong?" : `Mark this detection as wrong, it was a ${grant.suggestedClass}?`}</p><form method="post" action="${action}"><button type="submit">Confirm</button></form></main>`;
|
|
974
|
+
return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Confirm</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#111;color:#eee}main{max-width:24rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 1rem}button{font:inherit;padding:.6rem 1.4rem;border-radius:.5rem;border:0;background:#3b82f6;color:#fff}</style><main><h1>Confirm</h1><p>${grant.verdict === "wrong-label" ? "Mark the label of this detection as wrong (the class is right)?" : grant.verdict === "correct" ? "Mark this detection as correct?" : grant.suggestedClass === void 0 ? "Mark this detection as wrong?" : `Mark this detection as wrong, it was a ${grant.suggestedClass}?`}</p><form method="post" action="${action}"><button type="submit">Confirm</button></form></main>`;
|
|
973
975
|
}
|
|
974
976
|
function page(title, detail) {
|
|
975
977
|
return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title><style>body{font:16px/1.5 system-ui,sans-serif;margin:0;min-height:100vh;display:grid;place-items:center;background:#111;color:#eee}main{max-width:24rem;padding:2rem;text-align:center}h1{font-size:1.25rem;margin:0 0 .5rem}p{margin:0;color:#aaa}</style><main><h1>${title}</h1><p>${detail}</p></main>`;
|
|
@@ -2953,7 +2955,6 @@ var NIGHT = "night";
|
|
|
2953
2955
|
var IR = "ir";
|
|
2954
2956
|
var DAWN = "dawn";
|
|
2955
2957
|
var DUSK = "dusk";
|
|
2956
|
-
var DEG = Math.PI / 180;
|
|
2957
2958
|
/**
|
|
2958
2959
|
* Mean channel spread below which a frame is judged infrared.
|
|
2959
2960
|
*
|
|
@@ -2975,38 +2976,6 @@ var IR_CHROMA_THRESHOLD = 6;
|
|
|
2975
2976
|
* scene reports `unknown` — the honest answer.
|
|
2976
2977
|
*/
|
|
2977
2978
|
var TWILIGHT_BAND_MS = 2400 * 1e3;
|
|
2978
|
-
/** Julian day number for a JS Date (UTC). */
|
|
2979
|
-
function toJulian(date) {
|
|
2980
|
-
return date.getTime() / 864e5 + 2440587.5;
|
|
2981
|
-
}
|
|
2982
|
-
/** Julian day → epoch ms (UTC). */
|
|
2983
|
-
function fromJulian(j) {
|
|
2984
|
-
return (j - 2440587.5) * 864e5;
|
|
2985
|
-
}
|
|
2986
|
-
/**
|
|
2987
|
-
* Sunrise/sunset (UTC epoch ms) for a location and date via the standard NOAA
|
|
2988
|
-
* sunrise equation. Returns `null` for the polar edge cases (sun never
|
|
2989
|
-
* rises/sets that day) so the caller can degrade to the clock split.
|
|
2990
|
-
*/
|
|
2991
|
-
function sunTimes(lat, lng, date) {
|
|
2992
|
-
const jDate = toJulian(date);
|
|
2993
|
-
const jStar = Math.round(jDate - 2451545 + 8e-4) - lng / 360;
|
|
2994
|
-
const m = (357.5291 + .98560028 * jStar) % 360;
|
|
2995
|
-
const mRad = m * DEG;
|
|
2996
|
-
const lambda = (m + (1.9148 * Math.sin(mRad) + .02 * Math.sin(2 * mRad) + 3e-4 * Math.sin(3 * mRad)) + 180 + 102.9372) % 360 * DEG;
|
|
2997
|
-
const jTransit = 2451545 + jStar + .0053 * Math.sin(mRad) - .0069 * Math.sin(2 * lambda);
|
|
2998
|
-
const declination = Math.asin(Math.sin(lambda) * Math.sin(23.44 * DEG));
|
|
2999
|
-
const latRad = lat * DEG;
|
|
3000
|
-
const cosOmega = (Math.sin(-.833 * DEG) - Math.sin(latRad) * Math.sin(declination)) / (Math.cos(latRad) * Math.cos(declination));
|
|
3001
|
-
if (cosOmega > 1 || cosOmega < -1) return null;
|
|
3002
|
-
const omega = Math.acos(cosOmega) / DEG;
|
|
3003
|
-
const jRise = jTransit - omega / 360;
|
|
3004
|
-
const jSet = jTransit + omega / 360;
|
|
3005
|
-
return {
|
|
3006
|
-
rise: fromJulian(jRise),
|
|
3007
|
-
set: fromJulian(jSet)
|
|
3008
|
-
};
|
|
3009
|
-
}
|
|
3010
2979
|
/** Coarse UTC clock split — day between 06:00 and 20:00 UTC. Deterministic
|
|
3011
2980
|
* last resort when no coordinates let us compute real sun-times. */
|
|
3012
2981
|
function clockCondition(now) {
|
|
@@ -3047,7 +3016,7 @@ function resolveConditionDetailed(input) {
|
|
|
3047
3016
|
source: "ir-chroma"
|
|
3048
3017
|
};
|
|
3049
3018
|
if (input.coordinates) {
|
|
3050
|
-
const times = sunTimes(input.coordinates.lat, input.coordinates.lng, input.now);
|
|
3019
|
+
const times = require_clip_model_registry.sunTimes(input.coordinates.lat, input.coordinates.lng, input.now);
|
|
3051
3020
|
if (times) {
|
|
3052
3021
|
const t = input.now.getTime();
|
|
3053
3022
|
if (Math.abs(t - times.rise) <= TWILIGHT_BAND_MS) return {
|
|
@@ -9289,6 +9258,7 @@ var en_default = {
|
|
|
9289
9258
|
"button.verdict.correct": "Correct",
|
|
9290
9259
|
"button.verdict.wrong": "Wrong",
|
|
9291
9260
|
"button.verdict.wrongAs": "Wrong: it is {{class}}",
|
|
9261
|
+
"button.verdict.wrongLabel": "Wrong label, class OK",
|
|
9292
9262
|
"action.server-update.title": "Server update",
|
|
9293
9263
|
"action.server-update.started": "Updating server on {{nodeId}}…",
|
|
9294
9264
|
"action.server-update.restarting": "Server update staged on {{nodeId}} — the node is restarting. You will hear when it comes back.",
|
|
@@ -9694,6 +9664,7 @@ var it_default = {
|
|
|
9694
9664
|
"button.verdict.correct": "Corretto",
|
|
9695
9665
|
"button.verdict.wrong": "Sbagliato",
|
|
9696
9666
|
"button.verdict.wrongAs": "Sbagliato: è {{class}}",
|
|
9667
|
+
"button.verdict.wrongLabel": "Etichetta sbagliata, classe giusta",
|
|
9697
9668
|
"action.server-update.title": "Aggiornamento server",
|
|
9698
9669
|
"action.server-update.started": "Aggiornamento server su {{nodeId}}…",
|
|
9699
9670
|
"action.server-update.restarting": "Aggiornamento server predisposto su {{nodeId}} — il nodo si riavvia. Ti avviseremo quando torna.",
|
|
@@ -39079,6 +39050,11 @@ function rowIsDurableRetrainCopy(data) {
|
|
|
39079
39050
|
* audit resolves ownership from INDEX rows, but a future filesystem-side tool
|
|
39080
39051
|
* must be able to tell the two corpora apart by path alone. */
|
|
39081
39052
|
var RETRAIN_PATH_PREFIX = "retrain";
|
|
39053
|
+
/** The stored verdict subject, validated; anything unreadable reads as absent. */
|
|
39054
|
+
function narrowVerdictSubject(value) {
|
|
39055
|
+
const parsed = require_clip_model_registry.RetrainVerdictSubjectSchema.safeParse(value);
|
|
39056
|
+
return parsed.success ? { verdictSubject: parsed.data } : {};
|
|
39057
|
+
}
|
|
39082
39058
|
/** Ids per `IN (…)` page in {@link RetrainFrameStore.getMany}. */
|
|
39083
39059
|
var GET_MANY_PAGE = 500;
|
|
39084
39060
|
/**
|
|
@@ -39162,7 +39138,8 @@ var RetrainFrameStore = class {
|
|
|
39162
39138
|
complete: data["complete"] === true || data["complete"] === 1,
|
|
39163
39139
|
...finiteNumber(data["epochMs"]) !== void 0 ? { epochMs: finiteNumber(data["epochMs"]) } : {},
|
|
39164
39140
|
revision: finiteNumber(data["revision"]) ?? 0,
|
|
39165
|
-
dismissed: narrowDismissed(data["dismissed"])
|
|
39141
|
+
dismissed: narrowDismissed(data["dismissed"]),
|
|
39142
|
+
...narrowVerdictSubject(data["verdictSubject"])
|
|
39166
39143
|
};
|
|
39167
39144
|
}
|
|
39168
39145
|
/** The copies with these ids, in the order given; unknown ids are skipped. */
|
|
@@ -39515,7 +39492,8 @@ var RetrainFrameStore = class {
|
|
|
39515
39492
|
capturedAt: input.epochMs,
|
|
39516
39493
|
...chromaSpread !== void 0 ? { chromaSpread } : {},
|
|
39517
39494
|
complete: false,
|
|
39518
|
-
epochMs: input.epochMs
|
|
39495
|
+
epochMs: input.epochMs,
|
|
39496
|
+
...input.verdictSubject !== void 0 ? { verdictSubject: input.verdictSubject } : {}
|
|
39519
39497
|
};
|
|
39520
39498
|
const path = framePath(frame);
|
|
39521
39499
|
const persist = async () => {
|
|
@@ -39539,7 +39517,8 @@ var RetrainFrameStore = class {
|
|
|
39539
39517
|
chromaSpread: frame.chromaSpread ?? null,
|
|
39540
39518
|
complete: false,
|
|
39541
39519
|
origin: "recording",
|
|
39542
|
-
epochMs: input.epochMs
|
|
39520
|
+
epochMs: input.epochMs,
|
|
39521
|
+
...input.verdictSubject !== void 0 ? { verdictSubject: input.verdictSubject } : {}
|
|
39543
39522
|
});
|
|
39544
39523
|
await this.ledger.transact([row, ...withRow]);
|
|
39545
39524
|
};
|
|
@@ -39576,6 +39555,22 @@ var RetrainFrameStore = class {
|
|
|
39576
39555
|
};
|
|
39577
39556
|
}
|
|
39578
39557
|
/**
|
|
39558
|
+
* Record the verdict subject on a still that already existed (the moment was
|
|
39559
|
+
* in the dataset before the operator tapped). The latest answer wins, like
|
|
39560
|
+
* the verdict ledger.
|
|
39561
|
+
*
|
|
39562
|
+
* @returns the updated frame, or `null` when no such copy exists.
|
|
39563
|
+
*/
|
|
39564
|
+
async setVerdictSubject(frameId, verdictSubject) {
|
|
39565
|
+
const existing = await this.get(frameId);
|
|
39566
|
+
if (existing === null) return null;
|
|
39567
|
+
await this.ledger.update(frameId, { verdictSubject });
|
|
39568
|
+
return {
|
|
39569
|
+
...existing,
|
|
39570
|
+
verdictSubject
|
|
39571
|
+
};
|
|
39572
|
+
}
|
|
39573
|
+
/**
|
|
39579
39574
|
* The operator's "every subject in this frame is boxed" (D785 context: an
|
|
39580
39575
|
* unboxed subject is a false positive to a detector evaluation). Only the
|
|
39581
39576
|
* operator sets it; nothing derives it from the annotation count, because a
|
|
@@ -54723,6 +54718,12 @@ var FrameState = class {
|
|
|
54723
54718
|
* per 5 minutes, not one per governor round (the wedge re-sights all
|
|
54724
54719
|
* night; ~300 identical lines would bury the log it exists to serve). */
|
|
54725
54720
|
packageOversizeLogAt = /* @__PURE__ */ new Map();
|
|
54721
|
+
/**
|
|
54722
|
+
* The newest frame the package path held per camera — the picture a parcel
|
|
54723
|
+
* promoted from a raw look is stored from (`package-still-capture.ts`). The
|
|
54724
|
+
* scene path concludes without a frame of its own, so it reads this.
|
|
54725
|
+
*/
|
|
54726
|
+
packageLookFrames = /* @__PURE__ */ new Map();
|
|
54726
54727
|
/** Reasons logged on the previous package look — episode helper for drops. */
|
|
54727
54728
|
packageLookDropReasons = /* @__PURE__ */ new Map();
|
|
54728
54729
|
};
|
|
@@ -59027,6 +59028,89 @@ function sweepOne(deps, deviceId) {
|
|
|
59027
59028
|
if (outcome.departed.length > 0 || outcome.departedWithoutActivity.length > 0 || outcome.expired.length > 0) deps.onOutcome(deviceId, outcome, deps.now);
|
|
59028
59029
|
}
|
|
59029
59030
|
//#endregion
|
|
59031
|
+
//#region src/pipeline-analytics/pipeline/package/package-still-capture.ts
|
|
59032
|
+
/** A held look frame older than this is not the parcel's picture any more. */
|
|
59033
|
+
var PACKAGE_LOOK_FRAME_MAX_AGE_MS = 15e3;
|
|
59034
|
+
async function jpegOf(reply) {
|
|
59035
|
+
if (reply.tier === "ram-fullframe") return "preview-only";
|
|
59036
|
+
if (reply.jpegBytes !== void 0 && reply.jpegBytes.byteLength > 0) return reply.jpegBytes;
|
|
59037
|
+
if (reply.bytes !== void 0 && reply.bytes.length === reply.width * reply.height * 3) return encodeRgbCropToJpeg(reply.bytes, reply.width, reply.height);
|
|
59038
|
+
return "empty-frame";
|
|
59039
|
+
}
|
|
59040
|
+
/**
|
|
59041
|
+
* The entry carrying its still's `keyFrameMediaId`, or the entry unchanged when
|
|
59042
|
+
* it already had one or none could be stored (said, with the reason).
|
|
59043
|
+
*/
|
|
59044
|
+
async function withParcelStill(input) {
|
|
59045
|
+
const { entry, logger } = input;
|
|
59046
|
+
if (entry.keyFrameMediaId !== void 0) return entry;
|
|
59047
|
+
const miss = (reason, detail) => {
|
|
59048
|
+
logger.warn("package still NOT stored — the delivery ships without a picture of the parcel", {
|
|
59049
|
+
tags: { deviceId: entry.deviceId },
|
|
59050
|
+
meta: {
|
|
59051
|
+
entryId: entry.id,
|
|
59052
|
+
reason,
|
|
59053
|
+
...detail !== void 0 ? { error: detail } : {}
|
|
59054
|
+
}
|
|
59055
|
+
});
|
|
59056
|
+
return entry;
|
|
59057
|
+
};
|
|
59058
|
+
if (input.handle === null) return miss("no-frame-handle");
|
|
59059
|
+
if (input.writer === null) return miss("no-media-store");
|
|
59060
|
+
try {
|
|
59061
|
+
const reply = await input.query({
|
|
59062
|
+
handle: input.handle,
|
|
59063
|
+
bbox: {
|
|
59064
|
+
x: 0,
|
|
59065
|
+
y: 0,
|
|
59066
|
+
w: entry.frameWidth,
|
|
59067
|
+
h: entry.frameHeight
|
|
59068
|
+
},
|
|
59069
|
+
acceptJpegBytes: true,
|
|
59070
|
+
encodeJpeg: true,
|
|
59071
|
+
pin: require_clip_model_registry.nodePin(input.handle.nodeId)
|
|
59072
|
+
});
|
|
59073
|
+
if (reply === null) return miss("native-miss");
|
|
59074
|
+
const jpeg = await jpegOf(reply);
|
|
59075
|
+
if (typeof jpeg === "string") return miss(jpeg);
|
|
59076
|
+
const keyFrameMediaId = await input.writer.putReplacing({
|
|
59077
|
+
deviceId: entry.deviceId,
|
|
59078
|
+
ownerKind: "track",
|
|
59079
|
+
ownerId: entry.id,
|
|
59080
|
+
kind: "keyFrame",
|
|
59081
|
+
timestamp: input.timestamp,
|
|
59082
|
+
data: Buffer.from(jpeg),
|
|
59083
|
+
subjectBox: {
|
|
59084
|
+
x: entry.bbox.x,
|
|
59085
|
+
y: entry.bbox.y,
|
|
59086
|
+
w: entry.bbox.w,
|
|
59087
|
+
h: entry.bbox.h,
|
|
59088
|
+
frameWidth: entry.frameWidth,
|
|
59089
|
+
frameHeight: entry.frameHeight
|
|
59090
|
+
}
|
|
59091
|
+
});
|
|
59092
|
+
return {
|
|
59093
|
+
...entry,
|
|
59094
|
+
keyFrameMediaId
|
|
59095
|
+
};
|
|
59096
|
+
} catch (err) {
|
|
59097
|
+
return miss("write-failed", require_clip_model_registry.errMsg(err));
|
|
59098
|
+
}
|
|
59099
|
+
}
|
|
59100
|
+
/**
|
|
59101
|
+
* One promotion, in the order that matters: the still is stored FIRST, then the
|
|
59102
|
+
* detector emits `delivered` — the notification centre reads the still when the
|
|
59103
|
+
* event arrives, so a picture written after it is a picture nobody sees.
|
|
59104
|
+
*/
|
|
59105
|
+
async function promoteWithStill(input) {
|
|
59106
|
+
const entry = await withParcelStill(input);
|
|
59107
|
+
await input.detector.onStationaryChange({
|
|
59108
|
+
phase: "appeared",
|
|
59109
|
+
entry,
|
|
59110
|
+
timestamp: input.entry.becameStationaryAt
|
|
59111
|
+
});
|
|
59112
|
+
}
|
|
59113
|
+
//#endregion
|
|
59030
59114
|
//#region src/pipeline-analytics/pipeline/person-over-vehicle.ts
|
|
59031
59115
|
/**
|
|
59032
59116
|
* How much of the PERSON box must sit inside a vehicle box to be counted.
|
|
@@ -68426,6 +68510,7 @@ function wireSubscriptions(deps) {
|
|
|
68426
68510
|
deps.onClusterTopology(nodes);
|
|
68427
68511
|
}));
|
|
68428
68512
|
for (const category of NC_SYSTEM_EVENT_CATEGORIES) unsubs.push(deps.eventBus.subscribe({ category }, (ev) => deps.onSystemEvent(ev)));
|
|
68513
|
+
for (const category of require_clip_model_registry.ACTION_TARGET_INVALIDATION_EVENTS) unsubs.push(deps.eventBus.subscribe({ category }, () => deps.onDeviceRegistryChange()));
|
|
68429
68514
|
}
|
|
68430
68515
|
unsubs.push(deps.eventBus.subscribe({ category: require_clip_model_registry.EventCategory.DeviceBindingsChanged }, (ev) => {
|
|
68431
68516
|
deps.onBindingsChanged(ev.data);
|
|
@@ -68987,6 +69072,12 @@ async function composeEventKindsForDevices(deviceIds, composeOne, onError) {
|
|
|
68987
69072
|
* nearest one is normally the same detection; the delta is reported so a
|
|
68988
69073
|
* reviewer can see how far it was.
|
|
68989
69074
|
*
|
|
69075
|
+
* 0. **The subject a notification verdict froze** (a recording still landed by
|
|
69076
|
+
* a verdict whose track was already gone). It is the one subject the
|
|
69077
|
+
* operator answered about, so it comes FIRST and is the own box; the
|
|
69078
|
+
* neighbours below stay secondary. Its box is normalised against the full
|
|
69079
|
+
* frame already, so it is used as is — never re-scaled.
|
|
69080
|
+
*
|
|
68990
69081
|
* Every OTHER track alive on the camera at that instant contributes its own
|
|
68991
69082
|
* nearest position the same way — a frame with three people is three boxes,
|
|
68992
69083
|
* and a recording still (no track of its own) gets everything the pipeline
|
|
@@ -69041,6 +69132,18 @@ function toRetrainClassTiers(className, label) {
|
|
|
69041
69132
|
label: kept
|
|
69042
69133
|
} : { macroClass };
|
|
69043
69134
|
}
|
|
69135
|
+
/** A verdict on the wire: the operator's class split into tiers once, here. */
|
|
69136
|
+
function toRetrainVerdict(input) {
|
|
69137
|
+
const tiers = input.suggestedClass !== void 0 ? toRetrainClassTiers(input.suggestedClass, void 0) : null;
|
|
69138
|
+
return {
|
|
69139
|
+
verdict: input.verdict,
|
|
69140
|
+
trackId: input.trackId,
|
|
69141
|
+
...input.suggestedClass !== void 0 ? { suggestedClass: input.suggestedClass } : {},
|
|
69142
|
+
...tiers !== null ? { suggestedMacroClass: tiers.macroClass } : {},
|
|
69143
|
+
...tiers?.label !== void 0 ? { suggestedLabel: tiers.label } : {},
|
|
69144
|
+
...input.at !== void 0 ? { at: input.at } : {}
|
|
69145
|
+
};
|
|
69146
|
+
}
|
|
69044
69147
|
/** A pixel box as a clamped normalised one, or `null` when nothing is left. */
|
|
69045
69148
|
function normalise(box, frameWidth, frameHeight) {
|
|
69046
69149
|
if (frameWidth === void 0 || frameHeight === void 0) return null;
|
|
@@ -69098,6 +69201,22 @@ function boxFor(track, bbox, basis, deltaMs, score) {
|
|
|
69098
69201
|
deltaMs
|
|
69099
69202
|
};
|
|
69100
69203
|
}
|
|
69204
|
+
/** The box a notification verdict froze, or `null` when the event carried none. */
|
|
69205
|
+
function boxOfVerdict(subject) {
|
|
69206
|
+
const box = subject.bbox;
|
|
69207
|
+
if (box === void 0) return null;
|
|
69208
|
+
const clamped = normalise(box, 1, 1);
|
|
69209
|
+
if (clamped === null) return null;
|
|
69210
|
+
const tiers = toRetrainClassTiers(subject.className, subject.label);
|
|
69211
|
+
return {
|
|
69212
|
+
bbox: clamped,
|
|
69213
|
+
macroClass: tiers.macroClass,
|
|
69214
|
+
...tiers.label !== void 0 ? { label: tiers.label } : {},
|
|
69215
|
+
trackId: subject.trackId,
|
|
69216
|
+
basis: "verdict",
|
|
69217
|
+
deltaMs: 0
|
|
69218
|
+
};
|
|
69219
|
+
}
|
|
69101
69220
|
/** The own track's box: the recorded subject box first, the nearest position second. */
|
|
69102
69221
|
function ownBox(input, tolerance) {
|
|
69103
69222
|
const track = input.ownTrack;
|
|
@@ -69120,10 +69239,13 @@ function pipelineBoxesForFrame(input) {
|
|
|
69120
69239
|
const out = [];
|
|
69121
69240
|
const own = ownBox(input, tolerance);
|
|
69122
69241
|
if (own !== null) out.push(own);
|
|
69242
|
+
const verdictBox = input.verdictSubject !== void 0 ? boxOfVerdict(input.verdictSubject) : null;
|
|
69243
|
+
if (verdictBox !== null) out.push(verdictBox);
|
|
69123
69244
|
const at = input.frame.capturedAt;
|
|
69124
69245
|
if (at === void 0) return out;
|
|
69125
69246
|
const ownId = input.frame.trackId;
|
|
69126
69247
|
const seen = new Set(ownId !== void 0 ? [ownId] : []);
|
|
69248
|
+
if (input.verdictSubject !== void 0) seen.add(input.verdictSubject.trackId);
|
|
69127
69249
|
for (const track of input.neighbours) {
|
|
69128
69250
|
if (seen.has(track.trackId) || track.deviceId !== input.frame.deviceId) continue;
|
|
69129
69251
|
seen.add(track.trackId);
|
|
@@ -70516,7 +70638,12 @@ Object.freeze({
|
|
|
70516
70638
|
"listSupportedEntityKinds"
|
|
70517
70639
|
],
|
|
70518
70640
|
"device-extension": ["listSlots", "listSources"],
|
|
70519
|
-
"device-provider": [
|
|
70641
|
+
"device-provider": [
|
|
70642
|
+
"classifyDiscoveryHits",
|
|
70643
|
+
"discoverDevices",
|
|
70644
|
+
"getDevices",
|
|
70645
|
+
"getDiscoveryModes"
|
|
70646
|
+
],
|
|
70520
70647
|
"device-removal-hook": ["listDeviceData"],
|
|
70521
70648
|
"failure-contribution": ["list"],
|
|
70522
70649
|
"llm": [
|
|
@@ -82368,10 +82495,18 @@ function parseActions(raw) {
|
|
|
82368
82495
|
for (const entry of raw) {
|
|
82369
82496
|
const parsed = require_clip_model_registry.NcRuleActionSchema.safeParse(entry);
|
|
82370
82497
|
if (!parsed.success) return null;
|
|
82371
|
-
out.push(parsed.data);
|
|
82498
|
+
out.push(require_clip_model_registry.normalizeNcAction(parsed.data));
|
|
82372
82499
|
}
|
|
82373
82500
|
return out;
|
|
82374
82501
|
}
|
|
82502
|
+
/** Does a stored row still carry a pre-D983 cap step (`deviceId`, no `target`)? */
|
|
82503
|
+
function hasLegacyTarget(raw) {
|
|
82504
|
+
if (!Array.isArray(raw)) return false;
|
|
82505
|
+
return raw.some((entry) => {
|
|
82506
|
+
const parsed = require_clip_model_registry.NcRuleActionSchema.safeParse(entry);
|
|
82507
|
+
return parsed.success && parsed.data.kind === "cap" && parsed.data.target === void 0;
|
|
82508
|
+
});
|
|
82509
|
+
}
|
|
82375
82510
|
function rowToSequence(id, body) {
|
|
82376
82511
|
const actions = parseActions(body.actions);
|
|
82377
82512
|
if (actions === null) return null;
|
|
@@ -82410,6 +82545,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82410
82545
|
try {
|
|
82411
82546
|
const { records } = await this.kind.query({ limit: 1e4 });
|
|
82412
82547
|
const next = /* @__PURE__ */ new Map();
|
|
82548
|
+
const legacyRows = [];
|
|
82413
82549
|
let skipped = 0;
|
|
82414
82550
|
for (const row of records) {
|
|
82415
82551
|
const sequence = rowToSequence(row.id, row.data);
|
|
@@ -82418,9 +82554,11 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82418
82554
|
continue;
|
|
82419
82555
|
}
|
|
82420
82556
|
next.set(sequence.id, sequence);
|
|
82557
|
+
if (hasLegacyTarget(row.data.actions)) legacyRows.push(row.id);
|
|
82421
82558
|
}
|
|
82422
82559
|
this.byId.clear();
|
|
82423
82560
|
for (const [id, seq] of next) this.byId.set(id, seq);
|
|
82561
|
+
await this.rewriteLegacyRows(legacyRows, records);
|
|
82424
82562
|
if (skipped > 0) this.logger.warn("sequence rows skipped as malformed — bindings to them will miss", { meta: { skipped } });
|
|
82425
82563
|
} catch (err) {
|
|
82426
82564
|
this.logger.warn("sequence load failed — keeping the sequences already in memory", { meta: {
|
|
@@ -82429,6 +82567,28 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82429
82567
|
} });
|
|
82430
82568
|
}
|
|
82431
82569
|
}
|
|
82570
|
+
/**
|
|
82571
|
+
* One-shot, idempotent (D983): a row whose steps still carry `deviceId` is
|
|
82572
|
+
* stored again in the target shape. A failed write leaves the row as it was —
|
|
82573
|
+
* it is still READ as the new shape — and is on the record.
|
|
82574
|
+
*/
|
|
82575
|
+
async rewriteLegacyRows(ids, records) {
|
|
82576
|
+
for (const id of ids) {
|
|
82577
|
+
const sequence = this.byId.get(id) ?? null;
|
|
82578
|
+
const row = records.find((r) => r.id === id);
|
|
82579
|
+
if (sequence === null || row === void 0) continue;
|
|
82580
|
+
try {
|
|
82581
|
+
const createdAt = row.data.createdAt;
|
|
82582
|
+
await this.persist(sequence, typeof createdAt === "number" && Number.isFinite(createdAt) ? createdAt : this.now(), this.now());
|
|
82583
|
+
this.logger.info("notification sequence rewritten to the target shape", { meta: { sequenceId: id } });
|
|
82584
|
+
} catch (err) {
|
|
82585
|
+
this.logger.warn("notification sequence could not be rewritten — it is still read as the target shape", { meta: {
|
|
82586
|
+
sequenceId: id,
|
|
82587
|
+
error: String(err)
|
|
82588
|
+
} });
|
|
82589
|
+
}
|
|
82590
|
+
}
|
|
82591
|
+
}
|
|
82432
82592
|
list() {
|
|
82433
82593
|
return [...this.byId.values()].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
82434
82594
|
}
|
|
@@ -82448,7 +82608,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82448
82608
|
id: (0, node_crypto.randomUUID)(),
|
|
82449
82609
|
name: input.name,
|
|
82450
82610
|
...input.minDelaySec !== void 0 ? { minDelaySec: input.minDelaySec } : {},
|
|
82451
|
-
actions:
|
|
82611
|
+
actions: input.actions.map(require_clip_model_registry.normalizeNcAction)
|
|
82452
82612
|
};
|
|
82453
82613
|
await this.persist(sequence, now, now);
|
|
82454
82614
|
this.byId.set(sequence.id, sequence);
|
|
@@ -82464,7 +82624,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82464
82624
|
id: sequence.id,
|
|
82465
82625
|
name: sequence.name,
|
|
82466
82626
|
...sequence.minDelaySec !== void 0 ? { minDelaySec: sequence.minDelaySec } : {},
|
|
82467
|
-
actions:
|
|
82627
|
+
actions: sequence.actions.map(require_clip_model_registry.normalizeNcAction)
|
|
82468
82628
|
};
|
|
82469
82629
|
await this.persist(stored, now, now);
|
|
82470
82630
|
this.byId.set(stored.id, stored);
|
|
@@ -82477,7 +82637,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82477
82637
|
id: sequenceId,
|
|
82478
82638
|
name: input.name,
|
|
82479
82639
|
...input.minDelaySec !== void 0 ? { minDelaySec: input.minDelaySec } : {},
|
|
82480
|
-
actions:
|
|
82640
|
+
actions: input.actions.map(require_clip_model_registry.normalizeNcAction)
|
|
82481
82641
|
};
|
|
82482
82642
|
const createdAt = await this.readCreatedAt(sequenceId, now);
|
|
82483
82643
|
await this.persist(sequence, createdAt, now);
|
|
@@ -82545,7 +82705,11 @@ var NC_DETECTION_VERDICTS_COLUMNS = [
|
|
|
82545
82705
|
var NcDetectionVerdictRecordSchema = require_clip_model_registry.object({
|
|
82546
82706
|
trackId: require_clip_model_registry.string().min(1),
|
|
82547
82707
|
deviceId: require_clip_model_registry.number().int(),
|
|
82548
|
-
verdict: require_clip_model_registry._enum([
|
|
82708
|
+
verdict: require_clip_model_registry._enum([
|
|
82709
|
+
"correct",
|
|
82710
|
+
"wrong",
|
|
82711
|
+
"wrong-label"
|
|
82712
|
+
]),
|
|
82549
82713
|
/** The class the operator said it really was. Only with `wrong`. */
|
|
82550
82714
|
suggestedClass: require_clip_model_registry.string().min(1).optional(),
|
|
82551
82715
|
/** Whose notification it was (the token's frozen owner), NOT who tapped. */
|
|
@@ -82580,6 +82744,16 @@ var NcDetectionVerdictLedger = class {
|
|
|
82580
82744
|
logger: deps.logger
|
|
82581
82745
|
});
|
|
82582
82746
|
}
|
|
82747
|
+
/** The answers recorded for these tracks (a track nobody answered is absent).
|
|
82748
|
+
* Reads the in-memory mirror: no I/O, so a listing never waits on it. */
|
|
82749
|
+
forTracks(trackIds) {
|
|
82750
|
+
const out = /* @__PURE__ */ new Map();
|
|
82751
|
+
for (const id of trackIds) {
|
|
82752
|
+
const row = this.ledger.get(id);
|
|
82753
|
+
if (row !== void 0) out.set(id, row);
|
|
82754
|
+
}
|
|
82755
|
+
return out;
|
|
82756
|
+
}
|
|
82583
82757
|
/** Write-through; throws on a failed write so the tap reports it. */
|
|
82584
82758
|
async record(row) {
|
|
82585
82759
|
await this.ledger.put(NcDetectionVerdictRecordSchema.parse(row));
|
|
@@ -82713,10 +82887,33 @@ function pruneHookBindings(bindings, field, isDead, notes) {
|
|
|
82713
82887
|
kept.push(binding);
|
|
82714
82888
|
continue;
|
|
82715
82889
|
}
|
|
82716
|
-
if (binding.action.kind === "cap"
|
|
82717
|
-
|
|
82718
|
-
|
|
82719
|
-
|
|
82890
|
+
if (binding.action.kind === "cap") {
|
|
82891
|
+
const { target } = require_clip_model_registry.normalizeNcCapTarget(binding.action);
|
|
82892
|
+
if (target.kind === "devices") {
|
|
82893
|
+
const dead = target.ids.filter(isDead);
|
|
82894
|
+
if (dead.length === target.ids.length) {
|
|
82895
|
+
changed = true;
|
|
82896
|
+
notes.removed(field, deviceSubjects(dead), "binding-removed");
|
|
82897
|
+
continue;
|
|
82898
|
+
}
|
|
82899
|
+
if (dead.length > 0) {
|
|
82900
|
+
changed = true;
|
|
82901
|
+
notes.removed(field, deviceSubjects(dead), "binding-removed");
|
|
82902
|
+
const alive = target.ids.filter((id) => !isDead(id));
|
|
82903
|
+
kept.push({
|
|
82904
|
+
...binding,
|
|
82905
|
+
action: require_clip_model_registry.normalizeNcAction({
|
|
82906
|
+
...binding.action,
|
|
82907
|
+
deviceId: void 0,
|
|
82908
|
+
target: {
|
|
82909
|
+
kind: "devices",
|
|
82910
|
+
ids: alive
|
|
82911
|
+
}
|
|
82912
|
+
})
|
|
82913
|
+
});
|
|
82914
|
+
continue;
|
|
82915
|
+
}
|
|
82916
|
+
}
|
|
82720
82917
|
}
|
|
82721
82918
|
kept.push(binding);
|
|
82722
82919
|
}
|
|
@@ -82872,7 +83069,11 @@ function referencedDeviceIds(snapshot) {
|
|
|
82872
83069
|
add(c.systemEvent?.deviceIds);
|
|
82873
83070
|
if (c.deviceState !== void 0) ids.add(c.deviceState.deviceId);
|
|
82874
83071
|
if (c.scene?.deviceId !== void 0) ids.add(c.scene.deviceId);
|
|
82875
|
-
for (const binding of [...rule.actions?.onTrigger ?? [], ...rule.actions?.onTriggerOff ?? []])
|
|
83072
|
+
for (const binding of [...rule.actions?.onTrigger ?? [], ...rule.actions?.onTriggerOff ?? []]) {
|
|
83073
|
+
if (binding.kind !== "do" || binding.action.kind !== "cap") continue;
|
|
83074
|
+
const { target } = require_clip_model_registry.normalizeNcCapTarget(binding.action);
|
|
83075
|
+
if (target.kind === "devices") add(target.ids);
|
|
83076
|
+
}
|
|
82876
83077
|
}
|
|
82877
83078
|
for (const rule of snapshot.timelapse) add(rule.deviceIds);
|
|
82878
83079
|
for (const rule of snapshot.summary) add(rule.deviceIds);
|
|
@@ -83543,6 +83744,8 @@ var NcDeviceStateCache = class {
|
|
|
83543
83744
|
//#region src/notification-center/detection-verdict-buttons.ts
|
|
83544
83745
|
var VERDICT_CORRECT_ID = "detection-verdict-correct";
|
|
83545
83746
|
var VERDICT_WRONG_ID = "detection-verdict-wrong";
|
|
83747
|
+
/** Wrong LABEL, right class — only for a subject that has a label. */
|
|
83748
|
+
var VERDICT_WRONG_LABEL_ID = "detection-verdict-wrong-label";
|
|
83546
83749
|
/** `detection-verdict-wrong:<class>` — Wrong, and what it really was. */
|
|
83547
83750
|
var VERDICT_WRONG_AS_PREFIX = "detection-verdict-wrong:";
|
|
83548
83751
|
/** The macro classes offered as "it was really…". Short on purpose. */
|
|
@@ -83559,6 +83762,15 @@ function verdictButtonRefusal(subject) {
|
|
|
83559
83762
|
if (subject.ownerUserId === void 0) return "unowned-target";
|
|
83560
83763
|
return null;
|
|
83561
83764
|
}
|
|
83765
|
+
/**
|
|
83766
|
+
* Does the notified subject carry a label below its macro class — a sub-class
|
|
83767
|
+
* (`dog` under `animal`), a recognised name, a plate? The same tier split the
|
|
83768
|
+
* retrain annotations use, so "has a label" means one thing in both places.
|
|
83769
|
+
*/
|
|
83770
|
+
function subjectHasLabel(className, label) {
|
|
83771
|
+
if (className === void 0) return false;
|
|
83772
|
+
return toRetrainClassTiers(className, label).label !== void 0;
|
|
83773
|
+
}
|
|
83562
83774
|
/** The classes worth offering for a subject of `className`. */
|
|
83563
83775
|
function verdictClassChoices(className) {
|
|
83564
83776
|
return VERDICT_CLASS_CHOICES.filter((c) => c !== className).slice(0, VERDICT_MAX_CLASS_CHOICES);
|
|
@@ -83570,13 +83782,17 @@ function verdictClassChoices(className) {
|
|
|
83570
83782
|
* when the channel declared room for a fourth button.
|
|
83571
83783
|
*/
|
|
83572
83784
|
function verdictButtonSpecs(input) {
|
|
83573
|
-
const
|
|
83785
|
+
const plain = [{
|
|
83574
83786
|
actionId: VERDICT_CORRECT_ID,
|
|
83575
83787
|
verdict: "correct"
|
|
83576
83788
|
}, {
|
|
83577
83789
|
actionId: VERDICT_WRONG_ID,
|
|
83578
83790
|
verdict: "wrong"
|
|
83579
83791
|
}];
|
|
83792
|
+
const base = subjectHasLabel(input.className, input.label) ? [...plain, {
|
|
83793
|
+
actionId: VERDICT_WRONG_LABEL_ID,
|
|
83794
|
+
verdict: "wrong-label"
|
|
83795
|
+
}] : plain;
|
|
83580
83796
|
if (input.maxActions === void 0 || input.maxActions <= 3) return base;
|
|
83581
83797
|
const room = input.maxActions - base.length;
|
|
83582
83798
|
const extra = verdictClassChoices(input.className).slice(0, room).map((suggestedClass) => ({
|
|
@@ -87307,6 +87523,8 @@ var NcDispatcher = class {
|
|
|
87307
87523
|
...target.ownerUserId !== void 0 ? { ownerUserId: target.ownerUserId } : {},
|
|
87308
87524
|
...entry.payload.subject.className !== void 0 ? { className: entry.payload.subject.className } : {},
|
|
87309
87525
|
...entry.payload.subject.trackId !== void 0 ? { trackId: entry.payload.subject.trackId } : {},
|
|
87526
|
+
...entry.payload.subject.label !== void 0 ? { label: entry.payload.subject.label } : {},
|
|
87527
|
+
...entry.payload.subject.bbox !== void 0 ? { bbox: entry.payload.subject.bbox } : {},
|
|
87310
87528
|
eventAtMs: entry.payload.subject.timestamp,
|
|
87311
87529
|
...target.maxActions !== void 0 ? { maxActions: target.maxActions } : {},
|
|
87312
87530
|
...entry.payload.subject.systemEvent !== void 0 ? { systemEvent: entry.payload.subject.systemEvent } : {}
|
|
@@ -91670,7 +91888,7 @@ var NcRuleActionRunner = class {
|
|
|
91670
91888
|
if (sequence.throttle) this.lastRunAt.set(sequence.id, now);
|
|
91671
91889
|
let stepsRun = 0;
|
|
91672
91890
|
for (const [index, action] of sequence.actions.entries()) try {
|
|
91673
|
-
await this.step(action, input.origin);
|
|
91891
|
+
await this.step(action, input.origin, sequence.id);
|
|
91674
91892
|
stepsRun += 1;
|
|
91675
91893
|
} catch (err) {
|
|
91676
91894
|
this.deps.logger.warn("rule action failed — stopping the sequence", {
|
|
@@ -91710,20 +91928,28 @@ var NcRuleActionRunner = class {
|
|
|
91710
91928
|
stepsRun
|
|
91711
91929
|
};
|
|
91712
91930
|
}
|
|
91713
|
-
async step(action, origin) {
|
|
91931
|
+
async step(action, origin, owner) {
|
|
91714
91932
|
if (action.kind === "wait") {
|
|
91715
91933
|
await this.sleep(action.seconds * 1e3);
|
|
91716
91934
|
return;
|
|
91717
91935
|
}
|
|
91718
91936
|
if (!this.deps.isDeviceScoped(action.cap)) throw new Error(`rule action refused: "${action.cap}" is not a device capability — only devices can be actuated`);
|
|
91719
|
-
|
|
91720
|
-
|
|
91937
|
+
const { target, actionId } = require_clip_model_registry.normalizeNcCapTarget(action);
|
|
91938
|
+
const deviceIds = await this.devicesOf(target, actionId, owner);
|
|
91939
|
+
for (const deviceId of deviceIds) await this.deps.actuate({
|
|
91940
|
+
deviceId,
|
|
91721
91941
|
cap: action.cap,
|
|
91722
91942
|
method: action.method,
|
|
91723
91943
|
args: action.args ?? {},
|
|
91724
91944
|
...origin !== void 0 ? { origin } : {}
|
|
91725
91945
|
});
|
|
91726
91946
|
}
|
|
91947
|
+
async devicesOf(target, actionId, owner) {
|
|
91948
|
+
if (target.kind === "devices") return target.ids;
|
|
91949
|
+
if (target.kind === "var") throw new Error("rule action refused: a sequence has no variables to resolve a target from");
|
|
91950
|
+
if (this.deps.resolveTarget === void 0) throw new Error("rule action refused: this node cannot resolve a domain target");
|
|
91951
|
+
return (await this.deps.resolveTarget(target, actionId, owner)).deviceIds;
|
|
91952
|
+
}
|
|
91727
91953
|
};
|
|
91728
91954
|
/** Force-enable a shared sequence for a button tap (always runs, still throttled). */
|
|
91729
91955
|
function asForcedSequence(sequence) {
|
|
@@ -97314,6 +97540,15 @@ var NcSystemStateLedger = class {
|
|
|
97314
97540
|
closeLegacyStreamKeys() {
|
|
97315
97541
|
this.closeWhere("legacy-stream-keys", (row) => row.key.startsWith("stream:") && !DEVICE_STREAM_KEY.test(row.key), "legacy-stream-key");
|
|
97316
97542
|
}
|
|
97543
|
+
/**
|
|
97544
|
+
* Is this device switched off RIGHT NOW (an open `device-disabled` row)?
|
|
97545
|
+
* D62: the switch outranks the device's liveness and its streams — a device
|
|
97546
|
+
* somebody turned off is reported off, never as offline. `false` while the
|
|
97547
|
+
* store has never answered (unknown is never "disabled").
|
|
97548
|
+
*/
|
|
97549
|
+
isDeviceDisabled(deviceId) {
|
|
97550
|
+
return (this.openRows() ?? []).some((row) => row.kind === "device-disabled" && row.deviceId === deviceId);
|
|
97551
|
+
}
|
|
97317
97552
|
/** The open rows, or `null` while the store has never answered. */
|
|
97318
97553
|
openRows() {
|
|
97319
97554
|
return this.deps.rows.answered() ? this.deps.rows.all() : null;
|
|
@@ -97820,6 +98055,7 @@ function readRaw(section, ports, nowMs) {
|
|
|
97820
98055
|
reason: UNREAD_STATE
|
|
97821
98056
|
};
|
|
97822
98057
|
const items = [];
|
|
98058
|
+
const switchedOff = new Set(state.flatMap((row) => row.kind === "device-disabled" && row.deviceId !== void 0 ? [row.deviceId] : []));
|
|
97823
98059
|
if (section === "device-state" || section === "nodes") {
|
|
97824
98060
|
const liveness = ports.liveness();
|
|
97825
98061
|
if (liveness === null) return {
|
|
@@ -97829,10 +98065,14 @@ function readRaw(section, ports, nowMs) {
|
|
|
97829
98065
|
for (const row of liveness) {
|
|
97830
98066
|
if (row.state !== "offline" || nowMs - row.updatedAt < ports.livenessHoldoffMs) continue;
|
|
97831
98067
|
const item = livenessItem(row, ports.lookup);
|
|
97832
|
-
if (item
|
|
98068
|
+
if (item === null || item.section !== section) continue;
|
|
98069
|
+
const id = item.subject.deviceId;
|
|
98070
|
+
if (id !== void 0 && switchedOff.has(id)) continue;
|
|
98071
|
+
items.push(item);
|
|
97833
98072
|
}
|
|
97834
98073
|
}
|
|
97835
98074
|
for (const row of state) {
|
|
98075
|
+
if (row.kind === "stream-offline" && row.deviceId !== void 0 && switchedOff.has(row.deviceId)) continue;
|
|
97836
98076
|
const item = stateItem(row, ports.lookup, ports.nodes);
|
|
97837
98077
|
if (item.section === section) items.push(item);
|
|
97838
98078
|
}
|
|
@@ -100097,6 +100337,12 @@ function rowRefusalId(systemEvent) {
|
|
|
100097
100337
|
const value = systemEvent?.data["refusalId"];
|
|
100098
100338
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
100099
100339
|
}
|
|
100340
|
+
/** The text-catalog key of each verdict button. */
|
|
100341
|
+
var VERDICT_LABEL_KEY = {
|
|
100342
|
+
correct: "button.verdict.correct",
|
|
100343
|
+
wrong: "button.verdict.wrong",
|
|
100344
|
+
"wrong-label": "button.verdict.wrongLabel"
|
|
100345
|
+
};
|
|
100100
100346
|
/**
|
|
100101
100347
|
* The tap-through buttons for ONE delivery, each with its own single-use
|
|
100102
100348
|
* token.
|
|
@@ -100133,6 +100379,7 @@ async function mintButtons(host, input) {
|
|
|
100133
100379
|
const verdictOwner = input.ownerUserId;
|
|
100134
100380
|
const verdictButtons = verdictRefusal !== null || verdictTrackId === void 0 || verdictOwner === void 0 ? [] : verdictButtonSpecs({
|
|
100135
100381
|
className: input.className,
|
|
100382
|
+
label: input.label,
|
|
100136
100383
|
maxActions: input.maxActions
|
|
100137
100384
|
}).map((spec) => {
|
|
100138
100385
|
const id = (0, node_crypto.randomUUID)();
|
|
@@ -100150,6 +100397,11 @@ async function mintButtons(host, input) {
|
|
|
100150
100397
|
...spec.suggestedClass !== void 0 ? { suggestedClass: spec.suggestedClass } : {},
|
|
100151
100398
|
...input.maxActions === 0 ? { viaLink: true } : {},
|
|
100152
100399
|
...input.eventAtMs !== void 0 ? { eventAtMs: input.eventAtMs } : {},
|
|
100400
|
+
...input.className !== void 0 ? { subject: {
|
|
100401
|
+
className: input.className,
|
|
100402
|
+
...input.label !== void 0 ? { label: input.label } : {},
|
|
100403
|
+
...input.bbox !== void 0 ? { bbox: input.bbox } : {}
|
|
100404
|
+
} } : {},
|
|
100153
100405
|
deviceId: input.deviceId,
|
|
100154
100406
|
actionId: spec.actionId,
|
|
100155
100407
|
targetId: input.targetId,
|
|
@@ -100158,7 +100410,7 @@ async function mintButtons(host, input) {
|
|
|
100158
100410
|
});
|
|
100159
100411
|
return {
|
|
100160
100412
|
id: spec.actionId,
|
|
100161
|
-
label: spec.suggestedClass === void 0 ? host.texts.text({ key: spec.verdict
|
|
100413
|
+
label: spec.suggestedClass === void 0 ? host.texts.text({ key: VERDICT_LABEL_KEY[spec.verdict] }) : host.texts.text({
|
|
100162
100414
|
key: "button.verdict.wrongAs",
|
|
100163
100415
|
vars: { class: spec.suggestedClass }
|
|
100164
100416
|
}),
|
|
@@ -100617,6 +100869,18 @@ var VERDICT_FRAME_REFUSAL_COPY = {
|
|
|
100617
100869
|
"still-busy": "The track is gone and the recorder is busy — nothing was changed, try again later",
|
|
100618
100870
|
failed: "The track is gone and its frame could not be saved — nothing was changed"
|
|
100619
100871
|
};
|
|
100872
|
+
/** The grant's frozen subject, in the shape the retrain still stores it in. */
|
|
100873
|
+
function verdictSubjectOf(grant, subject, at) {
|
|
100874
|
+
return {
|
|
100875
|
+
trackId: grant.trackId,
|
|
100876
|
+
className: subject.className,
|
|
100877
|
+
...subject.label !== void 0 ? { label: subject.label } : {},
|
|
100878
|
+
...subject.bbox !== void 0 ? { bbox: subject.bbox } : {},
|
|
100879
|
+
verdict: grant.verdict,
|
|
100880
|
+
...grant.suggestedClass !== void 0 ? { suggestedClass: grant.suggestedClass } : {},
|
|
100881
|
+
at
|
|
100882
|
+
};
|
|
100883
|
+
}
|
|
100620
100884
|
/**
|
|
100621
100885
|
* The track is gone (aged out, or discarded as a still object): the camera's
|
|
100622
100886
|
* RECORDING still covers the instant, so the verdict lands a frame there
|
|
@@ -100639,7 +100903,8 @@ async function landFrameFromRecording(host, grant) {
|
|
|
100639
100903
|
}
|
|
100640
100904
|
const landed = await addFrame({
|
|
100641
100905
|
deviceId: grant.deviceId,
|
|
100642
|
-
epochMs: grant.eventAtMs
|
|
100906
|
+
epochMs: grant.eventAtMs,
|
|
100907
|
+
...grant.subject !== void 0 ? { subject: verdictSubjectOf(grant, grant.subject, host.now()) } : {}
|
|
100643
100908
|
});
|
|
100644
100909
|
if (!landed.ok) {
|
|
100645
100910
|
host.logger.warn("detection verdict dropped — the track is gone and the recording refused", {
|
|
@@ -101600,6 +101865,7 @@ function buildEntries(host, rule, subject, kind, matchedOn, userTargets = [], or
|
|
|
101600
101865
|
...alarmSource?.eventId !== void 0 ? { eventId: alarmSource.eventId } : hasEventMedia ? { eventId: subject.recordId } : {},
|
|
101601
101866
|
className: subject.classNames[0] ?? subject.sensorKind ?? "event",
|
|
101602
101867
|
...subject.label !== void 0 ? { label: subject.label } : {},
|
|
101868
|
+
...subject.bbox !== void 0 ? { bbox: subject.bbox } : {},
|
|
101603
101869
|
...subject.labelKind !== void 0 ? { labelKind: subject.labelKind } : {},
|
|
101604
101870
|
...subject.confidence !== void 0 ? { confidence: subject.confidence } : {},
|
|
101605
101871
|
zones: renderedZones,
|
|
@@ -101997,16 +102263,37 @@ function onSystemEvent(host, event) {
|
|
|
101997
102263
|
return;
|
|
101998
102264
|
}
|
|
101999
102265
|
if (isDeviceLivenessKind(observation.kind)) {
|
|
102266
|
+
if (dropSwitchedOff(host, system)) return;
|
|
102000
102267
|
holdoff.arm(observation.subject, incoming);
|
|
102001
102268
|
return;
|
|
102002
102269
|
}
|
|
102003
102270
|
} else if (system !== void 0 && (system.kind.startsWith("device-") || system.kind.startsWith("stream-"))) {
|
|
102004
102271
|
if (host.lastSystemLiveness.get(system.subject) === system.kind) return;
|
|
102005
102272
|
host.lastSystemLiveness.set(system.subject, system.kind);
|
|
102273
|
+
if (dropSwitchedOff(host, system)) return;
|
|
102006
102274
|
}
|
|
102007
102275
|
host.consumeEvent(incoming);
|
|
102008
102276
|
}
|
|
102009
102277
|
/**
|
|
102278
|
+
* D62: a device the operator switched off is reported off, never offline. Its
|
|
102279
|
+
* `device-offline` / `stream-offline` is dropped (and said) while an open
|
|
102280
|
+
* `device-disabled` row exists — the broker of a disabled device stopping is
|
|
102281
|
+
* the switch working, not an outage. The memory of the flip is already kept.
|
|
102282
|
+
*/
|
|
102283
|
+
function dropSwitchedOff(host, system) {
|
|
102284
|
+
if (system === void 0 || system.deviceId === void 0) return false;
|
|
102285
|
+
if (system.kind !== "device-offline" && system.kind !== "stream-offline") return false;
|
|
102286
|
+
if (!host.stateSummary.ledger.isDeviceDisabled(system.deviceId)) return false;
|
|
102287
|
+
host.logger.debug("offline not notified — the device is switched off (D62)", {
|
|
102288
|
+
tags: { deviceId: system.deviceId },
|
|
102289
|
+
meta: {
|
|
102290
|
+
kind: system.kind,
|
|
102291
|
+
subject: system.subject
|
|
102292
|
+
}
|
|
102293
|
+
});
|
|
102294
|
+
return true;
|
|
102295
|
+
}
|
|
102296
|
+
/**
|
|
102010
102297
|
* Arm a node liveness flip. An OFFLINE of a node that announced an update is
|
|
102011
102298
|
* held for the whole planned-update window and worded as such if it is still
|
|
102012
102299
|
* sent; the node's return inside the window is logged and sends nothing (the
|
|
@@ -105899,6 +106186,8 @@ var NotificationCenter = class {
|
|
|
105899
106186
|
zoneOwners;
|
|
105900
106187
|
/** Runs a matched rule's action sequences. Null when no actuator is wired. */
|
|
105901
106188
|
actionRunner;
|
|
106189
|
+
/** Domain action targets read from the registry (D983); null without a directory. */
|
|
106190
|
+
actionTargets;
|
|
105902
106191
|
/** Grants behind the tap-through buttons. In-process and single-use — read
|
|
105903
106192
|
* `action-token.ts` before assuming anything about what a tap proves. */
|
|
105904
106193
|
actionTokens = new NcActionTokenRegistry();
|
|
@@ -106306,9 +106595,15 @@ var NotificationCenter = class {
|
|
|
106306
106595
|
});
|
|
106307
106596
|
this.livenessHoldoff = new DeviceLivenessHoldoff((event) => this.consumeEvent(event), { delayMs: deps.livenessHoldoffMs ?? (process.env.VITEST ? 0 : DEVICE_LIVENESS_HOLDOFF_MS) });
|
|
106308
106597
|
this.nodeLivenessHoldoff = new DeviceLivenessHoldoff((event, timing) => this.consumeNodeLiveness(event, timing), { delayMs: deps.nodeLivenessHoldoffMs ?? (process.env.VITEST ? 0 : NODE_LIVENESS_HOLDOFF_MS) });
|
|
106598
|
+
const targets = deps.actionTargetDirectory !== void 0 ? new require_clip_model_registry.ActionTargetResolver(deps.actionTargetDirectory, this.logger.child("actions")) : null;
|
|
106599
|
+
this.actionTargets = targets;
|
|
106309
106600
|
this.actionRunner = deps.actuate !== void 0 && deps.isCapDeviceScoped !== void 0 ? new NcRuleActionRunner({
|
|
106310
106601
|
logger: this.logger.child("actions"),
|
|
106311
106602
|
actuate: (input) => this.actuateWithAttribution(input),
|
|
106603
|
+
...targets !== null ? { resolveTarget: (target, actionId, owner) => targets.resolve(target, actionId, {}, {
|
|
106604
|
+
actionId,
|
|
106605
|
+
owner
|
|
106606
|
+
}) } : {},
|
|
106312
106607
|
isDeviceScoped: deps.isCapDeviceScoped,
|
|
106313
106608
|
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
106314
106609
|
}) : null;
|
|
@@ -107098,6 +107393,21 @@ var NotificationCenter = class {
|
|
|
107098
107393
|
}
|
|
107099
107394
|
};
|
|
107100
107395
|
}
|
|
107396
|
+
/**
|
|
107397
|
+
* What the operator answered to the "was this detection right?" buttons, for
|
|
107398
|
+
* the retrain page: by track, the latest answer wins. A track nobody answered
|
|
107399
|
+
* is absent.
|
|
107400
|
+
*/
|
|
107401
|
+
detectionVerdictsOf(trackIds) {
|
|
107402
|
+
const out = /* @__PURE__ */ new Map();
|
|
107403
|
+
for (const [trackId, row] of this.verdicts.forTracks(trackIds)) out.set(trackId, toRetrainVerdict({
|
|
107404
|
+
verdict: row.verdict,
|
|
107405
|
+
trackId,
|
|
107406
|
+
at: row.at,
|
|
107407
|
+
suggestedClass: row.suggestedClass
|
|
107408
|
+
}));
|
|
107409
|
+
return out;
|
|
107410
|
+
}
|
|
107101
107411
|
observeOccupancy(deviceId, snapshot, evidence = "scene") {
|
|
107102
107412
|
return observeOccupancy(this.occupancyWatchHost(), deviceId, snapshot, evidence);
|
|
107103
107413
|
}
|
|
@@ -107273,6 +107583,10 @@ var NotificationCenter = class {
|
|
|
107273
107583
|
}
|
|
107274
107584
|
};
|
|
107275
107585
|
}
|
|
107586
|
+
/** The device registry changed: a cached domain action target is stale (D983). */
|
|
107587
|
+
invalidateActionTargets() {
|
|
107588
|
+
this.actionTargets?.invalidate();
|
|
107589
|
+
}
|
|
107276
107590
|
onSystemEvent(event) {
|
|
107277
107591
|
return onSystemEvent(this.systemEventsHost(), event);
|
|
107278
107592
|
}
|
|
@@ -108746,7 +109060,9 @@ async function addVerdictFrameFromRecording(api, logger, input) {
|
|
|
108746
109060
|
}
|
|
108747
109061
|
const result = await api.addRetrainFrameFromRecording({
|
|
108748
109062
|
collectionId,
|
|
108749
|
-
|
|
109063
|
+
deviceId: input.deviceId,
|
|
109064
|
+
epochMs: input.epochMs,
|
|
109065
|
+
...input.subject !== void 0 ? { verdictSubject: input.subject } : {}
|
|
108750
109066
|
});
|
|
108751
109067
|
if (result.kind === "added") return { ok: true };
|
|
108752
109068
|
return {
|
|
@@ -109033,6 +109349,20 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
109033
109349
|
});
|
|
109034
109350
|
},
|
|
109035
109351
|
isCapDeviceScoped: require_clip_model_registry.isDeviceScopedCap,
|
|
109352
|
+
actionTargetDirectory: {
|
|
109353
|
+
listDeviceIdsByCaps: (capNames) => api.deviceManager.listDeviceIdsByCaps.query({ capNames: [...capNames] }),
|
|
109354
|
+
listDevices: async (deviceIds) => {
|
|
109355
|
+
return (await api.deviceManager.listAll.query({
|
|
109356
|
+
deviceIds: [...deviceIds],
|
|
109357
|
+
projection: "slim"
|
|
109358
|
+
})).map((d) => ({
|
|
109359
|
+
id: d.id,
|
|
109360
|
+
type: d.type,
|
|
109361
|
+
location: d.location,
|
|
109362
|
+
disabled: d.disabled
|
|
109363
|
+
}));
|
|
109364
|
+
}
|
|
109365
|
+
},
|
|
109036
109366
|
generateVision: async (request) => {
|
|
109037
109367
|
const result = await api.llm.generateVision.mutate({
|
|
109038
109368
|
...request.profileId !== void 0 ? { profileId: request.profileId } : {},
|
|
@@ -113197,7 +113527,10 @@ var PackageDropDetector = class {
|
|
|
113197
113527
|
}
|
|
113198
113528
|
if (outcome.kind === "yes") {
|
|
113199
113529
|
this.reportLlmSilence(this.llmSilence.noteAnswered(entry.deviceId), tags);
|
|
113200
|
-
return {
|
|
113530
|
+
return {
|
|
113531
|
+
packageLlmVerdict: outcome.verdict,
|
|
113532
|
+
confirmElapsedMs: latencyMs
|
|
113533
|
+
};
|
|
113201
113534
|
}
|
|
113202
113535
|
this.dropUncertain(entry, tags, outcome.reason, latencyMs);
|
|
113203
113536
|
return null;
|
|
@@ -113389,10 +113722,12 @@ var PackageDropDetector = class {
|
|
|
113389
113722
|
}
|
|
113390
113723
|
const llm = await this.llmFields(entry, rules, hitZones, tags);
|
|
113391
113724
|
if (llm === null) return;
|
|
113725
|
+
const { confirmElapsedMs, ...llmRowFields } = llm;
|
|
113726
|
+
const rowTimestamp = timestamp + (confirmElapsedMs ?? 0);
|
|
113392
113727
|
const draft = {
|
|
113393
113728
|
kind: "object",
|
|
113394
113729
|
deviceId: entry.deviceId,
|
|
113395
|
-
timestamp,
|
|
113730
|
+
timestamp: rowTimestamp,
|
|
113396
113731
|
source: "pipeline",
|
|
113397
113732
|
idempotencyKey,
|
|
113398
113733
|
trackId: entry.sourceTrackId ?? entry.id,
|
|
@@ -113411,7 +113746,7 @@ var PackageDropDetector = class {
|
|
|
113411
113746
|
frameHeight: entry.frameHeight,
|
|
113412
113747
|
...entry.keyFrameMediaId !== void 0 ? { mediaId: entry.keyFrameMediaId } : {},
|
|
113413
113748
|
importance: PACKAGE_IMPORTANCE,
|
|
113414
|
-
...
|
|
113749
|
+
...llmRowFields
|
|
113415
113750
|
};
|
|
113416
113751
|
const rowId = await this.deps.events.insertObject(draft);
|
|
113417
113752
|
if (rowId === null) {
|
|
@@ -113442,7 +113777,7 @@ var PackageDropDetector = class {
|
|
|
113442
113777
|
w: entry.bbox.w,
|
|
113443
113778
|
h: entry.bbox.h
|
|
113444
113779
|
},
|
|
113445
|
-
timestamp
|
|
113780
|
+
timestamp: rowTimestamp
|
|
113446
113781
|
});
|
|
113447
113782
|
this.deps.logger.info("package delivered — all 5 gates passed", {
|
|
113448
113783
|
tags: { deviceId: entry.deviceId },
|
|
@@ -113453,6 +113788,7 @@ var PackageDropDetector = class {
|
|
|
113453
113788
|
zoneIds: hitZones,
|
|
113454
113789
|
dwellMs,
|
|
113455
113790
|
areaFrac: Number(minAreaFrac.toFixed(5)),
|
|
113791
|
+
...confirmElapsedMs !== void 0 ? { footageAnchorShiftMs: confirmElapsedMs } : {},
|
|
113456
113792
|
...entry.confidence !== void 0 ? { confidence: entry.confidence } : {}
|
|
113457
113793
|
}
|
|
113458
113794
|
});
|
|
@@ -113652,7 +113988,7 @@ var RetrainCollectionService = class {
|
|
|
113652
113988
|
frameId: recordingFrameId(input.deviceId, input.epochMs),
|
|
113653
113989
|
deviceId: input.deviceId
|
|
113654
113990
|
});
|
|
113655
|
-
const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp]);
|
|
113991
|
+
const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp], input.verdictSubject);
|
|
113656
113992
|
if (stored.kind === "refused") {
|
|
113657
113993
|
this.deps.logger.info("retrain collection add refused", {
|
|
113658
113994
|
tags,
|
|
@@ -113664,11 +114000,15 @@ var RetrainCollectionService = class {
|
|
|
113664
114000
|
});
|
|
113665
114001
|
return stored;
|
|
113666
114002
|
}
|
|
113667
|
-
|
|
113668
|
-
|
|
113669
|
-
|
|
113670
|
-
|
|
113671
|
-
|
|
114003
|
+
let storedFrame = stored.frame;
|
|
114004
|
+
if (stored.reused) {
|
|
114005
|
+
await this.deps.collections.addFrame({
|
|
114006
|
+
collectionId: input.collectionId,
|
|
114007
|
+
frameId: stored.frame.frameId,
|
|
114008
|
+
deviceId: input.deviceId
|
|
114009
|
+
});
|
|
114010
|
+
if (input.verdictSubject !== void 0) storedFrame = await this.deps.frames.setVerdictSubject(stored.frame.frameId, input.verdictSubject) ?? stored.frame;
|
|
114011
|
+
}
|
|
113672
114012
|
this.deps.logger.info("retrain frame added to a collection from the recording", {
|
|
113673
114013
|
tags,
|
|
113674
114014
|
meta: {
|
|
@@ -113678,7 +114018,7 @@ var RetrainCollectionService = class {
|
|
|
113678
114018
|
reused: stored.reused
|
|
113679
114019
|
}
|
|
113680
114020
|
});
|
|
113681
|
-
const [wire] = await this.deps.retrain.toWireFrames([
|
|
114021
|
+
const [wire] = await this.deps.retrain.toWireFrames([storedFrame]);
|
|
113682
114022
|
if (wire === void 0) throw new Error("retrain: a stored still produced no wire frame");
|
|
113683
114023
|
return {
|
|
113684
114024
|
kind: "added",
|
|
@@ -113729,7 +114069,7 @@ var RetrainCollectionService = class {
|
|
|
113729
114069
|
const ids = await this.deps.collections.listFrameIds(input.collectionId);
|
|
113730
114070
|
return this.deps.retrain.toWireFrames(await this.deps.frames.getMany(ids));
|
|
113731
114071
|
}
|
|
113732
|
-
async storeStill(deviceId, epochMs, withRow) {
|
|
114072
|
+
async storeStill(deviceId, epochMs, withRow, verdictSubject) {
|
|
113733
114073
|
const existing = await this.deps.frames.get(recordingFrameId(deviceId, epochMs));
|
|
113734
114074
|
if (existing !== null) return {
|
|
113735
114075
|
kind: "stored",
|
|
@@ -113748,7 +114088,8 @@ var RetrainCollectionService = class {
|
|
|
113748
114088
|
const stored = await this.deps.frames.storeRecordingStill({
|
|
113749
114089
|
deviceId,
|
|
113750
114090
|
epochMs,
|
|
113751
|
-
bytes: still.jpeg
|
|
114091
|
+
bytes: still.jpeg,
|
|
114092
|
+
...verdictSubject !== void 0 ? { verdictSubject } : {}
|
|
113752
114093
|
}, withRow);
|
|
113753
114094
|
return stored.kind === "copied" ? {
|
|
113754
114095
|
kind: "stored",
|
|
@@ -114534,6 +114875,14 @@ function toWireFrame(f) {
|
|
|
114534
114875
|
dismissed: f.dismissed ?? []
|
|
114535
114876
|
};
|
|
114536
114877
|
}
|
|
114878
|
+
/** The answer on a frame: a track copy asks the ledger, a still carries the
|
|
114879
|
+
* subject the notification froze. */
|
|
114880
|
+
function verdictOfFrame(copy, verdicts) {
|
|
114881
|
+
if (copy.origin === "track") return verdicts.get(copy.trackId);
|
|
114882
|
+
const s = copy.verdictSubject;
|
|
114883
|
+
if (s === void 0) return void 0;
|
|
114884
|
+
return toRetrainVerdict(s);
|
|
114885
|
+
}
|
|
114537
114886
|
/**
|
|
114538
114887
|
* What the operator did with the proposals on a frame (`retrain-review.ts`).
|
|
114539
114888
|
*
|
|
@@ -114594,7 +114943,11 @@ var RetrainService = class {
|
|
|
114594
114943
|
const tracks = [];
|
|
114595
114944
|
for (const deviceId of devices) tracks.push(...await this.deps.tracks.listStaging(deviceId, limit));
|
|
114596
114945
|
const ids = tracks.map((t) => t.trackId);
|
|
114597
|
-
const [frameCounts, annotationCounts] = await Promise.all([
|
|
114946
|
+
const [frameCounts, annotationCounts, verdicts] = await Promise.all([
|
|
114947
|
+
this.deps.frames.countsByTrack(ids),
|
|
114948
|
+
this.deps.annotations.countsByTrack(ids),
|
|
114949
|
+
this.verdictsOfTracks(ids)
|
|
114950
|
+
]);
|
|
114598
114951
|
return tracks.map((t) => ({
|
|
114599
114952
|
trackId: t.trackId,
|
|
114600
114953
|
deviceId: t.deviceId,
|
|
@@ -114603,7 +114956,8 @@ var RetrainService = class {
|
|
|
114603
114956
|
firstSeen: t.firstSeen,
|
|
114604
114957
|
lastSeen: t.lastSeen,
|
|
114605
114958
|
frameCount: frameCounts.get(t.trackId) ?? 0,
|
|
114606
|
-
annotationCount: annotationCounts.get(t.trackId) ?? 0
|
|
114959
|
+
annotationCount: annotationCounts.get(t.trackId) ?? 0,
|
|
114960
|
+
...verdicts.has(t.trackId) ? { verdict: verdicts.get(t.trackId) } : {}
|
|
114607
114961
|
}));
|
|
114608
114962
|
}
|
|
114609
114963
|
/**
|
|
@@ -114653,9 +115007,14 @@ var RetrainService = class {
|
|
|
114653
115007
|
const ownTracks = /* @__PURE__ */ new Map();
|
|
114654
115008
|
const subjectBoxes = /* @__PURE__ */ new Map();
|
|
114655
115009
|
const annotationsByFrame = await this.annotationsByFrame(copies);
|
|
115010
|
+
const verdicts = await this.verdictsOf(copies);
|
|
114656
115011
|
const out = [];
|
|
114657
115012
|
for (const copy of copies) {
|
|
114658
|
-
const
|
|
115013
|
+
const verdict = verdictOfFrame(copy, verdicts);
|
|
115014
|
+
const wire = {
|
|
115015
|
+
...toWireFrame(copy),
|
|
115016
|
+
...verdict !== void 0 ? { verdict } : {}
|
|
115017
|
+
};
|
|
114659
115018
|
const collectionIds = collections?.get(copy.frameId);
|
|
114660
115019
|
const withCollections = {
|
|
114661
115020
|
...collections !== null ? {
|
|
@@ -114686,7 +115045,8 @@ var RetrainService = class {
|
|
|
114686
115045
|
},
|
|
114687
115046
|
ownTrack: trackId !== "" ? ownTracks.get(trackId) ?? null : null,
|
|
114688
115047
|
ownSubjectBox: trackId !== "" ? subjectBoxes.get(trackId)?.get(copy.sourceMediaId) ?? null : null,
|
|
114689
|
-
neighbours
|
|
115048
|
+
neighbours,
|
|
115049
|
+
...copy.verdictSubject !== void 0 ? { verdictSubject: copy.verdictSubject } : {}
|
|
114690
115050
|
});
|
|
114691
115051
|
out.push({
|
|
114692
115052
|
...withCollections,
|
|
@@ -114696,6 +115056,22 @@ var RetrainService = class {
|
|
|
114696
115056
|
}
|
|
114697
115057
|
return out;
|
|
114698
115058
|
}
|
|
115059
|
+
/** The verdicts of the tracks these frames were copied from. */
|
|
115060
|
+
async verdictsOf(copies) {
|
|
115061
|
+
return this.verdictsOfTracks(copies.flatMap((c) => c.origin === "track" ? [c.trackId] : []));
|
|
115062
|
+
}
|
|
115063
|
+
async verdictsOfTracks(trackIds) {
|
|
115064
|
+
const port = this.deps.verdicts;
|
|
115065
|
+
const ids = [...new Set(trackIds)];
|
|
115066
|
+
if (port === void 0 || ids.length === 0) return /* @__PURE__ */ new Map();
|
|
115067
|
+
return port.forTracks(ids).catch((err) => {
|
|
115068
|
+
this.deps.logger.warn("retrain frames: verdicts unreadable", { meta: {
|
|
115069
|
+
tracks: ids.length,
|
|
115070
|
+
error: String(err)
|
|
115071
|
+
} });
|
|
115072
|
+
return /* @__PURE__ */ new Map();
|
|
115073
|
+
});
|
|
115074
|
+
}
|
|
114699
115075
|
/** The URLs of a frame's pixels, when a data plane is served for them. */
|
|
114700
115076
|
frameUrls(frameId) {
|
|
114701
115077
|
const frameUrl = this.deps.frameUrl;
|
|
@@ -116812,6 +117188,7 @@ var BuildStores = class extends BuildStoresBase {
|
|
|
116812
117188
|
subjectBoxes: (trackId, deviceId) => mediaStore.listSubjectBoxesByOwner("track", trackId, deviceId)
|
|
116813
117189
|
},
|
|
116814
117190
|
memberships: retrainCollectionStore,
|
|
117191
|
+
verdicts: { forTracks: async (trackIds) => this.notificationCenter?.detectionVerdictsOf(trackIds) ?? /* @__PURE__ */ new Map() },
|
|
116815
117192
|
frameUrl: (frameId, size) => retrainFramePath(this.ctx.id, frameId, size)
|
|
116816
117193
|
});
|
|
116817
117194
|
this.retrain = retrain;
|
|
@@ -122523,7 +122900,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
122523
122900
|
onBindingsChanged: (data) => this.handleBindingsChangedEvent(data),
|
|
122524
122901
|
onDeviceUnregistered: (deviceId) => this.handleDeviceUnregisteredEvent(deviceId),
|
|
122525
122902
|
onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now()),
|
|
122526
|
-
onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event)
|
|
122903
|
+
onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event),
|
|
122904
|
+
onDeviceRegistryChange: () => this.notificationCenter?.invalidateActionTargets()
|
|
122527
122905
|
});
|
|
122528
122906
|
await step("startTrackingTapes", () => this.startTrackingTapes(this.isPostProcessingNode));
|
|
122529
122907
|
if (this.isPostProcessingNode) await step("startEmbeddingDispatcher", () => this.startEmbeddingDispatcher(logger, transport));
|
|
@@ -123984,6 +124362,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
123984
124362
|
this.frameState.lastNonPackageActivityAt.delete(data.deviceId);
|
|
123985
124363
|
this.frameState.packageCollectors.forgetDevice(data.deviceId);
|
|
123986
124364
|
this.frameState.packageOversizeLogAt.delete(data.deviceId);
|
|
124365
|
+
this.frameState.packageLookFrames.delete(data.deviceId);
|
|
123987
124366
|
this.frameState.packageLookDropReasons.delete(data.deviceId);
|
|
123988
124367
|
this.overlayState.clearDevice(data.deviceId);
|
|
123989
124368
|
this.frameState.overlaySynthesisWarnAt.delete(data.deviceId);
|
|
@@ -124007,6 +124386,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
124007
124386
|
this.frameState.lastNonPackageActivityAt.delete(deviceId);
|
|
124008
124387
|
this.frameState.packageCollectors.forgetDevice(deviceId);
|
|
124009
124388
|
this.frameState.packageOversizeLogAt.delete(deviceId);
|
|
124389
|
+
this.frameState.packageLookFrames.delete(deviceId);
|
|
124010
124390
|
this.frameState.packageLookDropReasons.delete(deviceId);
|
|
124011
124391
|
this.overlayState.clearDevice(deviceId);
|
|
124012
124392
|
this.frameState.overlaySynthesisWarnAt.delete(deviceId);
|
|
@@ -126989,6 +127369,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
126989
127369
|
runPackageStillness(input) {
|
|
126990
127370
|
if (this.packageDropDetector === null) return;
|
|
126991
127371
|
const tags = { deviceId: input.deviceId };
|
|
127372
|
+
if (input.frameHandle !== null) this.frameState.packageLookFrames.set(input.deviceId, {
|
|
127373
|
+
handle: input.frameHandle,
|
|
127374
|
+
at: input.timestamp
|
|
127375
|
+
});
|
|
126992
127376
|
for (const t of input.tracked) {
|
|
126993
127377
|
if (input.packageClasses.has(t.className)) continue;
|
|
126994
127378
|
if (!isDelivererInPackageZone(t.zones, input.packageZoneIds)) continue;
|
|
@@ -127222,10 +127606,23 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
|
|
|
127222
127606
|
...entry.confidence !== void 0 ? { confidence: entry.confidence } : {}
|
|
127223
127607
|
}
|
|
127224
127608
|
});
|
|
127225
|
-
this.
|
|
127226
|
-
|
|
127609
|
+
const lookFrame = this.frameState.packageLookFrames.get(input.deviceId);
|
|
127610
|
+
promoteWithStill({
|
|
127227
127611
|
entry,
|
|
127228
|
-
timestamp:
|
|
127612
|
+
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <= PACKAGE_LOOK_FRAME_MAX_AGE_MS ? lookFrame.handle : null,
|
|
127613
|
+
timestamp: lookFrame?.at ?? input.timestamp,
|
|
127614
|
+
query: (request) => this.queryPackageSceneCrop(request),
|
|
127615
|
+
writer: this.mediaStore,
|
|
127616
|
+
logger: this.ctx.logger,
|
|
127617
|
+
detector: this.packageDropDetector
|
|
127618
|
+
}).catch((err) => {
|
|
127619
|
+
this.ctx.logger.warn("package promotion failed", {
|
|
127620
|
+
tags,
|
|
127621
|
+
meta: {
|
|
127622
|
+
entryId: entry.id,
|
|
127623
|
+
error: err instanceof Error ? err.message : String(err)
|
|
127624
|
+
}
|
|
127625
|
+
});
|
|
127229
127626
|
});
|
|
127230
127627
|
}
|
|
127231
127628
|
for (const entry of outcome.departed) {
|