@camstack/addon-provider-hikvision 1.2.29 → 1.2.31

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 (3) hide show
  1. package/dist/addon.js +167 -59
  2. package/dist/addon.mjs +167 -59
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7561,7 +7561,7 @@ var RecordingBandSchema = object({
7561
7561
  postBufferSec: number().min(0).optional()
7562
7562
  });
7563
7563
  ({
7564
- preBufferSec: 10,
7564
+ preBufferSec: 15,
7565
7565
  postBufferSec: 30
7566
7566
  }).postBufferSec * 1e3;
7567
7567
  /**
@@ -13951,128 +13951,103 @@ var COCO_TO_MACRO = {
13951
13951
  var AUDIO_MACRO_LABELS = [
13952
13952
  {
13953
13953
  id: "speech",
13954
- name: "Speech",
13955
- icon: "🗣️"
13954
+ name: "Speech"
13956
13955
  },
13957
13956
  {
13958
13957
  id: "scream",
13959
- name: "Scream / Shout",
13960
- icon: "😱"
13958
+ name: "Scream / Shout"
13961
13959
  },
13962
13960
  {
13963
13961
  id: "crying",
13964
- name: "Crying / Baby",
13965
- icon: "😢"
13962
+ name: "Crying / Baby"
13966
13963
  },
13967
13964
  {
13968
13965
  id: "laughter",
13969
- name: "Laughter",
13970
- icon: "😂"
13966
+ name: "Laughter"
13971
13967
  },
13972
13968
  {
13973
13969
  id: "music",
13974
- name: "Music",
13975
- icon: "🎵"
13970
+ name: "Music"
13976
13971
  },
13977
13972
  {
13978
13973
  id: "dog",
13979
- name: "Dog",
13980
- icon: "🐕"
13974
+ name: "Dog"
13981
13975
  },
13982
13976
  {
13983
13977
  id: "cat",
13984
- name: "Cat",
13985
- icon: "🐈"
13978
+ name: "Cat"
13986
13979
  },
13987
13980
  {
13988
13981
  id: "bird",
13989
- name: "Bird",
13990
- icon: "🐦"
13982
+ name: "Bird"
13991
13983
  },
13992
13984
  {
13993
13985
  id: "animal",
13994
- name: "Animal (other)",
13995
- icon: "🐾"
13986
+ name: "Animal (other)"
13996
13987
  },
13997
13988
  {
13998
13989
  id: "alarm",
13999
- name: "Alarm / Siren",
14000
- icon: "🚨"
13990
+ name: "Alarm / Siren"
14001
13991
  },
14002
13992
  {
14003
13993
  id: "doorbell",
14004
- name: "Doorbell / Knock",
14005
- icon: "🔔"
13994
+ name: "Doorbell / Knock"
14006
13995
  },
14007
13996
  {
14008
13997
  id: "glass_breaking",
14009
- name: "Glass Breaking",
14010
- icon: "💥"
13998
+ name: "Glass Breaking"
14011
13999
  },
14012
14000
  {
14013
14001
  id: "gunshot",
14014
- name: "Gunshot / Explosion",
14015
- icon: "💣"
14002
+ name: "Gunshot / Explosion"
14016
14003
  },
14017
14004
  {
14018
14005
  id: "vehicle",
14019
- name: "Vehicle",
14020
- icon: "🚗"
14006
+ name: "Vehicle"
14021
14007
  },
14022
14008
  {
14023
14009
  id: "siren",
14024
- name: "Emergency Siren",
14025
- icon: "🚑"
14010
+ name: "Emergency Siren"
14026
14011
  },
14027
14012
  {
14028
14013
  id: "fire",
14029
- name: "Fire / Smoke",
14030
- icon: "🔥"
14014
+ name: "Fire / Smoke"
14031
14015
  },
14032
14016
  {
14033
14017
  id: "water",
14034
- name: "Water",
14035
- icon: "💧"
14018
+ name: "Water"
14036
14019
  },
14037
14020
  {
14038
14021
  id: "wind",
14039
- name: "Wind / Weather",
14040
- icon: "🌬️"
14022
+ name: "Wind / Weather"
14041
14023
  },
14042
14024
  {
14043
14025
  id: "door",
14044
- name: "Door",
14045
- icon: "🚪"
14026
+ name: "Door"
14046
14027
  },
14047
14028
  {
14048
14029
  id: "footsteps",
14049
- name: "Footsteps",
14050
- icon: "👣"
14030
+ name: "Footsteps"
14051
14031
  },
14052
14032
  {
14053
14033
  id: "crowd",
14054
- name: "Crowd / Chatter",
14055
- icon: "👥"
14034
+ name: "Crowd / Chatter"
14056
14035
  },
14057
14036
  {
14058
14037
  id: "telephone",
14059
- name: "Telephone",
14060
- icon: "📞"
14038
+ name: "Telephone"
14061
14039
  },
14062
14040
  {
14063
14041
  id: "engine",
14064
- name: "Engine / Motor",
14065
- icon: "⚙️"
14042
+ name: "Engine / Motor"
14066
14043
  },
14067
14044
  {
14068
14045
  id: "tools",
14069
- name: "Tools / Construction",
14070
- icon: "🔨"
14046
+ name: "Tools / Construction"
14071
14047
  },
14072
14048
  {
14073
14049
  id: "silence",
14074
- name: "Silence",
14075
- icon: "🤫"
14050
+ name: "Silence"
14076
14051
  }
14077
14052
  ];
14078
14053
  var YAMNET_TO_MACRO = {
@@ -16574,7 +16549,7 @@ var TrackEnvelopeSchema = object({
16574
16549
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
16575
16550
  * keeps every scalar the list surfaces actually render (ids, class(es),
16576
16551
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
16577
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
16552
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
16578
16553
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
16579
16554
  * `getTrack`. Mirrors the event-store `projection` convention
16580
16555
  * (`getObjectEvents` et al.).
@@ -16869,6 +16844,35 @@ var TrackSchema = object({
16869
16844
  */
16870
16845
  hasFace: boolean().optional(),
16871
16846
  /**
16847
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
16848
+ * face an operator could ASSIGN to an identity.
16849
+ *
16850
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
16851
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
16852
+ * branch, before every gate, and means no more than "a face detector produced
16853
+ * a face detail". This one is stamped at the single moment the gallery row
16854
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
16855
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
16856
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
16857
+ * the crop-store drop. Everything between the detector and that insert can
16858
+ * legitimately refuse the face, so a flag written any earlier promises the
16859
+ * operator something to assign and delivers nothing.
16860
+ *
16861
+ * **Independent of recognition.** A face collected but never auto-matched is
16862
+ * still assignable — it is in fact the face an operator most wants to reach —
16863
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
16864
+ * `subLabel`; this says only that the raw material exists.
16865
+ *
16866
+ * **Set once, never cleared.** A track that produced a gallery row produced
16867
+ * one; deleting the row later is the gallery's business, not this flag's.
16868
+ *
16869
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
16870
+ * before the column omits it, and so does every server that predates the
16871
+ * field. A consumer must test `=== true` and render nothing otherwise —
16872
+ * never infer "no assignable face".
16873
+ */
16874
+ hasEmbeddedFace: boolean().optional(),
16875
+ /**
16872
16876
  * This subject CONTAINS a folded rider — a person the rider-pairing step
16873
16877
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16874
16878
  * so the passage is tracked once and as a VEHICLE.
@@ -18258,6 +18262,32 @@ var DetailResultSchema = object({
18258
18262
  /** FRAME-space bbox (already mapped back from crop space). */
18259
18263
  bbox: NativeCropBboxSchema.optional(),
18260
18264
  embedding: string().optional(),
18265
+ /**
18266
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
18267
+ * so an older runner that does not send it degrades to "unmeasurable", which
18268
+ * the consuming gate treats as accept — never as reject.
18269
+ */
18270
+ embeddingMagnitude: number().optional(),
18271
+ /**
18272
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
18273
+ * `label`, and for the same reason: the producing node is the only side that
18274
+ * knows which model actually ran, and re-deriving it downstream is exactly
18275
+ * the inference this model exists to forbid.
18276
+ *
18277
+ * Additive and optional, so a runner that predates it degrades to
18278
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
18279
+ * its previous assumption instead of refusing.
18280
+ *
18281
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
18282
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
18283
+ * with nothing on the wire it took the id from the GALLERY, so the gate
18284
+ * compared the gallery against itself and could never fire. That turned the
18285
+ * one guard protecting the face feature space into a tautology, and it is
18286
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
18287
+ * gallery as the catalog comment predicts — it would have scored the new
18288
+ * model's vectors against the old model's samples in one cosine space.
18289
+ */
18290
+ embeddingModelId: string().optional(),
18261
18291
  label: string().optional(),
18262
18292
  /**
18263
18293
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -19722,6 +19752,7 @@ var snapshotCapability = {
19722
19752
  isBattery: boolean(),
19723
19753
  reason: _enum([
19724
19754
  "disabled",
19755
+ "offline",
19725
19756
  "sleeping",
19726
19757
  "unreachable",
19727
19758
  "waking"
@@ -19811,9 +19842,17 @@ targets: array(object({
19811
19842
  sleeping: boolean(),
19812
19843
  /** Current device state rendered over the cached frame. State images
19813
19844
  * remain authoritative even when their photographic background is
19814
- * old; null means the link must carry a current camera frame. */
19845
+ * old; null means the link must carry a current camera frame.
19846
+ *
19847
+ * **This is the ONLY source a surface may derive an inactive tile
19848
+ * from.** A client that reads a device's `online`/`disabled` flags and
19849
+ * draws its own verdict will disagree with the pixels it is painting
19850
+ * — the frame comes from here, and so must the sentence over it
19851
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
19852
+ * state. */
19815
19853
  stateReason: _enum([
19816
19854
  "disabled",
19855
+ "offline",
19817
19856
  "sleeping",
19818
19857
  "unreachable",
19819
19858
  "waking"
@@ -26671,9 +26710,21 @@ var RecordingLocationUsageSchema = object({
26671
26710
  * previously rendered as two identical "disks" with a nonsensical used
26672
26711
  * split — hydrate attribution across aliases is arbitrary by nature. */
26673
26712
  locationIds: array(string()).optional(),
26674
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
26713
+ /**
26714
+ * Filesystem root of the physical volume this row accounts — the only
26715
+ * operator-legible identity when a location has no `displayName` yet, and
26716
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
26717
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
26718
+ * it, and this schema must keep validating that older provider's payload.
26719
+ */
26720
+ root: string().optional(),
26721
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
26722
+ * the footage's SHARE of the volume, not how full the volume is — the disk
26723
+ * also holds everything that is not recordings. Volume fill is
26724
+ * `(totalBytes - availableBytes) / totalBytes`. */
26675
26725
  usedBytes: number(),
26676
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
26726
+ /** Free bytes on the location's volume; null when capacity is unknown
26727
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
26677
26728
  availableBytes: number().nullable(),
26678
26729
  /** Total bytes of the location's volume; null when unknown. */
26679
26730
  totalBytes: number().nullable()
@@ -26685,7 +26736,15 @@ var RecordingStorageUsageSchema = object({
26685
26736
  nodeId: string(),
26686
26737
  totalUsedBytes: number(),
26687
26738
  devices: array(RecordingDeviceUsageSchema),
26688
- locations: array(RecordingLocationUsageSchema)
26739
+ /**
26740
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
26741
+ * of the payload without it: the hub serves this method through a BAKED
26742
+ * router, so a hub whose framework train predates a change to this array
26743
+ * strips it out of an answer a newer recorder happily produced. A UI that
26744
+ * dereferences it unconditionally crashes the whole Storage page over a
26745
+ * missing disk bar.
26746
+ */
26747
+ locations: array(RecordingLocationUsageSchema).optional()
26689
26748
  });
26690
26749
  /**
26691
26750
  * The OPERATOR-ARMED half of multi-location recordings (D116).
@@ -37814,8 +37873,13 @@ Object.freeze({
37814
37873
  "network-access": "ingress",
37815
37874
  "smtp-provider": "email"
37816
37875
  });
37817
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37876
+ /** Schema defaults an untouched sub-field must author exactly these. */
37877
+ var NC_AUDIO_DEFAULTS = {
37878
+ hitPercent: 60,
37879
+ samplingSeconds: 10
37880
+ };
37818
37881
  new Set(["devices", "classes"]);
37882
+ NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
37819
37883
  /**
37820
37884
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
37821
37885
  *
@@ -38047,6 +38111,39 @@ TimelapseRuleInputSchema.extend({
38047
38111
  createdAt: number(),
38048
38112
  updatedAt: number()
38049
38113
  });
38114
+ Object.freeze(Object.fromEntries([{
38115
+ stepId: "face-embedding",
38116
+ label: "Face recognition model",
38117
+ defaultModelId: "arcface-r100",
38118
+ indexName: "the enrolled face gallery",
38119
+ options: [
38120
+ {
38121
+ id: "arcface-r100",
38122
+ label: "ArcFace (ResNet34)"
38123
+ },
38124
+ {
38125
+ id: "auraface-r100",
38126
+ label: "AuraFace R100"
38127
+ },
38128
+ {
38129
+ id: "inception-resnet-v1",
38130
+ label: "Inception ResNet V1"
38131
+ }
38132
+ ]
38133
+ }, {
38134
+ stepId: "clip-embedding",
38135
+ label: "Semantic search model",
38136
+ defaultModelId: "mobileclip-s1",
38137
+ indexName: "the object semantic-search index",
38138
+ options: [{
38139
+ id: "mobileclip-s1",
38140
+ label: "MobileCLIP S1"
38141
+ }, {
38142
+ id: "mobileclip-s2",
38143
+ label: "MobileCLIP S2"
38144
+ }]
38145
+ }].map((s) => [s.stepId, s.defaultModelId])));
38146
+ string().min(1);
38050
38147
  object({
38051
38148
  /**
38052
38149
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38067,10 +38164,21 @@ object({
38067
38164
  */
38068
38165
  square: boolean()
38069
38166
  });
38070
- ({
38167
+ /**
38168
+ * The convention in force when nobody has configured one — byte-for-byte the
38169
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38170
+ */
38171
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38071
38172
  paddingRatio: .15,
38072
38173
  square: false
38073
- }).paddingRatio;
38174
+ };
38175
+ Object.freeze({
38176
+ x: 0,
38177
+ y: 0,
38178
+ w: 1,
38179
+ h: 1
38180
+ });
38181
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38074
38182
  /**
38075
38183
  * WHICH delivered frames the decode worker retains a native copy of.
38076
38184
  *
package/dist/addon.mjs CHANGED
@@ -7562,7 +7562,7 @@ var RecordingBandSchema = object({
7562
7562
  postBufferSec: number().min(0).optional()
7563
7563
  });
7564
7564
  ({
7565
- preBufferSec: 10,
7565
+ preBufferSec: 15,
7566
7566
  postBufferSec: 30
7567
7567
  }).postBufferSec * 1e3;
7568
7568
  /**
@@ -13952,128 +13952,103 @@ var COCO_TO_MACRO = {
13952
13952
  var AUDIO_MACRO_LABELS = [
13953
13953
  {
13954
13954
  id: "speech",
13955
- name: "Speech",
13956
- icon: "🗣️"
13955
+ name: "Speech"
13957
13956
  },
13958
13957
  {
13959
13958
  id: "scream",
13960
- name: "Scream / Shout",
13961
- icon: "😱"
13959
+ name: "Scream / Shout"
13962
13960
  },
13963
13961
  {
13964
13962
  id: "crying",
13965
- name: "Crying / Baby",
13966
- icon: "😢"
13963
+ name: "Crying / Baby"
13967
13964
  },
13968
13965
  {
13969
13966
  id: "laughter",
13970
- name: "Laughter",
13971
- icon: "😂"
13967
+ name: "Laughter"
13972
13968
  },
13973
13969
  {
13974
13970
  id: "music",
13975
- name: "Music",
13976
- icon: "🎵"
13971
+ name: "Music"
13977
13972
  },
13978
13973
  {
13979
13974
  id: "dog",
13980
- name: "Dog",
13981
- icon: "🐕"
13975
+ name: "Dog"
13982
13976
  },
13983
13977
  {
13984
13978
  id: "cat",
13985
- name: "Cat",
13986
- icon: "🐈"
13979
+ name: "Cat"
13987
13980
  },
13988
13981
  {
13989
13982
  id: "bird",
13990
- name: "Bird",
13991
- icon: "🐦"
13983
+ name: "Bird"
13992
13984
  },
13993
13985
  {
13994
13986
  id: "animal",
13995
- name: "Animal (other)",
13996
- icon: "🐾"
13987
+ name: "Animal (other)"
13997
13988
  },
13998
13989
  {
13999
13990
  id: "alarm",
14000
- name: "Alarm / Siren",
14001
- icon: "🚨"
13991
+ name: "Alarm / Siren"
14002
13992
  },
14003
13993
  {
14004
13994
  id: "doorbell",
14005
- name: "Doorbell / Knock",
14006
- icon: "🔔"
13995
+ name: "Doorbell / Knock"
14007
13996
  },
14008
13997
  {
14009
13998
  id: "glass_breaking",
14010
- name: "Glass Breaking",
14011
- icon: "💥"
13999
+ name: "Glass Breaking"
14012
14000
  },
14013
14001
  {
14014
14002
  id: "gunshot",
14015
- name: "Gunshot / Explosion",
14016
- icon: "💣"
14003
+ name: "Gunshot / Explosion"
14017
14004
  },
14018
14005
  {
14019
14006
  id: "vehicle",
14020
- name: "Vehicle",
14021
- icon: "🚗"
14007
+ name: "Vehicle"
14022
14008
  },
14023
14009
  {
14024
14010
  id: "siren",
14025
- name: "Emergency Siren",
14026
- icon: "🚑"
14011
+ name: "Emergency Siren"
14027
14012
  },
14028
14013
  {
14029
14014
  id: "fire",
14030
- name: "Fire / Smoke",
14031
- icon: "🔥"
14015
+ name: "Fire / Smoke"
14032
14016
  },
14033
14017
  {
14034
14018
  id: "water",
14035
- name: "Water",
14036
- icon: "💧"
14019
+ name: "Water"
14037
14020
  },
14038
14021
  {
14039
14022
  id: "wind",
14040
- name: "Wind / Weather",
14041
- icon: "🌬️"
14023
+ name: "Wind / Weather"
14042
14024
  },
14043
14025
  {
14044
14026
  id: "door",
14045
- name: "Door",
14046
- icon: "🚪"
14027
+ name: "Door"
14047
14028
  },
14048
14029
  {
14049
14030
  id: "footsteps",
14050
- name: "Footsteps",
14051
- icon: "👣"
14031
+ name: "Footsteps"
14052
14032
  },
14053
14033
  {
14054
14034
  id: "crowd",
14055
- name: "Crowd / Chatter",
14056
- icon: "👥"
14035
+ name: "Crowd / Chatter"
14057
14036
  },
14058
14037
  {
14059
14038
  id: "telephone",
14060
- name: "Telephone",
14061
- icon: "📞"
14039
+ name: "Telephone"
14062
14040
  },
14063
14041
  {
14064
14042
  id: "engine",
14065
- name: "Engine / Motor",
14066
- icon: "⚙️"
14043
+ name: "Engine / Motor"
14067
14044
  },
14068
14045
  {
14069
14046
  id: "tools",
14070
- name: "Tools / Construction",
14071
- icon: "🔨"
14047
+ name: "Tools / Construction"
14072
14048
  },
14073
14049
  {
14074
14050
  id: "silence",
14075
- name: "Silence",
14076
- icon: "🤫"
14051
+ name: "Silence"
14077
14052
  }
14078
14053
  ];
14079
14054
  var YAMNET_TO_MACRO = {
@@ -16575,7 +16550,7 @@ var TrackEnvelopeSchema = object({
16575
16550
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
16576
16551
  * keeps every scalar the list surfaces actually render (ids, class(es),
16577
16552
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
16578
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
16553
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
16579
16554
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
16580
16555
  * `getTrack`. Mirrors the event-store `projection` convention
16581
16556
  * (`getObjectEvents` et al.).
@@ -16870,6 +16845,35 @@ var TrackSchema = object({
16870
16845
  */
16871
16846
  hasFace: boolean().optional(),
16872
16847
  /**
16848
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
16849
+ * face an operator could ASSIGN to an identity.
16850
+ *
16851
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
16852
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
16853
+ * branch, before every gate, and means no more than "a face detector produced
16854
+ * a face detail". This one is stamped at the single moment the gallery row
16855
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
16856
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
16857
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
16858
+ * the crop-store drop. Everything between the detector and that insert can
16859
+ * legitimately refuse the face, so a flag written any earlier promises the
16860
+ * operator something to assign and delivers nothing.
16861
+ *
16862
+ * **Independent of recognition.** A face collected but never auto-matched is
16863
+ * still assignable — it is in fact the face an operator most wants to reach —
16864
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
16865
+ * `subLabel`; this says only that the raw material exists.
16866
+ *
16867
+ * **Set once, never cleared.** A track that produced a gallery row produced
16868
+ * one; deleting the row later is the gallery's business, not this flag's.
16869
+ *
16870
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
16871
+ * before the column omits it, and so does every server that predates the
16872
+ * field. A consumer must test `=== true` and render nothing otherwise —
16873
+ * never infer "no assignable face".
16874
+ */
16875
+ hasEmbeddedFace: boolean().optional(),
16876
+ /**
16873
16877
  * This subject CONTAINS a folded rider — a person the rider-pairing step
16874
16878
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16875
16879
  * so the passage is tracked once and as a VEHICLE.
@@ -18259,6 +18263,32 @@ var DetailResultSchema = object({
18259
18263
  /** FRAME-space bbox (already mapped back from crop space). */
18260
18264
  bbox: NativeCropBboxSchema.optional(),
18261
18265
  embedding: string().optional(),
18266
+ /**
18267
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
18268
+ * so an older runner that does not send it degrades to "unmeasurable", which
18269
+ * the consuming gate treats as accept — never as reject.
18270
+ */
18271
+ embeddingMagnitude: number().optional(),
18272
+ /**
18273
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
18274
+ * `label`, and for the same reason: the producing node is the only side that
18275
+ * knows which model actually ran, and re-deriving it downstream is exactly
18276
+ * the inference this model exists to forbid.
18277
+ *
18278
+ * Additive and optional, so a runner that predates it degrades to
18279
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
18280
+ * its previous assumption instead of refusing.
18281
+ *
18282
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
18283
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
18284
+ * with nothing on the wire it took the id from the GALLERY, so the gate
18285
+ * compared the gallery against itself and could never fire. That turned the
18286
+ * one guard protecting the face feature space into a tautology, and it is
18287
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
18288
+ * gallery as the catalog comment predicts — it would have scored the new
18289
+ * model's vectors against the old model's samples in one cosine space.
18290
+ */
18291
+ embeddingModelId: string().optional(),
18262
18292
  label: string().optional(),
18263
18293
  /**
18264
18294
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -19723,6 +19753,7 @@ var snapshotCapability = {
19723
19753
  isBattery: boolean(),
19724
19754
  reason: _enum([
19725
19755
  "disabled",
19756
+ "offline",
19726
19757
  "sleeping",
19727
19758
  "unreachable",
19728
19759
  "waking"
@@ -19812,9 +19843,17 @@ targets: array(object({
19812
19843
  sleeping: boolean(),
19813
19844
  /** Current device state rendered over the cached frame. State images
19814
19845
  * remain authoritative even when their photographic background is
19815
- * old; null means the link must carry a current camera frame. */
19846
+ * old; null means the link must carry a current camera frame.
19847
+ *
19848
+ * **This is the ONLY source a surface may derive an inactive tile
19849
+ * from.** A client that reads a device's `online`/`disabled` flags and
19850
+ * draws its own verdict will disagree with the pixels it is painting
19851
+ * — the frame comes from here, and so must the sentence over it
19852
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
19853
+ * state. */
19816
19854
  stateReason: _enum([
19817
19855
  "disabled",
19856
+ "offline",
19818
19857
  "sleeping",
19819
19858
  "unreachable",
19820
19859
  "waking"
@@ -26672,9 +26711,21 @@ var RecordingLocationUsageSchema = object({
26672
26711
  * previously rendered as two identical "disks" with a nonsensical used
26673
26712
  * split — hydrate attribution across aliases is arbitrary by nature. */
26674
26713
  locationIds: array(string()).optional(),
26675
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
26714
+ /**
26715
+ * Filesystem root of the physical volume this row accounts — the only
26716
+ * operator-legible identity when a location has no `displayName` yet, and
26717
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
26718
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
26719
+ * it, and this schema must keep validating that older provider's payload.
26720
+ */
26721
+ root: string().optional(),
26722
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
26723
+ * the footage's SHARE of the volume, not how full the volume is — the disk
26724
+ * also holds everything that is not recordings. Volume fill is
26725
+ * `(totalBytes - availableBytes) / totalBytes`. */
26676
26726
  usedBytes: number(),
26677
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
26727
+ /** Free bytes on the location's volume; null when capacity is unknown
26728
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
26678
26729
  availableBytes: number().nullable(),
26679
26730
  /** Total bytes of the location's volume; null when unknown. */
26680
26731
  totalBytes: number().nullable()
@@ -26686,7 +26737,15 @@ var RecordingStorageUsageSchema = object({
26686
26737
  nodeId: string(),
26687
26738
  totalUsedBytes: number(),
26688
26739
  devices: array(RecordingDeviceUsageSchema),
26689
- locations: array(RecordingLocationUsageSchema)
26740
+ /**
26741
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
26742
+ * of the payload without it: the hub serves this method through a BAKED
26743
+ * router, so a hub whose framework train predates a change to this array
26744
+ * strips it out of an answer a newer recorder happily produced. A UI that
26745
+ * dereferences it unconditionally crashes the whole Storage page over a
26746
+ * missing disk bar.
26747
+ */
26748
+ locations: array(RecordingLocationUsageSchema).optional()
26690
26749
  });
26691
26750
  /**
26692
26751
  * The OPERATOR-ARMED half of multi-location recordings (D116).
@@ -37815,8 +37874,13 @@ Object.freeze({
37815
37874
  "network-access": "ingress",
37816
37875
  "smtp-provider": "email"
37817
37876
  });
37818
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37877
+ /** Schema defaults an untouched sub-field must author exactly these. */
37878
+ var NC_AUDIO_DEFAULTS = {
37879
+ hitPercent: 60,
37880
+ samplingSeconds: 10
37881
+ };
37819
37882
  new Set(["devices", "classes"]);
37883
+ NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
37820
37884
  /**
37821
37885
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
37822
37886
  *
@@ -38048,6 +38112,39 @@ TimelapseRuleInputSchema.extend({
38048
38112
  createdAt: number(),
38049
38113
  updatedAt: number()
38050
38114
  });
38115
+ Object.freeze(Object.fromEntries([{
38116
+ stepId: "face-embedding",
38117
+ label: "Face recognition model",
38118
+ defaultModelId: "arcface-r100",
38119
+ indexName: "the enrolled face gallery",
38120
+ options: [
38121
+ {
38122
+ id: "arcface-r100",
38123
+ label: "ArcFace (ResNet34)"
38124
+ },
38125
+ {
38126
+ id: "auraface-r100",
38127
+ label: "AuraFace R100"
38128
+ },
38129
+ {
38130
+ id: "inception-resnet-v1",
38131
+ label: "Inception ResNet V1"
38132
+ }
38133
+ ]
38134
+ }, {
38135
+ stepId: "clip-embedding",
38136
+ label: "Semantic search model",
38137
+ defaultModelId: "mobileclip-s1",
38138
+ indexName: "the object semantic-search index",
38139
+ options: [{
38140
+ id: "mobileclip-s1",
38141
+ label: "MobileCLIP S1"
38142
+ }, {
38143
+ id: "mobileclip-s2",
38144
+ label: "MobileCLIP S2"
38145
+ }]
38146
+ }].map((s) => [s.stepId, s.defaultModelId])));
38147
+ string().min(1);
38051
38148
  object({
38052
38149
  /**
38053
38150
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38068,10 +38165,21 @@ object({
38068
38165
  */
38069
38166
  square: boolean()
38070
38167
  });
38071
- ({
38168
+ /**
38169
+ * The convention in force when nobody has configured one — byte-for-byte the
38170
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38171
+ */
38172
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38072
38173
  paddingRatio: .15,
38073
38174
  square: false
38074
- }).paddingRatio;
38175
+ };
38176
+ Object.freeze({
38177
+ x: 0,
38178
+ y: 0,
38179
+ w: 1,
38180
+ h: 1
38181
+ });
38182
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38075
38183
  /**
38076
38184
  * WHICH delivered frames the decode worker retains a native copy of.
38077
38185
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-hikvision",
3
- "version": "1.2.29",
3
+ "version": "1.2.31",
4
4
  "description": "Hikvision camera device provider addon for CamStack — ISAPI over HTTP(S) with digest auth (snapshot, alarm stream, RTSP discovery)",
5
5
  "keywords": [
6
6
  "camstack",