@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.
Files changed (16) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/{clip-model-registry-Bz1uQvQ8.js → clip-model-registry-C-en5vSD.js} +68371 -66865
  3. package/dist/{clip-model-registry-BrazZJvr.mjs → clip-model-registry-_b3KHYFm.mjs} +68490 -67020
  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-D6DnF6Vk.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CF-Mts9j.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-C7OJ7wek.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 +26 -0
  10. package/dist/pipeline-analytics/{hostInit-DtrcY1yz.mjs → hostInit-BSfz5s98.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +478 -81
  12. package/dist/pipeline-analytics/index.mjs +477 -80
  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-BKMr4IqL.mjs +0 -26
  16. 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 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-BrazZJvr.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-_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": 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) {
@@ -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": ["discoverDevices", "getDevices"],
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: [...input.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: [...sequence.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: [...input.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(["correct", "wrong"]),
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" && isDead(binding.action.deviceId)) {
82622
- changed = true;
82623
- notes.removed(field, deviceSubjects([binding.action.deviceId]), "binding-removed");
82624
- continue;
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 ?? []]) if (binding.kind === "do" && binding.action.kind === "cap") ids.add(binding.action.deviceId);
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 base = [{
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
- await this.deps.actuate({
91625
- deviceId: action.deviceId,
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 !== null && item.section === section) items.push(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 === "correct" ? "button.verdict.correct" : "button.verdict.wrong" }) : host.texts.text({
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
- ...input
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 { packageLlmVerdict: outcome.verdict };
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
- ...llm
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
- if (stored.reused) await this.deps.collections.addFrame({
113573
- collectionId: input.collectionId,
113574
- frameId: stored.frame.frameId,
113575
- deviceId: input.deviceId
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([stored.frame]);
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([this.deps.frames.countsByTrack(ids), this.deps.annotations.countsByTrack(ids)]);
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 wire = toWireFrame(copy);
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.packageDropDetector.onStationaryChange({
127131
- phase: "appeared",
127514
+ const lookFrame = this.frameState.packageLookFrames.get(input.deviceId);
127515
+ promoteWithStill({
127132
127516
  entry,
127133
- timestamp: entry.becameStationaryAt
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) {