@camstack/addon-post-analysis 1.2.664 → 1.2.666

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 +17 -0
  2. package/dist/{clip-model-registry-TeNUAAPP.js → clip-model-registry-BIhYJRFt.js} +324 -132
  3. package/dist/{clip-model-registry-Ch9Sz-Qb.mjs → clip-model-registry-Uc_C1x81.mjs} +319 -133
  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-CXG7YPYq.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-FkeZoNXj.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-CnYZAdvw.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-Br6eWzp_.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-BZ_D3_sF.mjs → hostInit-BBhgVoNl.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +430 -46
  12. package/dist/pipeline-analytics/index.mjs +430 -46
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +2 -2
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CIt_RDlm.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-4TTlyJ66.mjs +0 -26
@@ -1,4 +1,4 @@
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 strictObject, 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 discriminatedUnion, Ci as retrainRevisionConflictMessage, Cn as SetTrackRelationInputSchema, Cr as eventKindLabel, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as object, Di as ruleDeviceCompatCapQueries, Dn as TimelapseRulePatchSchema, Dr as gifDeriveSpeed, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as number, Ei as ruleBelongsToAlarm$1, En as TimelapseRuleInputSchema, Er as failureContributionCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as unknown, 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 DeviceType, 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 errMsg, Li as subjectPathSampleFromBox, Ln as alarmDeviceSecureOnArm, Lr as isEventKindLabelKey, Lt as NcRuleActionSchema, M as DeclaredDevices, Ma as templateLiteral, Mi as sensorRaisedOf, Mn as TrackRelationSchema, Mr as isAppLinkBase, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as tuple, Ni as servedTemplateVars, Nn as TrackSideSchema, Nr as isAudioClassName, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as partialRecord, Oi as ruleGroupingApplies, On as TimelapseRuleSchema, Or as gifFromMp4Args, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as union, 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, Ra as EventCategory, Ri as subjectPathSvgPoints, Rn as alarmPanelCapability, Rr as isEventOwnerType, Rt as NcRuleInputSchema, S as BucketAccumulator, Sa as boolean, Sn as SetTrackPurityResultSchema, Sr as ensureAlarmProfiles, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as looseObject, 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, _i as resolveDeliveryParams, _n as SCENE_DEFAULT_UNCOVERED_POLICY, _t as NC_CONFIRM_DEFAULT_TIMEOUT_MS, a as CLIP_EMBEDDING_STEP_ID, aa as CamProfileSchema, ai as paletteResultSchema, an as RECORDING_SOURCE_CAMSTACK_ADDON, ar as coreBlockIdFromAddonId, at as NC_ALARM_DELAY_FIELDS, b as BaseDevice, ba as _enum, bi as resolveModeNotifyPolicy, bn as SceneMonitorSchema, bt as NC_DEFAULT_SNOOZE_MINUTES, c as readClusterModelPinStrict, ca as RECORD_QUERY_MAX, ci as pickFaceRescoreIntervalMinutes, cn as RetrainOriginSchema, cr as deleteAlarmProfile, ct as NC_ALARM_SYSTEM_EVENT_KINDS, d as APPEARANCE_BIN_COUNT, da as defineAddonMigrations, 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 defineCustomActions, 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 nodePin, gi as resolveClipSearchMinScore, gn as SCENE_DEFAULT_QUIET_SECONDS, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as isDeviceScopedCap, hi as renameAlarmProfile, hn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, hr as deviceRemovalHookCapability, ht as NC_CONDITION_CATALOG, ia as CAP_ADVANCE_FIELDS, 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 string, ji as secureActuationFor, jn as TrackRelationKindSchema, jr as isAlertEpisodeSeverity, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as record, ki as ruleKindOf, kn as TrackPlacementSchema, kr as isAlarmControllableCap, kt as NC_TEMPLATE_VARS, l as resolveClusterModelPin, la as createEvent, 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 hydrateSchema, 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 InvalidRecordError, 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 defineRecordKinds, 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 BaseAddon, ri as occupancyScope, rr as consumableFormOf, rt as NC_ALARM_DEFAULT_PROFILE_ID, s as FACE_EMBEDDING_STEP_ID, sa as RECORD_MAX_DATA_BYTES, 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 customAction, ui as plateGalleryCapability, un as RetrainVerdictSubjectSchema, ur as derivePostTriggeredAction, ut as NC_AUDIO_CONFIRM_HITS_DEFAULT, v as ArchivedDebugNoteSchema, va as sleep$1, vi as resolveLocationMode, vn as SCENE_DIVERGED, vr as embeddingRefusalOf, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as literal, wi as rootSupplyServes, wn as TIMELAPSE_DENSE_FLOOR_SEC, wr as evictionPolicyOfLocation, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as array, xi as resolveNcMediaImage, xn as SetTrackPurityInputSchema, xr as encodeVectorBase64, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as ZodObject, 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-Ch9Sz-Qb.mjs";
1
+ import { $ as MAX_TRACK_RELATIONS, $i as zoneAnalyticsCapability, $n as buildViewerLink, $r as ncMediaPolicyError, $t as NcTemplatePreviewSchema, A as DETECTION_MACRO_CLASSES, Aa as record, Ai as ruleKindOf, An as TrackPlacementSchema, Ar as isAlarmControllableCap, At as NcAlarmConfigSchema, B as EXPORT_DENSE_MAX_RANGES, Bi as summarizeReview, Bn as alertEpisodeId, Br as isKnownTemplateVar, Bt as NcRuleSchema, C as COCO_TO_MACRO, Ca as boolean, Cn as SetTrackPurityResultSchema, Cr as ensureAlarmProfiles, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as number, Di as ruleBelongsToAlarm$1, Dn as TimelapseRuleInputSchema, Dr as failureContributionCapability, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as looseObject, Ei as ruleAutoTriggersAlarm, En as TRACK_RELATIONS_DEFAULT_LIMIT, Er as faceGalleryCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as union, Fi as startMoverUnderClearance, Fn as TrackSourceSchema, Fr as isDefaultAlarmProfileId, Ft as NcConditionDescriptorSchema, G as LabelAttributionSchema, Gi as systemEventFilterApplies, Gn as applyNonBlockingEdit, Gr as isScheduleActive, Gt as NcSnoozeInputSchema, H as FULL_IMAGE_BBOX, Hi as syncActiveAlarmProfileSettings, Hn as alertEpisodePayload, Hr as isMediaOwnerType, Ht as NcRuleUpdateInputSchema, I as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Ia as unknown, Ii as storageOccupancyCapability, In as activateAlarmProfile, Ir as isDetectionMacroClass, It as NcConditionsSchema, J as LockStateSchema, Ji as templateCarriesSummary, Jn as assertTimelapseCadences, Jr as isVectorIndexDimConflict, Jt as NcStateSummarySectionIdSchema, K as ListTrackRelationsInputSchema, Ki as systemEventSummaryScheduleOf, Kn as applySecureOnArmEdit, Kr as isSystemDelivery, Kt as NcSnoozeSchema, L as EVENT_DENSITY_BATCH_MAX, La as DeviceType, Li as subKindsOf, Ln as addonWidgetsSourceCapability, Lr as isDrawableSubjectPath, Lt as NcRuleActionSchema, M as DeclaredDevices, Ma as string, Mi as secureActuationFor, Mn as TrackRelationKindSchema, Mr as isAlertEpisodeSeverity, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as templateLiteral, Ni as sensorRaisedOf, Nn as TrackRelationSchema, Nr as isAppLinkBase, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as object, Oi as ruleDeviceCompatCapQueries, On as TimelapseRulePatchSchema, Or as gifDeriveSpeed, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as tuple, Pi as servedTemplateVars, Pn as TrackSideSchema, Pr as isAudioClassName, Pt as NcAlarmSettingsPatchSchema, Q as MAX_TRACK_PURITIES, Qi as withSystemEventClasses, Qn as buildSubjectPath, Qr as memoizePinnedFfmpeg, Qt as NcTemplatePreviewInputSchema, R as EVENT_KIND_BY_CAP, Ra as errMsg, Ri as subjectPathSampleFromBox, Rn as alarmDeviceSecureOnArm, Rr as isEventKindLabelKey, Rt as NcRuleInputSchema, S as BucketAccumulator, Sa as array, Si as resolveNcMediaImage, Sn as SetTrackPurityInputSchema, Sr as encodeVectorBase64, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as literal, Ti as rootSupplyServes, Tn as TIMELAPSE_DENSE_FLOOR_SEC, Tr as evictionPolicyOfLocation, Tt as NC_SNOOZE_MAX_MINUTES, U as FailureCounters, Ui as systemEventDeliveryProblems, Un as applyBlockingEdit, Ur as isRetiredClearKey, Ut as NcScheduleSchema, V as FIRST_LEVEL_MACRO_CLASSES, Vi as sunTimes, Vn as alertEpisodeKind, Vr as isLabelSupplyStepId, Vt as NcRuleTargetSchema, W as LABEL_SUPPLY_MACRO, Wi as systemEventFamilyProblems, Wn as applyModeDelaysEdits, Wr as isRootSuppliableStepId, Wt as NcSharedSequenceSchema, X as MAX_BIRTH_DECISION_RECORDS, Xi as vectorDimFromBase64, Xn as audioModeOf, Xr as linkedStepOf, Xt as NcTaxonomySchema, Y as MAX_ARCHIVED_DEBUG_NOTES, Yi as templateVarsFor, Yn as audioMetricsCapability, Yr as kebabToCamel, Yt as NcSystemEventKindSchema, Z as MAX_BIRTH_LATENCY_PROXY_MS, Zi as viewerLinkBaseOf, Zn as buildEventKindDescriptor, Zr as mayWriteToLocation, Zt as NcTemplateDefaultSchema, _ as AlarmArmModeSchema, _a as nodePin, _i as resolveClipSearchMinScore, _n as SCENE_DEFAULT_QUIET_SECONDS, _t as NC_CONFIRM_DEFAULT_TIMEOUT_MS, a as CLIP_EMBEDDING_STEP_ID, aa as CAP_ADVANCE_FIELDS, ai as paintHex, an as RECORDING_EXPORT_MAX_READ_BYTES, ar as consumableLowPercentOf, at as NC_ALARM_DELAY_FIELDS, b as BaseDevice, ba as ZodObject, bi as resolveModeDelays, bn as SCENE_RESET_RECAPTURES, bt as NC_DEFAULT_SNOOZE_MINUTES, c as readClusterModelPinStrict, ca as RECORD_MAX_DATA_BYTES, cn as RetrainDismissedSchema, cr as createAlarmProfile, ct as NC_ALARM_SYSTEM_EVENT_KINDS, d as APPEARANCE_BIN_COUNT, da as customAction, di as plateGalleryCapability, dn as RetrainVerdictSubjectSchema, dr as derivePostTriggeredAction, dt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, ea as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, ei as normalizeNcAction, en as NcTemplateVarDescriptorSchema, er as bundledCamstack, et as MOTION_CLOSE_AFTER_MS, f as APPEARANCE_BLOCKS, fa as defineAddonMigrations, fi as projectSubjectPath, fn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, fr as deriveRecordingMode, ft as NC_CLIP_POST_ROLL_DEFAULT_SEC, g as ActionTargetResolver, ga as isDeviceScopedCap, gi as renameAlarmProfile, gn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, gr as deviceRemovalHookCapability, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as hydrateSchema, hi as readTimelapseGeneratedAt, hn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, hr as deviceAttentionKind, ht as NC_CONDITION_CATALOG, ia as BaseAddon, ii as occupancyScope, ir as consumableFormOf, it as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, j as DETECTION_PIPELINE_CAP_NAME, ja as strictObject, ji as sceneMonitorCapability, jn as TrackPuritySchema, jr as isAlertEpisodeOpen, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as partialRecord, ki as ruleGroupingApplies, kn as TimelapseRuleSchema, kr as gifFromMp4Args, kt as NC_TEMPLATE_VARS, l as resolveClusterModelPin, la as RECORD_QUERY_MAX, li as pickFaceRescoreIntervalMinutes, ln as RetrainOriginSchema, lr as deleteAlarmProfile, lt as NC_ALARM_TRIGGERED_MAX_SEC, m as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ma as defineRecordKinds, mi as readMotionTriggeredBy, mn as SCENE_DEFAULT_ANCHOR_THRESHOLD, mr as describeSystemEventFamilyProblem, mt as NC_CLIP_RETENTION_MAX_SEC, na as evaluateSensorEdge, ni as normalizeNcMediaPolicy, nn as OpsLogEntrySchema, nr as classifyRetrainReview, nt as MotionTriggeredBySchema, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as CamProfileSchema, oi as paletteResultSchema, on as RECORDING_SOURCE_CAMSTACK_ADDON, or as coreBlockIdFromAddonId, ot as NC_ALARM_DELAY_MAX_SEC, p as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, pa as defineCustomActions, pi as readDeviceStateFrom, pn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, pr as describeSystemEventDeliveryProblem, pt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, q as ListTrackRelationsResultSchema, qi as systemEventSummarySplitOf, qn as armRemovalReconcile, qr as isSystemEventSummaryRule, qt as NcSnoozeSuppressedSchema, ra as isSourceCap, ri as notificationRulesCapability, rn as PackageLlmVerdictSchema, rr as cloneAlarmSettings, rt as NC_ALARM_DEFAULT_PROFILE_ID, s as FACE_EMBEDDING_STEP_ID, sa as InvalidRecordError, si as parseAlertEpisodePayload, sn as RebuildObjectEmbeddingsRefusalSchema, sr as cosineSimilarity$1, st as NC_ALARM_NOTIFY_FIELDS, t as BUILTIN_CLIP_MODELS, ta as canonicalHash, ti as normalizeNcCapTarget, tn as OPS_LOG_DEFAULT_LIMIT, tr as calendarEventLocalSpan, tt as MediaFileKindEnum, u as ACTION_TARGET_INVALIDATION_EVENTS, ua as createEvent, ui as pipelineAnalyticsCapability, un as RetrainStatusSchema, ur as deriveDetailCropRect, ut as NC_AUDIO_CONFIRM_HITS_DEFAULT, v as ArchivedDebugNoteSchema, vi as resolveDeliveryParams, vn as SCENE_DEFAULT_UNCOVERED_POLICY, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as discriminatedUnion, wi as retrainRevisionConflictMessage, wn as SetTrackRelationInputSchema, wr as eventKindLabel, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as _enum, xi as resolveModeNotifyPolicy, xn as SceneMonitorSchema, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as sleep$1, yi as resolveLocationMode, yn as SCENE_DIVERGED, yr as embeddingRefusalOf, yt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, z as EVENT_OWNER_TYPES, za as EventCategory, zi as subjectPathSvgPoints, zn as alarmPanelCapability, zr as isEventOwnerType, zt as NcRulePatchSchema } from "../clip-model-registry-Uc_C1x81.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";
@@ -7346,6 +7346,18 @@ function sensorEdgeOf(kind, value) {
7346
7346
  if (HALTED.has(opening.state)) return "neither";
7347
7347
  if (OPENING.has(opening.state)) return "opening";
7348
7348
  }
7349
+ /**
7350
+ * The WORD a sensor change is told with — the state the device moved to, read off
7351
+ * the same slice the edge is: `unlocked`, `open`, `closed`… for a lock, cover, contact
7352
+ * or switch, else `raised` / `cleared` for a boolean sensor (water, smoke…). Undefined
7353
+ * when the slice cannot say: a sentence never invents a state (D393).
7354
+ */
7355
+ function sensorStateWordOf(kind, value, raised) {
7356
+ const opening = alarmOpeningFromSensorEvent(kind, value);
7357
+ if (opening.kind === "reading") return opening.state;
7358
+ if (raised === void 0) return void 0;
7359
+ return raised ? "raised" : "cleared";
7360
+ }
7349
7361
  //#endregion
7350
7362
  //#region src/notification-center/audio-rule-matcher.ts
7351
7363
  /**
@@ -7853,6 +7865,7 @@ function subjectFromSensorEvent(ev, markerTrackId, linked) {
7853
7865
  const eventType = readSensorEventType(ev.value);
7854
7866
  const sensorEdge = sensorEdgeOf(ev.kind, ev.value);
7855
7867
  const sensorRaised = sensorRaisedOf(ev.kind, ev.value);
7868
+ const sensorState = sensorStateWordOf(ev.kind, ev.value, sensorRaised);
7856
7869
  return {
7857
7870
  kind: "device-event",
7858
7871
  recordId: String(ev.id),
@@ -7867,6 +7880,7 @@ function subjectFromSensorEvent(ev, markerTrackId, linked) {
7867
7880
  ...eventType !== void 0 ? { eventType } : {},
7868
7881
  ...sensorEdge !== void 0 ? { sensorEdge } : {},
7869
7882
  ...sensorRaised !== void 0 ? { sensorRaised } : {},
7883
+ ...sensorState !== void 0 ? { sensorState } : {},
7870
7884
  ...linked !== void 0 ? {
7871
7885
  linkedSensorId: ev.sourceDeviceId,
7872
7886
  linkedCameraIds: [...linked.linkedCameraIds],
@@ -9205,6 +9219,17 @@ var en_default = {
9205
9219
  "alarm.combined.title": "Alarm: {{deviceName}}",
9206
9220
  "alarm.intrusion.title": "Intrusion — {{locations}}",
9207
9221
  "alarm.intrusion.body": "Intrusion: {{locations}}",
9222
+ "alarm.intrusion.body.withChanges": "Intrusion: {{locations}} — {{changes}}",
9223
+ "alarm.change.lock.unlocked": "{{device}} unlocked",
9224
+ "alarm.change.lock.unlocking": "{{device}} unlocking",
9225
+ "alarm.change.lock.jammed": "{{device}} jammed",
9226
+ "alarm.change.lock.door-open": "{{device}} open",
9227
+ "alarm.change.open": "{{device}} open",
9228
+ "alarm.change.opening": "{{device}} opening",
9229
+ "alarm.change.closed": "{{device}} closed",
9230
+ "alarm.change.on": "{{device}} on",
9231
+ "alarm.change.raised": "{{device}} triggered",
9232
+ "alarm.change.cleared": "{{device}} cleared",
9208
9233
  "system.alarm-arm-refused.title.home": "Arming home refused",
9209
9234
  "system.alarm-arm-refused.title.away": "Arming away refused",
9210
9235
  "system.alarm-arm-refused.title.night": "Arming night refused",
@@ -9622,6 +9647,17 @@ var it_default = {
9622
9647
  "alarm.combined.title": "Allarme: {{deviceName}}",
9623
9648
  "alarm.intrusion.title": "Intrusione — {{locations}}",
9624
9649
  "alarm.intrusion.body": "Intrusione: {{locations}}",
9650
+ "alarm.intrusion.body.withChanges": "Intrusione: {{locations}} — {{changes}}",
9651
+ "alarm.change.lock.unlocked": "{{device}} aperta",
9652
+ "alarm.change.lock.unlocking": "{{device}} in apertura",
9653
+ "alarm.change.lock.jammed": "{{device}} bloccata",
9654
+ "alarm.change.lock.door-open": "{{device}} aperta",
9655
+ "alarm.change.open": "{{device}} aperto",
9656
+ "alarm.change.opening": "{{device}} in apertura",
9657
+ "alarm.change.closed": "{{device}} chiuso",
9658
+ "alarm.change.on": "{{device}} acceso",
9659
+ "alarm.change.raised": "{{device}} attivato",
9660
+ "alarm.change.cleared": "{{device}} ripristinato",
9625
9661
  "system.alarm-arm-refused.title.home": "Inserimento Casa rifiutato",
9626
9662
  "system.alarm-arm-refused.title.away": "Inserimento Fuori rifiutato",
9627
9663
  "system.alarm-arm-refused.title.night": "Inserimento Notte rifiutato",
@@ -12276,6 +12312,16 @@ var NcStateSummaryTestSchema = object({
12276
12312
  }))
12277
12313
  });
12278
12314
  //#endregion
12315
+ //#region src/notification-center/template-defaults.ts
12316
+ function servedTemplateDefaults(texts) {
12317
+ return [{
12318
+ id: "appliance-cycle",
12319
+ kind: "sensor",
12320
+ title: texts.raw("appliance.finished.title") ?? "",
12321
+ body: texts.raw("appliance.finished.body") ?? ""
12322
+ }];
12323
+ }
12324
+ //#endregion
12279
12325
  //#region src/notification-center/clock-text.ts
12280
12326
  /**
12281
12327
  * `{{time}}` — hours and minutes in the NOTIFICATION language.
@@ -13659,7 +13705,10 @@ var ncActions = defineCustomActions({
13659
13705
  catalog: array(NcConditionDescriptorSchema),
13660
13706
  taxonomy: NcTaxonomySchema
13661
13707
  })),
13662
- "nc.getTemplateCatalog": customAction(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })),
13708
+ "nc.getTemplateCatalog": customAction(object({}), object({
13709
+ vars: array(NcTemplateVarDescriptorSchema),
13710
+ defaults: array(NcTemplateDefaultSchema)
13711
+ })),
13663
13712
  "nc.previewTemplate": customAction(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, { kind: "mutation" }),
13664
13713
  "nc.createRule": customAction(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
13665
13714
  kind: "mutation",
@@ -14439,7 +14488,10 @@ function makeNcActionHandlers(deps) {
14439
14488
  catalog: [...NC_CONDITION_CATALOG],
14440
14489
  taxonomy: NC_TAXONOMY
14441
14490
  }),
14442
- "nc.getTemplateCatalog": async () => ({ vars: servedTemplateVars() }),
14491
+ "nc.getTemplateCatalog": async () => ({
14492
+ vars: servedTemplateVars(),
14493
+ defaults: deps.previewTexts === void 0 ? [] : servedTemplateDefaults(deps.previewTexts)
14494
+ }),
14443
14495
  "nc.previewTemplate": async (input) => {
14444
14496
  const texts = deps.previewTexts;
14445
14497
  if (texts === void 0) throw new Error("nc.previewTemplate: text catalog not wired");
@@ -49856,7 +49908,7 @@ async function encodeRgbCropToJpeg(bytes, width, height) {
49856
49908
  * what lets the caller stop base64-ing purely to be understood.
49857
49909
  */
49858
49910
  async function decodeJpegToRgb(jpeg) {
49859
- const { data, info } = await sharp(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).raw().toBuffer({ resolveWithObject: true });
49911
+ const { data, info } = await sharp(typeof jpeg === "string" ? Buffer.from(jpeg, "base64") : jpeg).removeAlpha().toColourspace("srgb").raw().toBuffer({ resolveWithObject: true });
49860
49912
  return {
49861
49913
  bytes: data,
49862
49914
  width: info.width,
@@ -52942,6 +52994,42 @@ function collectPackageZoneIds(rules) {
52942
52994
  }
52943
52995
  return ids;
52944
52996
  }
52997
+ /**
52998
+ * How much the package zone box grows on every side when asking whether motion
52999
+ * reached it. Mirrors the D144 dilation of the pipeline's burst trigger
53000
+ * (`PACKAGE_ZONE_DILATION` in addon-pipeline, not importable from here): about
53001
+ * one adult's width beside a doormat, the place a courier stands.
53002
+ */
53003
+ var PACKAGE_ZONE_MOTION_DILATION = .5;
53004
+ /**
53005
+ * A motion region covering more than this fraction of the frame is a light
53006
+ * change or the weather, not a person: it never counts as a deliverer.
53007
+ */
53008
+ var PACKAGE_ZONE_MOTION_MAX_REGION_FRAC = .8;
53009
+ /**
53010
+ * Did analyzer motion reach the dilated package zone with a person-sized
53011
+ * region? Unknown geometry answers false: this stamp only ever BACKS the open
53012
+ * of a candidate, so failing closed keeps the light-wedge guard intact.
53013
+ */
53014
+ function delivererMotionNearPackageZone(input) {
53015
+ const { zoneRect, frameWidth, frameHeight } = input;
53016
+ if (zoneRect === null || frameWidth <= 0 || frameHeight <= 0) return false;
53017
+ const dx = zoneRect.w * PACKAGE_ZONE_MOTION_DILATION;
53018
+ const dy = zoneRect.h * PACKAGE_ZONE_MOTION_DILATION;
53019
+ const left = Math.max(0, zoneRect.x - dx);
53020
+ const top = Math.max(0, zoneRect.y - dy);
53021
+ const right = Math.min(1, zoneRect.x + zoneRect.w + dx);
53022
+ const bottom = Math.min(1, zoneRect.y + zoneRect.h + dy);
53023
+ for (const region of input.regions) {
53024
+ const x = region.bbox.x / frameWidth;
53025
+ const y = region.bbox.y / frameHeight;
53026
+ const w = region.bbox.w / frameWidth;
53027
+ const h = region.bbox.h / frameHeight;
53028
+ if (w * h > PACKAGE_ZONE_MOTION_MAX_REGION_FRAC) continue;
53029
+ if (x < right && left < x + w && y < bottom && top < y + h) return true;
53030
+ }
53031
+ return false;
53032
+ }
52945
53033
  //#endregion
52946
53034
  //#region src/pipeline-analytics/pipeline/package/package-collector-mirror.ts
52947
53035
  /**
@@ -53887,6 +53975,15 @@ var PACKAGE_STILLNESS_TTL_MS = 5 * 6e4;
53887
53975
  */
53888
53976
  var PACKAGE_STILLNESS_ASSOC_IOU = .3;
53889
53977
  /**
53978
+ * Lowest score of a below-threshold box that may CONTINUE an open candidate
53979
+ * (D665: a box under the floor may continue, never open). Observed on a real
53980
+ * parcel after the day to IR flip: 0.35 to 0.50.
53981
+ */
53982
+ var PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE = .3;
53983
+ /** Above-floor sightings a candidate needs before a sub-floor box may count:
53984
+ * one confident look plus a doormat-shaped reading is not a parcel. */
53985
+ var PACKAGE_CONTINUATION_MIN_REAL_HITS = 2;
53986
+ /**
53890
53987
  * Two absence reads closer than this are ONE look, not two. A settle burst
53891
53988
  * runs the package step 5 s apart and a poll 45 s apart, so a real second
53892
53989
  * look is always further away; two frames of one moment (a re-dispatch, a
@@ -53936,6 +54033,7 @@ var PackageStillnessTracker = class {
53936
54033
  const next = [...this.byDevice.get(input.deviceId) ?? []];
53937
54034
  const consumed = /* @__PURE__ */ new Set();
53938
54035
  const promoted = [];
54036
+ const continuedLog = [];
53939
54037
  for (const s of input.sightings) {
53940
54038
  const index = this.bestMatch(next, s, consumed);
53941
54039
  if (index < 0) {
@@ -53948,17 +54046,33 @@ var PackageStillnessTracker = class {
53948
54046
  if (prev === void 0) continue;
53949
54047
  consumed.add(prev.id);
53950
54048
  const updated = confirm(prev, input, s);
53951
- const minHits = input.minHits ?? this.config.minHits;
53952
- if (!(updated.promotedAt === null && updated.activityBackedAtOpen && updated.hits >= minHits && input.timestamp - updated.firstSeenAt >= input.minDwellMs)) {
53953
- next[index] = updated;
53954
- continue;
53955
- }
53956
- const parked = {
53957
- ...updated,
53958
- promotedAt: input.timestamp
54049
+ const parked = this.promoteWhenReady(updated, input);
54050
+ next[index] = parked ?? updated;
54051
+ if (parked !== null) promoted.push(toEntry(parked));
54052
+ }
54053
+ if (input.looked === true && input.continuationBoxes !== void 0) for (let i = 0; i < next.length; i += 1) {
54054
+ const c = next[i];
54055
+ if (c === void 0 || c.promotedAt !== null || consumed.has(c.id)) continue;
54056
+ if (c.realHits < PACKAGE_CONTINUATION_MIN_REAL_HITS) continue;
54057
+ const box = this.continuationFor(c, input.continuationBoxes);
54058
+ if (box === null) continue;
54059
+ consumed.add(c.id);
54060
+ const continued = {
54061
+ ...c,
54062
+ lastSeenAt: input.timestamp,
54063
+ hits: c.hits + 1,
54064
+ absenceReads: [],
54065
+ lastLook: "present",
54066
+ earlyUnbackedReported: false
53959
54067
  };
53960
- next[index] = parked;
53961
- promoted.push(toEntry(parked));
54068
+ continuedLog.push({
54069
+ id: c.id,
54070
+ hits: continued.hits,
54071
+ score: box.score
54072
+ });
54073
+ const parked = this.promoteWhenReady(continued, input);
54074
+ next[i] = parked ?? continued;
54075
+ if (parked !== null) promoted.push(toEntry(parked));
53962
54076
  }
53963
54077
  const present = input.rawPackageBoxes !== void 0 ? withRawPresence(next, consumed, input.rawPackageBoxes, input.timestamp, this.config.assocIou) : next;
53964
54078
  const looked = input.looked === true && input.rawPackageBoxes !== void 0 ? withLook(present, consumed, input, input.rawPackageBoxes, this.config.assocIou) : present;
@@ -53994,7 +54108,8 @@ var PackageStillnessTracker = class {
53994
54108
  ...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
53995
54109
  }),
53996
54110
  earlyDepartureIds: [...early],
53997
- earlyEvidenceUnbacked: unbacked
54111
+ earlyEvidenceUnbacked: unbacked,
54112
+ continued: continuedLog
53998
54113
  };
53999
54114
  }
54000
54115
  /** Why complete early evidence may NOT conclude, or null when it may. */
@@ -54021,7 +54136,8 @@ var PackageStillnessTracker = class {
54021
54136
  ...input.lastActivityAt !== void 0 ? { lastActivityAt: input.lastActivityAt } : {}
54022
54137
  }),
54023
54138
  earlyDepartureIds: [],
54024
- earlyEvidenceUnbacked: []
54139
+ earlyEvidenceUnbacked: [],
54140
+ continued: []
54025
54141
  };
54026
54142
  }
54027
54143
  /** The devices holding a candidate — what a sweep has to visit. */
@@ -54085,6 +54201,43 @@ var PackageStillnessTracker = class {
54085
54201
  /** Best same-class candidate over the association floor, ignoring any already
54086
54202
  * consumed by an earlier sighting in THIS frame (two boxes never collapse
54087
54203
  * into one parcel). */
54204
+ continuationFor(c, boxes) {
54205
+ let best = null;
54206
+ let bestIou = this.config.assocIou;
54207
+ for (const b of boxes) {
54208
+ if (b.score < PACKAGE_STILLNESS_CONTINUATION_MIN_SCORE) continue;
54209
+ const overlap = iou$2(c.bbox, b.bbox);
54210
+ if (overlap >= bestIou) {
54211
+ bestIou = overlap;
54212
+ best = b;
54213
+ }
54214
+ }
54215
+ return best;
54216
+ }
54217
+ promoteWhenReady(candidate, input) {
54218
+ const updated = this.backedLate(candidate, input);
54219
+ const minHits = input.minHits ?? this.config.minHits;
54220
+ return updated.promotedAt === null && updated.activityBackedAtOpen && updated.hits >= minHits && input.timestamp - updated.firstSeenAt >= input.minDwellMs ? {
54221
+ ...updated,
54222
+ promotedAt: input.timestamp
54223
+ } : null;
54224
+ }
54225
+ /**
54226
+ * Zone motion learned AFTER the open still backs the candidate when its stamp
54227
+ * lies in the window around the FIRST sighting (the same slack a pick-up
54228
+ * gets): the backing stays anchored at the open, a later passer-by never
54229
+ * validates scenery (D1003).
54230
+ */
54231
+ backedLate(c, input) {
54232
+ if (c.activityBackedAtOpen || c.promotedAt !== null) return c;
54233
+ const at = input.lastDelivererMotionAt;
54234
+ const windowMs = input.activityWindowMs ?? 0;
54235
+ if (at === void 0 || windowMs <= 0) return c;
54236
+ return at >= c.firstSeenAt - windowMs && at <= c.firstSeenAt + this.config.roundGapMs ? {
54237
+ ...c,
54238
+ activityBackedAtOpen: true
54239
+ } : c;
54240
+ }
54088
54241
  bestMatch(candidates, s, consumed) {
54089
54242
  let bestIndex = -1;
54090
54243
  let bestIou = this.config.assocIou;
@@ -54101,7 +54254,7 @@ var PackageStillnessTracker = class {
54101
54254
  }
54102
54255
  openCandidate(input, s) {
54103
54256
  const windowMs = input.activityWindowMs ?? 0;
54104
- const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs;
54257
+ const activityBackedAtOpen = windowMs <= 0 || input.lastActivityAt !== void 0 && input.timestamp - input.lastActivityAt <= windowMs || input.lastDelivererMotionAt !== void 0 && input.timestamp - input.lastDelivererMotionAt <= windowMs;
54105
54258
  return {
54106
54259
  id: this.newId(),
54107
54260
  deviceId: input.deviceId,
@@ -54112,6 +54265,7 @@ var PackageStillnessTracker = class {
54112
54265
  firstSeenAt: input.timestamp,
54113
54266
  lastSeenAt: input.timestamp,
54114
54267
  hits: 1,
54268
+ realHits: 1,
54115
54269
  promotedAt: null,
54116
54270
  absenceReads: [],
54117
54271
  lastLook: null,
@@ -54134,6 +54288,7 @@ function confirm(prev, input, s) {
54134
54288
  frameHeight: input.frameHeight,
54135
54289
  lastSeenAt: input.timestamp,
54136
54290
  hits: prev.hits + 1,
54291
+ realHits: prev.realHits + 1,
54137
54292
  absenceReads: [],
54138
54293
  lastLook: "present",
54139
54294
  earlyUnbackedReported: false,
@@ -54670,6 +54825,9 @@ var FrameState = class {
54670
54825
  * which would let the streetlight wedge vouch for its own delivery.
54671
54826
  */
54672
54827
  lastNonPackageActivityAt = /* @__PURE__ */ new Map();
54828
+ /** Last analyzer motion near a package drop-off zone, per device (D1003).
54829
+ * Backs the OPEN of a candidate only; never a pick-up witness. */
54830
+ lastPackageZoneMotionAt = /* @__PURE__ */ new Map();
54673
54831
  /**
54674
54832
  * WHO was in the drop-off zone, and when (D404). The pick-up is CONCLUDED
54675
54833
  * minutes after the fact, so the collector must be remembered while it is
@@ -58029,6 +58187,7 @@ function nextPackageLookDropEpisodes(previous, drops) {
58029
58187
  //#region src/pipeline-analytics/pipeline/package/package-look-evidence.ts
58030
58188
  function packageLookEvidence(input) {
58031
58189
  const raw = [];
58190
+ const continuation = [];
58032
58191
  for (const d of input.frame.detections) {
58033
58192
  if (d.kind !== "first-level" || !input.packageClasses.has(d.macroClass)) continue;
58034
58193
  raw.push({
@@ -58041,11 +58200,16 @@ function packageLookEvidence(input) {
58041
58200
  for (const d of input.frame.discarded ?? []) {
58042
58201
  if (!input.packageClasses.has(d.macroClass)) continue;
58043
58202
  const [x1, y1, x2, y2] = d.bbox;
58044
- raw.push({
58203
+ const box = {
58045
58204
  x: x1,
58046
58205
  y: y1,
58047
58206
  w: x2 - x1,
58048
58207
  h: y2 - y1
58208
+ };
58209
+ raw.push(box);
58210
+ if (d.reason === "below-threshold") continuation.push({
58211
+ bbox: box,
58212
+ score: d.score
58049
58213
  });
58050
58214
  }
58051
58215
  return {
@@ -58057,6 +58221,7 @@ function packageLookEvidence(input) {
58057
58221
  h: t.bbox.h
58058
58222
  })),
58059
58223
  rawPackageBoxes: raw,
58224
+ continuationBoxes: continuation,
58060
58225
  earlyPickup: input.earlyPickupEnabled
58061
58226
  };
58062
58227
  }
@@ -58244,6 +58409,13 @@ function clamp01(value) {
58244
58409
  }
58245
58410
  //#endregion
58246
58411
  //#region src/pipeline-analytics/pipeline/package/package-scene-diff.ts
58412
+ /**
58413
+ * Zone-crop scene difference for a packages rule (D931).
58414
+ *
58415
+ * A uniform brightness shift is removed by subtracting the median luma delta,
58416
+ * so a shadow that lifts the whole crop leaves no blob. What remains is a
58417
+ * connected region. The package model is not consulted here.
58418
+ */
58247
58419
  /** Residual, after the median shift, that counts as a changed pixel. */
58248
58420
  var PACKAGE_SCENE_DIFF_LUMA_THRESHOLD = 28;
58249
58421
  /** A changed region this large, relative to the zone, is the zone itself. */
@@ -58258,12 +58430,26 @@ var PACKAGE_SCENE_DIFF_SETTLE_MS = 3e3;
58258
58430
  var PACKAGE_SCENE_DIFF_LOOK_GAP_MS = 2e3;
58259
58431
  /** Confidence stamped on a scene-diff sighting the model did not box. */
58260
58432
  var PACKAGE_SCENE_DIFF_CONFIDENCE = .5;
58433
+ /** True when the crop was shot in infrared (no colour). */
58434
+ function isIrCrop(crop) {
58435
+ return crop.chromaSpread < IR_CHROMA_THRESHOLD;
58436
+ }
58261
58437
  function diffZoneLuma(input) {
58262
58438
  const { before, after } = input;
58263
58439
  if (before.width !== after.width || before.height !== after.height || before.luma.length !== after.luma.length) return {
58264
58440
  blobs: [],
58265
58441
  rejections: [{ reason: "crop-mismatch" }]
58266
58442
  };
58443
+ if (isIrCrop(before) !== isIrCrop(after)) return {
58444
+ blobs: [],
58445
+ rejections: [{
58446
+ reason: "lighting-mode-changed",
58447
+ chroma: {
58448
+ before: before.chromaSpread,
58449
+ after: after.chromaSpread
58450
+ }
58451
+ }]
58452
+ };
58267
58453
  const median = medianDelta(before.luma, after.luma);
58268
58454
  const mask = residualMask(before.luma, after.luma, median);
58269
58455
  const zonePx = before.width * before.height;
@@ -58478,6 +58664,7 @@ function okCrop(rgb, width, height, jpeg) {
58478
58664
  width,
58479
58665
  height,
58480
58666
  luma: lumaFromRgb(rgb, width, height),
58667
+ chromaSpread: chromaSpreadFromRgb(rgb, width, height),
58481
58668
  jpeg: new Uint8Array(jpeg)
58482
58669
  };
58483
58670
  }
@@ -58494,6 +58681,46 @@ function lumaFromRgb(bytes, width, height) {
58494
58681
  }
58495
58682
  return luma;
58496
58683
  }
58684
+ /** The grid `IR_CHROMA_THRESHOLD` was calibrated on (`chromaSpread`, scene-crop.ts). */
58685
+ var CHROMA_GRID = 32;
58686
+ /**
58687
+ * Mean of max(r,g,b) - min(r,g,b), 0..255, measured on a 32x32 box-averaged
58688
+ * copy of the crop: the same measure and grid as `chromaSpread` in
58689
+ * `scene-crop.ts` (which needs sharp), so `IR_CHROMA_THRESHOLD` means the same
58690
+ * thing. Averaging first matters: sensor noise and JPEG chroma speckle on an IR
58691
+ * frame cancel out, where per-pixel they read as colour.
58692
+ */
58693
+ function chromaSpreadFromRgb(bytes, width, height) {
58694
+ if (width <= 0 || height <= 0 || bytes.length < width * height * 3) return 0;
58695
+ const gw = Math.min(CHROMA_GRID, width);
58696
+ const gh = Math.min(CHROMA_GRID, height);
58697
+ const sum = new Float64Array(gw * gh * 3);
58698
+ const count = new Float64Array(gw * gh);
58699
+ for (let y = 0; y < height; y++) {
58700
+ const cy = Math.min(gh - 1, Math.floor(y * gh / height));
58701
+ for (let x = 0; x < width; x++) {
58702
+ const cx = Math.min(gw - 1, Math.floor(x * gw / width));
58703
+ const cell = cy * gw + cx;
58704
+ const o = (y * width + x) * 3;
58705
+ sum[cell * 3] = (sum[cell * 3] ?? 0) + (bytes[o] ?? 0);
58706
+ sum[cell * 3 + 1] = (sum[cell * 3 + 1] ?? 0) + (bytes[o + 1] ?? 0);
58707
+ sum[cell * 3 + 2] = (sum[cell * 3 + 2] ?? 0) + (bytes[o + 2] ?? 0);
58708
+ count[cell] = (count[cell] ?? 0) + 1;
58709
+ }
58710
+ }
58711
+ let total = 0;
58712
+ let cells = 0;
58713
+ for (let cell = 0; cell < gw * gh; cell++) {
58714
+ const n = count[cell] ?? 0;
58715
+ if (n === 0) continue;
58716
+ const r = (sum[cell * 3] ?? 0) / n;
58717
+ const g = (sum[cell * 3 + 1] ?? 0) / n;
58718
+ const b = (sum[cell * 3 + 2] ?? 0) / n;
58719
+ total += Math.max(r, g, b) - Math.min(r, g, b);
58720
+ cells += 1;
58721
+ }
58722
+ return cells === 0 ? 0 : total / cells;
58723
+ }
58497
58724
  //#endregion
58498
58725
  //#region src/pipeline-analytics/pipeline/package/package-scene-step.ts
58499
58726
  /**
@@ -58593,6 +58820,14 @@ function compareAfterLeave(prev, input) {
58593
58820
  minAreaFrac: input.minAreaFrac,
58594
58821
  occluders: input.occluders
58595
58822
  });
58823
+ if (diff.rejections.some((r) => r.reason === "lighting-mode-changed")) return {
58824
+ state: {
58825
+ ...rememberClear(prev, input).state,
58826
+ lastDiffAt: input.timestamp
58827
+ },
58828
+ sightings: [],
58829
+ rejections: diff.rejections
58830
+ };
58596
58831
  const blocked = diff.rejections.some((r) => r.reason === "overlaps-person" || r.reason === "overlaps-vehicle");
58597
58832
  if (diff.blobs.length === 0) {
58598
58833
  if (blocked) return {
@@ -58636,6 +58871,20 @@ function confirmPending(prev, input) {
58636
58871
  sightings: [],
58637
58872
  rejections: []
58638
58873
  };
58874
+ if (isIrCrop(prev.after) !== isIrCrop(input.crop)) return {
58875
+ state: {
58876
+ ...rememberClear(prev, input).state,
58877
+ lastDiffAt: input.timestamp
58878
+ },
58879
+ sightings: [],
58880
+ rejections: [{
58881
+ reason: "lighting-mode-changed",
58882
+ chroma: {
58883
+ before: prev.after.chromaSpread,
58884
+ after: input.crop.chromaSpread
58885
+ }
58886
+ }]
58887
+ };
58639
58888
  const sightings = [];
58640
58889
  const rejections = [];
58641
58890
  const still = [];
@@ -58682,7 +58931,7 @@ function judgeBlob(prev, input, blob) {
58682
58931
  bbox: blob.bbox
58683
58932
  } };
58684
58933
  const stable = meanAbsInBox(after, crop, blob.bbox) <= PACKAGE_SCENE_DIFF_STABLE_MEAN;
58685
- const present = meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
58934
+ const present = isIrCrop(before) !== isIrCrop(crop) || meanAbsInBox(before, crop, blob.bbox) >= PACKAGE_SCENE_DIFF_LUMA_THRESHOLD;
58686
58935
  if (!stable || !present) return { rejection: {
58687
58936
  reason: "unstable",
58688
58937
  bbox: blob.bbox,
@@ -58850,7 +59099,8 @@ var PackageSceneCoordinator = class {
58850
59099
  const crop = {
58851
59100
  width: fetched.width,
58852
59101
  height: fetched.height,
58853
- luma: fetched.luma
59102
+ luma: fetched.luma,
59103
+ chromaSpread: fetched.chromaSpread
58854
59104
  };
58855
59105
  const stepped = stepPackageScene({
58856
59106
  deviceId,
@@ -58916,6 +59166,10 @@ var PackageSceneCoordinator = class {
58916
59166
  tags: { deviceId },
58917
59167
  meta: {
58918
59168
  reason: rejection.reason,
59169
+ ...rejection.chroma !== void 0 ? {
59170
+ chromaBefore: Number(rejection.chroma.before.toFixed(2)),
59171
+ chromaAfter: Number(rejection.chroma.after.toFixed(2))
59172
+ } : {},
58919
59173
  ...rejection.areaFrac !== void 0 ? { areaFrac: Number(rejection.areaFrac.toFixed(5)) } : {}
58920
59174
  }
58921
59175
  });
@@ -59024,14 +59278,8 @@ function planPackageSceneFrame(deviceId, input) {
59024
59278
  frameWidth: input.frameWidth,
59025
59279
  frameHeight: input.frameHeight
59026
59280
  });
59027
- const sceneRect = sceneZones.size === 0 ? null : packageZoneCropRect({
59028
- zones: input.zones,
59029
- packageZoneIds: sceneZones,
59030
- frameWidth: input.frameWidth,
59031
- frameHeight: input.frameHeight
59032
- });
59033
59281
  return {
59034
- sightings: sceneRect === null ? input.sightings : withoutSceneCenters(input, sceneRect),
59282
+ sightings: input.sightings,
59035
59283
  note: {
59036
59284
  deviceId,
59037
59285
  timestamp: input.timestamp,
@@ -59074,15 +59322,6 @@ function occludersOf(tracked) {
59074
59322
  }
59075
59323
  return out;
59076
59324
  }
59077
- function withoutSceneCenters(input, rect) {
59078
- if (input.frameWidth <= 0 || input.frameHeight <= 0) return input.sightings;
59079
- return input.sightings.filter((sighting) => !centerInside(sighting.bbox, rect, input));
59080
- }
59081
- function centerInside(bbox, rect, input) {
59082
- const cx = (bbox.x + bbox.w / 2) / input.frameWidth;
59083
- const cy = (bbox.y + bbox.h / 2) / input.frameHeight;
59084
- return cx >= rect.x && cx < rect.x + rect.w && cy >= rect.y && cy < rect.y + rect.h;
59085
- }
59086
59325
  //#endregion
59087
59326
  //#region src/pipeline-analytics/pipeline/package/package-llm-confirm.ts
59088
59327
  /**
@@ -71062,6 +71301,7 @@ Object.freeze({
71062
71301
  "addon-pages-source": ["listPages"],
71063
71302
  "addon-routes": ["getRoutes"],
71064
71303
  "addon-widgets-source": ["listWidgets"],
71304
+ "board-key-source": ["listSources"],
71065
71305
  "broker": ["list", "listProviders"],
71066
71306
  "calendar-source": ["listCalendars", "listEvents"],
71067
71307
  "custom-model-registry": ["listModels"],
@@ -71192,8 +71432,8 @@ Object.freeze({
71192
71432
  * ## Caps with no boolean level pass through untouched
71193
71433
  *
71194
71434
  * `EVENT_KIND_BY_CAP` covers sixteen caps; `SOURCE_CAP_ACTIVE_FIELD` covers the
71195
- * ten that have a boolean whose edge means something. `button`, `presence`,
71196
- * `enum-sensor` and `event-emitter` are NOT levels — their ARRIVAL is the
71435
+ * ten that have a boolean whose edge means something. `button`, `presence`
71436
+ * and `event-emitter` are NOT levels (`enum-sensor` is a stampless word level, see `UNSTAMPED_WORD_FIELD`) — their ARRIVAL is the
71197
71437
  * event, and three presses in a row are three markers. Gating them would
71198
71438
  * re-ship the failure of 2026-08-08, when camera 101's press produced a row, a
71199
71439
  * ring, a matched rule and no marker. (`doorbell` is not a level either, but
@@ -71319,6 +71559,15 @@ var WORD_LEVEL_FIELD = {
71319
71559
  * running for an hour at boot did not just start).
71320
71560
  */
71321
71561
  var BOOLEAN_LEVEL_FIELD = { "running-state": "running" };
71562
+ /**
71563
+ * Caps whose level is a WORD with NO change stamp: cap → field. `enum-sensor`
71564
+ * carries `{ value, lastFetchedAt }`, and `lastFetchedAt` moves on every
71565
+ * provider refresh, so it says nothing about when the word changed. Gated by
71566
+ * the word alone: the first sighting seeds, only a different word passes, and
71567
+ * a null / missing value is dropped and never remembered (planning#396: such
71568
+ * slices passed as `changed` many times a minute and could match an NC rule).
71569
+ */
71570
+ var UNSTAMPED_WORD_FIELD = { "enum-sensor": "value" };
71322
71571
  /** The word a word-level cap rests in — its `false`, for the first-sighting rule. */
71323
71572
  var WORD_LEVEL_INACTIVE = {
71324
71573
  cover: "closed",
@@ -71371,6 +71620,8 @@ var SensorTransitionGate = class {
71371
71620
  */
71372
71621
  observe(deviceId, capName, slice) {
71373
71622
  if (slice["lastChangedAt"] === 0) return this.drop(deviceId, capName, "cold-slice");
71623
+ const unstampedField = UNSTAMPED_WORD_FIELD[capName];
71624
+ if (unstampedField !== void 0) return this.observeUnstampedWord(deviceId, capName, slice, unstampedField);
71374
71625
  const wordField = WORD_LEVEL_FIELD[capName];
71375
71626
  if (wordField !== void 0) return this.observeWord(deviceId, capName, slice, wordField);
71376
71627
  const levelField = BOOLEAN_LEVEL_FIELD[capName];
@@ -71441,11 +71692,25 @@ var SensorTransitionGate = class {
71441
71692
  observeWord(deviceId, capName, slice, wordField) {
71442
71693
  const raw = slice[wordField];
71443
71694
  if (typeof raw !== "string" || raw.length === 0) return this.drop(deviceId, capName, "non-word-value");
71695
+ const stamp = slice["lastChangedAt"];
71696
+ if (typeof stamp !== "number" || !Number.isFinite(stamp) || stamp <= 0) return this.drop(deviceId, capName, "cold-slice");
71444
71697
  const key = `${deviceId}:${capName}`;
71445
71698
  const prior = this.lastWords.get(key);
71446
71699
  this.lastWords.set(key, raw);
71447
71700
  if (prior === void 0) return this.firstWordSighting(deviceId, capName, slice, raw);
71448
71701
  if (prior === raw) return this.drop(deviceId, capName, "no-change");
71702
+ if (stamp < this.startedAtMs) return this.drop(deviceId, capName, "baseline-seeded-stale-active");
71703
+ return { changed: true };
71704
+ }
71705
+ /** A word-level cap with no change stamp: only a DIFFERENT word is a transition. */
71706
+ observeUnstampedWord(deviceId, capName, slice, field) {
71707
+ const raw = slice[field];
71708
+ if (typeof raw !== "string" || raw.length === 0) return this.drop(deviceId, capName, "non-word-value");
71709
+ const key = `${deviceId}:${capName}`;
71710
+ const prior = this.lastWords.get(key);
71711
+ this.lastWords.set(key, raw);
71712
+ if (prior === void 0) return this.drop(deviceId, capName, "baseline-seeded-inactive");
71713
+ if (prior === raw) return this.drop(deviceId, capName, "no-change");
71449
71714
  return { changed: true };
71450
71715
  }
71451
71716
  /**
@@ -72590,7 +72855,7 @@ function buildGlobalSettingsSchema(cap) {
72590
72855
  type: "slider",
72591
72856
  key: "packageDropActivityWindowSec",
72592
72857
  label: "Deliverer window",
72593
- description: "A delivery only counts if a person or vehicle was in the configured package drop-off zone within this many seconds before the package appeared — and a pick-up only with somebody in that zone when it vanished. Motion elsewhere on the camera does not count. 0 disables the requirement.",
72858
+ description: "A delivery only counts if a person or vehicle was in the configured package drop-off zone within this many seconds before the package appeared — and a pick-up only with somebody in that zone when it vanished. Person-sized motion beside the drop-off zone also counts when nobody was tracked. Motion elsewhere on the camera does not count. 0 disables the requirement.",
72594
72859
  min: 0,
72595
72860
  max: 1800,
72596
72861
  step: 30,
@@ -81521,11 +81786,11 @@ function unique(values) {
81521
81786
  * therefore keeps the alarm's own sentence, which is also what every row
81522
81787
  * enqueued before this deploy carries.
81523
81788
  */
81524
- function combinedAlarmText(kind, source) {
81789
+ function combinedAlarmText(kind, source, followers = []) {
81525
81790
  if (kind !== "alarm-triggered" || source === void 0) return null;
81526
81791
  return {
81527
81792
  titleKey: source.className !== void 0 ? "alarm.combined.title" : "system.alarm-triggered.title",
81528
- bodyKey: "alarm.intrusion.body"
81793
+ bodyKey: [source, ...followers].some((ref) => ref.sensorChange !== void 0) ? "alarm.intrusion.body.withChanges" : "alarm.intrusion.body"
81529
81794
  };
81530
81795
  }
81531
81796
  /**
@@ -81540,7 +81805,7 @@ function combinedAlarmText(kind, source) {
81540
81805
  */
81541
81806
  function alarmTextKeys(transition, body) {
81542
81807
  const { kind, source } = transition;
81543
- const combined = combinedAlarmText(kind, source);
81808
+ const combined = combinedAlarmText(kind, source, transition.followers);
81544
81809
  if (combined !== null) return combined;
81545
81810
  const baseTitle = transition.mode !== void 0 ? `system.${kind}.title.${transition.mode}` : `system.${kind}.title`;
81546
81811
  const titleKey = body?.titleKeySuffix !== void 0 ? `${baseTitle}.${body.titleKeySuffix}` : baseTitle;
@@ -87110,6 +87375,13 @@ var NC_STILL_WAIT_MAX_AGE_MS = 3e4;
87110
87375
  function canStillGainMedia(entry) {
87111
87376
  return entry.recordKind === "object-event";
87112
87377
  }
87378
+ /** The variables that carry a finished cycle's run summary. */
87379
+ var CYCLE_SUMMARY_VARS = [
87380
+ "duration",
87381
+ "energy",
87382
+ "afterDuration",
87383
+ "usedEnergy"
87384
+ ];
87113
87385
  var NcDispatcher = class {
87114
87386
  deps;
87115
87387
  targetCache = null;
@@ -88013,6 +88285,7 @@ var NcDispatcher = class {
88013
88285
  }
88014
88286
  /** Rules already warned about a deprecated variable in this process — once each. */
88015
88287
  deprecatedVarWarned = /* @__PURE__ */ new Set();
88288
+ cycleSummaryWarned = /* @__PURE__ */ new Set();
88016
88289
  /**
88017
88290
  * A rule template written by an older client may still say `{{camera}}`. It
88018
88291
  * renders (the alias), and the rule is named once so the operator can edit it.
@@ -88032,6 +88305,27 @@ var NcDispatcher = class {
88032
88305
  });
88033
88306
  }
88034
88307
  /**
88308
+ * A FINISHED-cycle row whose own template places none of the four cycle
88309
+ * variables delivers no run summary (D391: accepted, produced less). Said
88310
+ * once per rule; the operator's text is never touched.
88311
+ */
88312
+ warnCycleSummaryDropped(entry) {
88313
+ const deviceId = entry.payload.subject.applianceCycle?.deviceId;
88314
+ const template = entry.payload.template;
88315
+ if (deviceId === void 0 || template === void 0) return;
88316
+ if (this.cycleSummaryWarned.has(entry.ruleId)) return;
88317
+ const used = new Set([...templatePlaceholders(template.title ?? ""), ...templatePlaceholders(template.body ?? "")]);
88318
+ if (CYCLE_SUMMARY_VARS.some((name) => used.has(name))) return;
88319
+ this.cycleSummaryWarned.add(entry.ruleId);
88320
+ this.deps.logger.warn("appliance cycle finished but the rule template places no run summary — the message carries no duration or energy", {
88321
+ tags: {
88322
+ deviceId,
88323
+ ruleId: entry.ruleId
88324
+ },
88325
+ meta: { placeholders: [...CYCLE_SUMMARY_VARS] }
88326
+ });
88327
+ }
88328
+ /**
88035
88329
  * The facts of a finished appliance cycle; unknown on every other row and on
88036
88330
  * any failure (a part that cannot be read is omitted, never a blocked send).
88037
88331
  */
@@ -88076,6 +88370,7 @@ var NcDispatcher = class {
88076
88370
  const cycleFacts = await this.applianceCycleFacts(entry);
88077
88371
  const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
88078
88372
  this.warnDeprecatedVars(entry);
88373
+ this.warnCycleSummaryDropped(entry);
88079
88374
  const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
88080
88375
  const title = renderRowTitle(this.texts, entry, vars);
88081
88376
  const authoredBody = entry.payload.template?.body;
@@ -89334,10 +89629,31 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone, cycle
89334
89629
  latestVersion: typeof systemEvent?.data["latestVersion"] === "string" ? systemEvent.data["latestVersion"] : "",
89335
89630
  ...systemEvent?.deviceId !== void 0 ? { deviceName: String(systemEvent.deviceId) } : {},
89336
89631
  ...systemEvent?.nodeId !== void 0 && (systemEvent.kind === "node-online" || systemEvent.kind === "node-offline") ? { nodeName: systemEvent.nodeId } : {},
89632
+ ...systemEvent?.alarmSource !== void 0 ? { changes: alarmChangesText(texts, systemEvent.alarmSource, systemEvent.alarmFollowers) } : {},
89337
89633
  ...systemEventParamsForTemplate(systemEvent?.textParams)
89338
89634
  };
89339
89635
  }
89340
89636
  /**
89637
+ * `{{changes}}` of the combined alarm body: what each sensor of the incident did, in
89638
+ * arrival order and one phrase per device — "Laundry lock unlocked". The phrase is a
89639
+ * catalog key per kind and state (`alarm.change.<kind>.<state>`), then per state
89640
+ * (`alarm.change.<state>`), then the bare name: a state nobody worded never drops the device.
89641
+ */
89642
+ function alarmChangesText(texts, source, followers) {
89643
+ const phrases = [];
89644
+ for (const ref of [source, ...followers ?? []]) {
89645
+ const change = ref.sensorChange;
89646
+ if (change === void 0) continue;
89647
+ const key = [`alarm.change.${change.kind}.${change.state}`, `alarm.change.${change.state}`].find((candidate) => texts.has(candidate));
89648
+ const phrase = key === void 0 ? change.name : texts.text({
89649
+ key,
89650
+ vars: { device: change.name }
89651
+ });
89652
+ if (!phrases.includes(phrase)) phrases.push(phrase);
89653
+ }
89654
+ return phrases.join(", ");
89655
+ }
89656
+ /**
89341
89657
  * Which catalog key supplies the DEFAULT body for a row.
89342
89658
  *
89343
89659
  * A system event names its own at intake (the variant selection — profiled or
@@ -102058,7 +102374,10 @@ function buildProvider(host) {
102058
102374
  catalog: [...NC_CONDITION_CATALOG],
102059
102375
  taxonomy: NC_TAXONOMY
102060
102376
  }),
102061
- getTemplateCatalog: async () => ({ vars: servedTemplateVars() }),
102377
+ getTemplateCatalog: async () => ({
102378
+ vars: servedTemplateVars(),
102379
+ defaults: servedTemplateDefaults(host.texts)
102380
+ }),
102062
102381
  previewTemplate: async (input) => previewTemplate(host.texts, input),
102063
102382
  getHistory: async ({ filter }) => {
102064
102383
  const entries = await host.outbox.queryHistory({
@@ -105620,7 +105939,7 @@ function groupedAlarmPhaseKey(ruleId, trackId) {
105620
105939
  * combined alarm notification would then arrive with no picture, which is the
105621
105940
  * one thing it exists to fix.
105622
105941
  */
105623
- function fireRefFor(rule, subject, kind) {
105942
+ function fireRefFor(rule, subject, kind, sensorName) {
105624
105943
  const hasEventMedia = kind === "object-event" || kind === "package-event";
105625
105944
  const className = subject.classNames[0];
105626
105945
  const linkedOwners = subject.trackId === void 0 && subject.linkedStills !== void 0 ? linkedMediaOwners(subject.linkedStills, linkedCameraMode(rule.media)) : void 0;
@@ -105630,6 +105949,11 @@ function fireRefFor(rule, subject, kind) {
105630
105949
  ruleName: rule.name,
105631
105950
  deviceId: subject.deviceId,
105632
105951
  ...subject.sourceDeviceId !== void 0 ? { sourceDeviceId: subject.sourceDeviceId } : {},
105952
+ ...subject.sensorKind !== void 0 && subject.sensorState !== void 0 && sensorName !== void 0 ? { sensorChange: {
105953
+ name: sensorName,
105954
+ kind: subject.sensorKind,
105955
+ state: subject.sensorState
105956
+ } } : {},
105633
105957
  recordId: subject.recordId,
105634
105958
  ...trackId !== void 0 ? { trackId } : {},
105635
105959
  ...linkedOwners !== void 0 && linkedOwners.extraStills.length > 0 ? { extraStills: linkedOwners.extraStills.map((x) => ({ ...x })) } : {},
@@ -105641,6 +105965,12 @@ function fireRefFor(rule, subject, kind) {
105641
105965
  ...subject.confidence !== void 0 ? { confidence: subject.confidence } : {}
105642
105966
  };
105643
105967
  }
105968
+ /** The directory name of the sensor a match was about; absent for a camera subject. */
105969
+ function sensorNameOf(host, subject) {
105970
+ if (subject.source !== "sensor") return void 0;
105971
+ const name = host.deviceDirectory.get(subject.sourceDeviceId ?? subject.deviceId)?.name.trim();
105972
+ return name !== void 0 && name.length > 0 ? name : void 0;
105973
+ }
105644
105974
  /**
105645
105975
  * Run this rule's onTrigger / onTriggerOff bindings for this match, and —
105646
105976
  * when the rule is gated on `armed_*` — auto-trigger the CamStack
@@ -105662,7 +105992,7 @@ function runRuleActions(host, rule, subject, kind, controls) {
105662
105992
  deviceId: subject.deviceId,
105663
105993
  bindings,
105664
105994
  lookup: (id) => host.sequences.get(id),
105665
- origin: fireRefFor(rule, subject, kind)
105995
+ origin: fireRefFor(rule, subject, kind, sensorNameOf(host, subject))
105666
105996
  });
105667
105997
  return phase;
105668
105998
  }
@@ -105686,7 +106016,7 @@ function autoTriggerAlarmPanel(host, rule, subject, kind) {
105686
106016
  });
105687
106017
  return NO_ALARM_PHASE;
105688
106018
  }
105689
- const origin = fireRefFor(rule, subject, kind);
106019
+ const origin = fireRefFor(rule, subject, kind, sensorNameOf(host, subject));
105690
106020
  const phase = panel.classifyRuleMatch(origin);
105691
106021
  try {
105692
106022
  panel.triggerFromRule(origin);
@@ -124995,6 +125325,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
124995
125325
  this.packageDropDetector?.forgetDevice(data.deviceId);
124996
125326
  this.packageScene?.forget(data.deviceId);
124997
125327
  this.frameState.lastNonPackageActivityAt.delete(data.deviceId);
125328
+ this.frameState.lastPackageZoneMotionAt.delete(data.deviceId);
124998
125329
  this.frameState.packageCollectors.forgetDevice(data.deviceId);
124999
125330
  this.frameState.packageOversizeLogAt.delete(data.deviceId);
125000
125331
  this.frameState.packageLookFrames.delete(data.deviceId);
@@ -125019,6 +125350,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
125019
125350
  this.packageDropDetector?.forgetDevice(deviceId);
125020
125351
  this.packageScene?.forget(deviceId);
125021
125352
  this.frameState.lastNonPackageActivityAt.delete(deviceId);
125353
+ this.frameState.lastPackageZoneMotionAt.delete(deviceId);
125022
125354
  this.frameState.packageCollectors.forgetDevice(deviceId);
125023
125355
  this.frameState.packageOversizeLogAt.delete(deviceId);
125024
125356
  this.frameState.packageLookFrames.delete(deviceId);
@@ -127591,6 +127923,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
127591
127923
  detected,
127592
127924
  atMs: timestamp
127593
127925
  });
127926
+ if (detected) await this.stampPackageZoneMotion(deviceId, payload, timestamp);
127594
127927
  if (detected) this.eventStore?.noteMotionSample(deviceId, payload.regionCount, timestamp);
127595
127928
  await this.motionEpisodes?.observe(deviceId, "analyzer", detected, timestamp, {
127596
127929
  regionCount: payload.regionCount,
@@ -127609,6 +127942,28 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
127609
127942
  });
127610
127943
  }
127611
127944
  /**
127945
+ * Stamp analyzer motion that reached a package drop-off zone (D1003): the
127946
+ * courier who never became an L1 track still backs the OPEN of a candidate.
127947
+ * Onboard motion carries no regions or dimensions and never stamps.
127948
+ */
127949
+ async stampPackageZoneMotion(deviceId, payload, timestamp) {
127950
+ const proxy = await this.ensureProxy(deviceId);
127951
+ const packageZoneIds = collectPackageZoneIds(proxy?.state.zoneRules.value?.package ?? []);
127952
+ if (packageZoneIds.size === 0) return;
127953
+ const zoneRect = packageZoneCropRect({
127954
+ zones: proxy?.state.zones.value?.zones ?? [],
127955
+ packageZoneIds,
127956
+ frameWidth: payload.frameWidth,
127957
+ frameHeight: payload.frameHeight
127958
+ });
127959
+ if (delivererMotionNearPackageZone({
127960
+ regions: payload.regions,
127961
+ frameWidth: payload.frameWidth,
127962
+ frameHeight: payload.frameHeight,
127963
+ zoneRect
127964
+ })) this.frameState.lastPackageZoneMotionAt.set(deviceId, timestamp);
127965
+ }
127966
+ /**
127612
127967
  * Persist firmware-driven (onboard / device-activity) motion events into
127613
127968
  * the SAME episode tracker as {@link handleMotionAnalysis}, keyed per
127614
127969
  * `(deviceId, source)` so the two paths can never share — or fight over —
@@ -127941,6 +128296,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
127941
128296
  encodeJpeg: request.encodeJpeg
127942
128297
  }, request.pin);
127943
128298
  }
128299
+ zoneMotionStamp(deviceId) {
128300
+ const at = this.frameState.lastPackageZoneMotionAt.get(deviceId);
128301
+ return at === void 0 ? {} : { lastDelivererMotionAt: at };
128302
+ }
127944
128303
  deliverPackageSceneSightings(delivery) {
127945
128304
  const settings = this.packageDropSettingsFromCache(delivery.deviceId);
127946
128305
  const lastActivityAt = this.frameState.lastNonPackageActivityAt.get(delivery.deviceId);
@@ -127954,6 +128313,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
127954
128313
  minDwellMs: settings.packageDropDwellSec * 1e3,
127955
128314
  activityWindowMs: settings.packageDropActivityWindowSec * 1e3,
127956
128315
  ...lastActivityAt !== void 0 ? { lastActivityAt } : {},
128316
+ ...this.zoneMotionStamp(delivery.deviceId),
127957
128317
  looked: false
127958
128318
  });
127959
128319
  this.bridgePackageStillness({
@@ -128139,6 +128499,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
128139
128499
  minDwellMs: input.settings.packageDropDwellSec * 1e3,
128140
128500
  activityWindowMs: input.settings.packageDropActivityWindowSec * 1e3,
128141
128501
  ...lastActivityAt !== void 0 ? { lastActivityAt } : {},
128502
+ ...this.zoneMotionStamp(input.deviceId),
128142
128503
  ...lookEvidence
128143
128504
  });
128144
128505
  this.bridgePackageStillness({
@@ -128334,6 +128695,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
128334
128695
  });
128335
128696
  }).catch(() => void 0);
128336
128697
  }
128698
+ for (const c of outcome.continued) this.ctx.logger.info("package candidate continued by a sub-floor box", {
128699
+ tags,
128700
+ meta: {
128701
+ entryId: c.id,
128702
+ hits: c.hits,
128703
+ score: c.score,
128704
+ via: input.via
128705
+ }
128706
+ });
128337
128707
  for (const u of outcome.earlyEvidenceUnbacked) this.ctx.logger.info("early pick-up evidence complete — nobody around, left to the TTL", {
128338
128708
  tags,
128339
128709
  meta: {
@@ -128361,6 +128731,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
128361
128731
  observedDwellMs,
128362
128732
  requiredDwellMs,
128363
128733
  activityBackedAtOpen: ex.activityBackedAtOpen,
128734
+ lastZoneMotionAt: this.frameState.lastPackageZoneMotionAt.get(input.deviceId) ?? null,
128364
128735
  reasons
128365
128736
  }
128366
128737
  });
@@ -129134,6 +129505,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
129134
129505
  }
129135
129506
  const verdict = this.sensorTransitions.observe(data.deviceId, data.capName, { ...slice });
129136
129507
  this.flushSensorTransitionDrops();
129508
+ if (verdict.changed) {
129509
+ const entries = Object.entries(slice);
129510
+ const field = (key) => entries.find(([k]) => k === key)?.[1];
129511
+ this.ctx.logger.info("sensor slice passed the transition gate", {
129512
+ tags: { deviceId: data.deviceId },
129513
+ meta: {
129514
+ capName: data.capName,
129515
+ state: field("state") ?? field("value") ?? field("entryOpen") ?? field("active") ?? null,
129516
+ lastChangedAt: field("lastChangedAt") ?? null,
129517
+ verdict: "changed"
129518
+ }
129519
+ });
129520
+ }
129137
129521
  return verdict.changed;
129138
129522
  }
129139
129523
  /**