@camstack/addon-provider-rademacher 0.2.21 → 0.2.23

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
@@ -8551,7 +8551,7 @@ var RecordingBandSchema = object({
8551
8551
  postBufferSec: number().min(0).optional()
8552
8552
  });
8553
8553
  ({
8554
- preBufferSec: 10,
8554
+ preBufferSec: 15,
8555
8555
  postBufferSec: 30
8556
8556
  }).postBufferSec * 1e3;
8557
8557
  /**
@@ -14754,128 +14754,103 @@ var COCO_TO_MACRO = {
14754
14754
  var AUDIO_MACRO_LABELS = [
14755
14755
  {
14756
14756
  id: "speech",
14757
- name: "Speech",
14758
- icon: "🗣️"
14757
+ name: "Speech"
14759
14758
  },
14760
14759
  {
14761
14760
  id: "scream",
14762
- name: "Scream / Shout",
14763
- icon: "😱"
14761
+ name: "Scream / Shout"
14764
14762
  },
14765
14763
  {
14766
14764
  id: "crying",
14767
- name: "Crying / Baby",
14768
- icon: "😢"
14765
+ name: "Crying / Baby"
14769
14766
  },
14770
14767
  {
14771
14768
  id: "laughter",
14772
- name: "Laughter",
14773
- icon: "😂"
14769
+ name: "Laughter"
14774
14770
  },
14775
14771
  {
14776
14772
  id: "music",
14777
- name: "Music",
14778
- icon: "🎵"
14773
+ name: "Music"
14779
14774
  },
14780
14775
  {
14781
14776
  id: "dog",
14782
- name: "Dog",
14783
- icon: "🐕"
14777
+ name: "Dog"
14784
14778
  },
14785
14779
  {
14786
14780
  id: "cat",
14787
- name: "Cat",
14788
- icon: "🐈"
14781
+ name: "Cat"
14789
14782
  },
14790
14783
  {
14791
14784
  id: "bird",
14792
- name: "Bird",
14793
- icon: "🐦"
14785
+ name: "Bird"
14794
14786
  },
14795
14787
  {
14796
14788
  id: "animal",
14797
- name: "Animal (other)",
14798
- icon: "🐾"
14789
+ name: "Animal (other)"
14799
14790
  },
14800
14791
  {
14801
14792
  id: "alarm",
14802
- name: "Alarm / Siren",
14803
- icon: "🚨"
14793
+ name: "Alarm / Siren"
14804
14794
  },
14805
14795
  {
14806
14796
  id: "doorbell",
14807
- name: "Doorbell / Knock",
14808
- icon: "🔔"
14797
+ name: "Doorbell / Knock"
14809
14798
  },
14810
14799
  {
14811
14800
  id: "glass_breaking",
14812
- name: "Glass Breaking",
14813
- icon: "💥"
14801
+ name: "Glass Breaking"
14814
14802
  },
14815
14803
  {
14816
14804
  id: "gunshot",
14817
- name: "Gunshot / Explosion",
14818
- icon: "💣"
14805
+ name: "Gunshot / Explosion"
14819
14806
  },
14820
14807
  {
14821
14808
  id: "vehicle",
14822
- name: "Vehicle",
14823
- icon: "🚗"
14809
+ name: "Vehicle"
14824
14810
  },
14825
14811
  {
14826
14812
  id: "siren",
14827
- name: "Emergency Siren",
14828
- icon: "🚑"
14813
+ name: "Emergency Siren"
14829
14814
  },
14830
14815
  {
14831
14816
  id: "fire",
14832
- name: "Fire / Smoke",
14833
- icon: "🔥"
14817
+ name: "Fire / Smoke"
14834
14818
  },
14835
14819
  {
14836
14820
  id: "water",
14837
- name: "Water",
14838
- icon: "💧"
14821
+ name: "Water"
14839
14822
  },
14840
14823
  {
14841
14824
  id: "wind",
14842
- name: "Wind / Weather",
14843
- icon: "🌬️"
14825
+ name: "Wind / Weather"
14844
14826
  },
14845
14827
  {
14846
14828
  id: "door",
14847
- name: "Door",
14848
- icon: "🚪"
14829
+ name: "Door"
14849
14830
  },
14850
14831
  {
14851
14832
  id: "footsteps",
14852
- name: "Footsteps",
14853
- icon: "👣"
14833
+ name: "Footsteps"
14854
14834
  },
14855
14835
  {
14856
14836
  id: "crowd",
14857
- name: "Crowd / Chatter",
14858
- icon: "👥"
14837
+ name: "Crowd / Chatter"
14859
14838
  },
14860
14839
  {
14861
14840
  id: "telephone",
14862
- name: "Telephone",
14863
- icon: "📞"
14841
+ name: "Telephone"
14864
14842
  },
14865
14843
  {
14866
14844
  id: "engine",
14867
- name: "Engine / Motor",
14868
- icon: "⚙️"
14845
+ name: "Engine / Motor"
14869
14846
  },
14870
14847
  {
14871
14848
  id: "tools",
14872
- name: "Tools / Construction",
14873
- icon: "🔨"
14849
+ name: "Tools / Construction"
14874
14850
  },
14875
14851
  {
14876
14852
  id: "silence",
14877
- name: "Silence",
14878
- icon: "🤫"
14853
+ name: "Silence"
14879
14854
  }
14880
14855
  ];
14881
14856
  var YAMNET_TO_MACRO = {
@@ -17377,7 +17352,7 @@ var TrackEnvelopeSchema = object({
17377
17352
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
17378
17353
  * keeps every scalar the list surfaces actually render (ids, class(es),
17379
17354
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
17380
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
17355
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
17381
17356
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
17382
17357
  * `getTrack`. Mirrors the event-store `projection` convention
17383
17358
  * (`getObjectEvents` et al.).
@@ -17672,6 +17647,35 @@ var TrackSchema = object({
17672
17647
  */
17673
17648
  hasFace: boolean().optional(),
17674
17649
  /**
17650
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
17651
+ * face an operator could ASSIGN to an identity.
17652
+ *
17653
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
17654
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
17655
+ * branch, before every gate, and means no more than "a face detector produced
17656
+ * a face detail". This one is stamped at the single moment the gallery row
17657
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
17658
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
17659
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
17660
+ * the crop-store drop. Everything between the detector and that insert can
17661
+ * legitimately refuse the face, so a flag written any earlier promises the
17662
+ * operator something to assign and delivers nothing.
17663
+ *
17664
+ * **Independent of recognition.** A face collected but never auto-matched is
17665
+ * still assignable — it is in fact the face an operator most wants to reach —
17666
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
17667
+ * `subLabel`; this says only that the raw material exists.
17668
+ *
17669
+ * **Set once, never cleared.** A track that produced a gallery row produced
17670
+ * one; deleting the row later is the gallery's business, not this flag's.
17671
+ *
17672
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
17673
+ * before the column omits it, and so does every server that predates the
17674
+ * field. A consumer must test `=== true` and render nothing otherwise —
17675
+ * never infer "no assignable face".
17676
+ */
17677
+ hasEmbeddedFace: boolean().optional(),
17678
+ /**
17675
17679
  * This subject CONTAINS a folded rider — a person the rider-pairing step
17676
17680
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
17677
17681
  * so the passage is tracked once and as a VEHICLE.
@@ -19061,6 +19065,32 @@ var DetailResultSchema = object({
19061
19065
  /** FRAME-space bbox (already mapped back from crop space). */
19062
19066
  bbox: NativeCropBboxSchema.optional(),
19063
19067
  embedding: string().optional(),
19068
+ /**
19069
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
19070
+ * so an older runner that does not send it degrades to "unmeasurable", which
19071
+ * the consuming gate treats as accept — never as reject.
19072
+ */
19073
+ embeddingMagnitude: number().optional(),
19074
+ /**
19075
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
19076
+ * `label`, and for the same reason: the producing node is the only side that
19077
+ * knows which model actually ran, and re-deriving it downstream is exactly
19078
+ * the inference this model exists to forbid.
19079
+ *
19080
+ * Additive and optional, so a runner that predates it degrades to
19081
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
19082
+ * its previous assumption instead of refusing.
19083
+ *
19084
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
19085
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
19086
+ * with nothing on the wire it took the id from the GALLERY, so the gate
19087
+ * compared the gallery against itself and could never fire. That turned the
19088
+ * one guard protecting the face feature space into a tautology, and it is
19089
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
19090
+ * gallery as the catalog comment predicts — it would have scored the new
19091
+ * model's vectors against the old model's samples in one cosine space.
19092
+ */
19093
+ embeddingModelId: string().optional(),
19064
19094
  label: string().optional(),
19065
19095
  /**
19066
19096
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -20471,6 +20501,7 @@ DeviceType.Camera, method(object({
20471
20501
  isBattery: boolean(),
20472
20502
  reason: _enum([
20473
20503
  "disabled",
20504
+ "offline",
20474
20505
  "sleeping",
20475
20506
  "unreachable",
20476
20507
  "waking"
@@ -20516,9 +20547,17 @@ targets: array(object({
20516
20547
  sleeping: boolean(),
20517
20548
  /** Current device state rendered over the cached frame. State images
20518
20549
  * remain authoritative even when their photographic background is
20519
- * old; null means the link must carry a current camera frame. */
20550
+ * old; null means the link must carry a current camera frame.
20551
+ *
20552
+ * **This is the ONLY source a surface may derive an inactive tile
20553
+ * from.** A client that reads a device's `online`/`disabled` flags and
20554
+ * draws its own verdict will disagree with the pixels it is painting
20555
+ * — the frame comes from here, and so must the sentence over it
20556
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
20557
+ * state. */
20520
20558
  stateReason: _enum([
20521
20559
  "disabled",
20560
+ "offline",
20522
20561
  "sleeping",
20523
20562
  "unreachable",
20524
20563
  "waking"
@@ -27237,9 +27276,21 @@ var RecordingLocationUsageSchema = object({
27237
27276
  * previously rendered as two identical "disks" with a nonsensical used
27238
27277
  * split — hydrate attribution across aliases is arbitrary by nature. */
27239
27278
  locationIds: array(string()).optional(),
27240
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
27279
+ /**
27280
+ * Filesystem root of the physical volume this row accounts — the only
27281
+ * operator-legible identity when a location has no `displayName` yet, and
27282
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
27283
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
27284
+ * it, and this schema must keep validating that older provider's payload.
27285
+ */
27286
+ root: string().optional(),
27287
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
27288
+ * the footage's SHARE of the volume, not how full the volume is — the disk
27289
+ * also holds everything that is not recordings. Volume fill is
27290
+ * `(totalBytes - availableBytes) / totalBytes`. */
27241
27291
  usedBytes: number(),
27242
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
27292
+ /** Free bytes on the location's volume; null when capacity is unknown
27293
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
27243
27294
  availableBytes: number().nullable(),
27244
27295
  /** Total bytes of the location's volume; null when unknown. */
27245
27296
  totalBytes: number().nullable()
@@ -27251,7 +27302,15 @@ var RecordingStorageUsageSchema = object({
27251
27302
  nodeId: string(),
27252
27303
  totalUsedBytes: number(),
27253
27304
  devices: array(RecordingDeviceUsageSchema),
27254
- locations: array(RecordingLocationUsageSchema)
27305
+ /**
27306
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
27307
+ * of the payload without it: the hub serves this method through a BAKED
27308
+ * router, so a hub whose framework train predates a change to this array
27309
+ * strips it out of an answer a newer recorder happily produced. A UI that
27310
+ * dereferences it unconditionally crashes the whole Storage page over a
27311
+ * missing disk bar.
27312
+ */
27313
+ locations: array(RecordingLocationUsageSchema).optional()
27255
27314
  });
27256
27315
  /**
27257
27316
  * The OPERATOR-ARMED half of multi-location recordings (D116).
@@ -38036,8 +38095,13 @@ Object.freeze({
38036
38095
  "network-access": "ingress",
38037
38096
  "smtp-provider": "email"
38038
38097
  });
38039
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
38098
+ /** Schema defaults an untouched sub-field must author exactly these. */
38099
+ var NC_AUDIO_DEFAULTS = {
38100
+ hitPercent: 60,
38101
+ samplingSeconds: 10
38102
+ };
38040
38103
  new Set(["devices", "classes"]);
38104
+ NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
38041
38105
  /**
38042
38106
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
38043
38107
  *
@@ -38269,6 +38333,39 @@ TimelapseRuleInputSchema.extend({
38269
38333
  createdAt: number(),
38270
38334
  updatedAt: number()
38271
38335
  });
38336
+ Object.freeze(Object.fromEntries([{
38337
+ stepId: "face-embedding",
38338
+ label: "Face recognition model",
38339
+ defaultModelId: "arcface-r100",
38340
+ indexName: "the enrolled face gallery",
38341
+ options: [
38342
+ {
38343
+ id: "arcface-r100",
38344
+ label: "ArcFace (ResNet34)"
38345
+ },
38346
+ {
38347
+ id: "auraface-r100",
38348
+ label: "AuraFace R100"
38349
+ },
38350
+ {
38351
+ id: "inception-resnet-v1",
38352
+ label: "Inception ResNet V1"
38353
+ }
38354
+ ]
38355
+ }, {
38356
+ stepId: "clip-embedding",
38357
+ label: "Semantic search model",
38358
+ defaultModelId: "mobileclip-s1",
38359
+ indexName: "the object semantic-search index",
38360
+ options: [{
38361
+ id: "mobileclip-s1",
38362
+ label: "MobileCLIP S1"
38363
+ }, {
38364
+ id: "mobileclip-s2",
38365
+ label: "MobileCLIP S2"
38366
+ }]
38367
+ }].map((s) => [s.stepId, s.defaultModelId])));
38368
+ string().min(1);
38272
38369
  object({
38273
38370
  /**
38274
38371
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38289,10 +38386,21 @@ object({
38289
38386
  */
38290
38387
  square: boolean()
38291
38388
  });
38292
- ({
38389
+ /**
38390
+ * The convention in force when nobody has configured one — byte-for-byte the
38391
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38392
+ */
38393
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38293
38394
  paddingRatio: .15,
38294
38395
  square: false
38295
- }).paddingRatio;
38396
+ };
38397
+ Object.freeze({
38398
+ x: 0,
38399
+ y: 0,
38400
+ w: 1,
38401
+ h: 1
38402
+ });
38403
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38296
38404
  /**
38297
38405
  * WHICH delivered frames the decode worker retains a native copy of.
38298
38406
  *
package/dist/addon.mjs CHANGED
@@ -8550,7 +8550,7 @@ var RecordingBandSchema = object({
8550
8550
  postBufferSec: number().min(0).optional()
8551
8551
  });
8552
8552
  ({
8553
- preBufferSec: 10,
8553
+ preBufferSec: 15,
8554
8554
  postBufferSec: 30
8555
8555
  }).postBufferSec * 1e3;
8556
8556
  /**
@@ -14753,128 +14753,103 @@ var COCO_TO_MACRO = {
14753
14753
  var AUDIO_MACRO_LABELS = [
14754
14754
  {
14755
14755
  id: "speech",
14756
- name: "Speech",
14757
- icon: "🗣️"
14756
+ name: "Speech"
14758
14757
  },
14759
14758
  {
14760
14759
  id: "scream",
14761
- name: "Scream / Shout",
14762
- icon: "😱"
14760
+ name: "Scream / Shout"
14763
14761
  },
14764
14762
  {
14765
14763
  id: "crying",
14766
- name: "Crying / Baby",
14767
- icon: "😢"
14764
+ name: "Crying / Baby"
14768
14765
  },
14769
14766
  {
14770
14767
  id: "laughter",
14771
- name: "Laughter",
14772
- icon: "😂"
14768
+ name: "Laughter"
14773
14769
  },
14774
14770
  {
14775
14771
  id: "music",
14776
- name: "Music",
14777
- icon: "🎵"
14772
+ name: "Music"
14778
14773
  },
14779
14774
  {
14780
14775
  id: "dog",
14781
- name: "Dog",
14782
- icon: "🐕"
14776
+ name: "Dog"
14783
14777
  },
14784
14778
  {
14785
14779
  id: "cat",
14786
- name: "Cat",
14787
- icon: "🐈"
14780
+ name: "Cat"
14788
14781
  },
14789
14782
  {
14790
14783
  id: "bird",
14791
- name: "Bird",
14792
- icon: "🐦"
14784
+ name: "Bird"
14793
14785
  },
14794
14786
  {
14795
14787
  id: "animal",
14796
- name: "Animal (other)",
14797
- icon: "🐾"
14788
+ name: "Animal (other)"
14798
14789
  },
14799
14790
  {
14800
14791
  id: "alarm",
14801
- name: "Alarm / Siren",
14802
- icon: "🚨"
14792
+ name: "Alarm / Siren"
14803
14793
  },
14804
14794
  {
14805
14795
  id: "doorbell",
14806
- name: "Doorbell / Knock",
14807
- icon: "🔔"
14796
+ name: "Doorbell / Knock"
14808
14797
  },
14809
14798
  {
14810
14799
  id: "glass_breaking",
14811
- name: "Glass Breaking",
14812
- icon: "💥"
14800
+ name: "Glass Breaking"
14813
14801
  },
14814
14802
  {
14815
14803
  id: "gunshot",
14816
- name: "Gunshot / Explosion",
14817
- icon: "💣"
14804
+ name: "Gunshot / Explosion"
14818
14805
  },
14819
14806
  {
14820
14807
  id: "vehicle",
14821
- name: "Vehicle",
14822
- icon: "🚗"
14808
+ name: "Vehicle"
14823
14809
  },
14824
14810
  {
14825
14811
  id: "siren",
14826
- name: "Emergency Siren",
14827
- icon: "🚑"
14812
+ name: "Emergency Siren"
14828
14813
  },
14829
14814
  {
14830
14815
  id: "fire",
14831
- name: "Fire / Smoke",
14832
- icon: "🔥"
14816
+ name: "Fire / Smoke"
14833
14817
  },
14834
14818
  {
14835
14819
  id: "water",
14836
- name: "Water",
14837
- icon: "💧"
14820
+ name: "Water"
14838
14821
  },
14839
14822
  {
14840
14823
  id: "wind",
14841
- name: "Wind / Weather",
14842
- icon: "🌬️"
14824
+ name: "Wind / Weather"
14843
14825
  },
14844
14826
  {
14845
14827
  id: "door",
14846
- name: "Door",
14847
- icon: "🚪"
14828
+ name: "Door"
14848
14829
  },
14849
14830
  {
14850
14831
  id: "footsteps",
14851
- name: "Footsteps",
14852
- icon: "👣"
14832
+ name: "Footsteps"
14853
14833
  },
14854
14834
  {
14855
14835
  id: "crowd",
14856
- name: "Crowd / Chatter",
14857
- icon: "👥"
14836
+ name: "Crowd / Chatter"
14858
14837
  },
14859
14838
  {
14860
14839
  id: "telephone",
14861
- name: "Telephone",
14862
- icon: "📞"
14840
+ name: "Telephone"
14863
14841
  },
14864
14842
  {
14865
14843
  id: "engine",
14866
- name: "Engine / Motor",
14867
- icon: "⚙️"
14844
+ name: "Engine / Motor"
14868
14845
  },
14869
14846
  {
14870
14847
  id: "tools",
14871
- name: "Tools / Construction",
14872
- icon: "🔨"
14848
+ name: "Tools / Construction"
14873
14849
  },
14874
14850
  {
14875
14851
  id: "silence",
14876
- name: "Silence",
14877
- icon: "🤫"
14852
+ name: "Silence"
14878
14853
  }
14879
14854
  ];
14880
14855
  var YAMNET_TO_MACRO = {
@@ -17376,7 +17351,7 @@ var TrackEnvelopeSchema = object({
17376
17351
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
17377
17352
  * keeps every scalar the list surfaces actually render (ids, class(es),
17378
17353
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
17379
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
17354
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
17380
17355
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
17381
17356
  * `getTrack`. Mirrors the event-store `projection` convention
17382
17357
  * (`getObjectEvents` et al.).
@@ -17671,6 +17646,35 @@ var TrackSchema = object({
17671
17646
  */
17672
17647
  hasFace: boolean().optional(),
17673
17648
  /**
17649
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
17650
+ * face an operator could ASSIGN to an identity.
17651
+ *
17652
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
17653
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
17654
+ * branch, before every gate, and means no more than "a face detector produced
17655
+ * a face detail". This one is stamped at the single moment the gallery row
17656
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
17657
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
17658
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
17659
+ * the crop-store drop. Everything between the detector and that insert can
17660
+ * legitimately refuse the face, so a flag written any earlier promises the
17661
+ * operator something to assign and delivers nothing.
17662
+ *
17663
+ * **Independent of recognition.** A face collected but never auto-matched is
17664
+ * still assignable — it is in fact the face an operator most wants to reach —
17665
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
17666
+ * `subLabel`; this says only that the raw material exists.
17667
+ *
17668
+ * **Set once, never cleared.** A track that produced a gallery row produced
17669
+ * one; deleting the row later is the gallery's business, not this flag's.
17670
+ *
17671
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
17672
+ * before the column omits it, and so does every server that predates the
17673
+ * field. A consumer must test `=== true` and render nothing otherwise —
17674
+ * never infer "no assignable face".
17675
+ */
17676
+ hasEmbeddedFace: boolean().optional(),
17677
+ /**
17674
17678
  * This subject CONTAINS a folded rider — a person the rider-pairing step
17675
17679
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
17676
17680
  * so the passage is tracked once and as a VEHICLE.
@@ -19060,6 +19064,32 @@ var DetailResultSchema = object({
19060
19064
  /** FRAME-space bbox (already mapped back from crop space). */
19061
19065
  bbox: NativeCropBboxSchema.optional(),
19062
19066
  embedding: string().optional(),
19067
+ /**
19068
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
19069
+ * so an older runner that does not send it degrades to "unmeasurable", which
19070
+ * the consuming gate treats as accept — never as reject.
19071
+ */
19072
+ embeddingMagnitude: number().optional(),
19073
+ /**
19074
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
19075
+ * `label`, and for the same reason: the producing node is the only side that
19076
+ * knows which model actually ran, and re-deriving it downstream is exactly
19077
+ * the inference this model exists to forbid.
19078
+ *
19079
+ * Additive and optional, so a runner that predates it degrades to
19080
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
19081
+ * its previous assumption instead of refusing.
19082
+ *
19083
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
19084
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
19085
+ * with nothing on the wire it took the id from the GALLERY, so the gate
19086
+ * compared the gallery against itself and could never fire. That turned the
19087
+ * one guard protecting the face feature space into a tautology, and it is
19088
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
19089
+ * gallery as the catalog comment predicts — it would have scored the new
19090
+ * model's vectors against the old model's samples in one cosine space.
19091
+ */
19092
+ embeddingModelId: string().optional(),
19063
19093
  label: string().optional(),
19064
19094
  /**
19065
19095
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -20470,6 +20500,7 @@ DeviceType.Camera, method(object({
20470
20500
  isBattery: boolean(),
20471
20501
  reason: _enum([
20472
20502
  "disabled",
20503
+ "offline",
20473
20504
  "sleeping",
20474
20505
  "unreachable",
20475
20506
  "waking"
@@ -20515,9 +20546,17 @@ targets: array(object({
20515
20546
  sleeping: boolean(),
20516
20547
  /** Current device state rendered over the cached frame. State images
20517
20548
  * remain authoritative even when their photographic background is
20518
- * old; null means the link must carry a current camera frame. */
20549
+ * old; null means the link must carry a current camera frame.
20550
+ *
20551
+ * **This is the ONLY source a surface may derive an inactive tile
20552
+ * from.** A client that reads a device's `online`/`disabled` flags and
20553
+ * draws its own verdict will disagree with the pixels it is painting
20554
+ * — the frame comes from here, and so must the sentence over it
20555
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
20556
+ * state. */
20519
20557
  stateReason: _enum([
20520
20558
  "disabled",
20559
+ "offline",
20521
20560
  "sleeping",
20522
20561
  "unreachable",
20523
20562
  "waking"
@@ -27236,9 +27275,21 @@ var RecordingLocationUsageSchema = object({
27236
27275
  * previously rendered as two identical "disks" with a nonsensical used
27237
27276
  * split — hydrate attribution across aliases is arbitrary by nature. */
27238
27277
  locationIds: array(string()).optional(),
27239
- /** Bytes of recordings stored on this PHYSICAL volume (all aliases). */
27278
+ /**
27279
+ * Filesystem root of the physical volume this row accounts — the only
27280
+ * operator-legible identity when a location has no `displayName` yet, and
27281
+ * what a "which disk is full" alert has to name. OPTIONAL for the same
27282
+ * train-skew reason as `oldestMs`: a recorder that predates the field omits
27283
+ * it, and this schema must keep validating that older provider's payload.
27284
+ */
27285
+ root: string().optional(),
27286
+ /** Bytes of recordings stored on this PHYSICAL volume (all aliases). This is
27287
+ * the footage's SHARE of the volume, not how full the volume is — the disk
27288
+ * also holds everything that is not recordings. Volume fill is
27289
+ * `(totalBytes - availableBytes) / totalBytes`. */
27240
27290
  usedBytes: number(),
27241
- /** Free bytes on the location's volume; null when capacity is unknown (remote). */
27291
+ /** Free bytes on the location's volume; null when capacity is unknown
27292
+ * (remote/unstattable — the recorder logs the statfs failure with the root). */
27242
27293
  availableBytes: number().nullable(),
27243
27294
  /** Total bytes of the location's volume; null when unknown. */
27244
27295
  totalBytes: number().nullable()
@@ -27250,7 +27301,15 @@ var RecordingStorageUsageSchema = object({
27250
27301
  nodeId: string(),
27251
27302
  totalUsedBytes: number(),
27252
27303
  devices: array(RecordingDeviceUsageSchema),
27253
- locations: array(RecordingLocationUsageSchema)
27304
+ /**
27305
+ * Per-physical-volume capacity. OPTIONAL, and consumers must render the rest
27306
+ * of the payload without it: the hub serves this method through a BAKED
27307
+ * router, so a hub whose framework train predates a change to this array
27308
+ * strips it out of an answer a newer recorder happily produced. A UI that
27309
+ * dereferences it unconditionally crashes the whole Storage page over a
27310
+ * missing disk bar.
27311
+ */
27312
+ locations: array(RecordingLocationUsageSchema).optional()
27254
27313
  });
27255
27314
  /**
27256
27315
  * The OPERATOR-ARMED half of multi-location recordings (D116).
@@ -38035,8 +38094,13 @@ Object.freeze({
38035
38094
  "network-access": "ingress",
38036
38095
  "smtp-provider": "email"
38037
38096
  });
38038
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
38097
+ /** Schema defaults an untouched sub-field must author exactly these. */
38098
+ var NC_AUDIO_DEFAULTS = {
38099
+ hitPercent: 60,
38100
+ samplingSeconds: 10
38101
+ };
38039
38102
  new Set(["devices", "classes"]);
38103
+ NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
38040
38104
  /**
38041
38105
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
38042
38106
  *
@@ -38268,6 +38332,39 @@ TimelapseRuleInputSchema.extend({
38268
38332
  createdAt: number(),
38269
38333
  updatedAt: number()
38270
38334
  });
38335
+ Object.freeze(Object.fromEntries([{
38336
+ stepId: "face-embedding",
38337
+ label: "Face recognition model",
38338
+ defaultModelId: "arcface-r100",
38339
+ indexName: "the enrolled face gallery",
38340
+ options: [
38341
+ {
38342
+ id: "arcface-r100",
38343
+ label: "ArcFace (ResNet34)"
38344
+ },
38345
+ {
38346
+ id: "auraface-r100",
38347
+ label: "AuraFace R100"
38348
+ },
38349
+ {
38350
+ id: "inception-resnet-v1",
38351
+ label: "Inception ResNet V1"
38352
+ }
38353
+ ]
38354
+ }, {
38355
+ stepId: "clip-embedding",
38356
+ label: "Semantic search model",
38357
+ defaultModelId: "mobileclip-s1",
38358
+ indexName: "the object semantic-search index",
38359
+ options: [{
38360
+ id: "mobileclip-s1",
38361
+ label: "MobileCLIP S1"
38362
+ }, {
38363
+ id: "mobileclip-s2",
38364
+ label: "MobileCLIP S2"
38365
+ }]
38366
+ }].map((s) => [s.stepId, s.defaultModelId])));
38367
+ string().min(1);
38271
38368
  object({
38272
38369
  /**
38273
38370
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38288,10 +38385,21 @@ object({
38288
38385
  */
38289
38386
  square: boolean()
38290
38387
  });
38291
- ({
38388
+ /**
38389
+ * The convention in force when nobody has configured one — byte-for-byte the
38390
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38391
+ */
38392
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38292
38393
  paddingRatio: .15,
38293
38394
  square: false
38294
- }).paddingRatio;
38395
+ };
38396
+ Object.freeze({
38397
+ x: 0,
38398
+ y: 0,
38399
+ w: 1,
38400
+ h: 1
38401
+ });
38402
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38295
38403
  /**
38296
38404
  * WHICH delivered frames the decode worker retains a native copy of.
38297
38405
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",