@camstack/addon-provider-rademacher 0.2.22 → 0.2.24

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 +204 -61
  2. package/dist/addon.mjs +204 -61
  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
@@ -19936,6 +19966,12 @@ var CameraStatusSchema = object({
19936
19966
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19937
19967
  fetchedAt: number()
19938
19968
  });
19969
+ var InferenceDeviceExclusionReasonSchema = _enum([
19970
+ "disabled",
19971
+ "unavailable",
19972
+ "cannot-host-camera-root",
19973
+ "accelerator-preferred"
19974
+ ]);
19939
19975
  var NodeInferenceDeviceSchema = object({
19940
19976
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
19941
19977
  key: string(),
@@ -19966,7 +20002,17 @@ var NodeInferenceDeviceSchema = object({
19966
20002
  * available per format; this is the stored selection that becomes the
19967
20003
  * default for EVERY camera landing on this accelerator.
19968
20004
  */
19969
- steps: record(string(), DeviceStepConfigSchema).optional()
20005
+ steps: record(string(), DeviceStepConfigSchema).optional(),
20006
+ /**
20007
+ * `null` when the device IS a camera-root candidate on this node; otherwise
20008
+ * the reason the dispatcher drops it. Computed by the SAME
20009
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
20010
+ * never disagree with the election — deriving it in the UI from
20011
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
20012
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
20013
+ * "an accelerator is serving" predicate).
20014
+ */
20015
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
19970
20016
  });
19971
20017
  var NodeInferenceDevicesSchema = object({
19972
20018
  nodeId: string(),
@@ -20471,6 +20517,7 @@ DeviceType.Camera, method(object({
20471
20517
  isBattery: boolean(),
20472
20518
  reason: _enum([
20473
20519
  "disabled",
20520
+ "offline",
20474
20521
  "sleeping",
20475
20522
  "unreachable",
20476
20523
  "waking"
@@ -20516,9 +20563,17 @@ targets: array(object({
20516
20563
  sleeping: boolean(),
20517
20564
  /** Current device state rendered over the cached frame. State images
20518
20565
  * remain authoritative even when their photographic background is
20519
- * old; null means the link must carry a current camera frame. */
20566
+ * old; null means the link must carry a current camera frame.
20567
+ *
20568
+ * **This is the ONLY source a surface may derive an inactive tile
20569
+ * from.** A client that reads a device's `online`/`disabled` flags and
20570
+ * draws its own verdict will disagree with the pixels it is painting
20571
+ * — the frame comes from here, and so must the sentence over it
20572
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
20573
+ * state. */
20520
20574
  stateReason: _enum([
20521
20575
  "disabled",
20576
+ "offline",
20522
20577
  "sleeping",
20523
20578
  "unreachable",
20524
20579
  "waking"
@@ -24808,7 +24863,12 @@ var ListResultSchema = object({
24808
24863
  probedAt: number()
24809
24864
  });
24810
24865
  var PreferredSchema = LocalInterfaceSchema.nullable();
24811
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
24866
+ /**
24867
+ * Candidate base URL for the SDK to race on connect. Order matters —
24868
+ * the SDK should attempt these top-to-bottom with a short per-candidate
24869
+ * timeout (e.g. 1500ms) and cache the winner for the session.
24870
+ */
24871
+ var ConnectionEndpointSchema = object({
24812
24872
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
24813
24873
  label: string(),
24814
24874
  /** Fully-formed base URL with scheme + host + port. */
@@ -24851,7 +24911,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
24851
24911
  * ordering between polls.
24852
24912
  */
24853
24913
  priority: number()
24854
- })).readonly() });
24914
+ });
24915
+ /**
24916
+ * Where the advertised local port came from. Ordered most → least
24917
+ * authoritative, and the whole point of returning it: a client must be able to
24918
+ * tell a FACT about the hub's socket from an echo of its own guess.
24919
+ */
24920
+ var LocalPortSourceEnum = _enum([
24921
+ "server-config",
24922
+ "server-env",
24923
+ "caller-hint",
24924
+ "default"
24925
+ ]);
24926
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
24927
+ var AdvertisedLocalPortSchema = object({
24928
+ port: number().int().min(1).max(65535),
24929
+ source: LocalPortSourceEnum
24930
+ });
24931
+ var GetConnectionEndpointsResultSchema = object({
24932
+ endpoints: array(ConnectionEndpointSchema).readonly(),
24933
+ /**
24934
+ * The port the hub built the LAN/loopback URLs with, and where that number
24935
+ * came from.
24936
+ *
24937
+ * Returned rather than merely applied, because "the URL is right" and "the
24938
+ * client can KNOW the URL is right" are different properties. A client that
24939
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
24940
+ * a hub that echoed the port the client sent, so it cannot decide whether to
24941
+ * race the candidate or discard it. With `source` it can: anything but
24942
+ * `caller-hint` is the hub's own socket.
24943
+ *
24944
+ * Absent on hubs predating this field — a client that finds it missing is
24945
+ * talking to an echoing hub and must degrade exactly as it does for
24946
+ * `caller-hint`.
24947
+ */
24948
+ localPort: AdvertisedLocalPortSchema
24949
+ });
24855
24950
  /**
24856
24951
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
24857
24952
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -24872,8 +24967,13 @@ var AllowedAddressesSchema = object({
24872
24967
  */
24873
24968
  addresses: array(string()).readonly() });
24874
24969
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24875
- /** Local hub HTTP port to use in base URLs. */
24876
- port: number().int().min(1).max(65535),
24970
+ /**
24971
+ * LEGACY HINT — do not send from new code. Kept optional so clients
24972
+ * written against the echoing contract keep working; the hub uses it
24973
+ * only when it cannot read its own port, and says so via
24974
+ * `localPort.source === 'caller-hint'`.
24975
+ */
24976
+ port: number().int().min(1).max(65535).optional(),
24877
24977
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24878
24978
  * candidate. Default `true`. */
24879
24979
  includeLoopback: boolean().optional(),
@@ -38061,7 +38161,6 @@ var NC_AUDIO_DEFAULTS = {
38061
38161
  hitPercent: 60,
38062
38162
  samplingSeconds: 10
38063
38163
  };
38064
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
38065
38164
  new Set(["devices", "classes"]);
38066
38165
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
38067
38166
  /**
@@ -38295,6 +38394,39 @@ TimelapseRuleInputSchema.extend({
38295
38394
  createdAt: number(),
38296
38395
  updatedAt: number()
38297
38396
  });
38397
+ Object.freeze(Object.fromEntries([{
38398
+ stepId: "face-embedding",
38399
+ label: "Face recognition model",
38400
+ defaultModelId: "arcface-r100",
38401
+ indexName: "the enrolled face gallery",
38402
+ options: [
38403
+ {
38404
+ id: "arcface-r100",
38405
+ label: "ArcFace (ResNet34)"
38406
+ },
38407
+ {
38408
+ id: "auraface-r100",
38409
+ label: "AuraFace R100"
38410
+ },
38411
+ {
38412
+ id: "inception-resnet-v1",
38413
+ label: "Inception ResNet V1"
38414
+ }
38415
+ ]
38416
+ }, {
38417
+ stepId: "clip-embedding",
38418
+ label: "Semantic search model",
38419
+ defaultModelId: "mobileclip-s1",
38420
+ indexName: "the object semantic-search index",
38421
+ options: [{
38422
+ id: "mobileclip-s1",
38423
+ label: "MobileCLIP S1"
38424
+ }, {
38425
+ id: "mobileclip-s2",
38426
+ label: "MobileCLIP S2"
38427
+ }]
38428
+ }].map((s) => [s.stepId, s.defaultModelId])));
38429
+ string().min(1);
38298
38430
  object({
38299
38431
  /**
38300
38432
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38315,10 +38447,21 @@ object({
38315
38447
  */
38316
38448
  square: boolean()
38317
38449
  });
38318
- ({
38450
+ /**
38451
+ * The convention in force when nobody has configured one — byte-for-byte the
38452
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38453
+ */
38454
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38319
38455
  paddingRatio: .15,
38320
38456
  square: false
38321
- }).paddingRatio;
38457
+ };
38458
+ Object.freeze({
38459
+ x: 0,
38460
+ y: 0,
38461
+ w: 1,
38462
+ h: 1
38463
+ });
38464
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38322
38465
  /**
38323
38466
  * WHICH delivered frames the decode worker retains a native copy of.
38324
38467
  *
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
@@ -19935,6 +19965,12 @@ var CameraStatusSchema = object({
19935
19965
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19936
19966
  fetchedAt: number()
19937
19967
  });
19968
+ var InferenceDeviceExclusionReasonSchema = _enum([
19969
+ "disabled",
19970
+ "unavailable",
19971
+ "cannot-host-camera-root",
19972
+ "accelerator-preferred"
19973
+ ]);
19938
19974
  var NodeInferenceDeviceSchema = object({
19939
19975
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
19940
19976
  key: string(),
@@ -19965,7 +20001,17 @@ var NodeInferenceDeviceSchema = object({
19965
20001
  * available per format; this is the stored selection that becomes the
19966
20002
  * default for EVERY camera landing on this accelerator.
19967
20003
  */
19968
- steps: record(string(), DeviceStepConfigSchema).optional()
20004
+ steps: record(string(), DeviceStepConfigSchema).optional(),
20005
+ /**
20006
+ * `null` when the device IS a camera-root candidate on this node; otherwise
20007
+ * the reason the dispatcher drops it. Computed by the SAME
20008
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
20009
+ * never disagree with the election — deriving it in the UI from
20010
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
20011
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
20012
+ * "an accelerator is serving" predicate).
20013
+ */
20014
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
19969
20015
  });
19970
20016
  var NodeInferenceDevicesSchema = object({
19971
20017
  nodeId: string(),
@@ -20470,6 +20516,7 @@ DeviceType.Camera, method(object({
20470
20516
  isBattery: boolean(),
20471
20517
  reason: _enum([
20472
20518
  "disabled",
20519
+ "offline",
20473
20520
  "sleeping",
20474
20521
  "unreachable",
20475
20522
  "waking"
@@ -20515,9 +20562,17 @@ targets: array(object({
20515
20562
  sleeping: boolean(),
20516
20563
  /** Current device state rendered over the cached frame. State images
20517
20564
  * remain authoritative even when their photographic background is
20518
- * old; null means the link must carry a current camera frame. */
20565
+ * old; null means the link must carry a current camera frame.
20566
+ *
20567
+ * **This is the ONLY source a surface may derive an inactive tile
20568
+ * from.** A client that reads a device's `online`/`disabled` flags and
20569
+ * draws its own verdict will disagree with the pixels it is painting
20570
+ * — the frame comes from here, and so must the sentence over it
20571
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
20572
+ * state. */
20519
20573
  stateReason: _enum([
20520
20574
  "disabled",
20575
+ "offline",
20521
20576
  "sleeping",
20522
20577
  "unreachable",
20523
20578
  "waking"
@@ -24807,7 +24862,12 @@ var ListResultSchema = object({
24807
24862
  probedAt: number()
24808
24863
  });
24809
24864
  var PreferredSchema = LocalInterfaceSchema.nullable();
24810
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
24865
+ /**
24866
+ * Candidate base URL for the SDK to race on connect. Order matters —
24867
+ * the SDK should attempt these top-to-bottom with a short per-candidate
24868
+ * timeout (e.g. 1500ms) and cache the winner for the session.
24869
+ */
24870
+ var ConnectionEndpointSchema = object({
24811
24871
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
24812
24872
  label: string(),
24813
24873
  /** Fully-formed base URL with scheme + host + port. */
@@ -24850,7 +24910,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
24850
24910
  * ordering between polls.
24851
24911
  */
24852
24912
  priority: number()
24853
- })).readonly() });
24913
+ });
24914
+ /**
24915
+ * Where the advertised local port came from. Ordered most → least
24916
+ * authoritative, and the whole point of returning it: a client must be able to
24917
+ * tell a FACT about the hub's socket from an echo of its own guess.
24918
+ */
24919
+ var LocalPortSourceEnum = _enum([
24920
+ "server-config",
24921
+ "server-env",
24922
+ "caller-hint",
24923
+ "default"
24924
+ ]);
24925
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
24926
+ var AdvertisedLocalPortSchema = object({
24927
+ port: number().int().min(1).max(65535),
24928
+ source: LocalPortSourceEnum
24929
+ });
24930
+ var GetConnectionEndpointsResultSchema = object({
24931
+ endpoints: array(ConnectionEndpointSchema).readonly(),
24932
+ /**
24933
+ * The port the hub built the LAN/loopback URLs with, and where that number
24934
+ * came from.
24935
+ *
24936
+ * Returned rather than merely applied, because "the URL is right" and "the
24937
+ * client can KNOW the URL is right" are different properties. A client that
24938
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
24939
+ * a hub that echoed the port the client sent, so it cannot decide whether to
24940
+ * race the candidate or discard it. With `source` it can: anything but
24941
+ * `caller-hint` is the hub's own socket.
24942
+ *
24943
+ * Absent on hubs predating this field — a client that finds it missing is
24944
+ * talking to an echoing hub and must degrade exactly as it does for
24945
+ * `caller-hint`.
24946
+ */
24947
+ localPort: AdvertisedLocalPortSchema
24948
+ });
24854
24949
  /**
24855
24950
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
24856
24951
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -24871,8 +24966,13 @@ var AllowedAddressesSchema = object({
24871
24966
  */
24872
24967
  addresses: array(string()).readonly() });
24873
24968
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
24874
- /** Local hub HTTP port to use in base URLs. */
24875
- port: number().int().min(1).max(65535),
24969
+ /**
24970
+ * LEGACY HINT — do not send from new code. Kept optional so clients
24971
+ * written against the echoing contract keep working; the hub uses it
24972
+ * only when it cannot read its own port, and says so via
24973
+ * `localPort.source === 'caller-hint'`.
24974
+ */
24975
+ port: number().int().min(1).max(65535).optional(),
24876
24976
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
24877
24977
  * candidate. Default `true`. */
24878
24978
  includeLoopback: boolean().optional(),
@@ -38060,7 +38160,6 @@ var NC_AUDIO_DEFAULTS = {
38060
38160
  hitPercent: 60,
38061
38161
  samplingSeconds: 10
38062
38162
  };
38063
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
38064
38163
  new Set(["devices", "classes"]);
38065
38164
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
38066
38165
  /**
@@ -38294,6 +38393,39 @@ TimelapseRuleInputSchema.extend({
38294
38393
  createdAt: number(),
38295
38394
  updatedAt: number()
38296
38395
  });
38396
+ Object.freeze(Object.fromEntries([{
38397
+ stepId: "face-embedding",
38398
+ label: "Face recognition model",
38399
+ defaultModelId: "arcface-r100",
38400
+ indexName: "the enrolled face gallery",
38401
+ options: [
38402
+ {
38403
+ id: "arcface-r100",
38404
+ label: "ArcFace (ResNet34)"
38405
+ },
38406
+ {
38407
+ id: "auraface-r100",
38408
+ label: "AuraFace R100"
38409
+ },
38410
+ {
38411
+ id: "inception-resnet-v1",
38412
+ label: "Inception ResNet V1"
38413
+ }
38414
+ ]
38415
+ }, {
38416
+ stepId: "clip-embedding",
38417
+ label: "Semantic search model",
38418
+ defaultModelId: "mobileclip-s1",
38419
+ indexName: "the object semantic-search index",
38420
+ options: [{
38421
+ id: "mobileclip-s1",
38422
+ label: "MobileCLIP S1"
38423
+ }, {
38424
+ id: "mobileclip-s2",
38425
+ label: "MobileCLIP S2"
38426
+ }]
38427
+ }].map((s) => [s.stepId, s.defaultModelId])));
38428
+ string().min(1);
38297
38429
  object({
38298
38430
  /**
38299
38431
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38314,10 +38446,21 @@ object({
38314
38446
  */
38315
38447
  square: boolean()
38316
38448
  });
38317
- ({
38449
+ /**
38450
+ * The convention in force when nobody has configured one — byte-for-byte the
38451
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38452
+ */
38453
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38318
38454
  paddingRatio: .15,
38319
38455
  square: false
38320
- }).paddingRatio;
38456
+ };
38457
+ Object.freeze({
38458
+ x: 0,
38459
+ y: 0,
38460
+ w: 1,
38461
+ h: 1
38462
+ });
38463
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38321
38464
  /**
38322
38465
  * WHICH delivered frames the decode worker retains a native copy of.
38323
38466
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
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",