@camstack/addon-post-analysis 1.2.654 → 1.2.656

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.
Files changed (15) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/{clip-model-registry-CFQBxtE2.js → clip-model-registry-BQ_2RsIp.js} +68511 -67387
  3. package/dist/{clip-model-registry-BX6j2lnF.mjs → clip-model-registry-C06M8A8U.mjs} +69016 -67928
  4. package/dist/embedding-encoder/index.js +1 -1
  5. package/dist/embedding-encoder/index.mjs +1 -1
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Vag6rhEh.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-8vah5-KD.mjs} +2 -2
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-YXyP9GJm.mjs +26 -0
  9. 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 → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-gv38vKug.mjs} +1 -1
  10. package/dist/pipeline-analytics/{hostInit-CnvnZsmk.mjs → hostInit-0O7AUcsV.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +334 -73
  12. package/dist/pipeline-analytics/index.mjs +333 -72
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +1 -1
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BUwWKfHC.mjs +0 -26
@@ -1,4 +1,4 @@
1
- import { $ as MediaFileKindEnum, $i as BaseAddon, $n as cloneAlarmSettings, $r as paintHex, $t as OpsLogEntrySchema, A as DeclaredDevices, Aa as unknown, Ai as storageOccupancyCapability, An as TrackSideSchema, Ar as isAudioClassName, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as systemEventSummarySplitOf, Bn as applyModeDelaysEdits, Br as isRootSuppliableStepId, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as partialRecord, Ci as ruleGroupingApplies, Cn as TimelapseRuleInputSchema, Cr as failureContributionCapability, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Da as templateLiteral, Di as sensorRaisedOf, Dn as TrackPuritySchema, Dr as isAlertEpisodeOpen, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ea as string, Ei as secureActuationFor, En as TrackPlacementSchema, Er as isAlarmControllableCap, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as syncActiveAlarmProfileSettings, Fn as alarmPanelCapability, Fr as isEventOwnerType, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as withSystemEventClasses, Gn as audioMetricsCapability, Gr as kebabToCamel, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as templateVarsFor, Hn as applySecureOnArmEdit, Hr as isSystemDelivery, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as systemEventDeliveryProblems, In as alertEpisodeId, Ir as isKnownTemplateVar, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as canonicalHash, Jn as buildSubjectPath, Jr as memoizePinnedFfmpeg, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as zoneAnalyticsCapability, Kn as audioModeOf, Kr as linkedStepOf, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as systemEventFamilyProblems, Ln as alertEpisodeKind, Lr as isLabelSupplyStepId, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as subjectPathSampleFromBox, Mn as activateAlarmProfile, Mr as isDetectionMacroClass, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as subjectPathSvgPoints, Nn as addonWidgetsSourceCapability, Nr as isDrawableSubjectPath, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oa as tuple, Oi as servedTemplateVars, On as TrackRelationKindSchema, Or as isAlertEpisodeSeverity, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as summarizeReview, Pn as alarmDeviceSecureOnArm, Pr as isEventKindLabelKey, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as errMsg, Qn as classifyRetrainReview, Qr as occupancyScope, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as systemEventFilterApplies, Rn as alertEpisodePayload, Rr as isMediaOwnerType, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as object, Si as ruleDeviceCompatCapQueries, Sn as TRACK_RELATIONS_DEFAULT_LIMIT, Sr as faceGalleryCapability, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ta as strictObject, Ti as sceneMonitorCapability, Tn as TimelapseRuleSchema, Tr as gifFromMp4Args, Tt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, U as LabelAttributionSchema, Ui as vectorDimFromBase64, Un as armRemovalReconcile, Ur as isSystemEventSummaryRule, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as templateCarriesSummary, Vn as applyNonBlockingEdit, Vr as isScheduleActive, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as viewerLinkBaseOf, Wn as assertTimelapseCadences, Wr as isVectorIndexDimConflict, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as isSourceCap, Xn as bundledCamstack, Xr as normalizeNcMediaPolicy, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as evaluateSensorEdge, Yn as buildViewerLink, Yr as ncMediaPolicyError, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as DeviceType, Zn as calendarEventLocalSpan, Zr as notificationRulesCapability, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as boolean, _n as SceneMonitorSchema, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as createEvent, ai as plateGalleryCapability, an as RetrainDismissedSchema, ar as deleteAlarmProfile, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as looseObject, bi as ruleAutoTriggersAlarm, bn as SetTrackRelationInputSchema, br as eventKindLabel, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as defineRecordKinds, ci as readMotionTriggeredBy, cn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, cr as deriveRecordingMode, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, da as nodePin, di as resolveClipSearchMinScore, dn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, dr as deviceAttentionKind, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as CAP_ADVANCE_FIELDS, ei as paletteResultSchema, en as PackageLlmVerdictSchema, er as consumableFormOf, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fi as resolveDeliveryParams, fn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, fr as deviceRemovalHookCapability, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as array, gi as resolveNcMediaImage, gn as SCENE_RESET_RECAPTURES, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as _enum, hi as resolveModeNotifyPolicy, hn as SCENE_DIVERGED, hr as embeddingRefusalOf, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ia as RECORD_QUERY_MAX, ii as pipelineAnalyticsCapability, in as RebuildObjectEmbeddingsRefusalSchema, ir as createAlarmProfile, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ja as EventCategory, ji as subKindsOf, jn as TrackSourceSchema, jr as isDefaultAlarmProfileId, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ka as union, ki as startMoverUnderClearance, kn as TrackRelationSchema, kr as isAppLinkBase, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as hydrateSchema, li as readTimelapseGeneratedAt, ln as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, lr as describeSystemEventDeliveryProblem, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as ZodObject, mi as resolveModeDelays, mn as SCENE_DEFAULT_UNCOVERED_POLICY, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as InvalidRecordError, nn as RECORDING_EXPORT_MAX_READ_BYTES, nr as coreBlockIdFromAddonId, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as customAction, oi as projectSubjectPath, on as RetrainOriginSchema, or as deriveDetailCropRect, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as sleep$1, pi as resolveLocationMode, pn as SCENE_DEFAULT_QUIET_SECONDS, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, qn as buildEventKindDescriptor, qr as mayWriteToLocation, qt as NcSystemEventKindSchema, ra as RECORD_MAX_DATA_BYTES, ri as pickFaceRescoreIntervalMinutes, rn as RECORDING_SOURCE_CAMSTACK_ADDON, rr as cosineSimilarity$1, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, sa as defineCustomActions, si as readDeviceStateFrom, sn as RetrainStatusSchema, sr as derivePostTriggeredAction, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as CamProfileSchema, ti as parseAlertEpisodePayload, tr as consumableLowPercentOf, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as isDeviceScopedCap, ui as renameAlarmProfile, un as SCENE_DEFAULT_ANCHOR_THRESHOLD, ur as describeSystemEventFamilyProblem, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as discriminatedUnion, vi as retrainRevisionConflictMessage, vn as SetTrackPurityInputSchema, vr as encodeVectorBase64, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wa as record, wi as ruleKindOf, wn as TimelapseRulePatchSchema, wr as gifDeriveSpeed, wt as NC_STATE_SUMMARY_SECTION_IDS, x as COCO_TO_MACRO, xa as number, xi as ruleBelongsToAlarm$1, xn as TIMELAPSE_DENSE_FLOOR_SEC, xr as evictionPolicyOfLocation, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as literal, yi as rootSupplyServes, yn as SetTrackPurityResultSchema, yr as ensureAlarmProfiles, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as systemEventSummaryScheduleOf, zn as applyBlockingEdit, zr as isRetiredClearKey, zt as NcRuleTargetSchema } from "../clip-model-registry-BX6j2lnF.mjs";
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-C06M8A8U.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": 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`;
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) {
@@ -7366,7 +7335,8 @@ var OPENING = new Set([
7366
7335
  "open",
7367
7336
  "opening",
7368
7337
  "unlocked",
7369
- "unlocking"
7338
+ "unlocking",
7339
+ "door-open"
7370
7340
  ]);
7371
7341
  function sensorEdgeOf(kind, value) {
7372
7342
  const opening = alarmOpeningFromSensorEvent(kind, value);
@@ -9281,6 +9251,7 @@ var en_default = {
9281
9251
  "button.verdict.correct": "Correct",
9282
9252
  "button.verdict.wrong": "Wrong",
9283
9253
  "button.verdict.wrongAs": "Wrong: it is {{class}}",
9254
+ "button.verdict.wrongLabel": "Wrong label, class OK",
9284
9255
  "action.server-update.title": "Server update",
9285
9256
  "action.server-update.started": "Updating server on {{nodeId}}…",
9286
9257
  "action.server-update.restarting": "Server update staged on {{nodeId}} — the node is restarting. You will hear when it comes back.",
@@ -9686,6 +9657,7 @@ var it_default = {
9686
9657
  "button.verdict.correct": "Corretto",
9687
9658
  "button.verdict.wrong": "Sbagliato",
9688
9659
  "button.verdict.wrongAs": "Sbagliato: è {{class}}",
9660
+ "button.verdict.wrongLabel": "Etichetta sbagliata, classe giusta",
9689
9661
  "action.server-update.title": "Aggiornamento server",
9690
9662
  "action.server-update.started": "Aggiornamento server su {{nodeId}}…",
9691
9663
  "action.server-update.restarting": "Aggiornamento server predisposto su {{nodeId}} — il nodo si riavvia. Ti avviseremo quando torna.",
@@ -39035,6 +39007,11 @@ function rowIsDurableRetrainCopy(data) {
39035
39007
  * audit resolves ownership from INDEX rows, but a future filesystem-side tool
39036
39008
  * must be able to tell the two corpora apart by path alone. */
39037
39009
  var RETRAIN_PATH_PREFIX = "retrain";
39010
+ /** The stored verdict subject, validated; anything unreadable reads as absent. */
39011
+ function narrowVerdictSubject(value) {
39012
+ const parsed = RetrainVerdictSubjectSchema.safeParse(value);
39013
+ return parsed.success ? { verdictSubject: parsed.data } : {};
39014
+ }
39038
39015
  /** Ids per `IN (…)` page in {@link RetrainFrameStore.getMany}. */
39039
39016
  var GET_MANY_PAGE = 500;
39040
39017
  /**
@@ -39118,7 +39095,8 @@ var RetrainFrameStore = class {
39118
39095
  complete: data["complete"] === true || data["complete"] === 1,
39119
39096
  ...finiteNumber(data["epochMs"]) !== void 0 ? { epochMs: finiteNumber(data["epochMs"]) } : {},
39120
39097
  revision: finiteNumber(data["revision"]) ?? 0,
39121
- dismissed: narrowDismissed(data["dismissed"])
39098
+ dismissed: narrowDismissed(data["dismissed"]),
39099
+ ...narrowVerdictSubject(data["verdictSubject"])
39122
39100
  };
39123
39101
  }
39124
39102
  /** The copies with these ids, in the order given; unknown ids are skipped. */
@@ -39471,7 +39449,8 @@ var RetrainFrameStore = class {
39471
39449
  capturedAt: input.epochMs,
39472
39450
  ...chromaSpread !== void 0 ? { chromaSpread } : {},
39473
39451
  complete: false,
39474
- epochMs: input.epochMs
39452
+ epochMs: input.epochMs,
39453
+ ...input.verdictSubject !== void 0 ? { verdictSubject: input.verdictSubject } : {}
39475
39454
  };
39476
39455
  const path = framePath(frame);
39477
39456
  const persist = async () => {
@@ -39495,7 +39474,8 @@ var RetrainFrameStore = class {
39495
39474
  chromaSpread: frame.chromaSpread ?? null,
39496
39475
  complete: false,
39497
39476
  origin: "recording",
39498
- epochMs: input.epochMs
39477
+ epochMs: input.epochMs,
39478
+ ...input.verdictSubject !== void 0 ? { verdictSubject: input.verdictSubject } : {}
39499
39479
  });
39500
39480
  await this.ledger.transact([row, ...withRow]);
39501
39481
  };
@@ -39532,6 +39512,22 @@ var RetrainFrameStore = class {
39532
39512
  };
39533
39513
  }
39534
39514
  /**
39515
+ * Record the verdict subject on a still that already existed (the moment was
39516
+ * in the dataset before the operator tapped). The latest answer wins, like
39517
+ * the verdict ledger.
39518
+ *
39519
+ * @returns the updated frame, or `null` when no such copy exists.
39520
+ */
39521
+ async setVerdictSubject(frameId, verdictSubject) {
39522
+ const existing = await this.get(frameId);
39523
+ if (existing === null) return null;
39524
+ await this.ledger.update(frameId, { verdictSubject });
39525
+ return {
39526
+ ...existing,
39527
+ verdictSubject
39528
+ };
39529
+ }
39530
+ /**
39535
39531
  * The operator's "every subject in this frame is boxed" (D785 context: an
39536
39532
  * unboxed subject is a false positive to a detector evaluation). Only the
39537
39533
  * operator sets it; nothing derives it from the annotation count, because a
@@ -68420,6 +68416,7 @@ function wireSubscriptions(deps) {
68420
68416
  deps.onClusterTopology(nodes);
68421
68417
  }));
68422
68418
  for (const category of NC_SYSTEM_EVENT_CATEGORIES) unsubs.push(deps.eventBus.subscribe({ category }, (ev) => deps.onSystemEvent(ev)));
68419
+ for (const category of ACTION_TARGET_INVALIDATION_EVENTS) unsubs.push(deps.eventBus.subscribe({ category }, () => deps.onDeviceRegistryChange()));
68423
68420
  }
68424
68421
  unsubs.push(deps.eventBus.subscribe({ category: EventCategory.DeviceBindingsChanged }, (ev) => {
68425
68422
  deps.onBindingsChanged(ev.data);
@@ -68981,6 +68978,12 @@ async function composeEventKindsForDevices(deviceIds, composeOne, onError) {
68981
68978
  * nearest one is normally the same detection; the delta is reported so a
68982
68979
  * reviewer can see how far it was.
68983
68980
  *
68981
+ * 0. **The subject a notification verdict froze** (a recording still landed by
68982
+ * a verdict whose track was already gone). It is the one subject the
68983
+ * operator answered about, so it comes FIRST and is the own box; the
68984
+ * neighbours below stay secondary. Its box is normalised against the full
68985
+ * frame already, so it is used as is — never re-scaled.
68986
+ *
68984
68987
  * Every OTHER track alive on the camera at that instant contributes its own
68985
68988
  * nearest position the same way — a frame with three people is three boxes,
68986
68989
  * and a recording still (no track of its own) gets everything the pipeline
@@ -69035,6 +69038,18 @@ function toRetrainClassTiers(className, label) {
69035
69038
  label: kept
69036
69039
  } : { macroClass };
69037
69040
  }
69041
+ /** A verdict on the wire: the operator's class split into tiers once, here. */
69042
+ function toRetrainVerdict(input) {
69043
+ const tiers = input.suggestedClass !== void 0 ? toRetrainClassTiers(input.suggestedClass, void 0) : null;
69044
+ return {
69045
+ verdict: input.verdict,
69046
+ trackId: input.trackId,
69047
+ ...input.suggestedClass !== void 0 ? { suggestedClass: input.suggestedClass } : {},
69048
+ ...tiers !== null ? { suggestedMacroClass: tiers.macroClass } : {},
69049
+ ...tiers?.label !== void 0 ? { suggestedLabel: tiers.label } : {},
69050
+ ...input.at !== void 0 ? { at: input.at } : {}
69051
+ };
69052
+ }
69038
69053
  /** A pixel box as a clamped normalised one, or `null` when nothing is left. */
69039
69054
  function normalise(box, frameWidth, frameHeight) {
69040
69055
  if (frameWidth === void 0 || frameHeight === void 0) return null;
@@ -69092,6 +69107,22 @@ function boxFor(track, bbox, basis, deltaMs, score) {
69092
69107
  deltaMs
69093
69108
  };
69094
69109
  }
69110
+ /** The box a notification verdict froze, or `null` when the event carried none. */
69111
+ function boxOfVerdict(subject) {
69112
+ const box = subject.bbox;
69113
+ if (box === void 0) return null;
69114
+ const clamped = normalise(box, 1, 1);
69115
+ if (clamped === null) return null;
69116
+ const tiers = toRetrainClassTiers(subject.className, subject.label);
69117
+ return {
69118
+ bbox: clamped,
69119
+ macroClass: tiers.macroClass,
69120
+ ...tiers.label !== void 0 ? { label: tiers.label } : {},
69121
+ trackId: subject.trackId,
69122
+ basis: "verdict",
69123
+ deltaMs: 0
69124
+ };
69125
+ }
69095
69126
  /** The own track's box: the recorded subject box first, the nearest position second. */
69096
69127
  function ownBox(input, tolerance) {
69097
69128
  const track = input.ownTrack;
@@ -69114,10 +69145,13 @@ function pipelineBoxesForFrame(input) {
69114
69145
  const out = [];
69115
69146
  const own = ownBox(input, tolerance);
69116
69147
  if (own !== null) out.push(own);
69148
+ const verdictBox = input.verdictSubject !== void 0 ? boxOfVerdict(input.verdictSubject) : null;
69149
+ if (verdictBox !== null) out.push(verdictBox);
69117
69150
  const at = input.frame.capturedAt;
69118
69151
  if (at === void 0) return out;
69119
69152
  const ownId = input.frame.trackId;
69120
69153
  const seen = new Set(ownId !== void 0 ? [ownId] : []);
69154
+ if (input.verdictSubject !== void 0) seen.add(input.verdictSubject.trackId);
69121
69155
  for (const track of input.neighbours) {
69122
69156
  if (seen.has(track.trackId) || track.deviceId !== input.frame.deviceId) continue;
69123
69157
  seen.add(track.trackId);
@@ -70439,6 +70473,23 @@ Object.freeze({
70439
70473
  "sendMediaCommand": { "repeat": 1 },
70440
70474
  "setConfig": { "config": { "__nested__": { "defaultTransmitterId": null } } }
70441
70475
  },
70476
+ "schedulers": {
70477
+ "conflicts": { "scheduler": { "__nested__": {
70478
+ "enabled": true,
70479
+ "priority": 0,
70480
+ "maxLateMs": 6e5
70481
+ } } },
70482
+ "create": { "scheduler": { "__nested__": {
70483
+ "enabled": true,
70484
+ "priority": 0,
70485
+ "maxLateMs": 6e5
70486
+ } } },
70487
+ "update": { "scheduler": { "__nested__": {
70488
+ "enabled": true,
70489
+ "priority": 0,
70490
+ "maxLateMs": 6e5
70491
+ } } }
70492
+ },
70442
70493
  "storage": { "upsertLocation": { "isSystem": false } },
70443
70494
  "storage-admin": { "readRecords": { "limit": 100 } },
70444
70495
  "storage-provider": {
@@ -82367,10 +82418,18 @@ function parseActions(raw) {
82367
82418
  for (const entry of raw) {
82368
82419
  const parsed = NcRuleActionSchema.safeParse(entry);
82369
82420
  if (!parsed.success) return null;
82370
- out.push(parsed.data);
82421
+ out.push(normalizeNcAction(parsed.data));
82371
82422
  }
82372
82423
  return out;
82373
82424
  }
82425
+ /** Does a stored row still carry a pre-D983 cap step (`deviceId`, no `target`)? */
82426
+ function hasLegacyTarget(raw) {
82427
+ if (!Array.isArray(raw)) return false;
82428
+ return raw.some((entry) => {
82429
+ const parsed = NcRuleActionSchema.safeParse(entry);
82430
+ return parsed.success && parsed.data.kind === "cap" && parsed.data.target === void 0;
82431
+ });
82432
+ }
82374
82433
  function rowToSequence(id, body) {
82375
82434
  const actions = parseActions(body.actions);
82376
82435
  if (actions === null) return null;
@@ -82409,6 +82468,7 @@ var NcSequenceStore = class NcSequenceStore {
82409
82468
  try {
82410
82469
  const { records } = await this.kind.query({ limit: 1e4 });
82411
82470
  const next = /* @__PURE__ */ new Map();
82471
+ const legacyRows = [];
82412
82472
  let skipped = 0;
82413
82473
  for (const row of records) {
82414
82474
  const sequence = rowToSequence(row.id, row.data);
@@ -82417,9 +82477,11 @@ var NcSequenceStore = class NcSequenceStore {
82417
82477
  continue;
82418
82478
  }
82419
82479
  next.set(sequence.id, sequence);
82480
+ if (hasLegacyTarget(row.data.actions)) legacyRows.push(row.id);
82420
82481
  }
82421
82482
  this.byId.clear();
82422
82483
  for (const [id, seq] of next) this.byId.set(id, seq);
82484
+ await this.rewriteLegacyRows(legacyRows, records);
82423
82485
  if (skipped > 0) this.logger.warn("sequence rows skipped as malformed — bindings to them will miss", { meta: { skipped } });
82424
82486
  } catch (err) {
82425
82487
  this.logger.warn("sequence load failed — keeping the sequences already in memory", { meta: {
@@ -82428,6 +82490,28 @@ var NcSequenceStore = class NcSequenceStore {
82428
82490
  } });
82429
82491
  }
82430
82492
  }
82493
+ /**
82494
+ * One-shot, idempotent (D983): a row whose steps still carry `deviceId` is
82495
+ * stored again in the target shape. A failed write leaves the row as it was —
82496
+ * it is still READ as the new shape — and is on the record.
82497
+ */
82498
+ async rewriteLegacyRows(ids, records) {
82499
+ for (const id of ids) {
82500
+ const sequence = this.byId.get(id) ?? null;
82501
+ const row = records.find((r) => r.id === id);
82502
+ if (sequence === null || row === void 0) continue;
82503
+ try {
82504
+ const createdAt = row.data.createdAt;
82505
+ await this.persist(sequence, typeof createdAt === "number" && Number.isFinite(createdAt) ? createdAt : this.now(), this.now());
82506
+ this.logger.info("notification sequence rewritten to the target shape", { meta: { sequenceId: id } });
82507
+ } catch (err) {
82508
+ this.logger.warn("notification sequence could not be rewritten — it is still read as the target shape", { meta: {
82509
+ sequenceId: id,
82510
+ error: String(err)
82511
+ } });
82512
+ }
82513
+ }
82514
+ }
82431
82515
  list() {
82432
82516
  return [...this.byId.values()].toSorted((a, b) => a.name.localeCompare(b.name));
82433
82517
  }
@@ -82447,7 +82531,7 @@ var NcSequenceStore = class NcSequenceStore {
82447
82531
  id: randomUUID(),
82448
82532
  name: input.name,
82449
82533
  ...input.minDelaySec !== void 0 ? { minDelaySec: input.minDelaySec } : {},
82450
- actions: [...input.actions]
82534
+ actions: input.actions.map(normalizeNcAction)
82451
82535
  };
82452
82536
  await this.persist(sequence, now, now);
82453
82537
  this.byId.set(sequence.id, sequence);
@@ -82463,7 +82547,7 @@ var NcSequenceStore = class NcSequenceStore {
82463
82547
  id: sequence.id,
82464
82548
  name: sequence.name,
82465
82549
  ...sequence.minDelaySec !== void 0 ? { minDelaySec: sequence.minDelaySec } : {},
82466
- actions: [...sequence.actions]
82550
+ actions: sequence.actions.map(normalizeNcAction)
82467
82551
  };
82468
82552
  await this.persist(stored, now, now);
82469
82553
  this.byId.set(stored.id, stored);
@@ -82476,7 +82560,7 @@ var NcSequenceStore = class NcSequenceStore {
82476
82560
  id: sequenceId,
82477
82561
  name: input.name,
82478
82562
  ...input.minDelaySec !== void 0 ? { minDelaySec: input.minDelaySec } : {},
82479
- actions: [...input.actions]
82563
+ actions: input.actions.map(normalizeNcAction)
82480
82564
  };
82481
82565
  const createdAt = await this.readCreatedAt(sequenceId, now);
82482
82566
  await this.persist(sequence, createdAt, now);
@@ -82544,7 +82628,11 @@ var NC_DETECTION_VERDICTS_COLUMNS = [
82544
82628
  var NcDetectionVerdictRecordSchema = object({
82545
82629
  trackId: string().min(1),
82546
82630
  deviceId: number().int(),
82547
- verdict: _enum(["correct", "wrong"]),
82631
+ verdict: _enum([
82632
+ "correct",
82633
+ "wrong",
82634
+ "wrong-label"
82635
+ ]),
82548
82636
  /** The class the operator said it really was. Only with `wrong`. */
82549
82637
  suggestedClass: string().min(1).optional(),
82550
82638
  /** Whose notification it was (the token's frozen owner), NOT who tapped. */
@@ -82579,6 +82667,16 @@ var NcDetectionVerdictLedger = class {
82579
82667
  logger: deps.logger
82580
82668
  });
82581
82669
  }
82670
+ /** The answers recorded for these tracks (a track nobody answered is absent).
82671
+ * Reads the in-memory mirror: no I/O, so a listing never waits on it. */
82672
+ forTracks(trackIds) {
82673
+ const out = /* @__PURE__ */ new Map();
82674
+ for (const id of trackIds) {
82675
+ const row = this.ledger.get(id);
82676
+ if (row !== void 0) out.set(id, row);
82677
+ }
82678
+ return out;
82679
+ }
82582
82680
  /** Write-through; throws on a failed write so the tap reports it. */
82583
82681
  async record(row) {
82584
82682
  await this.ledger.put(NcDetectionVerdictRecordSchema.parse(row));
@@ -82712,10 +82810,33 @@ function pruneHookBindings(bindings, field, isDead, notes) {
82712
82810
  kept.push(binding);
82713
82811
  continue;
82714
82812
  }
82715
- if (binding.action.kind === "cap" && isDead(binding.action.deviceId)) {
82716
- changed = true;
82717
- notes.removed(field, deviceSubjects([binding.action.deviceId]), "binding-removed");
82718
- continue;
82813
+ if (binding.action.kind === "cap") {
82814
+ const { target } = normalizeNcCapTarget(binding.action);
82815
+ if (target.kind === "devices") {
82816
+ const dead = target.ids.filter(isDead);
82817
+ if (dead.length === target.ids.length) {
82818
+ changed = true;
82819
+ notes.removed(field, deviceSubjects(dead), "binding-removed");
82820
+ continue;
82821
+ }
82822
+ if (dead.length > 0) {
82823
+ changed = true;
82824
+ notes.removed(field, deviceSubjects(dead), "binding-removed");
82825
+ const alive = target.ids.filter((id) => !isDead(id));
82826
+ kept.push({
82827
+ ...binding,
82828
+ action: normalizeNcAction({
82829
+ ...binding.action,
82830
+ deviceId: void 0,
82831
+ target: {
82832
+ kind: "devices",
82833
+ ids: alive
82834
+ }
82835
+ })
82836
+ });
82837
+ continue;
82838
+ }
82839
+ }
82719
82840
  }
82720
82841
  kept.push(binding);
82721
82842
  }
@@ -82871,7 +82992,11 @@ function referencedDeviceIds(snapshot) {
82871
82992
  add(c.systemEvent?.deviceIds);
82872
82993
  if (c.deviceState !== void 0) ids.add(c.deviceState.deviceId);
82873
82994
  if (c.scene?.deviceId !== void 0) ids.add(c.scene.deviceId);
82874
- for (const binding of [...rule.actions?.onTrigger ?? [], ...rule.actions?.onTriggerOff ?? []]) if (binding.kind === "do" && binding.action.kind === "cap") ids.add(binding.action.deviceId);
82995
+ for (const binding of [...rule.actions?.onTrigger ?? [], ...rule.actions?.onTriggerOff ?? []]) {
82996
+ if (binding.kind !== "do" || binding.action.kind !== "cap") continue;
82997
+ const { target } = normalizeNcCapTarget(binding.action);
82998
+ if (target.kind === "devices") add(target.ids);
82999
+ }
82875
83000
  }
82876
83001
  for (const rule of snapshot.timelapse) add(rule.deviceIds);
82877
83002
  for (const rule of snapshot.summary) add(rule.deviceIds);
@@ -83542,6 +83667,8 @@ var NcDeviceStateCache = class {
83542
83667
  //#region src/notification-center/detection-verdict-buttons.ts
83543
83668
  var VERDICT_CORRECT_ID = "detection-verdict-correct";
83544
83669
  var VERDICT_WRONG_ID = "detection-verdict-wrong";
83670
+ /** Wrong LABEL, right class — only for a subject that has a label. */
83671
+ var VERDICT_WRONG_LABEL_ID = "detection-verdict-wrong-label";
83545
83672
  /** `detection-verdict-wrong:<class>` — Wrong, and what it really was. */
83546
83673
  var VERDICT_WRONG_AS_PREFIX = "detection-verdict-wrong:";
83547
83674
  /** The macro classes offered as "it was really…". Short on purpose. */
@@ -83558,6 +83685,15 @@ function verdictButtonRefusal(subject) {
83558
83685
  if (subject.ownerUserId === void 0) return "unowned-target";
83559
83686
  return null;
83560
83687
  }
83688
+ /**
83689
+ * Does the notified subject carry a label below its macro class — a sub-class
83690
+ * (`dog` under `animal`), a recognised name, a plate? The same tier split the
83691
+ * retrain annotations use, so "has a label" means one thing in both places.
83692
+ */
83693
+ function subjectHasLabel(className, label) {
83694
+ if (className === void 0) return false;
83695
+ return toRetrainClassTiers(className, label).label !== void 0;
83696
+ }
83561
83697
  /** The classes worth offering for a subject of `className`. */
83562
83698
  function verdictClassChoices(className) {
83563
83699
  return VERDICT_CLASS_CHOICES.filter((c) => c !== className).slice(0, VERDICT_MAX_CLASS_CHOICES);
@@ -83569,13 +83705,17 @@ function verdictClassChoices(className) {
83569
83705
  * when the channel declared room for a fourth button.
83570
83706
  */
83571
83707
  function verdictButtonSpecs(input) {
83572
- const base = [{
83708
+ const plain = [{
83573
83709
  actionId: VERDICT_CORRECT_ID,
83574
83710
  verdict: "correct"
83575
83711
  }, {
83576
83712
  actionId: VERDICT_WRONG_ID,
83577
83713
  verdict: "wrong"
83578
83714
  }];
83715
+ const base = subjectHasLabel(input.className, input.label) ? [...plain, {
83716
+ actionId: VERDICT_WRONG_LABEL_ID,
83717
+ verdict: "wrong-label"
83718
+ }] : plain;
83579
83719
  if (input.maxActions === void 0 || input.maxActions <= 3) return base;
83580
83720
  const room = input.maxActions - base.length;
83581
83721
  const extra = verdictClassChoices(input.className).slice(0, room).map((suggestedClass) => ({
@@ -87306,6 +87446,8 @@ var NcDispatcher = class {
87306
87446
  ...target.ownerUserId !== void 0 ? { ownerUserId: target.ownerUserId } : {},
87307
87447
  ...entry.payload.subject.className !== void 0 ? { className: entry.payload.subject.className } : {},
87308
87448
  ...entry.payload.subject.trackId !== void 0 ? { trackId: entry.payload.subject.trackId } : {},
87449
+ ...entry.payload.subject.label !== void 0 ? { label: entry.payload.subject.label } : {},
87450
+ ...entry.payload.subject.bbox !== void 0 ? { bbox: entry.payload.subject.bbox } : {},
87309
87451
  eventAtMs: entry.payload.subject.timestamp,
87310
87452
  ...target.maxActions !== void 0 ? { maxActions: target.maxActions } : {},
87311
87453
  ...entry.payload.subject.systemEvent !== void 0 ? { systemEvent: entry.payload.subject.systemEvent } : {}
@@ -91669,7 +91811,7 @@ var NcRuleActionRunner = class {
91669
91811
  if (sequence.throttle) this.lastRunAt.set(sequence.id, now);
91670
91812
  let stepsRun = 0;
91671
91813
  for (const [index, action] of sequence.actions.entries()) try {
91672
- await this.step(action, input.origin);
91814
+ await this.step(action, input.origin, sequence.id);
91673
91815
  stepsRun += 1;
91674
91816
  } catch (err) {
91675
91817
  this.deps.logger.warn("rule action failed — stopping the sequence", {
@@ -91709,20 +91851,28 @@ var NcRuleActionRunner = class {
91709
91851
  stepsRun
91710
91852
  };
91711
91853
  }
91712
- async step(action, origin) {
91854
+ async step(action, origin, owner) {
91713
91855
  if (action.kind === "wait") {
91714
91856
  await this.sleep(action.seconds * 1e3);
91715
91857
  return;
91716
91858
  }
91717
91859
  if (!this.deps.isDeviceScoped(action.cap)) throw new Error(`rule action refused: "${action.cap}" is not a device capability — only devices can be actuated`);
91718
- await this.deps.actuate({
91719
- deviceId: action.deviceId,
91860
+ const { target, actionId } = normalizeNcCapTarget(action);
91861
+ const deviceIds = await this.devicesOf(target, actionId, owner);
91862
+ for (const deviceId of deviceIds) await this.deps.actuate({
91863
+ deviceId,
91720
91864
  cap: action.cap,
91721
91865
  method: action.method,
91722
91866
  args: action.args ?? {},
91723
91867
  ...origin !== void 0 ? { origin } : {}
91724
91868
  });
91725
91869
  }
91870
+ async devicesOf(target, actionId, owner) {
91871
+ if (target.kind === "devices") return target.ids;
91872
+ if (target.kind === "var") throw new Error("rule action refused: a sequence has no variables to resolve a target from");
91873
+ if (this.deps.resolveTarget === void 0) throw new Error("rule action refused: this node cannot resolve a domain target");
91874
+ return (await this.deps.resolveTarget(target, actionId, owner)).deviceIds;
91875
+ }
91726
91876
  };
91727
91877
  /** Force-enable a shared sequence for a button tap (always runs, still throttled). */
91728
91878
  function asForcedSequence(sequence) {
@@ -100110,6 +100260,12 @@ function rowRefusalId(systemEvent) {
100110
100260
  const value = systemEvent?.data["refusalId"];
100111
100261
  return typeof value === "string" && value.length > 0 ? value : void 0;
100112
100262
  }
100263
+ /** The text-catalog key of each verdict button. */
100264
+ var VERDICT_LABEL_KEY = {
100265
+ correct: "button.verdict.correct",
100266
+ wrong: "button.verdict.wrong",
100267
+ "wrong-label": "button.verdict.wrongLabel"
100268
+ };
100113
100269
  /**
100114
100270
  * The tap-through buttons for ONE delivery, each with its own single-use
100115
100271
  * token.
@@ -100146,6 +100302,7 @@ async function mintButtons(host, input) {
100146
100302
  const verdictOwner = input.ownerUserId;
100147
100303
  const verdictButtons = verdictRefusal !== null || verdictTrackId === void 0 || verdictOwner === void 0 ? [] : verdictButtonSpecs({
100148
100304
  className: input.className,
100305
+ label: input.label,
100149
100306
  maxActions: input.maxActions
100150
100307
  }).map((spec) => {
100151
100308
  const id = randomUUID();
@@ -100163,6 +100320,11 @@ async function mintButtons(host, input) {
100163
100320
  ...spec.suggestedClass !== void 0 ? { suggestedClass: spec.suggestedClass } : {},
100164
100321
  ...input.maxActions === 0 ? { viaLink: true } : {},
100165
100322
  ...input.eventAtMs !== void 0 ? { eventAtMs: input.eventAtMs } : {},
100323
+ ...input.className !== void 0 ? { subject: {
100324
+ className: input.className,
100325
+ ...input.label !== void 0 ? { label: input.label } : {},
100326
+ ...input.bbox !== void 0 ? { bbox: input.bbox } : {}
100327
+ } } : {},
100166
100328
  deviceId: input.deviceId,
100167
100329
  actionId: spec.actionId,
100168
100330
  targetId: input.targetId,
@@ -100171,7 +100333,7 @@ async function mintButtons(host, input) {
100171
100333
  });
100172
100334
  return {
100173
100335
  id: spec.actionId,
100174
- label: spec.suggestedClass === void 0 ? host.texts.text({ key: spec.verdict === "correct" ? "button.verdict.correct" : "button.verdict.wrong" }) : host.texts.text({
100336
+ label: spec.suggestedClass === void 0 ? host.texts.text({ key: VERDICT_LABEL_KEY[spec.verdict] }) : host.texts.text({
100175
100337
  key: "button.verdict.wrongAs",
100176
100338
  vars: { class: spec.suggestedClass }
100177
100339
  }),
@@ -100630,6 +100792,18 @@ var VERDICT_FRAME_REFUSAL_COPY = {
100630
100792
  "still-busy": "The track is gone and the recorder is busy — nothing was changed, try again later",
100631
100793
  failed: "The track is gone and its frame could not be saved — nothing was changed"
100632
100794
  };
100795
+ /** The grant's frozen subject, in the shape the retrain still stores it in. */
100796
+ function verdictSubjectOf(grant, subject, at) {
100797
+ return {
100798
+ trackId: grant.trackId,
100799
+ className: subject.className,
100800
+ ...subject.label !== void 0 ? { label: subject.label } : {},
100801
+ ...subject.bbox !== void 0 ? { bbox: subject.bbox } : {},
100802
+ verdict: grant.verdict,
100803
+ ...grant.suggestedClass !== void 0 ? { suggestedClass: grant.suggestedClass } : {},
100804
+ at
100805
+ };
100806
+ }
100633
100807
  /**
100634
100808
  * The track is gone (aged out, or discarded as a still object): the camera's
100635
100809
  * RECORDING still covers the instant, so the verdict lands a frame there
@@ -100652,7 +100826,8 @@ async function landFrameFromRecording(host, grant) {
100652
100826
  }
100653
100827
  const landed = await addFrame({
100654
100828
  deviceId: grant.deviceId,
100655
- epochMs: grant.eventAtMs
100829
+ epochMs: grant.eventAtMs,
100830
+ ...grant.subject !== void 0 ? { subject: verdictSubjectOf(grant, grant.subject, host.now()) } : {}
100656
100831
  });
100657
100832
  if (!landed.ok) {
100658
100833
  host.logger.warn("detection verdict dropped — the track is gone and the recording refused", {
@@ -101613,6 +101788,7 @@ function buildEntries(host, rule, subject, kind, matchedOn, userTargets = [], or
101613
101788
  ...alarmSource?.eventId !== void 0 ? { eventId: alarmSource.eventId } : hasEventMedia ? { eventId: subject.recordId } : {},
101614
101789
  className: subject.classNames[0] ?? subject.sensorKind ?? "event",
101615
101790
  ...subject.label !== void 0 ? { label: subject.label } : {},
101791
+ ...subject.bbox !== void 0 ? { bbox: subject.bbox } : {},
101616
101792
  ...subject.labelKind !== void 0 ? { labelKind: subject.labelKind } : {},
101617
101793
  ...subject.confidence !== void 0 ? { confidence: subject.confidence } : {},
101618
101794
  zones: renderedZones,
@@ -105933,6 +106109,8 @@ var NotificationCenter = class {
105933
106109
  zoneOwners;
105934
106110
  /** Runs a matched rule's action sequences. Null when no actuator is wired. */
105935
106111
  actionRunner;
106112
+ /** Domain action targets read from the registry (D983); null without a directory. */
106113
+ actionTargets;
105936
106114
  /** Grants behind the tap-through buttons. In-process and single-use — read
105937
106115
  * `action-token.ts` before assuming anything about what a tap proves. */
105938
106116
  actionTokens = new NcActionTokenRegistry();
@@ -106340,9 +106518,15 @@ var NotificationCenter = class {
106340
106518
  });
106341
106519
  this.livenessHoldoff = new DeviceLivenessHoldoff((event) => this.consumeEvent(event), { delayMs: deps.livenessHoldoffMs ?? (process.env.VITEST ? 0 : DEVICE_LIVENESS_HOLDOFF_MS) });
106342
106520
  this.nodeLivenessHoldoff = new DeviceLivenessHoldoff((event, timing) => this.consumeNodeLiveness(event, timing), { delayMs: deps.nodeLivenessHoldoffMs ?? (process.env.VITEST ? 0 : NODE_LIVENESS_HOLDOFF_MS) });
106521
+ const targets = deps.actionTargetDirectory !== void 0 ? new ActionTargetResolver(deps.actionTargetDirectory, this.logger.child("actions")) : null;
106522
+ this.actionTargets = targets;
106343
106523
  this.actionRunner = deps.actuate !== void 0 && deps.isCapDeviceScoped !== void 0 ? new NcRuleActionRunner({
106344
106524
  logger: this.logger.child("actions"),
106345
106525
  actuate: (input) => this.actuateWithAttribution(input),
106526
+ ...targets !== null ? { resolveTarget: (target, actionId, owner) => targets.resolve(target, actionId, {}, {
106527
+ actionId,
106528
+ owner
106529
+ }) } : {},
106346
106530
  isDeviceScoped: deps.isCapDeviceScoped,
106347
106531
  ...deps.now !== void 0 ? { now: deps.now } : {}
106348
106532
  }) : null;
@@ -107132,6 +107316,21 @@ var NotificationCenter = class {
107132
107316
  }
107133
107317
  };
107134
107318
  }
107319
+ /**
107320
+ * What the operator answered to the "was this detection right?" buttons, for
107321
+ * the retrain page: by track, the latest answer wins. A track nobody answered
107322
+ * is absent.
107323
+ */
107324
+ detectionVerdictsOf(trackIds) {
107325
+ const out = /* @__PURE__ */ new Map();
107326
+ for (const [trackId, row] of this.verdicts.forTracks(trackIds)) out.set(trackId, toRetrainVerdict({
107327
+ verdict: row.verdict,
107328
+ trackId,
107329
+ at: row.at,
107330
+ suggestedClass: row.suggestedClass
107331
+ }));
107332
+ return out;
107333
+ }
107135
107334
  observeOccupancy(deviceId, snapshot, evidence = "scene") {
107136
107335
  return observeOccupancy(this.occupancyWatchHost(), deviceId, snapshot, evidence);
107137
107336
  }
@@ -107307,6 +107506,10 @@ var NotificationCenter = class {
107307
107506
  }
107308
107507
  };
107309
107508
  }
107509
+ /** The device registry changed: a cached domain action target is stale (D983). */
107510
+ invalidateActionTargets() {
107511
+ this.actionTargets?.invalidate();
107512
+ }
107310
107513
  onSystemEvent(event) {
107311
107514
  return onSystemEvent(this.systemEventsHost(), event);
107312
107515
  }
@@ -108780,7 +108983,9 @@ async function addVerdictFrameFromRecording(api, logger, input) {
108780
108983
  }
108781
108984
  const result = await api.addRetrainFrameFromRecording({
108782
108985
  collectionId,
108783
- ...input
108986
+ deviceId: input.deviceId,
108987
+ epochMs: input.epochMs,
108988
+ ...input.subject !== void 0 ? { verdictSubject: input.subject } : {}
108784
108989
  });
108785
108990
  if (result.kind === "added") return { ok: true };
108786
108991
  return {
@@ -109067,6 +109272,20 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
109067
109272
  });
109068
109273
  },
109069
109274
  isCapDeviceScoped: isDeviceScopedCap,
109275
+ actionTargetDirectory: {
109276
+ listDeviceIdsByCaps: (capNames) => api.deviceManager.listDeviceIdsByCaps.query({ capNames: [...capNames] }),
109277
+ listDevices: async (deviceIds) => {
109278
+ return (await api.deviceManager.listAll.query({
109279
+ deviceIds: [...deviceIds],
109280
+ projection: "slim"
109281
+ })).map((d) => ({
109282
+ id: d.id,
109283
+ type: d.type,
109284
+ location: d.location,
109285
+ disabled: d.disabled
109286
+ }));
109287
+ }
109288
+ },
109070
109289
  generateVision: async (request) => {
109071
109290
  const result = await api.llm.generateVision.mutate({
109072
109291
  ...request.profileId !== void 0 ? { profileId: request.profileId } : {},
@@ -113692,7 +113911,7 @@ var RetrainCollectionService = class {
113692
113911
  frameId: recordingFrameId(input.deviceId, input.epochMs),
113693
113912
  deviceId: input.deviceId
113694
113913
  });
113695
- const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp]);
113914
+ const stored = await this.storeStill(input.deviceId, input.epochMs, membershipOp === null ? [] : [membershipOp], input.verdictSubject);
113696
113915
  if (stored.kind === "refused") {
113697
113916
  this.deps.logger.info("retrain collection add refused", {
113698
113917
  tags,
@@ -113704,11 +113923,15 @@ var RetrainCollectionService = class {
113704
113923
  });
113705
113924
  return stored;
113706
113925
  }
113707
- if (stored.reused) await this.deps.collections.addFrame({
113708
- collectionId: input.collectionId,
113709
- frameId: stored.frame.frameId,
113710
- deviceId: input.deviceId
113711
- });
113926
+ let storedFrame = stored.frame;
113927
+ if (stored.reused) {
113928
+ await this.deps.collections.addFrame({
113929
+ collectionId: input.collectionId,
113930
+ frameId: stored.frame.frameId,
113931
+ deviceId: input.deviceId
113932
+ });
113933
+ if (input.verdictSubject !== void 0) storedFrame = await this.deps.frames.setVerdictSubject(stored.frame.frameId, input.verdictSubject) ?? stored.frame;
113934
+ }
113712
113935
  this.deps.logger.info("retrain frame added to a collection from the recording", {
113713
113936
  tags,
113714
113937
  meta: {
@@ -113718,7 +113941,7 @@ var RetrainCollectionService = class {
113718
113941
  reused: stored.reused
113719
113942
  }
113720
113943
  });
113721
- const [wire] = await this.deps.retrain.toWireFrames([stored.frame]);
113944
+ const [wire] = await this.deps.retrain.toWireFrames([storedFrame]);
113722
113945
  if (wire === void 0) throw new Error("retrain: a stored still produced no wire frame");
113723
113946
  return {
113724
113947
  kind: "added",
@@ -113769,7 +113992,7 @@ var RetrainCollectionService = class {
113769
113992
  const ids = await this.deps.collections.listFrameIds(input.collectionId);
113770
113993
  return this.deps.retrain.toWireFrames(await this.deps.frames.getMany(ids));
113771
113994
  }
113772
- async storeStill(deviceId, epochMs, withRow) {
113995
+ async storeStill(deviceId, epochMs, withRow, verdictSubject) {
113773
113996
  const existing = await this.deps.frames.get(recordingFrameId(deviceId, epochMs));
113774
113997
  if (existing !== null) return {
113775
113998
  kind: "stored",
@@ -113788,7 +114011,8 @@ var RetrainCollectionService = class {
113788
114011
  const stored = await this.deps.frames.storeRecordingStill({
113789
114012
  deviceId,
113790
114013
  epochMs,
113791
- bytes: still.jpeg
114014
+ bytes: still.jpeg,
114015
+ ...verdictSubject !== void 0 ? { verdictSubject } : {}
113792
114016
  }, withRow);
113793
114017
  return stored.kind === "copied" ? {
113794
114018
  kind: "stored",
@@ -114574,6 +114798,14 @@ function toWireFrame(f) {
114574
114798
  dismissed: f.dismissed ?? []
114575
114799
  };
114576
114800
  }
114801
+ /** The answer on a frame: a track copy asks the ledger, a still carries the
114802
+ * subject the notification froze. */
114803
+ function verdictOfFrame(copy, verdicts) {
114804
+ if (copy.origin === "track") return verdicts.get(copy.trackId);
114805
+ const s = copy.verdictSubject;
114806
+ if (s === void 0) return void 0;
114807
+ return toRetrainVerdict(s);
114808
+ }
114577
114809
  /**
114578
114810
  * What the operator did with the proposals on a frame (`retrain-review.ts`).
114579
114811
  *
@@ -114634,7 +114866,11 @@ var RetrainService = class {
114634
114866
  const tracks = [];
114635
114867
  for (const deviceId of devices) tracks.push(...await this.deps.tracks.listStaging(deviceId, limit));
114636
114868
  const ids = tracks.map((t) => t.trackId);
114637
- const [frameCounts, annotationCounts] = await Promise.all([this.deps.frames.countsByTrack(ids), this.deps.annotations.countsByTrack(ids)]);
114869
+ const [frameCounts, annotationCounts, verdicts] = await Promise.all([
114870
+ this.deps.frames.countsByTrack(ids),
114871
+ this.deps.annotations.countsByTrack(ids),
114872
+ this.verdictsOfTracks(ids)
114873
+ ]);
114638
114874
  return tracks.map((t) => ({
114639
114875
  trackId: t.trackId,
114640
114876
  deviceId: t.deviceId,
@@ -114643,7 +114879,8 @@ var RetrainService = class {
114643
114879
  firstSeen: t.firstSeen,
114644
114880
  lastSeen: t.lastSeen,
114645
114881
  frameCount: frameCounts.get(t.trackId) ?? 0,
114646
- annotationCount: annotationCounts.get(t.trackId) ?? 0
114882
+ annotationCount: annotationCounts.get(t.trackId) ?? 0,
114883
+ ...verdicts.has(t.trackId) ? { verdict: verdicts.get(t.trackId) } : {}
114647
114884
  }));
114648
114885
  }
114649
114886
  /**
@@ -114693,9 +114930,14 @@ var RetrainService = class {
114693
114930
  const ownTracks = /* @__PURE__ */ new Map();
114694
114931
  const subjectBoxes = /* @__PURE__ */ new Map();
114695
114932
  const annotationsByFrame = await this.annotationsByFrame(copies);
114933
+ const verdicts = await this.verdictsOf(copies);
114696
114934
  const out = [];
114697
114935
  for (const copy of copies) {
114698
- const wire = toWireFrame(copy);
114936
+ const verdict = verdictOfFrame(copy, verdicts);
114937
+ const wire = {
114938
+ ...toWireFrame(copy),
114939
+ ...verdict !== void 0 ? { verdict } : {}
114940
+ };
114699
114941
  const collectionIds = collections?.get(copy.frameId);
114700
114942
  const withCollections = {
114701
114943
  ...collections !== null ? {
@@ -114726,7 +114968,8 @@ var RetrainService = class {
114726
114968
  },
114727
114969
  ownTrack: trackId !== "" ? ownTracks.get(trackId) ?? null : null,
114728
114970
  ownSubjectBox: trackId !== "" ? subjectBoxes.get(trackId)?.get(copy.sourceMediaId) ?? null : null,
114729
- neighbours
114971
+ neighbours,
114972
+ ...copy.verdictSubject !== void 0 ? { verdictSubject: copy.verdictSubject } : {}
114730
114973
  });
114731
114974
  out.push({
114732
114975
  ...withCollections,
@@ -114736,6 +114979,22 @@ var RetrainService = class {
114736
114979
  }
114737
114980
  return out;
114738
114981
  }
114982
+ /** The verdicts of the tracks these frames were copied from. */
114983
+ async verdictsOf(copies) {
114984
+ return this.verdictsOfTracks(copies.flatMap((c) => c.origin === "track" ? [c.trackId] : []));
114985
+ }
114986
+ async verdictsOfTracks(trackIds) {
114987
+ const port = this.deps.verdicts;
114988
+ const ids = [...new Set(trackIds)];
114989
+ if (port === void 0 || ids.length === 0) return /* @__PURE__ */ new Map();
114990
+ return port.forTracks(ids).catch((err) => {
114991
+ this.deps.logger.warn("retrain frames: verdicts unreadable", { meta: {
114992
+ tracks: ids.length,
114993
+ error: String(err)
114994
+ } });
114995
+ return /* @__PURE__ */ new Map();
114996
+ });
114997
+ }
114739
114998
  /** The URLs of a frame's pixels, when a data plane is served for them. */
114740
114999
  frameUrls(frameId) {
114741
115000
  const frameUrl = this.deps.frameUrl;
@@ -116852,6 +117111,7 @@ var BuildStores = class extends BuildStoresBase {
116852
117111
  subjectBoxes: (trackId, deviceId) => mediaStore.listSubjectBoxesByOwner("track", trackId, deviceId)
116853
117112
  },
116854
117113
  memberships: retrainCollectionStore,
117114
+ verdicts: { forTracks: async (trackIds) => this.notificationCenter?.detectionVerdictsOf(trackIds) ?? /* @__PURE__ */ new Map() },
116855
117115
  frameUrl: (frameId, size) => retrainFramePath(this.ctx.id, frameId, size)
116856
117116
  });
116857
117117
  this.retrain = retrain;
@@ -122563,7 +122823,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
122563
122823
  onBindingsChanged: (data) => this.handleBindingsChangedEvent(data),
122564
122824
  onDeviceUnregistered: (deviceId) => this.handleDeviceUnregisteredEvent(deviceId),
122565
122825
  onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now()),
122566
- onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event)
122826
+ onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event),
122827
+ onDeviceRegistryChange: () => this.notificationCenter?.invalidateActionTargets()
122567
122828
  });
122568
122829
  await step("startTrackingTapes", () => this.startTrackingTapes(this.isPostProcessingNode));
122569
122830
  if (this.isPostProcessingNode) await step("startEmbeddingDispatcher", () => this.startEmbeddingDispatcher(logger, transport));