@camstack/addon-post-analysis 1.2.660 → 1.2.662

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 +14 -0
  2. package/dist/{clip-model-registry-DAKPBZfa.js → clip-model-registry-C3N1NpST.js} +274 -36
  3. package/dist/{clip-model-registry-D9BbIQ-h.mjs → clip-model-registry-ELaSS9jq.mjs} +269 -37
  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-CvrKdp_8.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BvOlyiL9.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-D4zkNcgf.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-vaatUWY6.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit--CbMNXQy.mjs → hostInit-BjZchMMh.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +506 -172
  12. package/dist/pipeline-analytics/index.mjs +506 -172
  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-BDKliSzv.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-BwWOFFOc.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 string, 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 literal, Ci as retrainRevisionConflictMessage, Cn as SetTrackRelationInputSchema, Cr as eventKindLabel, Ct as NC_GROUPING_MIN_SEC, D as DEFAULT_DETAIL_CROP_CONVENTION, Da as partialRecord, Di as ruleDeviceCompatCapQueries, Dn as TimelapseRulePatchSchema, Dr as gifDeriveSpeed, Dt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, E as CoverStateSchema, Ea as object, Ei as ruleBelongsToAlarm$1, En as TimelapseRuleInputSchema, Er as failureContributionCapability, Et as NC_STATE_SUMMARY_SECTION_IDS, F as EMBEDDING_REQUIREMENTS_FAILED, Fa as DeviceType, 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 errMsg, 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 tuple, Mi as sensorRaisedOf, Mn as TrackRelationSchema, Mr as isAppLinkBase, Mt as NcAlarmSensorsInputSchema, N as DetectionSourceSchema, Na as union, Ni as servedTemplateVars, Nn as TrackSideSchema, Nr as isAudioClassName, Nt as NcAlarmSensorsSchema, O as DEFAULT_EVENT_COLOR, Oa as record, Oi as ruleGroupingApplies, On as TimelapseRuleSchema, Or as gifFromMp4Args, Ot as NC_TAXONOMY, P as EMBEDDING_MODEL_FAILED, Pa as unknown, 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 discriminatedUnion, Sn as SetTrackPurityResultSchema, Sr as ensureAlarmProfiles, St as NC_GROUPING_MAX_SEC, T as ClearTrackRelationResultSchema, Ta as number, 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 sleep$1, _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 array, 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 defineCustomActions, 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 defineRecordKinds, fi as readDeviceStateFrom, fn as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, fr as describeSystemEventDeliveryProblem, ft as NC_CLIP_POST_ROLL_DEFAULT_SEC, g as ActionTargetResolver, gi as resolveClipSearchMinScore, gn as SCENE_DEFAULT_QUIET_SECONDS, gt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, h as AUDIO_MACRO_LABELS, ha as nodePin, 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 templateLiteral, ji as secureActuationFor, jn as TrackRelationKindSchema, jr as isAlertEpisodeSeverity, jt as NcAlarmPostTriggeredActionSchema, k as DEFAULT_TIMELAPSE_PREVIEW_TEXT, ka as strictObject, 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 isDeviceScopedCap, 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 hydrateSchema, 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 ZodObject, vi as resolveLocationMode, vn as SCENE_DIVERGED, vr as embeddingRefusalOf, vt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, w as ClearTrackRelationInputSchema, wa as looseObject, wi as rootSupplyServes, wn as TIMELAPSE_DENSE_FLOOR_SEC, wr as evictionPolicyOfLocation, wt as NC_SENSOR_TRANSITION_KINDS, x as BirthDecisionRecordSchema, xa as boolean, xi as resolveNcMediaImage, xn as SetTrackPurityInputSchema, xr as encodeVectorBase64, xt as NC_GROUPING_DEFAULT_SEC, y as BIRTH_DECISION_DEFAULT_LIMIT, ya as _enum, 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-D9BbIQ-h.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 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-ELaSS9jq.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";
@@ -8880,11 +8880,25 @@ function cleanSourcePreference(ownerKind) {
8880
8880
  * The ONE placeholder grammar. `renderTemplate` substitutes what it matches and
8881
8881
  * {@link templatePlaceholders} lists it; a second regex here would be a legend
8882
8882
  * that disagrees with the renderer about what a variable is, which is the
8883
- * failure mode where an editor advertises `{{ camera }}` as unsupported and the
8883
+ * failure mode where an editor advertises `{{ device }}` as unsupported and the
8884
8884
  * notification prints the camera anyway.
8885
8885
  */
8886
8886
  var PLACEHOLDER_PATTERN = /\{\{\s*(\w+)\s*\}\}/g;
8887
8887
  /**
8888
+ * Names that were renamed and still resolve at render time, mapped to the name
8889
+ * that replaced them. `{{camera}}` became `{{device}}` (a rule on an appliance
8890
+ * names that device, not a camera); a template written by an older client still
8891
+ * prints the right name. An alias is NOT declared in the catalog and no picker
8892
+ * offers it: the stored rules are rewritten by the `pipeline-analytics` template
8893
+ * migration, and this table only covers a client that has not updated yet.
8894
+ */
8895
+ var NC_DEPRECATED_TEMPLATE_VARS = { camera: "device" };
8896
+ /** The deprecated names a template still uses, in order of first appearance. */
8897
+ function deprecatedPlaceholders(template) {
8898
+ if (template === void 0) return [];
8899
+ return templatePlaceholders(template).filter((name) => name in NC_DEPRECATED_TEMPLATE_VARS);
8900
+ }
8901
+ /**
8888
8902
  * The variable names a template names, de-duplicated, in order of first
8889
8903
  * appearance.
8890
8904
  *
@@ -8910,19 +8924,35 @@ function templatePlaceholders(template) {
8910
8924
  */
8911
8925
  function renderTemplate(template, vars) {
8912
8926
  if (template === void 0 || template.trim().length === 0) return null;
8913
- return template.replace(PLACEHOLDER_PATTERN, (_m, name) => vars[name] ?? "");
8927
+ return template.replace(PLACEHOLDER_PATTERN, (_m, name) => {
8928
+ const current = vars[name];
8929
+ if (current !== void 0) return current;
8930
+ const renamed = NC_DEPRECATED_TEMPLATE_VARS[name];
8931
+ return (renamed !== void 0 ? vars[renamed] : void 0) ?? "";
8932
+ });
8914
8933
  }
8915
8934
  //#endregion
8916
8935
  //#region src/notification-center/texts/en.json
8917
8936
  var en_default = {
8918
8937
  "detection.title": "{{rule}}",
8919
- "detection.body": "{{subject}} on {{camera}}{{inZones}}",
8920
- "detection.body.trackEnd": "{{subject}} on {{camera}}{{inZones}} — visit ended",
8938
+ "detection.body": "{{subject}} on {{device}}{{inZones}}",
8939
+ "detection.body.trackEnd": "{{subject}} on {{device}}{{inZones}} — visit ended",
8921
8940
  "detection.subject": "{{classNoun}}",
8922
8941
  "detection.subject.labelled": "{{classNoun}} ({{label}})",
8923
8942
  "detection.subject.identity": "{{label}}",
8924
8943
  "detection.subject.plate": "{{classNoun}} {{label}}",
8925
8944
  "detection.inZones": " in {{zones}}",
8945
+ "appliance.finished.title": "{{device}} finished",
8946
+ "appliance.finished.body": "{{device}} finished{{afterDuration}}{{usedEnergy}}",
8947
+ "appliance.finished.duration": " after {{duration}}",
8948
+ "appliance.finished.energy": " and used {{energy}}",
8949
+ "appliance.duration.minutes": {
8950
+ "one": "{{minutes}} minute",
8951
+ "other": "{{minutes}} minutes"
8952
+ },
8953
+ "appliance.duration.hours": "{{hours}} h",
8954
+ "appliance.duration.hoursMinutes": "{{hours}} h {{minutes}} min",
8955
+ "appliance.energy.value": "{{value}} kWh",
8926
8956
  "class.person": {
8927
8957
  "one": "person",
8928
8958
  "other": "people"
@@ -8989,11 +9019,11 @@ var en_default = {
8989
9019
  "other": "{{count}} other {{classNoun}}"
8990
9020
  },
8991
9021
  "package.delivered.title": "Parcel delivered",
8992
- "package.delivered.body": "Delivered at {{camera}}{{inZones}}",
9022
+ "package.delivered.body": "Delivered at {{device}}{{inZones}}",
8993
9023
  "package.delivered.possible.title": "Possible parcel",
8994
- "package.delivered.possible.body": "Possible parcel at {{camera}}{{inZones}}",
9024
+ "package.delivered.possible.body": "Possible parcel at {{device}}{{inZones}}",
8995
9025
  "package.picked-up.title": "Parcel collected",
8996
- "package.picked-up.body": "Collected from {{camera}}{{inZones}}",
9026
+ "package.picked-up.body": "Collected from {{device}}{{inZones}}",
8997
9027
  "occupancy.body": "{{scope}} {{op}} ({{count}}/{{capacity}})",
8998
9028
  "occupancy.op.occupied": "occupied",
8999
9029
  "occupancy.op.free": "free",
@@ -9129,7 +9159,7 @@ var en_default = {
9129
9159
  "system.camera-enabled.title": "Device switched on",
9130
9160
  "system.camera-enabled.body": "Device {{deviceName}} was switched back on",
9131
9161
  "test.prefix": "[TEST] {{body}}",
9132
- "timelapse.title": "Timelapse — {{camera}}",
9162
+ "timelapse.title": "Timelapse — {{device}}",
9133
9163
  "timelapse.body.window": "{{rule}} — {{from}}→{{to}}",
9134
9164
  "timelapse.body.tooManyEvents": "too many events for dense sampling: video at a uniform cadence",
9135
9165
  "timelapse.body.denseRanges": {
@@ -9143,7 +9173,7 @@ var en_default = {
9143
9173
  },
9144
9174
  "snooze.digest.header": "{{scope}} was snoozed, {{from}}–{{to}}.",
9145
9175
  "snooze.digest.header.global": "{{scope}} was snoozed for everyone, {{from}}–{{to}}.",
9146
- "snooze.digest.line": "• {{rule}} on {{camera}} — {{count}}",
9176
+ "snooze.digest.line": "• {{rule}} on {{device}} — {{count}}",
9147
9177
  "snooze.scope.all": "Everything",
9148
9178
  "snooze.scope.anyDevice": "A camera",
9149
9179
  "snooze.scope.anyRule": "A rule",
@@ -9322,13 +9352,24 @@ var en_default = {
9322
9352
  //#region src/notification-center/texts/it.json
9323
9353
  var it_default = {
9324
9354
  "detection.title": "{{rule}}",
9325
- "detection.body": "{{subject}} su {{camera}}{{inZones}}",
9326
- "detection.body.trackEnd": "{{subject}} su {{camera}}{{inZones}} — visita terminata",
9355
+ "detection.body": "{{subject}} su {{device}}{{inZones}}",
9356
+ "detection.body.trackEnd": "{{subject}} su {{device}}{{inZones}} — visita terminata",
9327
9357
  "detection.subject": "{{classNoun}}",
9328
9358
  "detection.subject.labelled": "{{classNoun}} ({{label}})",
9329
9359
  "detection.subject.identity": "{{label}}",
9330
9360
  "detection.subject.plate": "{{classNoun}} {{label}}",
9331
9361
  "detection.inZones": " in {{zones}}",
9362
+ "appliance.finished.title": "{{device}} ha terminato",
9363
+ "appliance.finished.body": "{{device}} ha terminato{{afterDuration}}{{usedEnergy}}",
9364
+ "appliance.finished.duration": " dopo {{duration}}",
9365
+ "appliance.finished.energy": " e ha consumato {{energy}}",
9366
+ "appliance.duration.minutes": {
9367
+ "one": "{{minutes}} minuto",
9368
+ "other": "{{minutes}} minuti"
9369
+ },
9370
+ "appliance.duration.hours": "{{hours}} h",
9371
+ "appliance.duration.hoursMinutes": "{{hours}} h {{minutes}} min",
9372
+ "appliance.energy.value": "{{value}} kWh",
9332
9373
  "class.person": {
9333
9374
  "one": "persona",
9334
9375
  "other": "persone"
@@ -9395,11 +9436,11 @@ var it_default = {
9395
9436
  "other": "{{count}} altri {{classNoun}}"
9396
9437
  },
9397
9438
  "package.delivered.title": "Pacco consegnato",
9398
- "package.delivered.body": "Consegnato su {{camera}}{{inZones}}",
9439
+ "package.delivered.body": "Consegnato su {{device}}{{inZones}}",
9399
9440
  "package.delivered.possible.title": "Possibile pacco",
9400
- "package.delivered.possible.body": "Possibile pacco su {{camera}}{{inZones}}",
9441
+ "package.delivered.possible.body": "Possibile pacco su {{device}}{{inZones}}",
9401
9442
  "package.picked-up.title": "Pacco ritirato",
9402
- "package.picked-up.body": "Ritirato da {{camera}}{{inZones}}",
9443
+ "package.picked-up.body": "Ritirato da {{device}}{{inZones}}",
9403
9444
  "occupancy.body": "{{scope}} {{op}} ({{count}}/{{capacity}})",
9404
9445
  "occupancy.op.occupied": "occupata",
9405
9446
  "occupancy.op.free": "libera",
@@ -9535,7 +9576,7 @@ var it_default = {
9535
9576
  "system.camera-enabled.title": "Dispositivo riacceso",
9536
9577
  "system.camera-enabled.body": "Il dispositivo {{deviceName}} è stato riacceso",
9537
9578
  "test.prefix": "[PROVA] {{body}}",
9538
- "timelapse.title": "Sequenza — {{camera}}",
9579
+ "timelapse.title": "Sequenza — {{device}}",
9539
9580
  "timelapse.body.window": "{{rule}} — {{from}}→{{to}}",
9540
9581
  "timelapse.body.tooManyEvents": "troppi eventi per il campionamento denso: video a cadenza uniforme",
9541
9582
  "timelapse.body.denseRanges": {
@@ -9549,7 +9590,7 @@ var it_default = {
9549
9590
  },
9550
9591
  "snooze.digest.header": "{{scope}} in pausa, {{from}}–{{to}}.",
9551
9592
  "snooze.digest.header.global": "{{scope}} in pausa per tutti, {{from}}–{{to}}.",
9552
- "snooze.digest.line": "• {{rule}} su {{camera}} — {{count}}",
9593
+ "snooze.digest.line": "• {{rule}} su {{device}} — {{count}}",
9553
9594
  "snooze.scope.all": "Tutto",
9554
9595
  "snooze.scope.anyDevice": "Una camera",
9555
9596
  "snooze.scope.anyRule": "Una regola",
@@ -9773,7 +9814,7 @@ var it_default = {
9773
9814
  * as plain strings. {@link renderTemplate} never re-scans a replacement, so
9774
9815
  * there is exactly one level of composition and it cannot grow: a fragment that
9775
9816
  * names another composite renders it empty, and a value that happens to look
9776
- * like `{{camera}}` is printed, not substituted.
9817
+ * like `{{device}}` is printed, not substituted.
9777
9818
  */
9778
9819
  /** The languages the hub ships texts for. */
9779
9820
  var NC_LANGUAGES = ["en", "it"];
@@ -56135,6 +56176,258 @@ var ZoneCatalogueCache = class {
56135
56176
  }
56136
56177
  };
56137
56178
  //#endregion
56179
+ //#region src/notification-center/record-kinds.ts
56180
+ /**
56181
+ * Record kinds owned by the pipeline-analytics addon (records plan phase 2,
56182
+ * steps 18-24): the notification-center config tables.
56183
+ *
56184
+ * The notification-center module is hosted inside the pipeline-analytics addon.
56185
+ * That addon declares no `execution.group`, so the UDS owner stamp is the addon
56186
+ * id: `pipeline-analytics`. Declared hub-only (agents have no `ctx.records`).
56187
+ * One deploy covers all 7 kinds.
56188
+ */
56189
+ /**
56190
+ * The hub language and operator-authored per-key overrides.
56191
+ * One row, id `texts`. Losing it degrades to the shipped English catalog.
56192
+ */
56193
+ var TextsBodySchema = object({
56194
+ language: NcLanguageSchema,
56195
+ overrides: partialRecord(NcLanguageSchema, record(string(), string()))
56196
+ });
56197
+ /**
56198
+ * A shared action sequence (without id, which is the record key).
56199
+ * Extends the wire shape with `createdAt` / `updatedAt` (stored by the legacy table).
56200
+ */
56201
+ var SequenceBodySchema = NcSharedSequenceSchema.omit({ id: true }).extend({
56202
+ createdAt: number(),
56203
+ updatedAt: number()
56204
+ });
56205
+ /**
56206
+ * The AI section of one ordinary rule. Record id = the rule id.
56207
+ */
56208
+ var RuleAiBodySchema = object({
56209
+ ai: NcRuleAiSchema,
56210
+ updatedAt: number()
56211
+ });
56212
+ /** A calendar-events rule (without `id`). */
56213
+ var CalendarEventsRuleBodySchema = NcCalendarEventsRuleSchema.omit({ id: true });
56214
+ /** A multi-camera digest rule (without `id`). */
56215
+ var SummaryRuleBodySchema = NcSummaryRuleSchema.omit({ id: true });
56216
+ /** A timelapse rule (without `id`). */
56217
+ var TimelapseRuleBodySchema = TimelapseRuleSchema.omit({ id: true });
56218
+ /** A notification rule (without `id`). */
56219
+ var RuleBodySchema = NcRuleSchema.omit({ id: true });
56220
+ var ncRecordKinds = defineRecordKinds({
56221
+ /**
56222
+ * The hub language + per-key text overrides. One row (id `texts`).
56223
+ */
56224
+ texts: {
56225
+ schema: TextsBodySchema,
56226
+ class: "config",
56227
+ writeTrigger: "an operator changes the hub language (setLanguage) or replaces the per-key override set (setOverrides)",
56228
+ device: "none",
56229
+ at: "none",
56230
+ retention: { none: "one row (id `texts`); losing it costs the chosen language and household wording, never a notification — every read degrades to the shipped English catalog" }
56231
+ },
56232
+ /**
56233
+ * Shared action sequences — named, ordered runs of steps with their own throttle.
56234
+ * One row per sequence, id = sequence UUID.
56235
+ */
56236
+ sequences: {
56237
+ schema: SequenceBodySchema,
56238
+ class: "config",
56239
+ writeTrigger: "an admin creates/edits/deletes a shared sequence via notificationRules.createSequence / updateSequence / deleteSequence, or the one-shot boot migration that materialises legacy inline sequences",
56240
+ device: "none",
56241
+ at: "none",
56242
+ retention: { none: "a row goes only when an admin deletes it; bounded by the sequence set a human is willing to author" }
56243
+ },
56244
+ /**
56245
+ * Per-rule AI section: whether and how a rule narrates itself.
56246
+ * Record id = the rule id it belongs to.
56247
+ */
56248
+ "rule-ai": {
56249
+ schema: RuleAiBodySchema,
56250
+ class: "config",
56251
+ writeTrigger: "an operator switches the AI section of a notification rule on or off, or edits its prompt / word cap / language",
56252
+ device: "none",
56253
+ at: "none",
56254
+ retention: { none: "a row goes when the operator clears the section or deletes the rule; bounded by the rule set a human is willing to author" }
56255
+ },
56256
+ /**
56257
+ * Calendar-events rules. One row per rule, id = rule UUID.
56258
+ */
56259
+ "calendar-events-rules": {
56260
+ schema: CalendarEventsRuleBodySchema,
56261
+ class: "config",
56262
+ writeTrigger: "an operator creates or edits a calendar-events rule, or the producer stamps a delivered window via markGenerated",
56263
+ device: "none",
56264
+ at: "none",
56265
+ indexes: [{
56266
+ name: "enabled",
56267
+ fields: ["enabled"]
56268
+ }],
56269
+ retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
56270
+ },
56271
+ /**
56272
+ * Multi-camera digest rules. One row per rule, id = rule UUID.
56273
+ */
56274
+ "summary-rules": {
56275
+ schema: SummaryRuleBodySchema,
56276
+ class: "config",
56277
+ writeTrigger: "an operator creates or edits a digest rule, or the producer stamps a delivered window via markGenerated",
56278
+ device: "none",
56279
+ at: "none",
56280
+ indexes: [{
56281
+ name: "enabled",
56282
+ fields: ["enabled"]
56283
+ }],
56284
+ retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
56285
+ },
56286
+ /**
56287
+ * Timelapse rules. One row per rule, id = rule UUID.
56288
+ * Three rows on prod (2026-10-18).
56289
+ */
56290
+ "timelapse-rules": {
56291
+ schema: TimelapseRuleBodySchema,
56292
+ class: "config",
56293
+ writeTrigger: "an operator creates or edits a timelapse rule, or the scheduler stamps a successful per-camera generation via markGenerated",
56294
+ device: "none",
56295
+ at: "none",
56296
+ indexes: [{
56297
+ name: "enabled",
56298
+ fields: ["enabled"]
56299
+ }],
56300
+ retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
56301
+ },
56302
+ /**
56303
+ * Notification rules — the main rule set. One row per rule, id = rule UUID.
56304
+ * 18 rows on prod (2026-10-18); most coupled kind in this owner.
56305
+ */
56306
+ rules: {
56307
+ schema: RuleBodySchema,
56308
+ class: "config",
56309
+ writeTrigger: "an operator creates, edits, enables/disables a rule or opts a target out of it; nothing writes on the evaluation path",
56310
+ device: "none",
56311
+ at: "none",
56312
+ indexes: [{
56313
+ name: "delivery",
56314
+ fields: ["delivery"]
56315
+ }],
56316
+ retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
56317
+ }
56318
+ });
56319
+ //#endregion
56320
+ //#region src/migrations/device-template-var.ts
56321
+ /** `{{camera}}` and `{{ camera }}`: the whitespace the renderer allows is kept as written. */
56322
+ var OLD_VARIABLE = /\{\{(\s*)camera(\s*)\}\}/g;
56323
+ /** The template text with the variable renamed; the same string when it names none. */
56324
+ function renameDeviceVariable(text) {
56325
+ return text.replace(OLD_VARIABLE, "{{$1device$2}}");
56326
+ }
56327
+ /** A `{ title, body }` template with both fields renamed; `null` when neither changed. */
56328
+ function renamedTemplate(template) {
56329
+ if (template === void 0) return null;
56330
+ const title = template.title === void 0 ? void 0 : renameDeviceVariable(template.title);
56331
+ const body = template.body === void 0 ? void 0 : renameDeviceVariable(template.body);
56332
+ if (title === template.title && body === template.body) return null;
56333
+ return {
56334
+ ...template,
56335
+ ...title !== void 0 ? { title } : {},
56336
+ ...body !== void 0 ? { body } : {}
56337
+ };
56338
+ }
56339
+ function renamedRule(data) {
56340
+ const template = renamedTemplate(data.template);
56341
+ return template === null ? null : {
56342
+ ...data,
56343
+ template
56344
+ };
56345
+ }
56346
+ function renamedTimelapseRule(data) {
56347
+ const template = renamedTemplate(data.template);
56348
+ const previewText = data.previewText === void 0 ? void 0 : renameDeviceVariable(data.previewText);
56349
+ const previewChanged = previewText !== data.previewText;
56350
+ if (template === null && !previewChanged) return null;
56351
+ return {
56352
+ ...data,
56353
+ ...template !== null ? { template } : {},
56354
+ ...previewChanged && previewText !== void 0 ? { previewText } : {}
56355
+ };
56356
+ }
56357
+ function renamedTexts(data) {
56358
+ let changed = false;
56359
+ const overrides = Object.fromEntries(Object.entries(data.overrides).map(([language, table]) => [language, Object.fromEntries(Object.entries(table).map(([key, text]) => {
56360
+ const next = renameDeviceVariable(text);
56361
+ if (next !== text) changed = true;
56362
+ return [key, next];
56363
+ }))]));
56364
+ return changed ? {
56365
+ ...data,
56366
+ overrides
56367
+ } : null;
56368
+ }
56369
+ var PAGE$1 = 500;
56370
+ /** Every row of a kind, page by page. */
56371
+ async function allRows(kind) {
56372
+ const rows = [];
56373
+ let cursor;
56374
+ do {
56375
+ const page = await kind.query({
56376
+ limit: PAGE$1,
56377
+ ...cursor !== void 0 ? { cursor } : {}
56378
+ });
56379
+ rows.push(...page.records);
56380
+ cursor = page.nextCursor ?? void 0;
56381
+ } while (cursor !== void 0);
56382
+ return rows;
56383
+ }
56384
+ /** Rewrite the rows of one kind that carry the old name; returns how many were written. */
56385
+ async function rewriteKind(kind, rewrite) {
56386
+ let written = 0;
56387
+ for (const row of await allRows(kind)) {
56388
+ const next = rewrite(row.data);
56389
+ if (next === null) continue;
56390
+ await kind.put({
56391
+ id: row.id,
56392
+ data: next
56393
+ }, { ifRev: row.rev });
56394
+ written += 1;
56395
+ }
56396
+ return written;
56397
+ }
56398
+ async function migrateDeviceTemplateVariable(ports) {
56399
+ const rules = await rewriteKind(ports.rules, renamedRule);
56400
+ const timelapseRules = await rewriteKind(ports.timelapseRules, renamedTimelapseRule);
56401
+ const texts = await rewriteKind(ports.texts, renamedTexts);
56402
+ return {
56403
+ outcome: rules + timelapseRules + texts === 0 ? "already-done" : "done",
56404
+ summary: {
56405
+ rules,
56406
+ timelapseRules,
56407
+ texts
56408
+ }
56409
+ };
56410
+ }
56411
+ //#endregion
56412
+ //#region src/migrations/index.ts
56413
+ /** The `pipeline-analytics` catalogue (D987): offered to the engine at the hub's boot. */
56414
+ var ADDON_MIGRATIONS = [{
56415
+ meta: {
56416
+ id: "pipeline-analytics/2026-11-01-template-camera-to-device",
56417
+ owner: "pipeline-analytics",
56418
+ level: "addon-state",
56419
+ seq: 1,
56420
+ dependsOn: [],
56421
+ store: "settings",
56422
+ scope: "cluster",
56423
+ onFailure: "tolerate",
56424
+ bodyKind: "procedure",
56425
+ description: "Rename the {{camera}} template variable to {{device}} in every stored notification rule, timelapse rule and text override",
56426
+ fixtureMark: "the rule name and the trigger conditions the migration does not know"
56427
+ },
56428
+ procedure: migrateDeviceTemplateVariable
56429
+ }];
56430
+ //#endregion
56138
56431
  //#region src/pipeline-analytics/clip-space-plan.ts
56139
56432
  /**
56140
56433
  * Which CLIP feature space a search reads and a rebuild fills — decided in one
@@ -67033,7 +67326,7 @@ var CaptureScheduler = class {
67033
67326
  for (let i = lane.queue.length - 1; i >= 0; i -= 1) {
67034
67327
  const entry = lane.queue[i];
67035
67328
  if (entry === void 0) continue;
67036
- if (entry.staleAfterMs === void 0 || nowMs - entry.enqueuedAt <= entry.staleAfterMs) continue;
67329
+ if (entry.staleAfterMs === void 0 || nowMs - entry.enqueuedAt < entry.staleAfterMs) continue;
67037
67330
  lane.queue.splice(i, 1);
67038
67331
  this.bump(deviceId, "droppedStale");
67039
67332
  this.noteDropped(deviceId, entry, "stale");
@@ -67109,7 +67402,7 @@ var CaptureScheduler = class {
67109
67402
  while (lane.inFlight < this.budget && lane.queue.length > 0) {
67110
67403
  const entry = lane.queue.shift();
67111
67404
  if (entry === void 0) return;
67112
- if (entry.staleAfterMs !== void 0 && this.now() - entry.enqueuedAt > entry.staleAfterMs) {
67405
+ if (entry.staleAfterMs !== void 0 && this.now() - entry.enqueuedAt >= entry.staleAfterMs) {
67113
67406
  this.bump(deviceId, "droppedStale");
67114
67407
  this.noteDropped(deviceId, entry, "stale");
67115
67408
  entry.drop();
@@ -68337,6 +68630,109 @@ function createAnalyticsWidgetsProvider() {
68337
68630
  ] };
68338
68631
  }
68339
68632
  //#endregion
68633
+ //#region src/notification-center/appliance-cycle.ts
68634
+ var NC_UNKNOWN_CYCLE_FACTS = {
68635
+ durationMs: null,
68636
+ energyKwh: null
68637
+ };
68638
+ var NC_EMPTY_CYCLE_VARS = {
68639
+ duration: "",
68640
+ energy: "",
68641
+ afterDuration: "",
68642
+ usedEnergy: ""
68643
+ };
68644
+ /** `52 minutes` / `1 h 12 min` / `2 h`; `''` below one minute (never "0 minutes"). */
68645
+ function durationText(texts, durationMs) {
68646
+ if (durationMs === null || !Number.isFinite(durationMs)) return "";
68647
+ const totalMinutes = Math.round(durationMs / 6e4);
68648
+ if (totalMinutes < 1) return "";
68649
+ if (totalMinutes < 60) return texts.text({
68650
+ key: "appliance.duration.minutes",
68651
+ count: totalMinutes,
68652
+ vars: { minutes: `${totalMinutes}` }
68653
+ });
68654
+ const hours = Math.floor(totalMinutes / 60);
68655
+ const minutes = totalMinutes % 60;
68656
+ return minutes === 0 ? texts.text({
68657
+ key: "appliance.duration.hours",
68658
+ vars: { hours: `${hours}` }
68659
+ }) : texts.text({
68660
+ key: "appliance.duration.hoursMinutes",
68661
+ vars: {
68662
+ hours: `${hours}`,
68663
+ minutes: `${minutes}`
68664
+ }
68665
+ });
68666
+ }
68667
+ /** `0,84 kWh` (it) / `0.84 kWh` (en); `''` when unmeasured. */
68668
+ function energyText(texts, energyKwh) {
68669
+ if (energyKwh === null || !Number.isFinite(energyKwh) || energyKwh < 0) return "";
68670
+ const value = energyKwh.toLocaleString(texts.language, {
68671
+ minimumFractionDigits: 2,
68672
+ maximumFractionDigits: 2
68673
+ });
68674
+ return texts.text({
68675
+ key: "appliance.energy.value",
68676
+ vars: { value }
68677
+ });
68678
+ }
68679
+ /** The four variables of a finished cycle, fragments rendered from scalars only. */
68680
+ function ncApplianceCycleVars(texts, facts) {
68681
+ const duration = durationText(texts, facts.durationMs);
68682
+ const energy = energyText(texts, facts.energyKwh);
68683
+ return {
68684
+ duration,
68685
+ energy,
68686
+ afterDuration: duration === "" ? "" : texts.text({
68687
+ key: "appliance.finished.duration",
68688
+ vars: { duration }
68689
+ }),
68690
+ usedEnergy: energy === "" ? "" : texts.text({
68691
+ key: "appliance.finished.energy",
68692
+ vars: { energy }
68693
+ })
68694
+ };
68695
+ }
68696
+ /**
68697
+ * The frozen reference a FINISHED appliance cycle row carries (`{}` for every
68698
+ * other row): kind `appliance-cycle`, edge `cleared`. The appliance is the
68699
+ * sensor itself — a linked camera is only where the row was attributed.
68700
+ */
68701
+ function applianceCycleOf(subject) {
68702
+ if (subject.sensorKind !== "appliance-cycle" || subject.sensorRaised !== false) return {};
68703
+ return { applianceCycle: { deviceId: subject.linkedSensorId ?? subject.sourceDeviceId ?? subject.deviceId } };
68704
+ }
68705
+ //#endregion
68706
+ //#region src/notification-center/appliance-cycle-runs.ts
68707
+ var CYCLE_FACTS_WAIT_MS = 3e3;
68708
+ var CYCLE_FACTS_POLL_MS = 250;
68709
+ /** A closed run belongs to this finish when it ended within this much of it. */
68710
+ var CYCLE_RUN_MATCH_MS = 10 * 6e4;
68711
+ function factsOfClosed(run, finishedAtMs) {
68712
+ if (run.endedAt === null || Math.abs(run.endedAt - finishedAtMs) > CYCLE_RUN_MATCH_MS) return NC_UNKNOWN_CYCLE_FACTS;
68713
+ return {
68714
+ durationMs: run.durationMs,
68715
+ energyKwh: run.detail.energyKwh
68716
+ };
68717
+ }
68718
+ function applianceCycleFactsFromRuns(ports) {
68719
+ return async ({ deviceId, finishedAtMs }) => {
68720
+ let waited = 0;
68721
+ for (;;) {
68722
+ const run = await ports.newestRun(deviceId);
68723
+ if (run === null) return NC_UNKNOWN_CYCLE_FACTS;
68724
+ if (run.state === "closed") return factsOfClosed(run, finishedAtMs);
68725
+ if (run.startedAt > finishedAtMs) return NC_UNKNOWN_CYCLE_FACTS;
68726
+ if (waited >= CYCLE_FACTS_WAIT_MS) return {
68727
+ durationMs: finishedAtMs - run.startedAt,
68728
+ energyKwh: null
68729
+ };
68730
+ await ports.sleep(CYCLE_FACTS_POLL_MS);
68731
+ waited += CYCLE_FACTS_POLL_MS;
68732
+ }
68733
+ };
68734
+ }
68735
+ //#endregion
68340
68736
  //#region src/pipeline-analytics/runtime/wire-subscriptions.ts
68341
68737
  /**
68342
68738
  * Every category the Notification Center's system-event intake can turn into a
@@ -70298,6 +70694,7 @@ Object.freeze({
70298
70694
  "page": 1,
70299
70695
  "pageSize": 50
70300
70696
  } },
70697
+ "device-runs": { "listRuns": { "limit": 50 } },
70301
70698
  "llm": { "upsertProfile": { "profile": { "__nested__": {
70302
70699
  "timeoutMs": 6e4,
70303
70700
  "connectTimeoutMs": 1e4,
@@ -82301,147 +82698,6 @@ var NcDeviceMuteStore = class {
82301
82698
  }
82302
82699
  };
82303
82700
  //#endregion
82304
- //#region src/notification-center/record-kinds.ts
82305
- /**
82306
- * Record kinds owned by the pipeline-analytics addon (records plan phase 2,
82307
- * steps 18-24): the notification-center config tables.
82308
- *
82309
- * The notification-center module is hosted inside the pipeline-analytics addon.
82310
- * That addon declares no `execution.group`, so the UDS owner stamp is the addon
82311
- * id: `pipeline-analytics`. Declared hub-only (agents have no `ctx.records`).
82312
- * One deploy covers all 7 kinds.
82313
- */
82314
- /**
82315
- * The hub language and operator-authored per-key overrides.
82316
- * One row, id `texts`. Losing it degrades to the shipped English catalog.
82317
- */
82318
- var TextsBodySchema = object({
82319
- language: NcLanguageSchema,
82320
- overrides: partialRecord(NcLanguageSchema, record(string(), string()))
82321
- });
82322
- /**
82323
- * A shared action sequence (without id, which is the record key).
82324
- * Extends the wire shape with `createdAt` / `updatedAt` (stored by the legacy table).
82325
- */
82326
- var SequenceBodySchema = NcSharedSequenceSchema.omit({ id: true }).extend({
82327
- createdAt: number(),
82328
- updatedAt: number()
82329
- });
82330
- /**
82331
- * The AI section of one ordinary rule. Record id = the rule id.
82332
- */
82333
- var RuleAiBodySchema = object({
82334
- ai: NcRuleAiSchema,
82335
- updatedAt: number()
82336
- });
82337
- /** A calendar-events rule (without `id`). */
82338
- var CalendarEventsRuleBodySchema = NcCalendarEventsRuleSchema.omit({ id: true });
82339
- /** A multi-camera digest rule (without `id`). */
82340
- var SummaryRuleBodySchema = NcSummaryRuleSchema.omit({ id: true });
82341
- /** A timelapse rule (without `id`). */
82342
- var TimelapseRuleBodySchema = TimelapseRuleSchema.omit({ id: true });
82343
- /** A notification rule (without `id`). */
82344
- var RuleBodySchema = NcRuleSchema.omit({ id: true });
82345
- var ncRecordKinds = defineRecordKinds({
82346
- /**
82347
- * The hub language + per-key text overrides. One row (id `texts`).
82348
- */
82349
- texts: {
82350
- schema: TextsBodySchema,
82351
- class: "config",
82352
- writeTrigger: "an operator changes the hub language (setLanguage) or replaces the per-key override set (setOverrides)",
82353
- device: "none",
82354
- at: "none",
82355
- retention: { none: "one row (id `texts`); losing it costs the chosen language and household wording, never a notification — every read degrades to the shipped English catalog" }
82356
- },
82357
- /**
82358
- * Shared action sequences — named, ordered runs of steps with their own throttle.
82359
- * One row per sequence, id = sequence UUID.
82360
- */
82361
- sequences: {
82362
- schema: SequenceBodySchema,
82363
- class: "config",
82364
- writeTrigger: "an admin creates/edits/deletes a shared sequence via notificationRules.createSequence / updateSequence / deleteSequence, or the one-shot boot migration that materialises legacy inline sequences",
82365
- device: "none",
82366
- at: "none",
82367
- retention: { none: "a row goes only when an admin deletes it; bounded by the sequence set a human is willing to author" }
82368
- },
82369
- /**
82370
- * Per-rule AI section: whether and how a rule narrates itself.
82371
- * Record id = the rule id it belongs to.
82372
- */
82373
- "rule-ai": {
82374
- schema: RuleAiBodySchema,
82375
- class: "config",
82376
- writeTrigger: "an operator switches the AI section of a notification rule on or off, or edits its prompt / word cap / language",
82377
- device: "none",
82378
- at: "none",
82379
- retention: { none: "a row goes when the operator clears the section or deletes the rule; bounded by the rule set a human is willing to author" }
82380
- },
82381
- /**
82382
- * Calendar-events rules. One row per rule, id = rule UUID.
82383
- */
82384
- "calendar-events-rules": {
82385
- schema: CalendarEventsRuleBodySchema,
82386
- class: "config",
82387
- writeTrigger: "an operator creates or edits a calendar-events rule, or the producer stamps a delivered window via markGenerated",
82388
- device: "none",
82389
- at: "none",
82390
- indexes: [{
82391
- name: "enabled",
82392
- fields: ["enabled"]
82393
- }],
82394
- retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
82395
- },
82396
- /**
82397
- * Multi-camera digest rules. One row per rule, id = rule UUID.
82398
- */
82399
- "summary-rules": {
82400
- schema: SummaryRuleBodySchema,
82401
- class: "config",
82402
- writeTrigger: "an operator creates or edits a digest rule, or the producer stamps a delivered window via markGenerated",
82403
- device: "none",
82404
- at: "none",
82405
- indexes: [{
82406
- name: "enabled",
82407
- fields: ["enabled"]
82408
- }],
82409
- retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
82410
- },
82411
- /**
82412
- * Timelapse rules. One row per rule, id = rule UUID.
82413
- * Three rows on prod (2026-10-18).
82414
- */
82415
- "timelapse-rules": {
82416
- schema: TimelapseRuleBodySchema,
82417
- class: "config",
82418
- writeTrigger: "an operator creates or edits a timelapse rule, or the scheduler stamps a successful per-camera generation via markGenerated",
82419
- device: "none",
82420
- at: "none",
82421
- indexes: [{
82422
- name: "enabled",
82423
- fields: ["enabled"]
82424
- }],
82425
- retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
82426
- },
82427
- /**
82428
- * Notification rules — the main rule set. One row per rule, id = rule UUID.
82429
- * 18 rows on prod (2026-10-18); most coupled kind in this owner.
82430
- */
82431
- rules: {
82432
- schema: RuleBodySchema,
82433
- class: "config",
82434
- writeTrigger: "an operator creates, edits, enables/disables a rule or opts a target out of it; nothing writes on the evaluation path",
82435
- device: "none",
82436
- at: "none",
82437
- indexes: [{
82438
- name: "delivery",
82439
- fields: ["delivery"]
82440
- }],
82441
- retention: { none: "a row goes only when the operator deletes the rule; bounded by the rule set a human is willing to author" }
82442
- }
82443
- });
82444
- //#endregion
82445
82701
  //#region src/notification-center/sequence-store.ts
82446
82702
  /**
82447
82703
  * Shared action sequences for the notification centre.
@@ -87526,6 +87782,56 @@ var NcDispatcher = class {
87526
87782
  renderText(input) {
87527
87783
  return renderRowText(this.texts, input);
87528
87784
  }
87785
+ /** Rules already warned about a deprecated variable in this process — once each. */
87786
+ deprecatedVarWarned = /* @__PURE__ */ new Set();
87787
+ /**
87788
+ * A rule template written by an older client may still say `{{camera}}`. It
87789
+ * renders (the alias), and the rule is named once so the operator can edit it.
87790
+ */
87791
+ warnDeprecatedVars(entry) {
87792
+ if (this.deprecatedVarWarned.has(entry.ruleId)) return;
87793
+ const template = entry.payload.template;
87794
+ const used = [...new Set([...deprecatedPlaceholders(template?.title), ...deprecatedPlaceholders(template?.body)])];
87795
+ if (used.length === 0) return;
87796
+ this.deprecatedVarWarned.add(entry.ruleId);
87797
+ this.deps.logger.warn("rule template uses a renamed variable — it still renders, edit it to the new name", {
87798
+ tags: { ruleId: entry.ruleId },
87799
+ meta: {
87800
+ variables: used,
87801
+ replacement: "device"
87802
+ }
87803
+ });
87804
+ }
87805
+ /**
87806
+ * The facts of a finished appliance cycle; unknown on every other row and on
87807
+ * any failure (a part that cannot be read is omitted, never a blocked send).
87808
+ */
87809
+ async applianceCycleFacts(entry) {
87810
+ const subject = entry.payload.subject;
87811
+ const resolve = this.deps.resolveApplianceCycleFacts;
87812
+ const deviceId = subject.applianceCycle?.deviceId;
87813
+ if (resolve === void 0 || deviceId === void 0) return NC_UNKNOWN_CYCLE_FACTS;
87814
+ try {
87815
+ const facts = await resolve({
87816
+ deviceId,
87817
+ finishedAtMs: subject.timestamp
87818
+ });
87819
+ if (facts.durationMs === null || facts.energyKwh === null) this.deps.logger.debug("appliance cycle part omitted — unknown", {
87820
+ tags: { deviceId },
87821
+ meta: {
87822
+ duration: facts.durationMs === null ? "unknown" : "known",
87823
+ energy: facts.energyKwh === null ? "unknown" : "known"
87824
+ }
87825
+ });
87826
+ return facts;
87827
+ } catch (err) {
87828
+ this.deps.logger.debug("appliance cycle facts failed — parts omitted", {
87829
+ tags: { deviceId },
87830
+ meta: { error: err instanceof Error ? err.message : String(err) }
87831
+ });
87832
+ return NC_UNKNOWN_CYCLE_FACTS;
87833
+ }
87834
+ }
87529
87835
  /**
87530
87836
  * Builds the notification AND returns the artefact ids its media resolve
87531
87837
  * indexed — see {@link withArtifactUrls}. A tuple rather than a field on
@@ -87538,7 +87844,9 @@ var NcDispatcher = class {
87538
87844
  const deviceName = await notificationDeviceName(entry, this.deps.getDeviceName);
87539
87845
  const alarmSource = systemEvent?.alarmSource;
87540
87846
  const zoneLabels = systemEvent === void 0 ? await resolveZoneLabels(this.deps.getZoneNames, subject.deviceId, subject.zones) : alarmSource?.zones !== void 0 && alarmSource.zones.length > 0 ? await resolveZoneLabels(this.deps.getZoneNames, alarmSource.deviceId, alarmSource.zones) : [];
87541
- const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels);
87847
+ const cycleFacts = await this.applianceCycleFacts(entry);
87848
+ const vars = buildTemplateVars(this.texts, entry, deviceName, zoneLabels, void 0, cycleFacts);
87849
+ this.warnDeprecatedVars(entry);
87542
87850
  const systemCount = systemEvent?.textCount !== void 0 ? { count: systemEvent.textCount } : {};
87543
87851
  const title = renderRowTitle(this.texts, entry, vars);
87544
87852
  const authoredBody = entry.payload.template?.body;
@@ -88653,15 +88961,15 @@ async function resolveZoneLabels(getZoneNames, deviceId, zoneIds) {
88653
88961
  function percentOf(confidence) {
88654
88962
  return confidence !== void 0 ? `${Math.round(confidence * 100)}%` : "";
88655
88963
  }
88656
- /** What `{{camera}}` renders on a system row that names no device. */
88964
+ /** What `{{device}}` renders on a system row that names no device. */
88657
88965
  var NC_DEVICELESS_CAMERA_NAME = "system";
88658
88966
  /**
88659
- * The name `{{camera}}` renders for a row — the ONE derivation, shared by the
88967
+ * The name `{{device}}` renders for a row — the ONE derivation, shared by the
88660
88968
  * dispatcher and the template-variable parity spec.
88661
88969
  *
88662
88970
  * A system row that names no device (a node, a backup, an update) renders the
88663
88971
  * filler {@link NC_DEVICELESS_CAMERA_NAME}: there is no camera to name, which
88664
- * is why the catalog offers `{{camera}}` on a system rule only for the kinds
88972
+ * is why the catalog offers `{{device}}` on a system rule only for the kinds
88665
88973
  * that carry a device (template-vars.ts). Everything else asks the directory,
88666
88974
  * and falls back to `camera <id>` when it cannot answer.
88667
88975
  */
@@ -88704,7 +89012,7 @@ function systemEventParamsForTemplate(params) {
88704
89012
  const { form: _form, attention: _attention, ...rest } = params;
88705
89013
  return rest;
88706
89014
  }
88707
- function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
89015
+ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone, cycle) {
88708
89016
  const subject = entry.payload.subject;
88709
89017
  const occupancy = subject.occupancy;
88710
89018
  const systemEvent = subject.systemEvent;
@@ -88714,7 +89022,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
88714
89022
  const cameraName = deviceName.toLocaleLowerCase(locale);
88715
89023
  const zonesLower = zoneLabels.map((z) => z.toLocaleLowerCase(locale));
88716
89024
  return {
88717
- camera: cameraName,
89025
+ device: cameraName,
88718
89026
  class: detection?.className ?? (systemEvent !== void 0 ? "" : subject.className),
88719
89027
  classLabel: (() => {
88720
89028
  const wire = detection?.className ?? (systemEvent !== void 0 ? "" : subject.className);
@@ -88740,6 +89048,7 @@ function buildTemplateVars(texts, entry, deviceName, zoneLabels, timeZone) {
88740
89048
  hitPercent: subject.audio?.hitPercent !== void 0 ? `${Math.round(subject.audio.hitPercent)}` : "",
88741
89049
  db: subject.audio?.peakDbfs !== void 0 ? `${Math.round(subject.audio.peakDbfs)}` : "",
88742
89050
  eventType: subject.eventType ?? "",
89051
+ ...cycle !== void 0 ? ncApplianceCycleVars(texts, cycle) : NC_EMPTY_CYCLE_VARS,
88743
89052
  rule: entry.payload.ruleName,
88744
89053
  count: occupancy !== void 0 ? `${occupancy.count}` : group !== void 0 && group.members.length > 1 ? `${group.members.length}` : "",
88745
89054
  capacity: occupancy !== void 0 ? `${occupancy.capacity}` : "",
@@ -88766,6 +89075,7 @@ function bodyKeyFor(entry) {
88766
89075
  const subject = entry.payload.subject;
88767
89076
  if (subject.systemEvent !== void 0) return subject.systemEvent.bodyKey ?? "";
88768
89077
  if (subject.occupancy !== void 0) return "occupancy.body";
89078
+ if (subject.applianceCycle !== void 0) return "appliance.finished.body";
88769
89079
  if (subject.packagePhase === "delivered" && subject.packagePossible === true) return "package.delivered.possible.body";
88770
89080
  if (subject.packagePhase !== void 0) return `package.${subject.packagePhase}.body`;
88771
89081
  return entry.recordKind === "track-end" ? "detection.body.trackEnd" : "detection.body";
@@ -88803,7 +89113,7 @@ function renderRowTitle(texts, entry, vars) {
88803
89113
  const systemEvent = entry.payload.subject.systemEvent;
88804
89114
  const intrusion = entry.payload.alarmIntrusion;
88805
89115
  const count = systemEvent?.textCount;
88806
- return renderRowText(texts, {
89116
+ const rendered = renderRowText(texts, {
88807
89117
  template: intrusion !== void 0 ? void 0 : entry.payload.template?.title,
88808
89118
  key: titleKeyFor(entry),
88809
89119
  vars: intrusion !== void 0 ? {
@@ -88813,11 +89123,13 @@ function renderRowTitle(texts, entry, vars) {
88813
89123
  ...count !== void 0 ? { count } : {},
88814
89124
  frozen: systemEvent?.title ?? entry.payload.ruleName
88815
89125
  });
89126
+ return titleKeyFor(entry) === "appliance.finished.title" && entry.payload.template?.title === void 0 ? ncSentenceCaseBody(rendered) : rendered;
88816
89127
  }
88817
89128
  /** The default TITLE key — same shape, same pre-catalog reasoning. */
88818
89129
  function titleKeyFor(entry) {
88819
89130
  const systemEvent = entry.payload.subject.systemEvent;
88820
89131
  if (systemEvent !== void 0) return systemEvent.titleKey ?? "";
89132
+ if (entry.payload.subject.applianceCycle !== void 0) return "appliance.finished.title";
88821
89133
  if (entry.payload.alarmIntrusion !== void 0) return "alarm.intrusion.title";
88822
89134
  const packagePhase = entry.payload.subject.packagePhase;
88823
89135
  if (packagePhase === "delivered" && entry.payload.subject.packagePossible === true) return "package.delivered.possible.title";
@@ -96578,7 +96890,7 @@ function clockOf(atMs) {
96578
96890
  * catalog, the same way `buildTemplateVars` is asserted for rule rows. */
96579
96891
  function timelapseTemplateVars(input) {
96580
96892
  return {
96581
- camera: input.deviceName ?? `camera ${input.deviceId}`,
96893
+ device: input.deviceName ?? `camera ${input.deviceId}`,
96582
96894
  rule: input.ruleName,
96583
96895
  from: clockOf(input.window.startMs),
96584
96896
  to: clockOf(input.window.endMs),
@@ -96644,7 +96956,7 @@ function paramsFor(target, clickUrl) {
96644
96956
  */
96645
96957
  function timelapsePreviewText(input) {
96646
96958
  const vars = {
96647
- camera: input.deviceName ?? `camera ${input.deviceId}`,
96959
+ device: input.deviceName ?? `camera ${input.deviceId}`,
96648
96960
  ...input.ruleName !== void 0 ? { rule: input.ruleName } : {},
96649
96961
  ...input.window !== void 0 ? {
96650
96962
  from: clockOf(input.window.startMs),
@@ -101889,6 +102201,7 @@ function buildEntries(host, rule, subject, kind, matchedOn, userTargets = [], or
101889
102201
  ...subject.audioWindow.peakDbfs !== void 0 ? { peakDbfs: subject.audioWindow.peakDbfs } : {}
101890
102202
  } } : {},
101891
102203
  ...subject.eventType !== void 0 ? { eventType: subject.eventType } : {},
102204
+ ...applianceCycleOf(subject),
101892
102205
  ...subject.systemEvent !== void 0 ? { systemEvent: subject.systemEvent } : {}
101893
102206
  }
101894
102207
  };
@@ -104176,7 +104489,7 @@ function buildSnoozeDigest(texts, snooze, rows, deviceNames) {
104176
104489
  key: "snooze.digest.line",
104177
104490
  vars: {
104178
104491
  rule: r.ruleName,
104179
- camera: deviceNames.get(r.deviceId) ?? `camera ${r.deviceId}`,
104492
+ device: deviceNames.get(r.deviceId) ?? `camera ${r.deviceId}`,
104180
104493
  count: `${r.count}`
104181
104494
  }
104182
104495
  }));
@@ -109626,6 +109939,7 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
109626
109939
  }));
109627
109940
  },
109628
109941
  getDeviceName,
109942
+ resolveApplianceCycleFacts: this.deps.resolveApplianceCycleFacts,
109629
109943
  getDeviceLocation,
109630
109944
  buildActions: (input) => this.notificationCenter?.mintButtons(input) ?? Promise.resolve([])
109631
109945
  },
@@ -122811,6 +123125,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
122811
123125
  return out;
122812
123126
  };
122813
123127
  await step("declareCollections", () => this.declareCollections(api));
123128
+ if (this.localOwnNodeId() === "hub") await step("migrations", () => this.runTemplateMigrations());
122814
123129
  this.armAlarmPanelDeclaration(api);
122815
123130
  const logger = this.ctx.logger;
122816
123131
  const storage = await step("resolveMediaStorage", () => this.resolveMediaStorage(logger));
@@ -123161,6 +123476,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
123161
123476
  });
123162
123477
  return this.collectionsBootInstance;
123163
123478
  }
123479
+ /** The addon's migration catalogue (D987), bound to the notification kinds. */
123480
+ async runTemplateMigrations() {
123481
+ const records = this.requireRecords();
123482
+ const ports = {
123483
+ rules: records.kind(ncRecordKinds["rules"]),
123484
+ timelapseRules: records.kind(ncRecordKinds["timelapse-rules"]),
123485
+ texts: records.kind(ncRecordKinds.texts)
123486
+ };
123487
+ await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, ports));
123488
+ }
123164
123489
  /** `declare-collections.ts` — declareCollections. */
123165
123490
  async declareCollections(api) {
123166
123491
  return this.collectionsBoot.declareCollections(api);
@@ -123486,7 +123811,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
123486
123811
  buildTimelapsePorts: (api, stores) => this.buildTimelapsePorts(api, stores),
123487
123812
  buildSummaryPorts: (api, stores) => this.buildSummaryPorts(api, stores),
123488
123813
  wireAlarmPanel: (center) => this.wireAlarmPanel(center),
123489
- serveNcActionPlane: (center) => this.serveNcActionPlane(center)
123814
+ serveNcActionPlane: (center) => this.serveNcActionPlane(center),
123815
+ resolveApplianceCycleFacts: applianceCycleFactsFromRuns({
123816
+ newestRun: async (deviceId) => {
123817
+ return (await this.ctx.api.deviceRuns.listRuns.query({
123818
+ deviceId,
123819
+ limit: 1
123820
+ })).runs[0] ?? null;
123821
+ },
123822
+ sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms))
123823
+ })
123490
123824
  });
123491
123825
  return this.notificationCenterBootInstance;
123492
123826
  }