@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as MAX_TRACK_RELATIONS, $i as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, $n as bundledCamstack, $r as normalizeNcAction, $t as NcTemplateVarDescriptorSchema, A as DETECTION_MACRO_CLASSES, Aa as record, Ai as sceneMonitorCapability, An as TrackPuritySchema, Ar as isAlertEpisodeOpen, At as NcAlarmConfigSchema, B as EXPORT_DENSE_MAX_RANGES, Bi as sunTimes, Bn as alertEpisodeKind, Br as isLabelSupplyStepId, Bt as NcRuleSchema, C as COCO_TO_MACRO, Ca as boolean, Ci as retrainRevisionConflictMessage, Cn as SetTrackRelationInputSchema, Cr as eventKindLabel, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as number, Di as ruleDeviceCompatCapQueries, Dn as TimelapseRulePatchSchema, Dr as gifDeriveSpeed, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as looseObject, Ei as ruleBelongsToAlarm$1, En as TimelapseRuleInputSchema, Er as failureContributionCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as union, Fi as storageOccupancyCapability, Fn as activateAlarmProfile, Fr as isDetectionMacroClass, Ft as NcConditionDescriptorSchema, G as LabelAttributionSchema, Gi as systemEventSummaryScheduleOf, Gn as applySecureOnArmEdit, Gr as isSystemDelivery, Gt as NcSnoozeInputSchema, H as FULL_IMAGE_BBOX, Hi as systemEventDeliveryProblems, Hn as applyBlockingEdit, Hr as isRetiredClearKey, Ht as NcRuleUpdateInputSchema, I as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Ia as unknown, Ii as subKindsOf, In as addonWidgetsSourceCapability, Ir as isDrawableSubjectPath, It as NcConditionsSchema, J as LockStateSchema, Ji as templateVarsFor, Jn as audioMetricsCapability, Jr as kebabToCamel, Jt as NcStateSummarySectionIdSchema, K as ListTrackRelationsInputSchema, Ki as systemEventSummarySplitOf, Kn as armRemovalReconcile, Kr as isSystemEventSummaryRule, Kt as NcSnoozeSchema, L as EVENT_DENSITY_BATCH_MAX, La as EventCategory, Li as subjectPathSampleFromBox, Ln as alarmDeviceSecureOnArm, Lr as isEventKindLabelKey, Lt as NcRuleActionSchema, M as DeclaredDevices, Ma as string, Mi as sensorRaisedOf, Mn as TrackRelationSchema, Mr as isAppLinkBase, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as templateLiteral, Ni as servedTemplateVars, Nn as TrackSideSchema, Nr as isAudioClassName, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as object, Oi as ruleGroupingApplies, On as TimelapseRuleSchema, Or as gifFromMp4Args, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as tuple, Pi as startMoverUnderClearance, Pn as TrackSourceSchema, Pr as isDefaultAlarmProfileId, Pt as NcAlarmSettingsPatchSchema, Q as MAX_TRACK_PURITIES, Qi as zoneAnalyticsCapability, Qn as buildViewerLink, Qr as ncMediaPolicyError, Qt as NcTemplatePreviewSchema, R as EVENT_KIND_BY_CAP, Ri as subjectPathSvgPoints, Rn as alarmPanelCapability, Rr as isEventOwnerType, Rt as NcRuleInputSchema, S as BucketAccumulator, Sa as array, Sn as SetTrackPurityResultSchema, Sr as ensureAlarmProfiles, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as literal, Ti as ruleAutoTriggersAlarm, Tn as TRACK_RELATIONS_DEFAULT_LIMIT, Tr as faceGalleryCapability, Tt as NC_SNOOZE_MAX_MINUTES, U as FailureCounters, Ui as systemEventFamilyProblems, Un as applyModeDelaysEdits, Ur as isRootSuppliableStepId, Ut as NcScheduleSchema, V as FIRST_LEVEL_MACRO_CLASSES, Vi as syncActiveAlarmProfileSettings, Vn as alertEpisodePayload, Vr as isMediaOwnerType, Vt as NcRuleTargetSchema, W as LABEL_SUPPLY_MACRO, Wi as systemEventFilterApplies, Wn as applyNonBlockingEdit, Wr as isScheduleActive, Wt as NcSharedSequenceSchema, X as MAX_BIRTH_DECISION_RECORDS, Xi as viewerLinkBaseOf, Xn as buildEventKindDescriptor, Xr as mayWriteToLocation, Xt as NcTaxonomySchema, Y as MAX_ARCHIVED_DEBUG_NOTES, Yi as vectorDimFromBase64, Yn as audioModeOf, Yr as linkedStepOf, Yt as NcSystemEventKindSchema, Z as MAX_BIRTH_LATENCY_PROXY_MS, Zi as withSystemEventClasses, Zn as buildSubjectPath, Zr as memoizePinnedFfmpeg, Zt as NcTemplatePreviewInputSchema, _ as AlarmArmModeSchema, _a as nodePin, _i as resolveDeliveryParams, _n as SCENE_DEFAULT_UNCOVERED_POLICY, _t as NC_CONFIRM_DEFAULT_TIMEOUT_MS, a as CLIP_EMBEDDING_STEP_ID, aa as BaseAddon, ai as paletteResultSchema, an as RECORDING_SOURCE_CAMSTACK_ADDON, ar as coreBlockIdFromAddonId, at as NC_ALARM_DELAY_FIELDS, b as BaseDevice, ba as ZodObject, bi as resolveModeNotifyPolicy, bn as SceneMonitorSchema, bt as NC_DEFAULT_SNOOZE_MINUTES, c as readClusterModelPinStrict, ca as InvalidRecordError, ci as pickFaceRescoreIntervalMinutes, cn as RetrainOriginSchema, cr as deleteAlarmProfile, ct as NC_ALARM_SYSTEM_EVENT_KINDS, d as APPEARANCE_BIN_COUNT, da as createEvent, di as projectSubjectPath, dn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, dr as deriveRecordingMode, dt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, ea as canonicalHash, ei as normalizeNcCapTarget, en as OPS_LOG_DEFAULT_LIMIT, er as calendarEventLocalSpan, et as MOTION_CLOSE_AFTER_MS, f as APPEARANCE_BLOCKS, fa as customAction, fi as readDeviceStateFrom, fn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, fr as describeSystemEventDeliveryProblem, ft as NC_CLIP_POST_ROLL_DEFAULT_SEC, g as ActionTargetResolver, ga as isDeviceScopedCap, gi as resolveClipSearchMinScore, gn as SCENE_DEFAULT_QUIET_SECONDS, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as hydrateSchema, hi as renameAlarmProfile, hn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, hr as deviceRemovalHookCapability, ht as NC_CONDITION_CATALOG, ia as errMsg, ii as paintHex, in as RECORDING_EXPORT_MAX_READ_BYTES, ir as consumableLowPercentOf, it as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, j as DETECTION_PIPELINE_CAP_NAME, ja as strictObject, ji as secureActuationFor, jn as TrackRelationKindSchema, jr as isAlertEpisodeSeverity, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as partialRecord, ki as ruleKindOf, kn as TrackPlacementSchema, kr as isAlarmControllableCap, kt as NC_TEMPLATE_VARS, l as resolveClusterModelPin, la as RECORD_MAX_DATA_BYTES, li as pipelineAnalyticsCapability, ln as RetrainStatusSchema, lr as deriveDetailCropRect, lt as NC_ALARM_TRIGGERED_MAX_SEC, m as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ma as defineRecordKinds, mi as readTimelapseGeneratedAt, mn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, mr as deviceAttentionKind, mt as NC_CLIP_RETENTION_MAX_SEC, na as isSourceCap, ni as notificationRulesCapability, nn as PackageLlmVerdictSchema, nr as cloneAlarmSettings, nt as MotionTriggeredBySchema, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as CAP_ADVANCE_FIELDS, oi as parseAlertEpisodePayload, on as RebuildObjectEmbeddingsRefusalSchema, or as cosineSimilarity$1, ot as NC_ALARM_DELAY_MAX_SEC, p as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, pa as defineCustomActions, pi as readMotionTriggeredBy, pn as SCENE_DEFAULT_ANCHOR_THRESHOLD, pr as describeSystemEventFamilyProblem, pt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, q as ListTrackRelationsResultSchema, qi as templateCarriesSummary, qn as assertTimelapseCadences, qr as isVectorIndexDimConflict, qt as NcSnoozeSuppressedSchema, ra as DeviceType, ri as occupancyScope, rr as consumableFormOf, rt as NC_ALARM_DEFAULT_PROFILE_ID, s as FACE_EMBEDDING_STEP_ID, sa as CamProfileSchema, sn as RetrainDismissedSchema, sr as createAlarmProfile, st as NC_ALARM_NOTIFY_FIELDS, t as BUILTIN_CLIP_MODELS, ta as evaluateSensorEdge, ti as normalizeNcMediaPolicy, tn as OpsLogEntrySchema, tr as classifyRetrainReview, tt as MediaFileKindEnum, u as ACTION_TARGET_INVALIDATION_EVENTS, ua as RECORD_QUERY_MAX, ui as plateGalleryCapability, un as RetrainVerdictSubjectSchema, ur as derivePostTriggeredAction, ut as NC_AUDIO_CONFIRM_HITS_DEFAULT, v as ArchivedDebugNoteSchema, vi as resolveLocationMode, vn as SCENE_DIVERGED, vr as embeddingRefusalOf, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as discriminatedUnion, wi as rootSupplyServes, wn as TIMELAPSE_DENSE_FLOOR_SEC, wr as evictionPolicyOfLocation, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as _enum, xi as resolveNcMediaImage, xn as SetTrackPurityInputSchema, xr as encodeVectorBase64, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as sleep$1, yi as resolveModeDelays, yn as SCENE_RESET_RECAPTURES, yt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, z as EVENT_OWNER_TYPES, zi as summarizeReview, zn as alertEpisodeId, zr as isKnownTemplateVar, zt as NcRulePatchSchema } from "../clip-model-registry-_b3KHYFm.mjs";
|
|
2
2
|
import { t as __exportAll } from "../embedding-encoder/index.mjs";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import { createWriteStream, promises } from "node:fs";
|
|
@@ -888,7 +888,9 @@ function describeGrant(grant) {
|
|
|
888
888
|
return grant.action === "disarm" ? "Alarm disarmed" : `Alarm armed${grant.mode !== void 0 ? ` (${grant.mode})` : ""}`;
|
|
889
889
|
case "addon-update": return `${grant.packages.length} addon update(s) on ${grant.nodeId}`;
|
|
890
890
|
case "server-update": return `Server update on ${grant.nodeId}`;
|
|
891
|
-
case "detection-verdict":
|
|
891
|
+
case "detection-verdict":
|
|
892
|
+
if (grant.verdict === "wrong-label") return "Marked label wrong, class right — kept for retraining";
|
|
893
|
+
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`;
|
|
892
894
|
}
|
|
893
895
|
}
|
|
894
896
|
/** The audit line's `meta`, per family. A snooze that logged a `sequence` it
|
|
@@ -962,7 +964,7 @@ var FAILURE_COPY = {
|
|
|
962
964
|
/** The question a link-only channel lands on, answered by a POST form. */
|
|
963
965
|
function confirmPage(url, grant) {
|
|
964
966
|
const action = `${url.pathname}?exp=${encodeURIComponent(url.searchParams.get("exp") ?? "")}&sig=${encodeURIComponent(url.searchParams.get("sig") ?? "")}&confirm=1`;
|
|
965
|
-
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>`;
|
|
967
|
+
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>`;
|
|
966
968
|
}
|
|
967
969
|
function page(title, detail) {
|
|
968
970
|
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>`;
|
|
@@ -2945,7 +2947,6 @@ var NIGHT = "night";
|
|
|
2945
2947
|
var IR = "ir";
|
|
2946
2948
|
var DAWN = "dawn";
|
|
2947
2949
|
var DUSK = "dusk";
|
|
2948
|
-
var DEG = Math.PI / 180;
|
|
2949
2950
|
/**
|
|
2950
2951
|
* Mean channel spread below which a frame is judged infrared.
|
|
2951
2952
|
*
|
|
@@ -2967,38 +2968,6 @@ var IR_CHROMA_THRESHOLD = 6;
|
|
|
2967
2968
|
* scene reports `unknown` — the honest answer.
|
|
2968
2969
|
*/
|
|
2969
2970
|
var TWILIGHT_BAND_MS = 2400 * 1e3;
|
|
2970
|
-
/** Julian day number for a JS Date (UTC). */
|
|
2971
|
-
function toJulian(date) {
|
|
2972
|
-
return date.getTime() / 864e5 + 2440587.5;
|
|
2973
|
-
}
|
|
2974
|
-
/** Julian day → epoch ms (UTC). */
|
|
2975
|
-
function fromJulian(j) {
|
|
2976
|
-
return (j - 2440587.5) * 864e5;
|
|
2977
|
-
}
|
|
2978
|
-
/**
|
|
2979
|
-
* Sunrise/sunset (UTC epoch ms) for a location and date via the standard NOAA
|
|
2980
|
-
* sunrise equation. Returns `null` for the polar edge cases (sun never
|
|
2981
|
-
* rises/sets that day) so the caller can degrade to the clock split.
|
|
2982
|
-
*/
|
|
2983
|
-
function sunTimes(lat, lng, date) {
|
|
2984
|
-
const jDate = toJulian(date);
|
|
2985
|
-
const jStar = Math.round(jDate - 2451545 + 8e-4) - lng / 360;
|
|
2986
|
-
const m = (357.5291 + .98560028 * jStar) % 360;
|
|
2987
|
-
const mRad = m * DEG;
|
|
2988
|
-
const lambda = (m + (1.9148 * Math.sin(mRad) + .02 * Math.sin(2 * mRad) + 3e-4 * Math.sin(3 * mRad)) + 180 + 102.9372) % 360 * DEG;
|
|
2989
|
-
const jTransit = 2451545 + jStar + .0053 * Math.sin(mRad) - .0069 * Math.sin(2 * lambda);
|
|
2990
|
-
const declination = Math.asin(Math.sin(lambda) * Math.sin(23.44 * DEG));
|
|
2991
|
-
const latRad = lat * DEG;
|
|
2992
|
-
const cosOmega = (Math.sin(-.833 * DEG) - Math.sin(latRad) * Math.sin(declination)) / (Math.cos(latRad) * Math.cos(declination));
|
|
2993
|
-
if (cosOmega > 1 || cosOmega < -1) return null;
|
|
2994
|
-
const omega = Math.acos(cosOmega) / DEG;
|
|
2995
|
-
const jRise = jTransit - omega / 360;
|
|
2996
|
-
const jSet = jTransit + omega / 360;
|
|
2997
|
-
return {
|
|
2998
|
-
rise: fromJulian(jRise),
|
|
2999
|
-
set: fromJulian(jSet)
|
|
3000
|
-
};
|
|
3001
|
-
}
|
|
3002
2971
|
/** Coarse UTC clock split — day between 06:00 and 20:00 UTC. Deterministic
|
|
3003
2972
|
* last resort when no coordinates let us compute real sun-times. */
|
|
3004
2973
|
function clockCondition(now) {
|
|
@@ -9281,6 +9250,7 @@ var en_default = {
|
|
|
9281
9250
|
"button.verdict.correct": "Correct",
|
|
9282
9251
|
"button.verdict.wrong": "Wrong",
|
|
9283
9252
|
"button.verdict.wrongAs": "Wrong: it is {{class}}",
|
|
9253
|
+
"button.verdict.wrongLabel": "Wrong label, class OK",
|
|
9284
9254
|
"action.server-update.title": "Server update",
|
|
9285
9255
|
"action.server-update.started": "Updating server on {{nodeId}}…",
|
|
9286
9256
|
"action.server-update.restarting": "Server update staged on {{nodeId}} — the node is restarting. You will hear when it comes back.",
|
|
@@ -9686,6 +9656,7 @@ var it_default = {
|
|
|
9686
9656
|
"button.verdict.correct": "Corretto",
|
|
9687
9657
|
"button.verdict.wrong": "Sbagliato",
|
|
9688
9658
|
"button.verdict.wrongAs": "Sbagliato: è {{class}}",
|
|
9659
|
+
"button.verdict.wrongLabel": "Etichetta sbagliata, classe giusta",
|
|
9689
9660
|
"action.server-update.title": "Aggiornamento server",
|
|
9690
9661
|
"action.server-update.started": "Aggiornamento server su {{nodeId}}…",
|
|
9691
9662
|
"action.server-update.restarting": "Aggiornamento server predisposto su {{nodeId}} — il nodo si riavvia. Ti avviseremo quando torna.",
|
|
@@ -39035,6 +39006,11 @@ function rowIsDurableRetrainCopy(data) {
|
|
|
39035
39006
|
* audit resolves ownership from INDEX rows, but a future filesystem-side tool
|
|
39036
39007
|
* must be able to tell the two corpora apart by path alone. */
|
|
39037
39008
|
var RETRAIN_PATH_PREFIX = "retrain";
|
|
39009
|
+
/** The stored verdict subject, validated; anything unreadable reads as absent. */
|
|
39010
|
+
function narrowVerdictSubject(value) {
|
|
39011
|
+
const parsed = RetrainVerdictSubjectSchema.safeParse(value);
|
|
39012
|
+
return parsed.success ? { verdictSubject: parsed.data } : {};
|
|
39013
|
+
}
|
|
39038
39014
|
/** Ids per `IN (…)` page in {@link RetrainFrameStore.getMany}. */
|
|
39039
39015
|
var GET_MANY_PAGE = 500;
|
|
39040
39016
|
/**
|
|
@@ -39118,7 +39094,8 @@ var RetrainFrameStore = class {
|
|
|
39118
39094
|
complete: data["complete"] === true || data["complete"] === 1,
|
|
39119
39095
|
...finiteNumber(data["epochMs"]) !== void 0 ? { epochMs: finiteNumber(data["epochMs"]) } : {},
|
|
39120
39096
|
revision: finiteNumber(data["revision"]) ?? 0,
|
|
39121
|
-
dismissed: narrowDismissed(data["dismissed"])
|
|
39097
|
+
dismissed: narrowDismissed(data["dismissed"]),
|
|
39098
|
+
...narrowVerdictSubject(data["verdictSubject"])
|
|
39122
39099
|
};
|
|
39123
39100
|
}
|
|
39124
39101
|
/** The copies with these ids, in the order given; unknown ids are skipped. */
|
|
@@ -39471,7 +39448,8 @@ var RetrainFrameStore = class {
|
|
|
39471
39448
|
capturedAt: input.epochMs,
|
|
39472
39449
|
...chromaSpread !== void 0 ? { chromaSpread } : {},
|
|
39473
39450
|
complete: false,
|
|
39474
|
-
epochMs: input.epochMs
|
|
39451
|
+
epochMs: input.epochMs,
|
|
39452
|
+
...input.verdictSubject !== void 0 ? { verdictSubject: input.verdictSubject } : {}
|
|
39475
39453
|
};
|
|
39476
39454
|
const path = framePath(frame);
|
|
39477
39455
|
const persist = async () => {
|
|
@@ -39495,7 +39473,8 @@ var RetrainFrameStore = class {
|
|
|
39495
39473
|
chromaSpread: frame.chromaSpread ?? null,
|
|
39496
39474
|
complete: false,
|
|
39497
39475
|
origin: "recording",
|
|
39498
|
-
epochMs: input.epochMs
|
|
39476
|
+
epochMs: input.epochMs,
|
|
39477
|
+
...input.verdictSubject !== void 0 ? { verdictSubject: input.verdictSubject } : {}
|
|
39499
39478
|
});
|
|
39500
39479
|
await this.ledger.transact([row, ...withRow]);
|
|
39501
39480
|
};
|
|
@@ -39532,6 +39511,22 @@ var RetrainFrameStore = class {
|
|
|
39532
39511
|
};
|
|
39533
39512
|
}
|
|
39534
39513
|
/**
|
|
39514
|
+
* Record the verdict subject on a still that already existed (the moment was
|
|
39515
|
+
* in the dataset before the operator tapped). The latest answer wins, like
|
|
39516
|
+
* the verdict ledger.
|
|
39517
|
+
*
|
|
39518
|
+
* @returns the updated frame, or `null` when no such copy exists.
|
|
39519
|
+
*/
|
|
39520
|
+
async setVerdictSubject(frameId, verdictSubject) {
|
|
39521
|
+
const existing = await this.get(frameId);
|
|
39522
|
+
if (existing === null) return null;
|
|
39523
|
+
await this.ledger.update(frameId, { verdictSubject });
|
|
39524
|
+
return {
|
|
39525
|
+
...existing,
|
|
39526
|
+
verdictSubject
|
|
39527
|
+
};
|
|
39528
|
+
}
|
|
39529
|
+
/**
|
|
39535
39530
|
* The operator's "every subject in this frame is boxed" (D785 context: an
|
|
39536
39531
|
* unboxed subject is a false positive to a detector evaluation). Only the
|
|
39537
39532
|
* operator sets it; nothing derives it from the annotation count, because a
|
|
@@ -54628,6 +54623,12 @@ var FrameState = class {
|
|
|
54628
54623
|
* per 5 minutes, not one per governor round (the wedge re-sights all
|
|
54629
54624
|
* night; ~300 identical lines would bury the log it exists to serve). */
|
|
54630
54625
|
packageOversizeLogAt = /* @__PURE__ */ new Map();
|
|
54626
|
+
/**
|
|
54627
|
+
* The newest frame the package path held per camera — the picture a parcel
|
|
54628
|
+
* promoted from a raw look is stored from (`package-still-capture.ts`). The
|
|
54629
|
+
* scene path concludes without a frame of its own, so it reads this.
|
|
54630
|
+
*/
|
|
54631
|
+
packageLookFrames = /* @__PURE__ */ new Map();
|
|
54631
54632
|
/** Reasons logged on the previous package look — episode helper for drops. */
|
|
54632
54633
|
packageLookDropReasons = /* @__PURE__ */ new Map();
|
|
54633
54634
|
};
|
|
@@ -58932,6 +58933,89 @@ function sweepOne(deps, deviceId) {
|
|
|
58932
58933
|
if (outcome.departed.length > 0 || outcome.departedWithoutActivity.length > 0 || outcome.expired.length > 0) deps.onOutcome(deviceId, outcome, deps.now);
|
|
58933
58934
|
}
|
|
58934
58935
|
//#endregion
|
|
58936
|
+
//#region src/pipeline-analytics/pipeline/package/package-still-capture.ts
|
|
58937
|
+
/** A held look frame older than this is not the parcel's picture any more. */
|
|
58938
|
+
var PACKAGE_LOOK_FRAME_MAX_AGE_MS = 15e3;
|
|
58939
|
+
async function jpegOf(reply) {
|
|
58940
|
+
if (reply.tier === "ram-fullframe") return "preview-only";
|
|
58941
|
+
if (reply.jpegBytes !== void 0 && reply.jpegBytes.byteLength > 0) return reply.jpegBytes;
|
|
58942
|
+
if (reply.bytes !== void 0 && reply.bytes.length === reply.width * reply.height * 3) return encodeRgbCropToJpeg(reply.bytes, reply.width, reply.height);
|
|
58943
|
+
return "empty-frame";
|
|
58944
|
+
}
|
|
58945
|
+
/**
|
|
58946
|
+
* The entry carrying its still's `keyFrameMediaId`, or the entry unchanged when
|
|
58947
|
+
* it already had one or none could be stored (said, with the reason).
|
|
58948
|
+
*/
|
|
58949
|
+
async function withParcelStill(input) {
|
|
58950
|
+
const { entry, logger } = input;
|
|
58951
|
+
if (entry.keyFrameMediaId !== void 0) return entry;
|
|
58952
|
+
const miss = (reason, detail) => {
|
|
58953
|
+
logger.warn("package still NOT stored — the delivery ships without a picture of the parcel", {
|
|
58954
|
+
tags: { deviceId: entry.deviceId },
|
|
58955
|
+
meta: {
|
|
58956
|
+
entryId: entry.id,
|
|
58957
|
+
reason,
|
|
58958
|
+
...detail !== void 0 ? { error: detail } : {}
|
|
58959
|
+
}
|
|
58960
|
+
});
|
|
58961
|
+
return entry;
|
|
58962
|
+
};
|
|
58963
|
+
if (input.handle === null) return miss("no-frame-handle");
|
|
58964
|
+
if (input.writer === null) return miss("no-media-store");
|
|
58965
|
+
try {
|
|
58966
|
+
const reply = await input.query({
|
|
58967
|
+
handle: input.handle,
|
|
58968
|
+
bbox: {
|
|
58969
|
+
x: 0,
|
|
58970
|
+
y: 0,
|
|
58971
|
+
w: entry.frameWidth,
|
|
58972
|
+
h: entry.frameHeight
|
|
58973
|
+
},
|
|
58974
|
+
acceptJpegBytes: true,
|
|
58975
|
+
encodeJpeg: true,
|
|
58976
|
+
pin: nodePin(input.handle.nodeId)
|
|
58977
|
+
});
|
|
58978
|
+
if (reply === null) return miss("native-miss");
|
|
58979
|
+
const jpeg = await jpegOf(reply);
|
|
58980
|
+
if (typeof jpeg === "string") return miss(jpeg);
|
|
58981
|
+
const keyFrameMediaId = await input.writer.putReplacing({
|
|
58982
|
+
deviceId: entry.deviceId,
|
|
58983
|
+
ownerKind: "track",
|
|
58984
|
+
ownerId: entry.id,
|
|
58985
|
+
kind: "keyFrame",
|
|
58986
|
+
timestamp: input.timestamp,
|
|
58987
|
+
data: Buffer.from(jpeg),
|
|
58988
|
+
subjectBox: {
|
|
58989
|
+
x: entry.bbox.x,
|
|
58990
|
+
y: entry.bbox.y,
|
|
58991
|
+
w: entry.bbox.w,
|
|
58992
|
+
h: entry.bbox.h,
|
|
58993
|
+
frameWidth: entry.frameWidth,
|
|
58994
|
+
frameHeight: entry.frameHeight
|
|
58995
|
+
}
|
|
58996
|
+
});
|
|
58997
|
+
return {
|
|
58998
|
+
...entry,
|
|
58999
|
+
keyFrameMediaId
|
|
59000
|
+
};
|
|
59001
|
+
} catch (err) {
|
|
59002
|
+
return miss("write-failed", errMsg(err));
|
|
59003
|
+
}
|
|
59004
|
+
}
|
|
59005
|
+
/**
|
|
59006
|
+
* One promotion, in the order that matters: the still is stored FIRST, then the
|
|
59007
|
+
* detector emits `delivered` — the notification centre reads the still when the
|
|
59008
|
+
* event arrives, so a picture written after it is a picture nobody sees.
|
|
59009
|
+
*/
|
|
59010
|
+
async function promoteWithStill(input) {
|
|
59011
|
+
const entry = await withParcelStill(input);
|
|
59012
|
+
await input.detector.onStationaryChange({
|
|
59013
|
+
phase: "appeared",
|
|
59014
|
+
entry,
|
|
59015
|
+
timestamp: input.entry.becameStationaryAt
|
|
59016
|
+
});
|
|
59017
|
+
}
|
|
59018
|
+
//#endregion
|
|
58935
59019
|
//#region src/pipeline-analytics/pipeline/person-over-vehicle.ts
|
|
58936
59020
|
/**
|
|
58937
59021
|
* How much of the PERSON box must sit inside a vehicle box to be counted.
|
|
@@ -68331,6 +68415,7 @@ function wireSubscriptions(deps) {
|
|
|
68331
68415
|
deps.onClusterTopology(nodes);
|
|
68332
68416
|
}));
|
|
68333
68417
|
for (const category of NC_SYSTEM_EVENT_CATEGORIES) unsubs.push(deps.eventBus.subscribe({ category }, (ev) => deps.onSystemEvent(ev)));
|
|
68418
|
+
for (const category of ACTION_TARGET_INVALIDATION_EVENTS) unsubs.push(deps.eventBus.subscribe({ category }, () => deps.onDeviceRegistryChange()));
|
|
68334
68419
|
}
|
|
68335
68420
|
unsubs.push(deps.eventBus.subscribe({ category: EventCategory.DeviceBindingsChanged }, (ev) => {
|
|
68336
68421
|
deps.onBindingsChanged(ev.data);
|
|
@@ -68892,6 +68977,12 @@ async function composeEventKindsForDevices(deviceIds, composeOne, onError) {
|
|
|
68892
68977
|
* nearest one is normally the same detection; the delta is reported so a
|
|
68893
68978
|
* reviewer can see how far it was.
|
|
68894
68979
|
*
|
|
68980
|
+
* 0. **The subject a notification verdict froze** (a recording still landed by
|
|
68981
|
+
* a verdict whose track was already gone). It is the one subject the
|
|
68982
|
+
* operator answered about, so it comes FIRST and is the own box; the
|
|
68983
|
+
* neighbours below stay secondary. Its box is normalised against the full
|
|
68984
|
+
* frame already, so it is used as is — never re-scaled.
|
|
68985
|
+
*
|
|
68895
68986
|
* Every OTHER track alive on the camera at that instant contributes its own
|
|
68896
68987
|
* nearest position the same way — a frame with three people is three boxes,
|
|
68897
68988
|
* and a recording still (no track of its own) gets everything the pipeline
|
|
@@ -68946,6 +69037,18 @@ function toRetrainClassTiers(className, label) {
|
|
|
68946
69037
|
label: kept
|
|
68947
69038
|
} : { macroClass };
|
|
68948
69039
|
}
|
|
69040
|
+
/** A verdict on the wire: the operator's class split into tiers once, here. */
|
|
69041
|
+
function toRetrainVerdict(input) {
|
|
69042
|
+
const tiers = input.suggestedClass !== void 0 ? toRetrainClassTiers(input.suggestedClass, void 0) : null;
|
|
69043
|
+
return {
|
|
69044
|
+
verdict: input.verdict,
|
|
69045
|
+
trackId: input.trackId,
|
|
69046
|
+
...input.suggestedClass !== void 0 ? { suggestedClass: input.suggestedClass } : {},
|
|
69047
|
+
...tiers !== null ? { suggestedMacroClass: tiers.macroClass } : {},
|
|
69048
|
+
...tiers?.label !== void 0 ? { suggestedLabel: tiers.label } : {},
|
|
69049
|
+
...input.at !== void 0 ? { at: input.at } : {}
|
|
69050
|
+
};
|
|
69051
|
+
}
|
|
68949
69052
|
/** A pixel box as a clamped normalised one, or `null` when nothing is left. */
|
|
68950
69053
|
function normalise(box, frameWidth, frameHeight) {
|
|
68951
69054
|
if (frameWidth === void 0 || frameHeight === void 0) return null;
|
|
@@ -69003,6 +69106,22 @@ function boxFor(track, bbox, basis, deltaMs, score) {
|
|
|
69003
69106
|
deltaMs
|
|
69004
69107
|
};
|
|
69005
69108
|
}
|
|
69109
|
+
/** The box a notification verdict froze, or `null` when the event carried none. */
|
|
69110
|
+
function boxOfVerdict(subject) {
|
|
69111
|
+
const box = subject.bbox;
|
|
69112
|
+
if (box === void 0) return null;
|
|
69113
|
+
const clamped = normalise(box, 1, 1);
|
|
69114
|
+
if (clamped === null) return null;
|
|
69115
|
+
const tiers = toRetrainClassTiers(subject.className, subject.label);
|
|
69116
|
+
return {
|
|
69117
|
+
bbox: clamped,
|
|
69118
|
+
macroClass: tiers.macroClass,
|
|
69119
|
+
...tiers.label !== void 0 ? { label: tiers.label } : {},
|
|
69120
|
+
trackId: subject.trackId,
|
|
69121
|
+
basis: "verdict",
|
|
69122
|
+
deltaMs: 0
|
|
69123
|
+
};
|
|
69124
|
+
}
|
|
69006
69125
|
/** The own track's box: the recorded subject box first, the nearest position second. */
|
|
69007
69126
|
function ownBox(input, tolerance) {
|
|
69008
69127
|
const track = input.ownTrack;
|
|
@@ -69025,10 +69144,13 @@ function pipelineBoxesForFrame(input) {
|
|
|
69025
69144
|
const out = [];
|
|
69026
69145
|
const own = ownBox(input, tolerance);
|
|
69027
69146
|
if (own !== null) out.push(own);
|
|
69147
|
+
const verdictBox = input.verdictSubject !== void 0 ? boxOfVerdict(input.verdictSubject) : null;
|
|
69148
|
+
if (verdictBox !== null) out.push(verdictBox);
|
|
69028
69149
|
const at = input.frame.capturedAt;
|
|
69029
69150
|
if (at === void 0) return out;
|
|
69030
69151
|
const ownId = input.frame.trackId;
|
|
69031
69152
|
const seen = new Set(ownId !== void 0 ? [ownId] : []);
|
|
69153
|
+
if (input.verdictSubject !== void 0) seen.add(input.verdictSubject.trackId);
|
|
69032
69154
|
for (const track of input.neighbours) {
|
|
69033
69155
|
if (seen.has(track.trackId) || track.deviceId !== input.frame.deviceId) continue;
|
|
69034
69156
|
seen.add(track.trackId);
|
|
@@ -70421,7 +70543,12 @@ Object.freeze({
|
|
|
70421
70543
|
"listSupportedEntityKinds"
|
|
70422
70544
|
],
|
|
70423
70545
|
"device-extension": ["listSlots", "listSources"],
|
|
70424
|
-
"device-provider": [
|
|
70546
|
+
"device-provider": [
|
|
70547
|
+
"classifyDiscoveryHits",
|
|
70548
|
+
"discoverDevices",
|
|
70549
|
+
"getDevices",
|
|
70550
|
+
"getDiscoveryModes"
|
|
70551
|
+
],
|
|
70425
70552
|
"device-removal-hook": ["listDeviceData"],
|
|
70426
70553
|
"failure-contribution": ["list"],
|
|
70427
70554
|
"llm": [
|
|
@@ -82273,10 +82400,18 @@ function parseActions(raw) {
|
|
|
82273
82400
|
for (const entry of raw) {
|
|
82274
82401
|
const parsed = NcRuleActionSchema.safeParse(entry);
|
|
82275
82402
|
if (!parsed.success) return null;
|
|
82276
|
-
out.push(parsed.data);
|
|
82403
|
+
out.push(normalizeNcAction(parsed.data));
|
|
82277
82404
|
}
|
|
82278
82405
|
return out;
|
|
82279
82406
|
}
|
|
82407
|
+
/** Does a stored row still carry a pre-D983 cap step (`deviceId`, no `target`)? */
|
|
82408
|
+
function hasLegacyTarget(raw) {
|
|
82409
|
+
if (!Array.isArray(raw)) return false;
|
|
82410
|
+
return raw.some((entry) => {
|
|
82411
|
+
const parsed = NcRuleActionSchema.safeParse(entry);
|
|
82412
|
+
return parsed.success && parsed.data.kind === "cap" && parsed.data.target === void 0;
|
|
82413
|
+
});
|
|
82414
|
+
}
|
|
82280
82415
|
function rowToSequence(id, body) {
|
|
82281
82416
|
const actions = parseActions(body.actions);
|
|
82282
82417
|
if (actions === null) return null;
|
|
@@ -82315,6 +82450,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82315
82450
|
try {
|
|
82316
82451
|
const { records } = await this.kind.query({ limit: 1e4 });
|
|
82317
82452
|
const next = /* @__PURE__ */ new Map();
|
|
82453
|
+
const legacyRows = [];
|
|
82318
82454
|
let skipped = 0;
|
|
82319
82455
|
for (const row of records) {
|
|
82320
82456
|
const sequence = rowToSequence(row.id, row.data);
|
|
@@ -82323,9 +82459,11 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82323
82459
|
continue;
|
|
82324
82460
|
}
|
|
82325
82461
|
next.set(sequence.id, sequence);
|
|
82462
|
+
if (hasLegacyTarget(row.data.actions)) legacyRows.push(row.id);
|
|
82326
82463
|
}
|
|
82327
82464
|
this.byId.clear();
|
|
82328
82465
|
for (const [id, seq] of next) this.byId.set(id, seq);
|
|
82466
|
+
await this.rewriteLegacyRows(legacyRows, records);
|
|
82329
82467
|
if (skipped > 0) this.logger.warn("sequence rows skipped as malformed — bindings to them will miss", { meta: { skipped } });
|
|
82330
82468
|
} catch (err) {
|
|
82331
82469
|
this.logger.warn("sequence load failed — keeping the sequences already in memory", { meta: {
|
|
@@ -82334,6 +82472,28 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82334
82472
|
} });
|
|
82335
82473
|
}
|
|
82336
82474
|
}
|
|
82475
|
+
/**
|
|
82476
|
+
* One-shot, idempotent (D983): a row whose steps still carry `deviceId` is
|
|
82477
|
+
* stored again in the target shape. A failed write leaves the row as it was —
|
|
82478
|
+
* it is still READ as the new shape — and is on the record.
|
|
82479
|
+
*/
|
|
82480
|
+
async rewriteLegacyRows(ids, records) {
|
|
82481
|
+
for (const id of ids) {
|
|
82482
|
+
const sequence = this.byId.get(id) ?? null;
|
|
82483
|
+
const row = records.find((r) => r.id === id);
|
|
82484
|
+
if (sequence === null || row === void 0) continue;
|
|
82485
|
+
try {
|
|
82486
|
+
const createdAt = row.data.createdAt;
|
|
82487
|
+
await this.persist(sequence, typeof createdAt === "number" && Number.isFinite(createdAt) ? createdAt : this.now(), this.now());
|
|
82488
|
+
this.logger.info("notification sequence rewritten to the target shape", { meta: { sequenceId: id } });
|
|
82489
|
+
} catch (err) {
|
|
82490
|
+
this.logger.warn("notification sequence could not be rewritten — it is still read as the target shape", { meta: {
|
|
82491
|
+
sequenceId: id,
|
|
82492
|
+
error: String(err)
|
|
82493
|
+
} });
|
|
82494
|
+
}
|
|
82495
|
+
}
|
|
82496
|
+
}
|
|
82337
82497
|
list() {
|
|
82338
82498
|
return [...this.byId.values()].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
82339
82499
|
}
|
|
@@ -82353,7 +82513,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82353
82513
|
id: randomUUID(),
|
|
82354
82514
|
name: input.name,
|
|
82355
82515
|
...input.minDelaySec !== void 0 ? { minDelaySec: input.minDelaySec } : {},
|
|
82356
|
-
actions:
|
|
82516
|
+
actions: input.actions.map(normalizeNcAction)
|
|
82357
82517
|
};
|
|
82358
82518
|
await this.persist(sequence, now, now);
|
|
82359
82519
|
this.byId.set(sequence.id, sequence);
|
|
@@ -82369,7 +82529,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82369
82529
|
id: sequence.id,
|
|
82370
82530
|
name: sequence.name,
|
|
82371
82531
|
...sequence.minDelaySec !== void 0 ? { minDelaySec: sequence.minDelaySec } : {},
|
|
82372
|
-
actions:
|
|
82532
|
+
actions: sequence.actions.map(normalizeNcAction)
|
|
82373
82533
|
};
|
|
82374
82534
|
await this.persist(stored, now, now);
|
|
82375
82535
|
this.byId.set(stored.id, stored);
|
|
@@ -82382,7 +82542,7 @@ var NcSequenceStore = class NcSequenceStore {
|
|
|
82382
82542
|
id: sequenceId,
|
|
82383
82543
|
name: input.name,
|
|
82384
82544
|
...input.minDelaySec !== void 0 ? { minDelaySec: input.minDelaySec } : {},
|
|
82385
|
-
actions:
|
|
82545
|
+
actions: input.actions.map(normalizeNcAction)
|
|
82386
82546
|
};
|
|
82387
82547
|
const createdAt = await this.readCreatedAt(sequenceId, now);
|
|
82388
82548
|
await this.persist(sequence, createdAt, now);
|
|
@@ -82450,7 +82610,11 @@ var NC_DETECTION_VERDICTS_COLUMNS = [
|
|
|
82450
82610
|
var NcDetectionVerdictRecordSchema = object({
|
|
82451
82611
|
trackId: string().min(1),
|
|
82452
82612
|
deviceId: number().int(),
|
|
82453
|
-
verdict: _enum([
|
|
82613
|
+
verdict: _enum([
|
|
82614
|
+
"correct",
|
|
82615
|
+
"wrong",
|
|
82616
|
+
"wrong-label"
|
|
82617
|
+
]),
|
|
82454
82618
|
/** The class the operator said it really was. Only with `wrong`. */
|
|
82455
82619
|
suggestedClass: string().min(1).optional(),
|
|
82456
82620
|
/** Whose notification it was (the token's frozen owner), NOT who tapped. */
|
|
@@ -82485,6 +82649,16 @@ var NcDetectionVerdictLedger = class {
|
|
|
82485
82649
|
logger: deps.logger
|
|
82486
82650
|
});
|
|
82487
82651
|
}
|
|
82652
|
+
/** The answers recorded for these tracks (a track nobody answered is absent).
|
|
82653
|
+
* Reads the in-memory mirror: no I/O, so a listing never waits on it. */
|
|
82654
|
+
forTracks(trackIds) {
|
|
82655
|
+
const out = /* @__PURE__ */ new Map();
|
|
82656
|
+
for (const id of trackIds) {
|
|
82657
|
+
const row = this.ledger.get(id);
|
|
82658
|
+
if (row !== void 0) out.set(id, row);
|
|
82659
|
+
}
|
|
82660
|
+
return out;
|
|
82661
|
+
}
|
|
82488
82662
|
/** Write-through; throws on a failed write so the tap reports it. */
|
|
82489
82663
|
async record(row) {
|
|
82490
82664
|
await this.ledger.put(NcDetectionVerdictRecordSchema.parse(row));
|
|
@@ -82618,10 +82792,33 @@ function pruneHookBindings(bindings, field, isDead, notes) {
|
|
|
82618
82792
|
kept.push(binding);
|
|
82619
82793
|
continue;
|
|
82620
82794
|
}
|
|
82621
|
-
if (binding.action.kind === "cap"
|
|
82622
|
-
|
|
82623
|
-
|
|
82624
|
-
|
|
82795
|
+
if (binding.action.kind === "cap") {
|
|
82796
|
+
const { target } = normalizeNcCapTarget(binding.action);
|
|
82797
|
+
if (target.kind === "devices") {
|
|
82798
|
+
const dead = target.ids.filter(isDead);
|
|
82799
|
+
if (dead.length === target.ids.length) {
|
|
82800
|
+
changed = true;
|
|
82801
|
+
notes.removed(field, deviceSubjects(dead), "binding-removed");
|
|
82802
|
+
continue;
|
|
82803
|
+
}
|
|
82804
|
+
if (dead.length > 0) {
|
|
82805
|
+
changed = true;
|
|
82806
|
+
notes.removed(field, deviceSubjects(dead), "binding-removed");
|
|
82807
|
+
const alive = target.ids.filter((id) => !isDead(id));
|
|
82808
|
+
kept.push({
|
|
82809
|
+
...binding,
|
|
82810
|
+
action: normalizeNcAction({
|
|
82811
|
+
...binding.action,
|
|
82812
|
+
deviceId: void 0,
|
|
82813
|
+
target: {
|
|
82814
|
+
kind: "devices",
|
|
82815
|
+
ids: alive
|
|
82816
|
+
}
|
|
82817
|
+
})
|
|
82818
|
+
});
|
|
82819
|
+
continue;
|
|
82820
|
+
}
|
|
82821
|
+
}
|
|
82625
82822
|
}
|
|
82626
82823
|
kept.push(binding);
|
|
82627
82824
|
}
|
|
@@ -82777,7 +82974,11 @@ function referencedDeviceIds(snapshot) {
|
|
|
82777
82974
|
add(c.systemEvent?.deviceIds);
|
|
82778
82975
|
if (c.deviceState !== void 0) ids.add(c.deviceState.deviceId);
|
|
82779
82976
|
if (c.scene?.deviceId !== void 0) ids.add(c.scene.deviceId);
|
|
82780
|
-
for (const binding of [...rule.actions?.onTrigger ?? [], ...rule.actions?.onTriggerOff ?? []])
|
|
82977
|
+
for (const binding of [...rule.actions?.onTrigger ?? [], ...rule.actions?.onTriggerOff ?? []]) {
|
|
82978
|
+
if (binding.kind !== "do" || binding.action.kind !== "cap") continue;
|
|
82979
|
+
const { target } = normalizeNcCapTarget(binding.action);
|
|
82980
|
+
if (target.kind === "devices") add(target.ids);
|
|
82981
|
+
}
|
|
82781
82982
|
}
|
|
82782
82983
|
for (const rule of snapshot.timelapse) add(rule.deviceIds);
|
|
82783
82984
|
for (const rule of snapshot.summary) add(rule.deviceIds);
|
|
@@ -83448,6 +83649,8 @@ var NcDeviceStateCache = class {
|
|
|
83448
83649
|
//#region src/notification-center/detection-verdict-buttons.ts
|
|
83449
83650
|
var VERDICT_CORRECT_ID = "detection-verdict-correct";
|
|
83450
83651
|
var VERDICT_WRONG_ID = "detection-verdict-wrong";
|
|
83652
|
+
/** Wrong LABEL, right class — only for a subject that has a label. */
|
|
83653
|
+
var VERDICT_WRONG_LABEL_ID = "detection-verdict-wrong-label";
|
|
83451
83654
|
/** `detection-verdict-wrong:<class>` — Wrong, and what it really was. */
|
|
83452
83655
|
var VERDICT_WRONG_AS_PREFIX = "detection-verdict-wrong:";
|
|
83453
83656
|
/** The macro classes offered as "it was really…". Short on purpose. */
|
|
@@ -83464,6 +83667,15 @@ function verdictButtonRefusal(subject) {
|
|
|
83464
83667
|
if (subject.ownerUserId === void 0) return "unowned-target";
|
|
83465
83668
|
return null;
|
|
83466
83669
|
}
|
|
83670
|
+
/**
|
|
83671
|
+
* Does the notified subject carry a label below its macro class — a sub-class
|
|
83672
|
+
* (`dog` under `animal`), a recognised name, a plate? The same tier split the
|
|
83673
|
+
* retrain annotations use, so "has a label" means one thing in both places.
|
|
83674
|
+
*/
|
|
83675
|
+
function subjectHasLabel(className, label) {
|
|
83676
|
+
if (className === void 0) return false;
|
|
83677
|
+
return toRetrainClassTiers(className, label).label !== void 0;
|
|
83678
|
+
}
|
|
83467
83679
|
/** The classes worth offering for a subject of `className`. */
|
|
83468
83680
|
function verdictClassChoices(className) {
|
|
83469
83681
|
return VERDICT_CLASS_CHOICES.filter((c) => c !== className).slice(0, VERDICT_MAX_CLASS_CHOICES);
|
|
@@ -83475,13 +83687,17 @@ function verdictClassChoices(className) {
|
|
|
83475
83687
|
* when the channel declared room for a fourth button.
|
|
83476
83688
|
*/
|
|
83477
83689
|
function verdictButtonSpecs(input) {
|
|
83478
|
-
const
|
|
83690
|
+
const plain = [{
|
|
83479
83691
|
actionId: VERDICT_CORRECT_ID,
|
|
83480
83692
|
verdict: "correct"
|
|
83481
83693
|
}, {
|
|
83482
83694
|
actionId: VERDICT_WRONG_ID,
|
|
83483
83695
|
verdict: "wrong"
|
|
83484
83696
|
}];
|
|
83697
|
+
const base = subjectHasLabel(input.className, input.label) ? [...plain, {
|
|
83698
|
+
actionId: VERDICT_WRONG_LABEL_ID,
|
|
83699
|
+
verdict: "wrong-label"
|
|
83700
|
+
}] : plain;
|
|
83485
83701
|
if (input.maxActions === void 0 || input.maxActions <= 3) return base;
|
|
83486
83702
|
const room = input.maxActions - base.length;
|
|
83487
83703
|
const extra = verdictClassChoices(input.className).slice(0, room).map((suggestedClass) => ({
|
|
@@ -87212,6 +87428,8 @@ var NcDispatcher = class {
|
|
|
87212
87428
|
...target.ownerUserId !== void 0 ? { ownerUserId: target.ownerUserId } : {},
|
|
87213
87429
|
...entry.payload.subject.className !== void 0 ? { className: entry.payload.subject.className } : {},
|
|
87214
87430
|
...entry.payload.subject.trackId !== void 0 ? { trackId: entry.payload.subject.trackId } : {},
|
|
87431
|
+
...entry.payload.subject.label !== void 0 ? { label: entry.payload.subject.label } : {},
|
|
87432
|
+
...entry.payload.subject.bbox !== void 0 ? { bbox: entry.payload.subject.bbox } : {},
|
|
87215
87433
|
eventAtMs: entry.payload.subject.timestamp,
|
|
87216
87434
|
...target.maxActions !== void 0 ? { maxActions: target.maxActions } : {},
|
|
87217
87435
|
...entry.payload.subject.systemEvent !== void 0 ? { systemEvent: entry.payload.subject.systemEvent } : {}
|
|
@@ -91575,7 +91793,7 @@ var NcRuleActionRunner = class {
|
|
|
91575
91793
|
if (sequence.throttle) this.lastRunAt.set(sequence.id, now);
|
|
91576
91794
|
let stepsRun = 0;
|
|
91577
91795
|
for (const [index, action] of sequence.actions.entries()) try {
|
|
91578
|
-
await this.step(action, input.origin);
|
|
91796
|
+
await this.step(action, input.origin, sequence.id);
|
|
91579
91797
|
stepsRun += 1;
|
|
91580
91798
|
} catch (err) {
|
|
91581
91799
|
this.deps.logger.warn("rule action failed — stopping the sequence", {
|
|
@@ -91615,20 +91833,28 @@ var NcRuleActionRunner = class {
|
|
|
91615
91833
|
stepsRun
|
|
91616
91834
|
};
|
|
91617
91835
|
}
|
|
91618
|
-
async step(action, origin) {
|
|
91836
|
+
async step(action, origin, owner) {
|
|
91619
91837
|
if (action.kind === "wait") {
|
|
91620
91838
|
await this.sleep(action.seconds * 1e3);
|
|
91621
91839
|
return;
|
|
91622
91840
|
}
|
|
91623
91841
|
if (!this.deps.isDeviceScoped(action.cap)) throw new Error(`rule action refused: "${action.cap}" is not a device capability — only devices can be actuated`);
|
|
91624
|
-
|
|
91625
|
-
|
|
91842
|
+
const { target, actionId } = normalizeNcCapTarget(action);
|
|
91843
|
+
const deviceIds = await this.devicesOf(target, actionId, owner);
|
|
91844
|
+
for (const deviceId of deviceIds) await this.deps.actuate({
|
|
91845
|
+
deviceId,
|
|
91626
91846
|
cap: action.cap,
|
|
91627
91847
|
method: action.method,
|
|
91628
91848
|
args: action.args ?? {},
|
|
91629
91849
|
...origin !== void 0 ? { origin } : {}
|
|
91630
91850
|
});
|
|
91631
91851
|
}
|
|
91852
|
+
async devicesOf(target, actionId, owner) {
|
|
91853
|
+
if (target.kind === "devices") return target.ids;
|
|
91854
|
+
if (target.kind === "var") throw new Error("rule action refused: a sequence has no variables to resolve a target from");
|
|
91855
|
+
if (this.deps.resolveTarget === void 0) throw new Error("rule action refused: this node cannot resolve a domain target");
|
|
91856
|
+
return (await this.deps.resolveTarget(target, actionId, owner)).deviceIds;
|
|
91857
|
+
}
|
|
91632
91858
|
};
|
|
91633
91859
|
/** Force-enable a shared sequence for a button tap (always runs, still throttled). */
|
|
91634
91860
|
function asForcedSequence(sequence) {
|
|
@@ -97219,6 +97445,15 @@ var NcSystemStateLedger = class {
|
|
|
97219
97445
|
closeLegacyStreamKeys() {
|
|
97220
97446
|
this.closeWhere("legacy-stream-keys", (row) => row.key.startsWith("stream:") && !DEVICE_STREAM_KEY.test(row.key), "legacy-stream-key");
|
|
97221
97447
|
}
|
|
97448
|
+
/**
|
|
97449
|
+
* Is this device switched off RIGHT NOW (an open `device-disabled` row)?
|
|
97450
|
+
* D62: the switch outranks the device's liveness and its streams — a device
|
|
97451
|
+
* somebody turned off is reported off, never as offline. `false` while the
|
|
97452
|
+
* store has never answered (unknown is never "disabled").
|
|
97453
|
+
*/
|
|
97454
|
+
isDeviceDisabled(deviceId) {
|
|
97455
|
+
return (this.openRows() ?? []).some((row) => row.kind === "device-disabled" && row.deviceId === deviceId);
|
|
97456
|
+
}
|
|
97222
97457
|
/** The open rows, or `null` while the store has never answered. */
|
|
97223
97458
|
openRows() {
|
|
97224
97459
|
return this.deps.rows.answered() ? this.deps.rows.all() : null;
|
|
@@ -97725,6 +97960,7 @@ function readRaw(section, ports, nowMs) {
|
|
|
97725
97960
|
reason: UNREAD_STATE
|
|
97726
97961
|
};
|
|
97727
97962
|
const items = [];
|
|
97963
|
+
const switchedOff = new Set(state.flatMap((row) => row.kind === "device-disabled" && row.deviceId !== void 0 ? [row.deviceId] : []));
|
|
97728
97964
|
if (section === "device-state" || section === "nodes") {
|
|
97729
97965
|
const liveness = ports.liveness();
|
|
97730
97966
|
if (liveness === null) return {
|
|
@@ -97734,10 +97970,14 @@ function readRaw(section, ports, nowMs) {
|
|
|
97734
97970
|
for (const row of liveness) {
|
|
97735
97971
|
if (row.state !== "offline" || nowMs - row.updatedAt < ports.livenessHoldoffMs) continue;
|
|
97736
97972
|
const item = livenessItem(row, ports.lookup);
|
|
97737
|
-
if (item
|
|
97973
|
+
if (item === null || item.section !== section) continue;
|
|
97974
|
+
const id = item.subject.deviceId;
|
|
97975
|
+
if (id !== void 0 && switchedOff.has(id)) continue;
|
|
97976
|
+
items.push(item);
|
|
97738
97977
|
}
|
|
97739
97978
|
}
|
|
97740
97979
|
for (const row of state) {
|
|
97980
|
+
if (row.kind === "stream-offline" && row.deviceId !== void 0 && switchedOff.has(row.deviceId)) continue;
|
|
97741
97981
|
const item = stateItem(row, ports.lookup, ports.nodes);
|
|
97742
97982
|
if (item.section === section) items.push(item);
|
|
97743
97983
|
}
|
|
@@ -100002,6 +100242,12 @@ function rowRefusalId(systemEvent) {
|
|
|
100002
100242
|
const value = systemEvent?.data["refusalId"];
|
|
100003
100243
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
100004
100244
|
}
|
|
100245
|
+
/** The text-catalog key of each verdict button. */
|
|
100246
|
+
var VERDICT_LABEL_KEY = {
|
|
100247
|
+
correct: "button.verdict.correct",
|
|
100248
|
+
wrong: "button.verdict.wrong",
|
|
100249
|
+
"wrong-label": "button.verdict.wrongLabel"
|
|
100250
|
+
};
|
|
100005
100251
|
/**
|
|
100006
100252
|
* The tap-through buttons for ONE delivery, each with its own single-use
|
|
100007
100253
|
* token.
|
|
@@ -100038,6 +100284,7 @@ async function mintButtons(host, input) {
|
|
|
100038
100284
|
const verdictOwner = input.ownerUserId;
|
|
100039
100285
|
const verdictButtons = verdictRefusal !== null || verdictTrackId === void 0 || verdictOwner === void 0 ? [] : verdictButtonSpecs({
|
|
100040
100286
|
className: input.className,
|
|
100287
|
+
label: input.label,
|
|
100041
100288
|
maxActions: input.maxActions
|
|
100042
100289
|
}).map((spec) => {
|
|
100043
100290
|
const id = randomUUID();
|
|
@@ -100055,6 +100302,11 @@ async function mintButtons(host, input) {
|
|
|
100055
100302
|
...spec.suggestedClass !== void 0 ? { suggestedClass: spec.suggestedClass } : {},
|
|
100056
100303
|
...input.maxActions === 0 ? { viaLink: true } : {},
|
|
100057
100304
|
...input.eventAtMs !== void 0 ? { eventAtMs: input.eventAtMs } : {},
|
|
100305
|
+
...input.className !== void 0 ? { subject: {
|
|
100306
|
+
className: input.className,
|
|
100307
|
+
...input.label !== void 0 ? { label: input.label } : {},
|
|
100308
|
+
...input.bbox !== void 0 ? { bbox: input.bbox } : {}
|
|
100309
|
+
} } : {},
|
|
100058
100310
|
deviceId: input.deviceId,
|
|
100059
100311
|
actionId: spec.actionId,
|
|
100060
100312
|
targetId: input.targetId,
|
|
@@ -100063,7 +100315,7 @@ async function mintButtons(host, input) {
|
|
|
100063
100315
|
});
|
|
100064
100316
|
return {
|
|
100065
100317
|
id: spec.actionId,
|
|
100066
|
-
label: spec.suggestedClass === void 0 ? host.texts.text({ key: spec.verdict
|
|
100318
|
+
label: spec.suggestedClass === void 0 ? host.texts.text({ key: VERDICT_LABEL_KEY[spec.verdict] }) : host.texts.text({
|
|
100067
100319
|
key: "button.verdict.wrongAs",
|
|
100068
100320
|
vars: { class: spec.suggestedClass }
|
|
100069
100321
|
}),
|
|
@@ -100522,6 +100774,18 @@ var VERDICT_FRAME_REFUSAL_COPY = {
|
|
|
100522
100774
|
"still-busy": "The track is gone and the recorder is busy — nothing was changed, try again later",
|
|
100523
100775
|
failed: "The track is gone and its frame could not be saved — nothing was changed"
|
|
100524
100776
|
};
|
|
100777
|
+
/** The grant's frozen subject, in the shape the retrain still stores it in. */
|
|
100778
|
+
function verdictSubjectOf(grant, subject, at) {
|
|
100779
|
+
return {
|
|
100780
|
+
trackId: grant.trackId,
|
|
100781
|
+
className: subject.className,
|
|
100782
|
+
...subject.label !== void 0 ? { label: subject.label } : {},
|
|
100783
|
+
...subject.bbox !== void 0 ? { bbox: subject.bbox } : {},
|
|
100784
|
+
verdict: grant.verdict,
|
|
100785
|
+
...grant.suggestedClass !== void 0 ? { suggestedClass: grant.suggestedClass } : {},
|
|
100786
|
+
at
|
|
100787
|
+
};
|
|
100788
|
+
}
|
|
100525
100789
|
/**
|
|
100526
100790
|
* The track is gone (aged out, or discarded as a still object): the camera's
|
|
100527
100791
|
* RECORDING still covers the instant, so the verdict lands a frame there
|
|
@@ -100544,7 +100808,8 @@ async function landFrameFromRecording(host, grant) {
|
|
|
100544
100808
|
}
|
|
100545
100809
|
const landed = await addFrame({
|
|
100546
100810
|
deviceId: grant.deviceId,
|
|
100547
|
-
epochMs: grant.eventAtMs
|
|
100811
|
+
epochMs: grant.eventAtMs,
|
|
100812
|
+
...grant.subject !== void 0 ? { subject: verdictSubjectOf(grant, grant.subject, host.now()) } : {}
|
|
100548
100813
|
});
|
|
100549
100814
|
if (!landed.ok) {
|
|
100550
100815
|
host.logger.warn("detection verdict dropped — the track is gone and the recording refused", {
|
|
@@ -101505,6 +101770,7 @@ function buildEntries(host, rule, subject, kind, matchedOn, userTargets = [], or
|
|
|
101505
101770
|
...alarmSource?.eventId !== void 0 ? { eventId: alarmSource.eventId } : hasEventMedia ? { eventId: subject.recordId } : {},
|
|
101506
101771
|
className: subject.classNames[0] ?? subject.sensorKind ?? "event",
|
|
101507
101772
|
...subject.label !== void 0 ? { label: subject.label } : {},
|
|
101773
|
+
...subject.bbox !== void 0 ? { bbox: subject.bbox } : {},
|
|
101508
101774
|
...subject.labelKind !== void 0 ? { labelKind: subject.labelKind } : {},
|
|
101509
101775
|
...subject.confidence !== void 0 ? { confidence: subject.confidence } : {},
|
|
101510
101776
|
zones: renderedZones,
|
|
@@ -101902,16 +102168,37 @@ function onSystemEvent(host, event) {
|
|
|
101902
102168
|
return;
|
|
101903
102169
|
}
|
|
101904
102170
|
if (isDeviceLivenessKind(observation.kind)) {
|
|
102171
|
+
if (dropSwitchedOff(host, system)) return;
|
|
101905
102172
|
holdoff.arm(observation.subject, incoming);
|
|
101906
102173
|
return;
|
|
101907
102174
|
}
|
|
101908
102175
|
} else if (system !== void 0 && (system.kind.startsWith("device-") || system.kind.startsWith("stream-"))) {
|
|
101909
102176
|
if (host.lastSystemLiveness.get(system.subject) === system.kind) return;
|
|
101910
102177
|
host.lastSystemLiveness.set(system.subject, system.kind);
|
|
102178
|
+
if (dropSwitchedOff(host, system)) return;
|
|
101911
102179
|
}
|
|
101912
102180
|
host.consumeEvent(incoming);
|
|
101913
102181
|
}
|
|
101914
102182
|
/**
|
|
102183
|
+
* D62: a device the operator switched off is reported off, never offline. Its
|
|
102184
|
+
* `device-offline` / `stream-offline` is dropped (and said) while an open
|
|
102185
|
+
* `device-disabled` row exists — the broker of a disabled device stopping is
|
|
102186
|
+
* the switch working, not an outage. The memory of the flip is already kept.
|
|
102187
|
+
*/
|
|
102188
|
+
function dropSwitchedOff(host, system) {
|
|
102189
|
+
if (system === void 0 || system.deviceId === void 0) return false;
|
|
102190
|
+
if (system.kind !== "device-offline" && system.kind !== "stream-offline") return false;
|
|
102191
|
+
if (!host.stateSummary.ledger.isDeviceDisabled(system.deviceId)) return false;
|
|
102192
|
+
host.logger.debug("offline not notified — the device is switched off (D62)", {
|
|
102193
|
+
tags: { deviceId: system.deviceId },
|
|
102194
|
+
meta: {
|
|
102195
|
+
kind: system.kind,
|
|
102196
|
+
subject: system.subject
|
|
102197
|
+
}
|
|
102198
|
+
});
|
|
102199
|
+
return true;
|
|
102200
|
+
}
|
|
102201
|
+
/**
|
|
101915
102202
|
* Arm a node liveness flip. An OFFLINE of a node that announced an update is
|
|
101916
102203
|
* held for the whole planned-update window and worded as such if it is still
|
|
101917
102204
|
* sent; the node's return inside the window is logged and sends nothing (the
|
|
@@ -105804,6 +106091,8 @@ var NotificationCenter = class {
|
|
|
105804
106091
|
zoneOwners;
|
|
105805
106092
|
/** Runs a matched rule's action sequences. Null when no actuator is wired. */
|
|
105806
106093
|
actionRunner;
|
|
106094
|
+
/** Domain action targets read from the registry (D983); null without a directory. */
|
|
106095
|
+
actionTargets;
|
|
105807
106096
|
/** Grants behind the tap-through buttons. In-process and single-use — read
|
|
105808
106097
|
* `action-token.ts` before assuming anything about what a tap proves. */
|
|
105809
106098
|
actionTokens = new NcActionTokenRegistry();
|
|
@@ -106211,9 +106500,15 @@ var NotificationCenter = class {
|
|
|
106211
106500
|
});
|
|
106212
106501
|
this.livenessHoldoff = new DeviceLivenessHoldoff((event) => this.consumeEvent(event), { delayMs: deps.livenessHoldoffMs ?? (process.env.VITEST ? 0 : DEVICE_LIVENESS_HOLDOFF_MS) });
|
|
106213
106502
|
this.nodeLivenessHoldoff = new DeviceLivenessHoldoff((event, timing) => this.consumeNodeLiveness(event, timing), { delayMs: deps.nodeLivenessHoldoffMs ?? (process.env.VITEST ? 0 : NODE_LIVENESS_HOLDOFF_MS) });
|
|
106503
|
+
const targets = deps.actionTargetDirectory !== void 0 ? new ActionTargetResolver(deps.actionTargetDirectory, this.logger.child("actions")) : null;
|
|
106504
|
+
this.actionTargets = targets;
|
|
106214
106505
|
this.actionRunner = deps.actuate !== void 0 && deps.isCapDeviceScoped !== void 0 ? new NcRuleActionRunner({
|
|
106215
106506
|
logger: this.logger.child("actions"),
|
|
106216
106507
|
actuate: (input) => this.actuateWithAttribution(input),
|
|
106508
|
+
...targets !== null ? { resolveTarget: (target, actionId, owner) => targets.resolve(target, actionId, {}, {
|
|
106509
|
+
actionId,
|
|
106510
|
+
owner
|
|
106511
|
+
}) } : {},
|
|
106217
106512
|
isDeviceScoped: deps.isCapDeviceScoped,
|
|
106218
106513
|
...deps.now !== void 0 ? { now: deps.now } : {}
|
|
106219
106514
|
}) : null;
|
|
@@ -107003,6 +107298,21 @@ var NotificationCenter = class {
|
|
|
107003
107298
|
}
|
|
107004
107299
|
};
|
|
107005
107300
|
}
|
|
107301
|
+
/**
|
|
107302
|
+
* What the operator answered to the "was this detection right?" buttons, for
|
|
107303
|
+
* the retrain page: by track, the latest answer wins. A track nobody answered
|
|
107304
|
+
* is absent.
|
|
107305
|
+
*/
|
|
107306
|
+
detectionVerdictsOf(trackIds) {
|
|
107307
|
+
const out = /* @__PURE__ */ new Map();
|
|
107308
|
+
for (const [trackId, row] of this.verdicts.forTracks(trackIds)) out.set(trackId, toRetrainVerdict({
|
|
107309
|
+
verdict: row.verdict,
|
|
107310
|
+
trackId,
|
|
107311
|
+
at: row.at,
|
|
107312
|
+
suggestedClass: row.suggestedClass
|
|
107313
|
+
}));
|
|
107314
|
+
return out;
|
|
107315
|
+
}
|
|
107006
107316
|
observeOccupancy(deviceId, snapshot, evidence = "scene") {
|
|
107007
107317
|
return observeOccupancy(this.occupancyWatchHost(), deviceId, snapshot, evidence);
|
|
107008
107318
|
}
|
|
@@ -107178,6 +107488,10 @@ var NotificationCenter = class {
|
|
|
107178
107488
|
}
|
|
107179
107489
|
};
|
|
107180
107490
|
}
|
|
107491
|
+
/** The device registry changed: a cached domain action target is stale (D983). */
|
|
107492
|
+
invalidateActionTargets() {
|
|
107493
|
+
this.actionTargets?.invalidate();
|
|
107494
|
+
}
|
|
107181
107495
|
onSystemEvent(event) {
|
|
107182
107496
|
return onSystemEvent(this.systemEventsHost(), event);
|
|
107183
107497
|
}
|
|
@@ -108651,7 +108965,9 @@ async function addVerdictFrameFromRecording(api, logger, input) {
|
|
|
108651
108965
|
}
|
|
108652
108966
|
const result = await api.addRetrainFrameFromRecording({
|
|
108653
108967
|
collectionId,
|
|
108654
|
-
|
|
108968
|
+
deviceId: input.deviceId,
|
|
108969
|
+
epochMs: input.epochMs,
|
|
108970
|
+
...input.subject !== void 0 ? { verdictSubject: input.subject } : {}
|
|
108655
108971
|
});
|
|
108656
108972
|
if (result.kind === "added") return { ok: true };
|
|
108657
108973
|
return {
|
|
@@ -108938,6 +109254,20 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
|
|
|
108938
109254
|
});
|
|
108939
109255
|
},
|
|
108940
109256
|
isCapDeviceScoped: isDeviceScopedCap,
|
|
109257
|
+
actionTargetDirectory: {
|
|
109258
|
+
listDeviceIdsByCaps: (capNames) => api.deviceManager.listDeviceIdsByCaps.query({ capNames: [...capNames] }),
|
|
109259
|
+
listDevices: async (deviceIds) => {
|
|
109260
|
+
return (await api.deviceManager.listAll.query({
|
|
109261
|
+
deviceIds: [...deviceIds],
|
|
109262
|
+
projection: "slim"
|
|
109263
|
+
})).map((d) => ({
|
|
109264
|
+
id: d.id,
|
|
109265
|
+
type: d.type,
|
|
109266
|
+
location: d.location,
|
|
109267
|
+
disabled: d.disabled
|
|
109268
|
+
}));
|
|
109269
|
+
}
|
|
109270
|
+
},
|
|
108941
109271
|
generateVision: async (request) => {
|
|
108942
109272
|
const result = await api.llm.generateVision.mutate({
|
|
108943
109273
|
...request.profileId !== void 0 ? { profileId: request.profileId } : {},
|
|
@@ -113102,7 +113432,10 @@ var PackageDropDetector = class {
|
|
|
113102
113432
|
}
|
|
113103
113433
|
if (outcome.kind === "yes") {
|
|
113104
113434
|
this.reportLlmSilence(this.llmSilence.noteAnswered(entry.deviceId), tags);
|
|
113105
|
-
return {
|
|
113435
|
+
return {
|
|
113436
|
+
packageLlmVerdict: outcome.verdict,
|
|
113437
|
+
confirmElapsedMs: latencyMs
|
|
113438
|
+
};
|
|
113106
113439
|
}
|
|
113107
113440
|
this.dropUncertain(entry, tags, outcome.reason, latencyMs);
|
|
113108
113441
|
return null;
|
|
@@ -113294,10 +113627,12 @@ var PackageDropDetector = class {
|
|
|
113294
113627
|
}
|
|
113295
113628
|
const llm = await this.llmFields(entry, rules, hitZones, tags);
|
|
113296
113629
|
if (llm === null) return;
|
|
113630
|
+
const { confirmElapsedMs, ...llmRowFields } = llm;
|
|
113631
|
+
const rowTimestamp = timestamp + (confirmElapsedMs ?? 0);
|
|
113297
113632
|
const draft = {
|
|
113298
113633
|
kind: "object",
|
|
113299
113634
|
deviceId: entry.deviceId,
|
|
113300
|
-
timestamp,
|
|
113635
|
+
timestamp: rowTimestamp,
|
|
113301
113636
|
source: "pipeline",
|
|
113302
113637
|
idempotencyKey,
|
|
113303
113638
|
trackId: entry.sourceTrackId ?? entry.id,
|
|
@@ -113316,7 +113651,7 @@ var PackageDropDetector = class {
|
|
|
113316
113651
|
frameHeight: entry.frameHeight,
|
|
113317
113652
|
...entry.keyFrameMediaId !== void 0 ? { mediaId: entry.keyFrameMediaId } : {},
|
|
113318
113653
|
importance: PACKAGE_IMPORTANCE,
|
|
113319
|
-
...
|
|
113654
|
+
...llmRowFields
|
|
113320
113655
|
};
|
|
113321
113656
|
const rowId = await this.deps.events.insertObject(draft);
|
|
113322
113657
|
if (rowId === null) {
|
|
@@ -113347,7 +113682,7 @@ var PackageDropDetector = class {
|
|
|
113347
113682
|
w: entry.bbox.w,
|
|
113348
113683
|
h: entry.bbox.h
|
|
113349
113684
|
},
|
|
113350
|
-
timestamp
|
|
113685
|
+
timestamp: rowTimestamp
|
|
113351
113686
|
});
|
|
113352
113687
|
this.deps.logger.info("package delivered — all 5 gates passed", {
|
|
113353
113688
|
tags: { deviceId: entry.deviceId },
|
|
@@ -113358,6 +113693,7 @@ var PackageDropDetector = class {
|
|
|
113358
113693
|
zoneIds: hitZones,
|
|
113359
113694
|
dwellMs,
|
|
113360
113695
|
areaFrac: Number(minAreaFrac.toFixed(5)),
|
|
113696
|
+
...confirmElapsedMs !== void 0 ? { footageAnchorShiftMs: confirmElapsedMs } : {},
|
|
113361
113697
|
...entry.confidence !== void 0 ? { confidence: entry.confidence } : {}
|
|
113362
113698
|
}
|
|
113363
113699
|
});
|
|
@@ -113557,7 +113893,7 @@ var RetrainCollectionService = class {
|
|
|
113557
113893
|
frameId: recordingFrameId(input.deviceId, input.epochMs),
|
|
113558
113894
|
deviceId: input.deviceId
|
|
113559
113895
|
});
|
|
113560
|
-
const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp]);
|
|
113896
|
+
const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp], input.verdictSubject);
|
|
113561
113897
|
if (stored.kind === "refused") {
|
|
113562
113898
|
this.deps.logger.info("retrain collection add refused", {
|
|
113563
113899
|
tags,
|
|
@@ -113569,11 +113905,15 @@ var RetrainCollectionService = class {
|
|
|
113569
113905
|
});
|
|
113570
113906
|
return stored;
|
|
113571
113907
|
}
|
|
113572
|
-
|
|
113573
|
-
|
|
113574
|
-
|
|
113575
|
-
|
|
113576
|
-
|
|
113908
|
+
let storedFrame = stored.frame;
|
|
113909
|
+
if (stored.reused) {
|
|
113910
|
+
await this.deps.collections.addFrame({
|
|
113911
|
+
collectionId: input.collectionId,
|
|
113912
|
+
frameId: stored.frame.frameId,
|
|
113913
|
+
deviceId: input.deviceId
|
|
113914
|
+
});
|
|
113915
|
+
if (input.verdictSubject !== void 0) storedFrame = await this.deps.frames.setVerdictSubject(stored.frame.frameId, input.verdictSubject) ?? stored.frame;
|
|
113916
|
+
}
|
|
113577
113917
|
this.deps.logger.info("retrain frame added to a collection from the recording", {
|
|
113578
113918
|
tags,
|
|
113579
113919
|
meta: {
|
|
@@ -113583,7 +113923,7 @@ var RetrainCollectionService = class {
|
|
|
113583
113923
|
reused: stored.reused
|
|
113584
113924
|
}
|
|
113585
113925
|
});
|
|
113586
|
-
const [wire] = await this.deps.retrain.toWireFrames([
|
|
113926
|
+
const [wire] = await this.deps.retrain.toWireFrames([storedFrame]);
|
|
113587
113927
|
if (wire === void 0) throw new Error("retrain: a stored still produced no wire frame");
|
|
113588
113928
|
return {
|
|
113589
113929
|
kind: "added",
|
|
@@ -113634,7 +113974,7 @@ var RetrainCollectionService = class {
|
|
|
113634
113974
|
const ids = await this.deps.collections.listFrameIds(input.collectionId);
|
|
113635
113975
|
return this.deps.retrain.toWireFrames(await this.deps.frames.getMany(ids));
|
|
113636
113976
|
}
|
|
113637
|
-
async storeStill(deviceId, epochMs, withRow) {
|
|
113977
|
+
async storeStill(deviceId, epochMs, withRow, verdictSubject) {
|
|
113638
113978
|
const existing = await this.deps.frames.get(recordingFrameId(deviceId, epochMs));
|
|
113639
113979
|
if (existing !== null) return {
|
|
113640
113980
|
kind: "stored",
|
|
@@ -113653,7 +113993,8 @@ var RetrainCollectionService = class {
|
|
|
113653
113993
|
const stored = await this.deps.frames.storeRecordingStill({
|
|
113654
113994
|
deviceId,
|
|
113655
113995
|
epochMs,
|
|
113656
|
-
bytes: still.jpeg
|
|
113996
|
+
bytes: still.jpeg,
|
|
113997
|
+
...verdictSubject !== void 0 ? { verdictSubject } : {}
|
|
113657
113998
|
}, withRow);
|
|
113658
113999
|
return stored.kind === "copied" ? {
|
|
113659
114000
|
kind: "stored",
|
|
@@ -114439,6 +114780,14 @@ function toWireFrame(f) {
|
|
|
114439
114780
|
dismissed: f.dismissed ?? []
|
|
114440
114781
|
};
|
|
114441
114782
|
}
|
|
114783
|
+
/** The answer on a frame: a track copy asks the ledger, a still carries the
|
|
114784
|
+
* subject the notification froze. */
|
|
114785
|
+
function verdictOfFrame(copy, verdicts) {
|
|
114786
|
+
if (copy.origin === "track") return verdicts.get(copy.trackId);
|
|
114787
|
+
const s = copy.verdictSubject;
|
|
114788
|
+
if (s === void 0) return void 0;
|
|
114789
|
+
return toRetrainVerdict(s);
|
|
114790
|
+
}
|
|
114442
114791
|
/**
|
|
114443
114792
|
* What the operator did with the proposals on a frame (`retrain-review.ts`).
|
|
114444
114793
|
*
|
|
@@ -114499,7 +114848,11 @@ var RetrainService = class {
|
|
|
114499
114848
|
const tracks = [];
|
|
114500
114849
|
for (const deviceId of devices) tracks.push(...await this.deps.tracks.listStaging(deviceId, limit));
|
|
114501
114850
|
const ids = tracks.map((t) => t.trackId);
|
|
114502
|
-
const [frameCounts, annotationCounts] = await Promise.all([
|
|
114851
|
+
const [frameCounts, annotationCounts, verdicts] = await Promise.all([
|
|
114852
|
+
this.deps.frames.countsByTrack(ids),
|
|
114853
|
+
this.deps.annotations.countsByTrack(ids),
|
|
114854
|
+
this.verdictsOfTracks(ids)
|
|
114855
|
+
]);
|
|
114503
114856
|
return tracks.map((t) => ({
|
|
114504
114857
|
trackId: t.trackId,
|
|
114505
114858
|
deviceId: t.deviceId,
|
|
@@ -114508,7 +114861,8 @@ var RetrainService = class {
|
|
|
114508
114861
|
firstSeen: t.firstSeen,
|
|
114509
114862
|
lastSeen: t.lastSeen,
|
|
114510
114863
|
frameCount: frameCounts.get(t.trackId) ?? 0,
|
|
114511
|
-
annotationCount: annotationCounts.get(t.trackId) ?? 0
|
|
114864
|
+
annotationCount: annotationCounts.get(t.trackId) ?? 0,
|
|
114865
|
+
...verdicts.has(t.trackId) ? { verdict: verdicts.get(t.trackId) } : {}
|
|
114512
114866
|
}));
|
|
114513
114867
|
}
|
|
114514
114868
|
/**
|
|
@@ -114558,9 +114912,14 @@ var RetrainService = class {
|
|
|
114558
114912
|
const ownTracks = /* @__PURE__ */ new Map();
|
|
114559
114913
|
const subjectBoxes = /* @__PURE__ */ new Map();
|
|
114560
114914
|
const annotationsByFrame = await this.annotationsByFrame(copies);
|
|
114915
|
+
const verdicts = await this.verdictsOf(copies);
|
|
114561
114916
|
const out = [];
|
|
114562
114917
|
for (const copy of copies) {
|
|
114563
|
-
const
|
|
114918
|
+
const verdict = verdictOfFrame(copy, verdicts);
|
|
114919
|
+
const wire = {
|
|
114920
|
+
...toWireFrame(copy),
|
|
114921
|
+
...verdict !== void 0 ? { verdict } : {}
|
|
114922
|
+
};
|
|
114564
114923
|
const collectionIds = collections?.get(copy.frameId);
|
|
114565
114924
|
const withCollections = {
|
|
114566
114925
|
...collections !== null ? {
|
|
@@ -114591,7 +114950,8 @@ var RetrainService = class {
|
|
|
114591
114950
|
},
|
|
114592
114951
|
ownTrack: trackId !== "" ? ownTracks.get(trackId) ?? null : null,
|
|
114593
114952
|
ownSubjectBox: trackId !== "" ? subjectBoxes.get(trackId)?.get(copy.sourceMediaId) ?? null : null,
|
|
114594
|
-
neighbours
|
|
114953
|
+
neighbours,
|
|
114954
|
+
...copy.verdictSubject !== void 0 ? { verdictSubject: copy.verdictSubject } : {}
|
|
114595
114955
|
});
|
|
114596
114956
|
out.push({
|
|
114597
114957
|
...withCollections,
|
|
@@ -114601,6 +114961,22 @@ var RetrainService = class {
|
|
|
114601
114961
|
}
|
|
114602
114962
|
return out;
|
|
114603
114963
|
}
|
|
114964
|
+
/** The verdicts of the tracks these frames were copied from. */
|
|
114965
|
+
async verdictsOf(copies) {
|
|
114966
|
+
return this.verdictsOfTracks(copies.flatMap((c) => c.origin === "track" ? [c.trackId] : []));
|
|
114967
|
+
}
|
|
114968
|
+
async verdictsOfTracks(trackIds) {
|
|
114969
|
+
const port = this.deps.verdicts;
|
|
114970
|
+
const ids = [...new Set(trackIds)];
|
|
114971
|
+
if (port === void 0 || ids.length === 0) return /* @__PURE__ */ new Map();
|
|
114972
|
+
return port.forTracks(ids).catch((err) => {
|
|
114973
|
+
this.deps.logger.warn("retrain frames: verdicts unreadable", { meta: {
|
|
114974
|
+
tracks: ids.length,
|
|
114975
|
+
error: String(err)
|
|
114976
|
+
} });
|
|
114977
|
+
return /* @__PURE__ */ new Map();
|
|
114978
|
+
});
|
|
114979
|
+
}
|
|
114604
114980
|
/** The URLs of a frame's pixels, when a data plane is served for them. */
|
|
114605
114981
|
frameUrls(frameId) {
|
|
114606
114982
|
const frameUrl = this.deps.frameUrl;
|
|
@@ -116717,6 +117093,7 @@ var BuildStores = class extends BuildStoresBase {
|
|
|
116717
117093
|
subjectBoxes: (trackId, deviceId) => mediaStore.listSubjectBoxesByOwner("track", trackId, deviceId)
|
|
116718
117094
|
},
|
|
116719
117095
|
memberships: retrainCollectionStore,
|
|
117096
|
+
verdicts: { forTracks: async (trackIds) => this.notificationCenter?.detectionVerdictsOf(trackIds) ?? /* @__PURE__ */ new Map() },
|
|
116720
117097
|
frameUrl: (frameId, size) => retrainFramePath(this.ctx.id, frameId, size)
|
|
116721
117098
|
});
|
|
116722
117099
|
this.retrain = retrain;
|
|
@@ -122428,7 +122805,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
122428
122805
|
onBindingsChanged: (data) => this.handleBindingsChangedEvent(data),
|
|
122429
122806
|
onDeviceUnregistered: (deviceId) => this.handleDeviceUnregisteredEvent(deviceId),
|
|
122430
122807
|
onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now()),
|
|
122431
|
-
onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event)
|
|
122808
|
+
onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event),
|
|
122809
|
+
onDeviceRegistryChange: () => this.notificationCenter?.invalidateActionTargets()
|
|
122432
122810
|
});
|
|
122433
122811
|
await step("startTrackingTapes", () => this.startTrackingTapes(this.isPostProcessingNode));
|
|
122434
122812
|
if (this.isPostProcessingNode) await step("startEmbeddingDispatcher", () => this.startEmbeddingDispatcher(logger, transport));
|
|
@@ -123889,6 +124267,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
123889
124267
|
this.frameState.lastNonPackageActivityAt.delete(data.deviceId);
|
|
123890
124268
|
this.frameState.packageCollectors.forgetDevice(data.deviceId);
|
|
123891
124269
|
this.frameState.packageOversizeLogAt.delete(data.deviceId);
|
|
124270
|
+
this.frameState.packageLookFrames.delete(data.deviceId);
|
|
123892
124271
|
this.frameState.packageLookDropReasons.delete(data.deviceId);
|
|
123893
124272
|
this.overlayState.clearDevice(data.deviceId);
|
|
123894
124273
|
this.frameState.overlaySynthesisWarnAt.delete(data.deviceId);
|
|
@@ -123912,6 +124291,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
123912
124291
|
this.frameState.lastNonPackageActivityAt.delete(deviceId);
|
|
123913
124292
|
this.frameState.packageCollectors.forgetDevice(deviceId);
|
|
123914
124293
|
this.frameState.packageOversizeLogAt.delete(deviceId);
|
|
124294
|
+
this.frameState.packageLookFrames.delete(deviceId);
|
|
123915
124295
|
this.frameState.packageLookDropReasons.delete(deviceId);
|
|
123916
124296
|
this.overlayState.clearDevice(deviceId);
|
|
123917
124297
|
this.frameState.overlaySynthesisWarnAt.delete(deviceId);
|
|
@@ -126894,6 +127274,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
126894
127274
|
runPackageStillness(input) {
|
|
126895
127275
|
if (this.packageDropDetector === null) return;
|
|
126896
127276
|
const tags = { deviceId: input.deviceId };
|
|
127277
|
+
if (input.frameHandle !== null) this.frameState.packageLookFrames.set(input.deviceId, {
|
|
127278
|
+
handle: input.frameHandle,
|
|
127279
|
+
at: input.timestamp
|
|
127280
|
+
});
|
|
126897
127281
|
for (const t of input.tracked) {
|
|
126898
127282
|
if (input.packageClasses.has(t.className)) continue;
|
|
126899
127283
|
if (!isDelivererInPackageZone(t.zones, input.packageZoneIds)) continue;
|
|
@@ -127127,10 +127511,23 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
127127
127511
|
...entry.confidence !== void 0 ? { confidence: entry.confidence } : {}
|
|
127128
127512
|
}
|
|
127129
127513
|
});
|
|
127130
|
-
this.
|
|
127131
|
-
|
|
127514
|
+
const lookFrame = this.frameState.packageLookFrames.get(input.deviceId);
|
|
127515
|
+
promoteWithStill({
|
|
127132
127516
|
entry,
|
|
127133
|
-
timestamp:
|
|
127517
|
+
handle: lookFrame !== void 0 && input.timestamp - lookFrame.at <= PACKAGE_LOOK_FRAME_MAX_AGE_MS ? lookFrame.handle : null,
|
|
127518
|
+
timestamp: lookFrame?.at ?? input.timestamp,
|
|
127519
|
+
query: (request) => this.queryPackageSceneCrop(request),
|
|
127520
|
+
writer: this.mediaStore,
|
|
127521
|
+
logger: this.ctx.logger,
|
|
127522
|
+
detector: this.packageDropDetector
|
|
127523
|
+
}).catch((err) => {
|
|
127524
|
+
this.ctx.logger.warn("package promotion failed", {
|
|
127525
|
+
tags,
|
|
127526
|
+
meta: {
|
|
127527
|
+
entryId: entry.id,
|
|
127528
|
+
error: err instanceof Error ? err.message : String(err)
|
|
127529
|
+
}
|
|
127530
|
+
});
|
|
127134
127531
|
});
|
|
127135
127532
|
}
|
|
127136
127533
|
for (const entry of outcome.departed) {
|