@camstack/addon-post-analysis 1.2.302 → 1.2.304

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.
@@ -1,4 +1,4 @@
1
- import { $ as NC_CLIP_RETENTION_MAX_SEC, $n as subKindsOf, $t as TrackPuritySchema, A as EVENT_KIND_BY_CAP, An as isScheduleActive, Ar as object, B as MAX_ARCHIVED_DEBUG_NOTES, Bn as pipelineAnalyticsCapability, Bt as SCENE_DEFAULT_QUIET_SECONDS, C as DETECTION_PIPELINE_CAP_NAME, Cn as failureContributionCapability, Cr as sleep$1, Ct as NcSystemEventKindSchema, D as EMBEDDING_REQUIREMENTS_FAILED, Dn as isEventOwnerType, Dr as discriminatedUnion, Dt as NcTemplateVarDescriptorSchema, E as EMBEDDING_MODEL_FAILED, En as isDetectionMacroClass, Er as boolean, Et as NcTemplatePreviewSchema, F as FailureCounters, Fn as mayWriteToLocation, Fr as tuple, Ft as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, G as MOTION_CLOSE_AFTER_MS, Gn as resolveClipSearchMinScore, Gt as SetTrackPurityInputSchema, H as MAX_BIRTH_LATENCY_PROXY_MS, Hn as readDeviceStateFrom, Ht as SCENE_DIVERGED, I as LabelAttributionSchema, In as memoizePinnedFfmpeg, Ir as union, It as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, J as NC_ALARM_SYSTEM_EVENT_KINDS, Jn as ruleBelongsToAlarm$1, Jt as TIMELAPSE_DENSE_FLOOR_SEC, K as MediaFileKindEnum, Kn as resolveLocationMode, Kt as SetTrackPurityResultSchema, L as ListTrackRelationsInputSchema, Ln as notificationRulesCapability, Lr as unknown, Lt as SCENE_DEFAULT_ANCHOR_THRESHOLD, M as EXPORT_DENSE_MAX_RANGES, Mn as isSystemEventSummaryRule, Mr as record, Mt as RECORDING_SOURCE_CAMSTACK_ADDON, N as FIRST_LEVEL_MACRO_CLASSES, Nn as isVectorIndexDimConflict, Nr as strictObject, Nt as RebuildObjectEmbeddingsRefusalSchema, O as EMBEDDING_REQUIREMENTS_UNAVAILABLE, On as isKnownTemplateVar, Or as literal, Ot as OPS_LOG_DEFAULT_LIMIT, P as FULL_IMAGE_BBOX, Pn as kebabToCamel, Pr as string, Pt as RetrainStatusSchema, Q as NC_CLIP_PRE_ROLL_DEFAULT_SEC, Qn as storageOccupancyCapability, Qt as TimelapseRuleSchema, R as ListTrackRelationsResultSchema, Rn as occupancyScope, Rr as EventCategory, Rt as SCENE_DEFAULT_CHECK_INTERVAL_SEC, S as DETECTION_MACRO_CLASSES, Sn as faceGalleryCapability, St as NcSnoozeSuppressedSchema, T as DetectionSourceSchema, Tn as gifFromMp4Args, Tr as array, Tt as NcTemplatePreviewInputSchema, U as MAX_TRACK_PURITIES, Un as readMotionTriggeredBy, Ut as SCENE_RESET_RECAPTURES, V as MAX_BIRTH_DECISION_RECORDS, Vn as plateGalleryCapability, Vt as SCENE_DEFAULT_UNCOVERED_POLICY, W as MAX_TRACK_RELATIONS, Wn as readTimelapseGeneratedAt, Wt as SceneMonitorSchema, X as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, Xn as sensorRaisedOf, Xt as TimelapseRuleInputSchema, Y as NC_AUDIO_CONFIRM_HITS_DEFAULT, Yn as sceneMonitorCapability, Yt as TRACK_RELATIONS_DEFAULT_LIMIT, Z as NC_CLIP_POST_ROLL_DEFAULT_SEC, Zn as servedTemplateVars, Zt as TimelapseRulePatchSchema, _ as ClearTrackRelationInputSchema, _n as embeddingRefusalOf, _r as customAction, _t as NcRuleTargetSchema, a as CLIP_EMBEDDING_STEP_ID, an as alarmPanelCapability, ar as templateVarsFor, at as NC_SNOOZE_MAX_MINUTES, b as DEFAULT_EVENT_COLOR, bn as encodeVectorBase64, br as isDeviceScopedCap, bt as NcSnoozeInputSchema, c as resolveClusterModelPin, cn as audioModeOf, cr as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, ct as NC_TAXONOMY, d as AlarmArmModeSchema, dn as cosineSimilarity$1, dr as isSourceCap, dt as NcAlarmSettingsPatchSchema, en as TrackRelationKindSchema, er as systemEventDeliveryProblems, et as NC_CONDITION_CATALOG, f as ArchivedDebugNoteSchema, fn as deriveRecordingMode, fr as DeviceType, ft as NcConditionDescriptorSchema, g as COCO_TO_MACRO, gr as createEvent, gt as NcRuleSchema, h as BirthDecisionRecordSchema, hr as CamProfileSchema, ht as NcRulePatchSchema, in as addonWidgetsSourceCapability, ir as templateCarriesSummary, it as NC_SENSOR_TRANSITION_KINDS, j as EVENT_OWNER_TYPES, jn as isSystemDelivery, jr as partialRecord, jt as RECORDING_EXPORT_MAX_READ_BYTES, k as EVENT_DENSITY_BATCH_MAX, kn as isMediaOwnerType, kr as number, kt as OpsLogEntrySchema, l as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ln as buildEventKindDescriptor, lr as canonicalHash, lt as NC_TEMPLATE_VARS, m as BaseDevice, mn as describeSystemEventFamilyProblem, mr as BaseAddon, mt as NcRuleInputSchema, nn as TrackSideSchema, nr as systemEventFilterApplies, nt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, o as FACE_EMBEDDING_STEP_ID, on as assertTimelapseCadences, or as vectorDimFromBase64, ot as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, p as BIRTH_DECISION_DEFAULT_LIMIT, pn as describeSystemEventDeliveryProblem, pr as errMsg, pt as NcConditionsSchema, q as MotionTriggeredBySchema, qt as SetTrackRelationInputSchema, rn as TrackSourceSchema, rr as systemEventSummaryScheduleOf, rt as NC_DEFAULT_SNOOZE_MINUTES, s as readClusterModelPinStrict, sn as audioMetricsCapability, sr as zoneAnalyticsCapability, st as NC_TANK_SYSTEM_EVENT_KINDS, t as BUILTIN_CLIP_MODELS, tn as TrackRelationSchema, tr as systemEventFamilyProblems, tt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, u as AUDIO_MACRO_LABELS, un as consumableLowPercentOf, ur as evaluateSensorEdge, ut as NcAlarmConfigSchema, v as ClearTrackRelationResultSchema, vr as defineCustomActions, vt as NcRuleUpdateInputSchema, w as DeclaredDevices, wn as gifDeriveSpeed, wr as _enum, wt as NcTaxonomySchema, x as DEFAULT_TIMELAPSE_PREVIEW_TEXT, xn as evictionPolicyOfLocation, xr as nodePin, xt as NcSnoozeSchema, y as CoverStateSchema, yr as hydrateSchema, yt as NcScheduleSchema, z as LockStateSchema, zt as SCENE_DEFAULT_OBSERVATION_SPACING_SEC } from "../clip-model-registry-BtY4fS3d.mjs";
1
+ import { $ as NC_CLIP_RETENTION_MAX_SEC, $n as storageOccupancyCapability, $t as TrackPuritySchema, A as EVENT_KIND_BY_CAP, An as isMediaOwnerType, Ar as number, B as MAX_ARCHIVED_DEBUG_NOTES, Bt as SCENE_DEFAULT_QUIET_SECONDS, C as DETECTION_PIPELINE_CAP_NAME, Cn as failureContributionCapability, Ct as NcSystemEventKindSchema, D as EMBEDDING_REQUIREMENTS_FAILED, Dn as isDetectionMacroClass, Dr as boolean, Dt as NcTemplateVarDescriptorSchema, E as EMBEDDING_MODEL_FAILED, En as isAudioClassName, Er as array, Et as NcTemplatePreviewSchema, F as FailureCounters, Fn as kebabToCamel, Fr as string, Ft as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, G as MOTION_CLOSE_AFTER_MS, Gn as readTimelapseGeneratedAt, Gt as SetTrackPurityInputSchema, H as MAX_BIRTH_LATENCY_PROXY_MS, Hn as plateGalleryCapability, Ht as SCENE_DIVERGED, I as LabelAttributionSchema, In as mayWriteToLocation, Ir as tuple, It as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, J as NC_ALARM_SYSTEM_EVENT_KINDS, Jt as TIMELAPSE_DENSE_FLOOR_SEC, K as MediaFileKindEnum, Kn as resolveClipSearchMinScore, Kt as SetTrackPurityResultSchema, L as ListTrackRelationsInputSchema, Ln as memoizePinnedFfmpeg, Lr as union, Lt as SCENE_DEFAULT_ANCHOR_THRESHOLD, M as EXPORT_DENSE_MAX_RANGES, Mn as isSystemDelivery, Mr as partialRecord, Mt as RECORDING_SOURCE_CAMSTACK_ADDON, N as FIRST_LEVEL_MACRO_CLASSES, Nn as isSystemEventSummaryRule, Nr as record, Nt as RebuildObjectEmbeddingsRefusalSchema, O as EMBEDDING_REQUIREMENTS_UNAVAILABLE, On as isEventOwnerType, Or as discriminatedUnion, Ot as OPS_LOG_DEFAULT_LIMIT, P as FULL_IMAGE_BBOX, Pn as isVectorIndexDimConflict, Pr as strictObject, Pt as RetrainStatusSchema, Q as NC_CLIP_PRE_ROLL_DEFAULT_SEC, Qn as servedTemplateVars, Qt as TimelapseRuleSchema, R as ListTrackRelationsResultSchema, Rn as notificationRulesCapability, Rr as unknown, Rt as SCENE_DEFAULT_CHECK_INTERVAL_SEC, S as DETECTION_MACRO_CLASSES, Sn as faceGalleryCapability, Sr as nodePin, St as NcSnoozeSuppressedSchema, T as DetectionSourceSchema, Tn as gifFromMp4Args, Tr as _enum, Tt as NcTemplatePreviewInputSchema, U as MAX_TRACK_PURITIES, Un as readDeviceStateFrom, Ut as SCENE_RESET_RECAPTURES, V as MAX_BIRTH_DECISION_RECORDS, Vn as pipelineAnalyticsCapability, Vt as SCENE_DEFAULT_UNCOVERED_POLICY, W as MAX_TRACK_RELATIONS, Wn as readMotionTriggeredBy, Wt as SceneMonitorSchema, X as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, Xn as sceneMonitorCapability, Xt as TimelapseRuleInputSchema, Y as NC_AUDIO_CONFIRM_HITS_DEFAULT, Yn as ruleBelongsToAlarm$1, Yt as TRACK_RELATIONS_DEFAULT_LIMIT, Z as NC_CLIP_POST_ROLL_DEFAULT_SEC, Zn as sensorRaisedOf, Zt as TimelapseRulePatchSchema, _ as ClearTrackRelationInputSchema, _n as embeddingRefusalOf, _r as createEvent, _t as NcRuleTargetSchema, a as CLIP_EMBEDDING_STEP_ID, an as alarmPanelCapability, ar as templateCarriesSummary, at as NC_SNOOZE_MAX_MINUTES, b as DEFAULT_EVENT_COLOR, bn as encodeVectorBase64, br as hydrateSchema, bt as NcSnoozeInputSchema, c as resolveClusterModelPin, cn as audioModeOf, cr as zoneAnalyticsCapability, ct as NC_TAXONOMY, d as AlarmArmModeSchema, dn as cosineSimilarity$1, dr as evaluateSensorEdge, dt as NcAlarmSettingsPatchSchema, en as TrackRelationKindSchema, er as subKindsOf, et as NC_CONDITION_CATALOG, f as ArchivedDebugNoteSchema, fn as deriveRecordingMode, fr as isSourceCap, ft as NcConditionDescriptorSchema, g as COCO_TO_MACRO, gr as CamProfileSchema, gt as NcRuleSchema, h as BirthDecisionRecordSchema, hr as BaseAddon, ht as NcRulePatchSchema, in as addonWidgetsSourceCapability, ir as systemEventSummaryScheduleOf, it as NC_SENSOR_TRANSITION_KINDS, j as EVENT_OWNER_TYPES, jn as isScheduleActive, jr as object, jt as RECORDING_EXPORT_MAX_READ_BYTES, k as EVENT_DENSITY_BATCH_MAX, kn as isKnownTemplateVar, kr as literal, kt as OpsLogEntrySchema, l as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, ln as buildEventKindDescriptor, lr as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, lt as NC_TEMPLATE_VARS, m as BaseDevice, mn as describeSystemEventFamilyProblem, mr as errMsg, mt as NcRuleInputSchema, nn as TrackSideSchema, nr as systemEventFamilyProblems, nt as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, o as FACE_EMBEDDING_STEP_ID, on as assertTimelapseCadences, or as templateVarsFor, ot as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, p as BIRTH_DECISION_DEFAULT_LIMIT, pn as describeSystemEventDeliveryProblem, pr as DeviceType, pt as NcConditionsSchema, q as MotionTriggeredBySchema, qn as resolveLocationMode, qt as SetTrackRelationInputSchema, rn as TrackSourceSchema, rr as systemEventFilterApplies, rt as NC_DEFAULT_SNOOZE_MINUTES, s as readClusterModelPinStrict, sn as audioMetricsCapability, sr as vectorDimFromBase64, st as NC_TANK_SYSTEM_EVENT_KINDS, t as BUILTIN_CLIP_MODELS, tn as TrackRelationSchema, tr as systemEventDeliveryProblems, tt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, u as AUDIO_MACRO_LABELS, un as consumableLowPercentOf, ur as canonicalHash, ut as NcAlarmConfigSchema, v as ClearTrackRelationResultSchema, vr as customAction, vt as NcRuleUpdateInputSchema, w as DeclaredDevices, wn as gifDeriveSpeed, wr as sleep$1, wt as NcTaxonomySchema, x as DEFAULT_TIMELAPSE_PREVIEW_TEXT, xn as evictionPolicyOfLocation, xr as isDeviceScopedCap, xt as NcSnoozeSchema, y as CoverStateSchema, yr as defineCustomActions, yt as NcScheduleSchema, z as LockStateSchema, zn as occupancyScope, zr as EventCategory, zt as SCENE_DEFAULT_OBSERVATION_SPACING_SEC } from "../clip-model-registry-DBKCbXPJ.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";
@@ -14201,11 +14201,26 @@ function sortedSystemEvent(condition) {
14201
14201
  ...packageNames !== void 0 ? { packageNames: packageNames.toSorted(byValue) } : {}
14202
14202
  };
14203
14203
  }
14204
+ /**
14205
+ * `zones` with its redundant quantifier dropped: `match: 'any'` says exactly
14206
+ * what an absent `match` says (D690 — the editors write `{ ids }`, the store
14207
+ * still holds `match: 'any'` on every rule written before). `'all'` is kept:
14208
+ * it IS a different rule.
14209
+ */
14210
+ function canonicalZones(conditions) {
14211
+ const zones = conditions.zones;
14212
+ if (zones?.match !== "any") return conditions;
14213
+ return {
14214
+ ...conditions,
14215
+ zones: { ids: zones.ids }
14216
+ };
14217
+ }
14204
14218
  /** The canonical form two conditions are compared in. */
14205
14219
  function conditionsFingerprint(conditions) {
14206
- const { systemEvent } = conditions;
14207
- return canonicalHash(systemEvent === void 0 ? conditions : {
14208
- ...conditions,
14220
+ const canonical = canonicalZones(conditions);
14221
+ const { systemEvent } = canonical;
14222
+ return canonicalHash(systemEvent === void 0 ? canonical : {
14223
+ ...canonical,
14209
14224
  systemEvent: sortedSystemEvent(systemEvent)
14210
14225
  });
14211
14226
  }
@@ -14214,7 +14229,8 @@ function conditionsFingerprint(conditions) {
14214
14229
  * (the normalisation the store applies on every write) and are compared
14215
14230
  * canonically: key order is irrelevant, an `undefined` key equals an absent
14216
14231
  * one, and the system-event `kinds` and id-list filters are compared as SETS.
14217
- * Every other array keeps its order. A side that does not parse is treated as
14232
+ * Every other array keeps its order, and `zones.match: 'any'` equals an absent
14233
+ * `match`. A side that does not parse is treated as
14218
14234
  * a change, so it is judged rather than waved through.
14219
14235
  */
14220
14236
  function sameConditions(a, b) {
@@ -14259,7 +14275,7 @@ function assertPatchKeepsFamily(existing, patch) {
14259
14275
  * the offending kind or filter). Then rethrow. A refused save is dropped work,
14260
14276
  * and dropped work is never silent. Any other error passes through unlogged.
14261
14277
  */
14262
- function logRefusal$3(check, ctx) {
14278
+ function logRefusal$4(check, ctx) {
14263
14279
  try {
14264
14280
  check();
14265
14281
  } catch (err) {
@@ -14275,11 +14291,11 @@ function logRefusal$3(check, ctx) {
14275
14291
  }
14276
14292
  /** {@link assertRuleFamily}, logged. The create path of both routes. */
14277
14293
  function guardRuleFamily(rule, ctx) {
14278
- logRefusal$3(() => assertRuleFamily(rule), ctx);
14294
+ logRefusal$4(() => assertRuleFamily(rule), ctx);
14279
14295
  }
14280
14296
  /** {@link assertPatchKeepsFamily}, logged. The update path of both routes. */
14281
14297
  function guardPatchFamily(existing, patch, ctx) {
14282
- logRefusal$3(() => assertPatchKeepsFamily(existing, patch), ctx);
14298
+ logRefusal$4(() => assertPatchKeepsFamily(existing, patch), ctx);
14283
14299
  }
14284
14300
  //#endregion
14285
14301
  //#region src/notification-center/device-scope-guard.ts
@@ -14304,15 +14320,15 @@ function assertDevicesExcludeDelivery(rule) {
14304
14320
  throw new NcDevicesExcludeRefusal(rule.delivery, excluded);
14305
14321
  }
14306
14322
  /** Run the check; a refusal is dropped work, so it is logged before it is rethrown. */
14307
- function logRefusal$2(check, ctx) {
14323
+ function logRefusal$3(check, ctx) {
14308
14324
  try {
14309
14325
  check();
14310
14326
  } catch (err) {
14311
- if (err instanceof NcDevicesExcludeRefusal) warnRefusal$1(ctx.logger, err, ctx);
14327
+ if (err instanceof NcDevicesExcludeRefusal) warnRefusal$2(ctx.logger, err, ctx);
14312
14328
  throw err;
14313
14329
  }
14314
14330
  }
14315
- function warnRefusal$1(logger, err, ctx) {
14331
+ function warnRefusal$2(logger, err, ctx) {
14316
14332
  logger.warn("nc rule refused: devicesExclude on a system rule", { meta: {
14317
14333
  ...ctx.ruleId !== void 0 ? { ruleId: ctx.ruleId } : {},
14318
14334
  by: ctx.by,
@@ -14322,7 +14338,7 @@ function warnRefusal$1(logger, err, ctx) {
14322
14338
  }
14323
14339
  /** The create path of both routes. */
14324
14340
  function guardRuleDevicesExclude(rule, ctx) {
14325
- logRefusal$2(() => assertDevicesExcludeDelivery(rule), ctx);
14341
+ logRefusal$3(() => assertDevicesExcludeDelivery(rule), ctx);
14326
14342
  }
14327
14343
  /**
14328
14344
  * The update path of both routes: the MERGED rule is judged, and only when the
@@ -14332,7 +14348,7 @@ function guardPatchDevicesExclude(existing, patch, ctx) {
14332
14348
  if (existing === null) return;
14333
14349
  const merged = mergedFamilySubject(existing, patch);
14334
14350
  if (merged === null) return;
14335
- logRefusal$2(() => assertDevicesExcludeDelivery(merged), ctx);
14351
+ logRefusal$3(() => assertDevicesExcludeDelivery(merged), ctx);
14336
14352
  }
14337
14353
  //#endregion
14338
14354
  //#region src/notification-center/rule-summary-guard.ts
@@ -14390,7 +14406,7 @@ function mergedDeliverySubject(existing, patch, clear) {
14390
14406
  };
14391
14407
  return JUDGED_KEYS.some((key) => canonicalHash(merged[key] ?? null) !== canonicalHash(existing[key] ?? null)) ? merged : null;
14392
14408
  }
14393
- function logRefusal$1(check, ctx) {
14409
+ function logRefusal$2(check, ctx) {
14394
14410
  try {
14395
14411
  check();
14396
14412
  } catch (err) {
@@ -14404,14 +14420,14 @@ function logRefusal$1(check, ctx) {
14404
14420
  }
14405
14421
  /** The create path of both routes. */
14406
14422
  function guardRuleSystemEventDelivery(rule, ctx) {
14407
- logRefusal$1(() => assertSystemEventDelivery(rule), ctx);
14423
+ logRefusal$2(() => assertSystemEventDelivery(rule), ctx);
14408
14424
  }
14409
14425
  /** The update path of both routes: the MERGED rule, when the patch changes what is judged. */
14410
14426
  function guardPatchSystemEventDelivery(existing, patch, clear, ctx) {
14411
14427
  if (existing === null) return;
14412
14428
  const merged = mergedDeliverySubject(existing, patch, clear);
14413
14429
  if (merged === null) return;
14414
- logRefusal$1(() => assertSystemEventDelivery(merged), ctx);
14430
+ logRefusal$2(() => assertSystemEventDelivery(merged), ctx);
14415
14431
  }
14416
14432
  //#endregion
14417
14433
  //#region src/notification-center/sensor-transition-guard.ts
@@ -14433,15 +14449,15 @@ function assertSensorTransitionDelivery(rule) {
14433
14449
  throw new NcSensorTransitionRefusal(rule.delivery, transition);
14434
14450
  }
14435
14451
  /** Run the check; a refusal is dropped work, so it is logged before it is rethrown. */
14436
- function logRefusal(check, ctx) {
14452
+ function logRefusal$1(check, ctx) {
14437
14453
  try {
14438
14454
  check();
14439
14455
  } catch (err) {
14440
- if (err instanceof NcSensorTransitionRefusal) warnRefusal(ctx.logger, err, ctx);
14456
+ if (err instanceof NcSensorTransitionRefusal) warnRefusal$1(ctx.logger, err, ctx);
14441
14457
  throw err;
14442
14458
  }
14443
14459
  }
14444
- function warnRefusal(logger, err, ctx) {
14460
+ function warnRefusal$1(logger, err, ctx) {
14445
14461
  logger.warn("nc rule refused: sensorTransition off a device-event rule", { meta: {
14446
14462
  ...ctx.ruleId !== void 0 ? { ruleId: ctx.ruleId } : {},
14447
14463
  by: ctx.by,
@@ -14451,7 +14467,7 @@ function warnRefusal(logger, err, ctx) {
14451
14467
  }
14452
14468
  /** The create path of both routes. */
14453
14469
  function guardRuleSensorTransition(rule, ctx) {
14454
- logRefusal(() => assertSensorTransitionDelivery(rule), ctx);
14470
+ logRefusal$1(() => assertSensorTransitionDelivery(rule), ctx);
14455
14471
  }
14456
14472
  /**
14457
14473
  * The update path of both routes: the MERGED rule is judged, and only when the
@@ -14462,7 +14478,92 @@ function guardPatchSensorTransition(existing, patch, ctx) {
14462
14478
  if (existing === null) return;
14463
14479
  const merged = mergedFamilySubject(existing, patch);
14464
14480
  if (merged === null) return;
14465
- logRefusal(() => assertSensorTransitionDelivery(merged), ctx);
14481
+ logRefusal$1(() => assertSensorTransitionDelivery(merged), ctx);
14482
+ }
14483
+ //#endregion
14484
+ //#region src/notification-center/detection-audio-guard.ts
14485
+ var NcDetectionAudioRefusal = class extends Error {
14486
+ audioClasses;
14487
+ constructor(audioClasses) {
14488
+ super(`rule refused: a detection rule cannot name sounds (${audioClasses.join(", ")}) — create a Sound rule for them`);
14489
+ this.name = "NcDetectionAudioRefusal";
14490
+ this.audioClasses = audioClasses;
14491
+ }
14492
+ };
14493
+ /**
14494
+ * The audio classes a DETECTION rule carries (include and exclude), in stored
14495
+ * order. Empty for a Sound rule (its `audio` condition owns its sounds) and for
14496
+ * a system rule (it has no class scope).
14497
+ */
14498
+ function audioClassesOnDetectionRule(rule) {
14499
+ if (isSystemDelivery(rule.delivery)) return [];
14500
+ if (rule.conditions.audio !== void 0) return [];
14501
+ return [...rule.conditions.classes ?? [], ...rule.conditions.classesExclude ?? []].filter(isAudioClassName);
14502
+ }
14503
+ function assertNoAudioClassOnDetectionRule(rule) {
14504
+ const audioClasses = audioClassesOnDetectionRule(rule);
14505
+ if (audioClasses.length > 0) throw new NcDetectionAudioRefusal(audioClasses);
14506
+ }
14507
+ /** Run the check; a refusal is dropped work, so it is logged before it is rethrown. */
14508
+ function logRefusal(check, ctx) {
14509
+ try {
14510
+ check();
14511
+ } catch (err) {
14512
+ if (err instanceof NcDetectionAudioRefusal) warnRefusal(ctx.logger, err, ctx);
14513
+ throw err;
14514
+ }
14515
+ }
14516
+ function warnRefusal(logger, err, ctx) {
14517
+ logger.warn("nc rule refused: a detection rule names sounds", { meta: {
14518
+ ...ctx.ruleId !== void 0 ? { ruleId: ctx.ruleId } : {},
14519
+ by: ctx.by,
14520
+ audioClasses: err.audioClasses.join(", ")
14521
+ } });
14522
+ }
14523
+ /** The create path of both routes. */
14524
+ function guardRuleDetectionAudio(rule, ctx) {
14525
+ logRefusal(() => assertNoAudioClassOnDetectionRule(rule), ctx);
14526
+ }
14527
+ /**
14528
+ * The update path of both routes: the MERGED rule, only when the patch changes
14529
+ * it — and only the sounds the save ADDS. A legacy rule that already names a
14530
+ * sound stays editable (a threshold, a schedule, a zone); what is refused is
14531
+ * naming a new one.
14532
+ */
14533
+ function guardPatchDetectionAudio(existing, patch, ctx) {
14534
+ if (existing === null) return;
14535
+ const merged = mergedFamilySubject(existing, patch);
14536
+ if (merged === null) return;
14537
+ const stored = new Set(audioClassesOnDetectionRule(existing).map(normalisedClass));
14538
+ const added = audioClassesOnDetectionRule(merged).filter((name) => !stored.has(normalisedClass(name)));
14539
+ if (added.length === 0) return;
14540
+ logRefusal(() => {
14541
+ throw new NcDetectionAudioRefusal(added);
14542
+ }, ctx);
14543
+ }
14544
+ function normalisedClass(name) {
14545
+ return name.trim().toLowerCase();
14546
+ }
14547
+ /**
14548
+ * The loader's half: every stored detection rule that names a sound is KEPT
14549
+ * (it loads and fires as it always did) and reported at WARN with its id —
14550
+ * once per rule per process, never on every reload tick. Returns the ids it
14551
+ * reported this time, for the caller's bookkeeping.
14552
+ */
14553
+ function warnLegacyAudioClassRules(rules, alreadyWarned, logger) {
14554
+ const reported = [];
14555
+ for (const rule of rules) {
14556
+ if (alreadyWarned.has(rule.id)) continue;
14557
+ const audioClasses = audioClassesOnDetectionRule(rule);
14558
+ if (audioClasses.length === 0) continue;
14559
+ logger.warn("notification rule is a detection rule naming sounds — kept as it is; recreate it as a Sound rule", { meta: {
14560
+ ruleId: rule.id,
14561
+ name: rule.name,
14562
+ audioClasses: audioClasses.join(", ")
14563
+ } });
14564
+ reported.push(rule.id);
14565
+ }
14566
+ return reported;
14466
14567
  }
14467
14568
  //#endregion
14468
14569
  //#region src/notification-center/occupancy-watcher.ts
@@ -17198,6 +17299,10 @@ function makeNcActionHandlers(deps) {
17198
17299
  logger: deps.logger,
17199
17300
  by: c.userId
17200
17301
  });
17302
+ guardRuleDetectionAudio(parsed, {
17303
+ logger: deps.logger,
17304
+ by: c.userId
17305
+ });
17201
17306
  guardRuleDevicesExclude(parsed, {
17202
17307
  logger: deps.logger,
17203
17308
  by: c.userId
@@ -17234,6 +17339,11 @@ function makeNcActionHandlers(deps) {
17234
17339
  by: c.userId,
17235
17340
  ruleId: input.ruleId
17236
17341
  });
17342
+ guardPatchDetectionAudio(existing, patch, {
17343
+ logger: deps.logger,
17344
+ by: c.userId,
17345
+ ruleId: input.ruleId
17346
+ });
17237
17347
  guardPatchDevicesExclude(existing, patch, {
17238
17348
  logger: deps.logger,
17239
17349
  by: c.userId,
@@ -25397,6 +25507,8 @@ var NcRuleStore = class {
25397
25507
  identityIdsByName;
25398
25508
  /** Has a {@link load} ever completed? See the note there. */
25399
25509
  loaded = false;
25510
+ /** Legacy detection rules naming sounds already reported (D690) — once each. */
25511
+ audioClassWarned = /* @__PURE__ */ new Set();
25400
25512
  constructor(deps) {
25401
25513
  this.store = deps.store;
25402
25514
  this.ledger = new DurableLedger({
@@ -25431,6 +25543,7 @@ var NcRuleStore = class {
25431
25543
  async load() {
25432
25544
  await this.ledger.load();
25433
25545
  if (this.ledger.hasAnswered()) this.loaded = true;
25546
+ for (const id of warnLegacyAudioClassRules(this.ledger.snapshot(), this.audioClassWarned, this.logger)) this.audioClassWarned.add(id);
25434
25547
  this.logger.debug("notification rules loaded", { meta: { rules: this.ledger.size } });
25435
25548
  }
25436
25549
  /**
@@ -33662,6 +33775,10 @@ var NotificationCenter = class NotificationCenter {
33662
33775
  logger: this.logger,
33663
33776
  by: caller.userId
33664
33777
  });
33778
+ guardRuleDetectionAudio(rule, {
33779
+ logger: this.logger,
33780
+ by: caller.userId
33781
+ });
33665
33782
  guardRuleDevicesExclude(rule, {
33666
33783
  logger: this.logger,
33667
33784
  by: caller.userId
@@ -33693,6 +33810,11 @@ var NotificationCenter = class NotificationCenter {
33693
33810
  by: caller.userId,
33694
33811
  ruleId
33695
33812
  });
33813
+ guardPatchDetectionAudio(existing, patch, {
33814
+ logger: this.logger,
33815
+ by: caller.userId,
33816
+ ruleId
33817
+ });
33696
33818
  guardPatchDevicesExclude(existing, patch, {
33697
33819
  logger: this.logger,
33698
33820
  by: caller.userId,
@@ -67228,6 +67350,22 @@ function messageOf(err) {
67228
67350
  function isDetailNotAttachedRefusal(err) {
67229
67351
  return messageOf(err).includes(DETAIL_NOT_ATTACHED_MARKER);
67230
67352
  }
67353
+ /**
67354
+ * The runner's TYPED handle-expired refusal (D688): the frame handle a dispatch
67355
+ * named is past the native lease age bound, and the registry will refuse it on
67356
+ * every later ask. TWIN of `addon-pipeline/src/pipeline-runner/detail-park.ts`
67357
+ * `DETAIL_HANDLE_EXPIRED_MARKER` — keep the two identical.
67358
+ *
67359
+ * Why the dispatcher cares: unlike not-attached this is about ONE handle, not
67360
+ * the session — the parked frame still serves — but the handle itself is
67361
+ * finished. Naming it again (next tick, or through the native-strict crop rung)
67362
+ * can only be refused again; on the live fleet that was ~7 refusals per handle.
67363
+ */
67364
+ var DETAIL_HANDLE_EXPIRED_MARKER = "runDetailSubtree-refused:handle-expired";
67365
+ /** Did this `runDetailSubtree` error carry the runner's handle-expired marker? */
67366
+ function isDetailHandleExpiredRefusal(err) {
67367
+ return messageOf(err).includes(DETAIL_HANDLE_EXPIRED_MARKER);
67368
+ }
67231
67369
  //#endregion
67232
67370
  //#region src/pipeline-analytics/detail-scheduler.ts
67233
67371
  /** Default backoff/period when a step's cadence omits `minIntervalMs`. */
@@ -67684,6 +67822,7 @@ var TrackDetailDispatcher = class {
67684
67822
  className: frame.className
67685
67823
  };
67686
67824
  const steps = composeDetailSteps(req, (trackId) => this.deps.hasTrackLabel?.(trackId) ?? false);
67825
+ let handleRefused = false;
67687
67826
  if (frame.frameHandle !== void 0) try {
67688
67827
  const primary = await this.deps.runDetailSubtree({
67689
67828
  deviceId,
@@ -67697,7 +67836,10 @@ var TrackDetailDispatcher = class {
67697
67836
  this.warnNotAttachedThrottled(deviceId, dev, req);
67698
67837
  return null;
67699
67838
  }
67700
- this.deps.logger.debug("detail primary call failed — trying frame fallback", {
67839
+ if (isDetailHandleExpiredRefusal(err)) {
67840
+ handleRefused = true;
67841
+ this.retireRefusedHandle(deviceId, dev, req, frame);
67842
+ } else this.deps.logger.debug("detail primary call failed — trying frame fallback", {
67701
67843
  tags: { deviceId },
67702
67844
  meta: {
67703
67845
  trackId: req.trackId,
@@ -67747,7 +67889,7 @@ var TrackDetailDispatcher = class {
67747
67889
  }
67748
67890
  });
67749
67891
  }
67750
- if (this.deps.captureCropBase64 !== void 0) try {
67892
+ if (this.deps.captureCropBase64 !== void 0 && frame.frameHandle !== void 0 && !handleRefused) try {
67751
67893
  const cropJpeg = await this.deps.captureCropBase64(frame);
67752
67894
  if (cropJpeg !== null) {
67753
67895
  const retry = await this.deps.runDetailSubtree({
@@ -67776,6 +67918,35 @@ var TrackDetailDispatcher = class {
67776
67918
  return null;
67777
67919
  }
67778
67920
  /**
67921
+ * The runner refused `frame`'s handle as past the lease age bound (D688).
67922
+ * That verdict is final for the handle, so the track's stored association
67923
+ * loses it: later dispatches skip rung 1 and the native-strict crop rung
67924
+ * (both would name the same handle and be refused again — ~7 times per
67925
+ * handle on the live fleet) and go straight to the parked frame, until a
67926
+ * newly OBSERVED frame brings a fresh handle via {@link onFrame}.
67927
+ *
67928
+ * Only the refused handle is dropped: if a newer frame replaced it while the
67929
+ * dispatch was in flight, that one stands. The line is therefore emitted once
67930
+ * per (track, handle) — a concurrent dispatch refused on the same handle
67931
+ * finds it already gone and says nothing.
67932
+ */
67933
+ retireRefusedHandle(deviceId, dev, req, frame) {
67934
+ const current = dev.tracks.get(req.trackId);
67935
+ if (current === void 0 || current.frameHandle !== frame.frameHandle) return;
67936
+ const { frameHandle: refused, ...withoutHandle } = current;
67937
+ dev.tracks.set(req.trackId, withoutHandle);
67938
+ this.deps.logger.info("detail dispatch: frame handle refused as expired — retired for this track, parked frame only until a fresh observation", {
67939
+ tags: { deviceId },
67940
+ meta: {
67941
+ trackId: req.trackId,
67942
+ stepId: req.stepId,
67943
+ reason: "handle-expired",
67944
+ handle: refused === void 0 ? null : `${refused.shmId}#${refused.slot}#${refused.seq}`,
67945
+ parkedFrameRung: this.deps.captureParkedFrame !== void 0
67946
+ }
67947
+ });
67948
+ }
67949
+ /**
67779
67950
  * The typed refusal is EXPECTED for a track that outlives the runner's
67780
67951
  * detail park grace (a closing track's last dispatches), so it logs its own
67781
67952
  * line — the reason is knowable, unlike the every-rung-missed warn — and
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DVMLLink.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-sLPWG5v8.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.302",
3
+ "version": "1.2.304",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",