@camstack/addon-provider-petkit 0.2.23 → 0.2.25

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
@@ -8668,7 +8668,7 @@ var RecordingBandSchema = object({
8668
8668
  postBufferSec: number().min(0).optional()
8669
8669
  });
8670
8670
  ({
8671
- preBufferSec: 10,
8671
+ preBufferSec: 15,
8672
8672
  postBufferSec: 30
8673
8673
  }).postBufferSec * 1e3;
8674
8674
  /**
@@ -14888,128 +14888,103 @@ var COCO_TO_MACRO = {
14888
14888
  var AUDIO_MACRO_LABELS = [
14889
14889
  {
14890
14890
  id: "speech",
14891
- name: "Speech",
14892
- icon: "🗣️"
14891
+ name: "Speech"
14893
14892
  },
14894
14893
  {
14895
14894
  id: "scream",
14896
- name: "Scream / Shout",
14897
- icon: "😱"
14895
+ name: "Scream / Shout"
14898
14896
  },
14899
14897
  {
14900
14898
  id: "crying",
14901
- name: "Crying / Baby",
14902
- icon: "😢"
14899
+ name: "Crying / Baby"
14903
14900
  },
14904
14901
  {
14905
14902
  id: "laughter",
14906
- name: "Laughter",
14907
- icon: "😂"
14903
+ name: "Laughter"
14908
14904
  },
14909
14905
  {
14910
14906
  id: "music",
14911
- name: "Music",
14912
- icon: "🎵"
14907
+ name: "Music"
14913
14908
  },
14914
14909
  {
14915
14910
  id: "dog",
14916
- name: "Dog",
14917
- icon: "🐕"
14911
+ name: "Dog"
14918
14912
  },
14919
14913
  {
14920
14914
  id: "cat",
14921
- name: "Cat",
14922
- icon: "🐈"
14915
+ name: "Cat"
14923
14916
  },
14924
14917
  {
14925
14918
  id: "bird",
14926
- name: "Bird",
14927
- icon: "🐦"
14919
+ name: "Bird"
14928
14920
  },
14929
14921
  {
14930
14922
  id: "animal",
14931
- name: "Animal (other)",
14932
- icon: "🐾"
14923
+ name: "Animal (other)"
14933
14924
  },
14934
14925
  {
14935
14926
  id: "alarm",
14936
- name: "Alarm / Siren",
14937
- icon: "🚨"
14927
+ name: "Alarm / Siren"
14938
14928
  },
14939
14929
  {
14940
14930
  id: "doorbell",
14941
- name: "Doorbell / Knock",
14942
- icon: "🔔"
14931
+ name: "Doorbell / Knock"
14943
14932
  },
14944
14933
  {
14945
14934
  id: "glass_breaking",
14946
- name: "Glass Breaking",
14947
- icon: "💥"
14935
+ name: "Glass Breaking"
14948
14936
  },
14949
14937
  {
14950
14938
  id: "gunshot",
14951
- name: "Gunshot / Explosion",
14952
- icon: "💣"
14939
+ name: "Gunshot / Explosion"
14953
14940
  },
14954
14941
  {
14955
14942
  id: "vehicle",
14956
- name: "Vehicle",
14957
- icon: "🚗"
14943
+ name: "Vehicle"
14958
14944
  },
14959
14945
  {
14960
14946
  id: "siren",
14961
- name: "Emergency Siren",
14962
- icon: "🚑"
14947
+ name: "Emergency Siren"
14963
14948
  },
14964
14949
  {
14965
14950
  id: "fire",
14966
- name: "Fire / Smoke",
14967
- icon: "🔥"
14951
+ name: "Fire / Smoke"
14968
14952
  },
14969
14953
  {
14970
14954
  id: "water",
14971
- name: "Water",
14972
- icon: "💧"
14955
+ name: "Water"
14973
14956
  },
14974
14957
  {
14975
14958
  id: "wind",
14976
- name: "Wind / Weather",
14977
- icon: "🌬️"
14959
+ name: "Wind / Weather"
14978
14960
  },
14979
14961
  {
14980
14962
  id: "door",
14981
- name: "Door",
14982
- icon: "🚪"
14963
+ name: "Door"
14983
14964
  },
14984
14965
  {
14985
14966
  id: "footsteps",
14986
- name: "Footsteps",
14987
- icon: "👣"
14967
+ name: "Footsteps"
14988
14968
  },
14989
14969
  {
14990
14970
  id: "crowd",
14991
- name: "Crowd / Chatter",
14992
- icon: "👥"
14971
+ name: "Crowd / Chatter"
14993
14972
  },
14994
14973
  {
14995
14974
  id: "telephone",
14996
- name: "Telephone",
14997
- icon: "📞"
14975
+ name: "Telephone"
14998
14976
  },
14999
14977
  {
15000
14978
  id: "engine",
15001
- name: "Engine / Motor",
15002
- icon: "⚙️"
14979
+ name: "Engine / Motor"
15003
14980
  },
15004
14981
  {
15005
14982
  id: "tools",
15006
- name: "Tools / Construction",
15007
- icon: "🔨"
14983
+ name: "Tools / Construction"
15008
14984
  },
15009
14985
  {
15010
14986
  id: "silence",
15011
- name: "Silence",
15012
- icon: "🤫"
14987
+ name: "Silence"
15013
14988
  }
15014
14989
  ];
15015
14990
  var YAMNET_TO_MACRO = {
@@ -17511,7 +17486,7 @@ var TrackEnvelopeSchema = object({
17511
17486
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
17512
17487
  * keeps every scalar the list surfaces actually render (ids, class(es),
17513
17488
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
17514
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
17489
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
17515
17490
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
17516
17491
  * `getTrack`. Mirrors the event-store `projection` convention
17517
17492
  * (`getObjectEvents` et al.).
@@ -17806,6 +17781,35 @@ var TrackSchema = object({
17806
17781
  */
17807
17782
  hasFace: boolean().optional(),
17808
17783
  /**
17784
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
17785
+ * face an operator could ASSIGN to an identity.
17786
+ *
17787
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
17788
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
17789
+ * branch, before every gate, and means no more than "a face detector produced
17790
+ * a face detail". This one is stamped at the single moment the gallery row
17791
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
17792
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
17793
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
17794
+ * the crop-store drop. Everything between the detector and that insert can
17795
+ * legitimately refuse the face, so a flag written any earlier promises the
17796
+ * operator something to assign and delivers nothing.
17797
+ *
17798
+ * **Independent of recognition.** A face collected but never auto-matched is
17799
+ * still assignable — it is in fact the face an operator most wants to reach —
17800
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
17801
+ * `subLabel`; this says only that the raw material exists.
17802
+ *
17803
+ * **Set once, never cleared.** A track that produced a gallery row produced
17804
+ * one; deleting the row later is the gallery's business, not this flag's.
17805
+ *
17806
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
17807
+ * before the column omits it, and so does every server that predates the
17808
+ * field. A consumer must test `=== true` and render nothing otherwise —
17809
+ * never infer "no assignable face".
17810
+ */
17811
+ hasEmbeddedFace: boolean().optional(),
17812
+ /**
17809
17813
  * This subject CONTAINS a folded rider — a person the rider-pairing step
17810
17814
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
17811
17815
  * so the passage is tracked once and as a VEHICLE.
@@ -19195,6 +19199,32 @@ var DetailResultSchema = object({
19195
19199
  /** FRAME-space bbox (already mapped back from crop space). */
19196
19200
  bbox: NativeCropBboxSchema.optional(),
19197
19201
  embedding: string().optional(),
19202
+ /**
19203
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
19204
+ * so an older runner that does not send it degrades to "unmeasurable", which
19205
+ * the consuming gate treats as accept — never as reject.
19206
+ */
19207
+ embeddingMagnitude: number().optional(),
19208
+ /**
19209
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
19210
+ * `label`, and for the same reason: the producing node is the only side that
19211
+ * knows which model actually ran, and re-deriving it downstream is exactly
19212
+ * the inference this model exists to forbid.
19213
+ *
19214
+ * Additive and optional, so a runner that predates it degrades to
19215
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
19216
+ * its previous assumption instead of refusing.
19217
+ *
19218
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
19219
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
19220
+ * with nothing on the wire it took the id from the GALLERY, so the gate
19221
+ * compared the gallery against itself and could never fire. That turned the
19222
+ * one guard protecting the face feature space into a tautology, and it is
19223
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
19224
+ * gallery as the catalog comment predicts — it would have scored the new
19225
+ * model's vectors against the old model's samples in one cosine space.
19226
+ */
19227
+ embeddingModelId: string().optional(),
19198
19228
  label: string().optional(),
19199
19229
  /**
19200
19230
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -20070,6 +20100,12 @@ var CameraStatusSchema = object({
20070
20100
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20071
20101
  fetchedAt: number()
20072
20102
  });
20103
+ var InferenceDeviceExclusionReasonSchema = _enum([
20104
+ "disabled",
20105
+ "unavailable",
20106
+ "cannot-host-camera-root",
20107
+ "accelerator-preferred"
20108
+ ]);
20073
20109
  var NodeInferenceDeviceSchema = object({
20074
20110
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20075
20111
  key: string(),
@@ -20100,7 +20136,17 @@ var NodeInferenceDeviceSchema = object({
20100
20136
  * available per format; this is the stored selection that becomes the
20101
20137
  * default for EVERY camera landing on this accelerator.
20102
20138
  */
20103
- steps: record(string(), DeviceStepConfigSchema).optional()
20139
+ steps: record(string(), DeviceStepConfigSchema).optional(),
20140
+ /**
20141
+ * `null` when the device IS a camera-root candidate on this node; otherwise
20142
+ * the reason the dispatcher drops it. Computed by the SAME
20143
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
20144
+ * never disagree with the election — deriving it in the UI from
20145
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
20146
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
20147
+ * "an accelerator is serving" predicate).
20148
+ */
20149
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
20104
20150
  });
20105
20151
  var NodeInferenceDevicesSchema = object({
20106
20152
  nodeId: string(),
@@ -20605,6 +20651,7 @@ DeviceType.Camera, method(object({
20605
20651
  isBattery: boolean(),
20606
20652
  reason: _enum([
20607
20653
  "disabled",
20654
+ "offline",
20608
20655
  "sleeping",
20609
20656
  "unreachable",
20610
20657
  "waking"
@@ -20650,9 +20697,17 @@ targets: array(object({
20650
20697
  sleeping: boolean(),
20651
20698
  /** Current device state rendered over the cached frame. State images
20652
20699
  * remain authoritative even when their photographic background is
20653
- * old; null means the link must carry a current camera frame. */
20700
+ * old; null means the link must carry a current camera frame.
20701
+ *
20702
+ * **This is the ONLY source a surface may derive an inactive tile
20703
+ * from.** A client that reads a device's `online`/`disabled` flags and
20704
+ * draws its own verdict will disagree with the pixels it is painting
20705
+ * — the frame comes from here, and so must the sentence over it
20706
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
20707
+ * state. */
20654
20708
  stateReason: _enum([
20655
20709
  "disabled",
20710
+ "offline",
20656
20711
  "sleeping",
20657
20712
  "unreachable",
20658
20713
  "waking"
@@ -24950,7 +25005,12 @@ var ListResultSchema = object({
24950
25005
  probedAt: number()
24951
25006
  });
24952
25007
  var PreferredSchema = LocalInterfaceSchema.nullable();
24953
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
25008
+ /**
25009
+ * Candidate base URL for the SDK to race on connect. Order matters —
25010
+ * the SDK should attempt these top-to-bottom with a short per-candidate
25011
+ * timeout (e.g. 1500ms) and cache the winner for the session.
25012
+ */
25013
+ var ConnectionEndpointSchema = object({
24954
25014
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
24955
25015
  label: string(),
24956
25016
  /** Fully-formed base URL with scheme + host + port. */
@@ -24993,7 +25053,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
24993
25053
  * ordering between polls.
24994
25054
  */
24995
25055
  priority: number()
24996
- })).readonly() });
25056
+ });
25057
+ /**
25058
+ * Where the advertised local port came from. Ordered most → least
25059
+ * authoritative, and the whole point of returning it: a client must be able to
25060
+ * tell a FACT about the hub's socket from an echo of its own guess.
25061
+ */
25062
+ var LocalPortSourceEnum = _enum([
25063
+ "server-config",
25064
+ "server-env",
25065
+ "caller-hint",
25066
+ "default"
25067
+ ]);
25068
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
25069
+ var AdvertisedLocalPortSchema = object({
25070
+ port: number().int().min(1).max(65535),
25071
+ source: LocalPortSourceEnum
25072
+ });
25073
+ var GetConnectionEndpointsResultSchema = object({
25074
+ endpoints: array(ConnectionEndpointSchema).readonly(),
25075
+ /**
25076
+ * The port the hub built the LAN/loopback URLs with, and where that number
25077
+ * came from.
25078
+ *
25079
+ * Returned rather than merely applied, because "the URL is right" and "the
25080
+ * client can KNOW the URL is right" are different properties. A client that
25081
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
25082
+ * a hub that echoed the port the client sent, so it cannot decide whether to
25083
+ * race the candidate or discard it. With `source` it can: anything but
25084
+ * `caller-hint` is the hub's own socket.
25085
+ *
25086
+ * Absent on hubs predating this field — a client that finds it missing is
25087
+ * talking to an echoing hub and must degrade exactly as it does for
25088
+ * `caller-hint`.
25089
+ */
25090
+ localPort: AdvertisedLocalPortSchema
25091
+ });
24997
25092
  /**
24998
25093
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
24999
25094
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -25014,8 +25109,13 @@ var AllowedAddressesSchema = object({
25014
25109
  */
25015
25110
  addresses: array(string()).readonly() });
25016
25111
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
25017
- /** Local hub HTTP port to use in base URLs. */
25018
- port: number().int().min(1).max(65535),
25112
+ /**
25113
+ * LEGACY HINT — do not send from new code. Kept optional so clients
25114
+ * written against the echoing contract keep working; the hub uses it
25115
+ * only when it cannot read its own port, and says so via
25116
+ * `localPort.source === 'caller-hint'`.
25117
+ */
25118
+ port: number().int().min(1).max(65535).optional(),
25019
25119
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
25020
25120
  * candidate. Default `true`. */
25021
25121
  includeLoopback: boolean().optional(),
@@ -38203,7 +38303,6 @@ var NC_AUDIO_DEFAULTS = {
38203
38303
  hitPercent: 60,
38204
38304
  samplingSeconds: 10
38205
38305
  };
38206
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
38207
38306
  new Set(["devices", "classes"]);
38208
38307
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
38209
38308
  /**
@@ -38437,6 +38536,39 @@ TimelapseRuleInputSchema.extend({
38437
38536
  createdAt: number(),
38438
38537
  updatedAt: number()
38439
38538
  });
38539
+ Object.freeze(Object.fromEntries([{
38540
+ stepId: "face-embedding",
38541
+ label: "Face recognition model",
38542
+ defaultModelId: "arcface-r100",
38543
+ indexName: "the enrolled face gallery",
38544
+ options: [
38545
+ {
38546
+ id: "arcface-r100",
38547
+ label: "ArcFace (ResNet34)"
38548
+ },
38549
+ {
38550
+ id: "auraface-r100",
38551
+ label: "AuraFace R100"
38552
+ },
38553
+ {
38554
+ id: "inception-resnet-v1",
38555
+ label: "Inception ResNet V1"
38556
+ }
38557
+ ]
38558
+ }, {
38559
+ stepId: "clip-embedding",
38560
+ label: "Semantic search model",
38561
+ defaultModelId: "mobileclip-s1",
38562
+ indexName: "the object semantic-search index",
38563
+ options: [{
38564
+ id: "mobileclip-s1",
38565
+ label: "MobileCLIP S1"
38566
+ }, {
38567
+ id: "mobileclip-s2",
38568
+ label: "MobileCLIP S2"
38569
+ }]
38570
+ }].map((s) => [s.stepId, s.defaultModelId])));
38571
+ string().min(1);
38440
38572
  object({
38441
38573
  /**
38442
38574
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38457,10 +38589,21 @@ object({
38457
38589
  */
38458
38590
  square: boolean()
38459
38591
  });
38460
- ({
38592
+ /**
38593
+ * The convention in force when nobody has configured one — byte-for-byte the
38594
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38595
+ */
38596
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38461
38597
  paddingRatio: .15,
38462
38598
  square: false
38463
- }).paddingRatio;
38599
+ };
38600
+ Object.freeze({
38601
+ x: 0,
38602
+ y: 0,
38603
+ w: 1,
38604
+ h: 1
38605
+ });
38606
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38464
38607
  /**
38465
38608
  * WHICH delivered frames the decode worker retains a native copy of.
38466
38609
  *
package/dist/addon.mjs CHANGED
@@ -8667,7 +8667,7 @@ var RecordingBandSchema = object({
8667
8667
  postBufferSec: number().min(0).optional()
8668
8668
  });
8669
8669
  ({
8670
- preBufferSec: 10,
8670
+ preBufferSec: 15,
8671
8671
  postBufferSec: 30
8672
8672
  }).postBufferSec * 1e3;
8673
8673
  /**
@@ -14887,128 +14887,103 @@ var COCO_TO_MACRO = {
14887
14887
  var AUDIO_MACRO_LABELS = [
14888
14888
  {
14889
14889
  id: "speech",
14890
- name: "Speech",
14891
- icon: "🗣️"
14890
+ name: "Speech"
14892
14891
  },
14893
14892
  {
14894
14893
  id: "scream",
14895
- name: "Scream / Shout",
14896
- icon: "😱"
14894
+ name: "Scream / Shout"
14897
14895
  },
14898
14896
  {
14899
14897
  id: "crying",
14900
- name: "Crying / Baby",
14901
- icon: "😢"
14898
+ name: "Crying / Baby"
14902
14899
  },
14903
14900
  {
14904
14901
  id: "laughter",
14905
- name: "Laughter",
14906
- icon: "😂"
14902
+ name: "Laughter"
14907
14903
  },
14908
14904
  {
14909
14905
  id: "music",
14910
- name: "Music",
14911
- icon: "🎵"
14906
+ name: "Music"
14912
14907
  },
14913
14908
  {
14914
14909
  id: "dog",
14915
- name: "Dog",
14916
- icon: "🐕"
14910
+ name: "Dog"
14917
14911
  },
14918
14912
  {
14919
14913
  id: "cat",
14920
- name: "Cat",
14921
- icon: "🐈"
14914
+ name: "Cat"
14922
14915
  },
14923
14916
  {
14924
14917
  id: "bird",
14925
- name: "Bird",
14926
- icon: "🐦"
14918
+ name: "Bird"
14927
14919
  },
14928
14920
  {
14929
14921
  id: "animal",
14930
- name: "Animal (other)",
14931
- icon: "🐾"
14922
+ name: "Animal (other)"
14932
14923
  },
14933
14924
  {
14934
14925
  id: "alarm",
14935
- name: "Alarm / Siren",
14936
- icon: "🚨"
14926
+ name: "Alarm / Siren"
14937
14927
  },
14938
14928
  {
14939
14929
  id: "doorbell",
14940
- name: "Doorbell / Knock",
14941
- icon: "🔔"
14930
+ name: "Doorbell / Knock"
14942
14931
  },
14943
14932
  {
14944
14933
  id: "glass_breaking",
14945
- name: "Glass Breaking",
14946
- icon: "💥"
14934
+ name: "Glass Breaking"
14947
14935
  },
14948
14936
  {
14949
14937
  id: "gunshot",
14950
- name: "Gunshot / Explosion",
14951
- icon: "💣"
14938
+ name: "Gunshot / Explosion"
14952
14939
  },
14953
14940
  {
14954
14941
  id: "vehicle",
14955
- name: "Vehicle",
14956
- icon: "🚗"
14942
+ name: "Vehicle"
14957
14943
  },
14958
14944
  {
14959
14945
  id: "siren",
14960
- name: "Emergency Siren",
14961
- icon: "🚑"
14946
+ name: "Emergency Siren"
14962
14947
  },
14963
14948
  {
14964
14949
  id: "fire",
14965
- name: "Fire / Smoke",
14966
- icon: "🔥"
14950
+ name: "Fire / Smoke"
14967
14951
  },
14968
14952
  {
14969
14953
  id: "water",
14970
- name: "Water",
14971
- icon: "💧"
14954
+ name: "Water"
14972
14955
  },
14973
14956
  {
14974
14957
  id: "wind",
14975
- name: "Wind / Weather",
14976
- icon: "🌬️"
14958
+ name: "Wind / Weather"
14977
14959
  },
14978
14960
  {
14979
14961
  id: "door",
14980
- name: "Door",
14981
- icon: "🚪"
14962
+ name: "Door"
14982
14963
  },
14983
14964
  {
14984
14965
  id: "footsteps",
14985
- name: "Footsteps",
14986
- icon: "👣"
14966
+ name: "Footsteps"
14987
14967
  },
14988
14968
  {
14989
14969
  id: "crowd",
14990
- name: "Crowd / Chatter",
14991
- icon: "👥"
14970
+ name: "Crowd / Chatter"
14992
14971
  },
14993
14972
  {
14994
14973
  id: "telephone",
14995
- name: "Telephone",
14996
- icon: "📞"
14974
+ name: "Telephone"
14997
14975
  },
14998
14976
  {
14999
14977
  id: "engine",
15000
- name: "Engine / Motor",
15001
- icon: "⚙️"
14978
+ name: "Engine / Motor"
15002
14979
  },
15003
14980
  {
15004
14981
  id: "tools",
15005
- name: "Tools / Construction",
15006
- icon: "🔨"
14982
+ name: "Tools / Construction"
15007
14983
  },
15008
14984
  {
15009
14985
  id: "silence",
15010
- name: "Silence",
15011
- icon: "🤫"
14986
+ name: "Silence"
15012
14987
  }
15013
14988
  ];
15014
14989
  var YAMNET_TO_MACRO = {
@@ -17510,7 +17485,7 @@ var TrackEnvelopeSchema = object({
17510
17485
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
17511
17486
  * keeps every scalar the list surfaces actually render (ids, class(es),
17512
17487
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
17513
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
17488
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
17514
17489
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
17515
17490
  * `getTrack`. Mirrors the event-store `projection` convention
17516
17491
  * (`getObjectEvents` et al.).
@@ -17805,6 +17780,35 @@ var TrackSchema = object({
17805
17780
  */
17806
17781
  hasFace: boolean().optional(),
17807
17782
  /**
17783
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
17784
+ * face an operator could ASSIGN to an identity.
17785
+ *
17786
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
17787
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
17788
+ * branch, before every gate, and means no more than "a face detector produced
17789
+ * a face detail". This one is stamped at the single moment the gallery row
17790
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
17791
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
17792
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
17793
+ * the crop-store drop. Everything between the detector and that insert can
17794
+ * legitimately refuse the face, so a flag written any earlier promises the
17795
+ * operator something to assign and delivers nothing.
17796
+ *
17797
+ * **Independent of recognition.** A face collected but never auto-matched is
17798
+ * still assignable — it is in fact the face an operator most wants to reach —
17799
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
17800
+ * `subLabel`; this says only that the raw material exists.
17801
+ *
17802
+ * **Set once, never cleared.** A track that produced a gallery row produced
17803
+ * one; deleting the row later is the gallery's business, not this flag's.
17804
+ *
17805
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
17806
+ * before the column omits it, and so does every server that predates the
17807
+ * field. A consumer must test `=== true` and render nothing otherwise —
17808
+ * never infer "no assignable face".
17809
+ */
17810
+ hasEmbeddedFace: boolean().optional(),
17811
+ /**
17808
17812
  * This subject CONTAINS a folded rider — a person the rider-pairing step
17809
17813
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
17810
17814
  * so the passage is tracked once and as a VEHICLE.
@@ -19194,6 +19198,32 @@ var DetailResultSchema = object({
19194
19198
  /** FRAME-space bbox (already mapped back from crop space). */
19195
19199
  bbox: NativeCropBboxSchema.optional(),
19196
19200
  embedding: string().optional(),
19201
+ /**
19202
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
19203
+ * so an older runner that does not send it degrades to "unmeasurable", which
19204
+ * the consuming gate treats as accept — never as reject.
19205
+ */
19206
+ embeddingMagnitude: number().optional(),
19207
+ /**
19208
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
19209
+ * `label`, and for the same reason: the producing node is the only side that
19210
+ * knows which model actually ran, and re-deriving it downstream is exactly
19211
+ * the inference this model exists to forbid.
19212
+ *
19213
+ * Additive and optional, so a runner that predates it degrades to
19214
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
19215
+ * its previous assumption instead of refusing.
19216
+ *
19217
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
19218
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
19219
+ * with nothing on the wire it took the id from the GALLERY, so the gate
19220
+ * compared the gallery against itself and could never fire. That turned the
19221
+ * one guard protecting the face feature space into a tautology, and it is
19222
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
19223
+ * gallery as the catalog comment predicts — it would have scored the new
19224
+ * model's vectors against the old model's samples in one cosine space.
19225
+ */
19226
+ embeddingModelId: string().optional(),
19197
19227
  label: string().optional(),
19198
19228
  /**
19199
19229
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -20069,6 +20099,12 @@ var CameraStatusSchema = object({
20069
20099
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20070
20100
  fetchedAt: number()
20071
20101
  });
20102
+ var InferenceDeviceExclusionReasonSchema = _enum([
20103
+ "disabled",
20104
+ "unavailable",
20105
+ "cannot-host-camera-root",
20106
+ "accelerator-preferred"
20107
+ ]);
20072
20108
  var NodeInferenceDeviceSchema = object({
20073
20109
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20074
20110
  key: string(),
@@ -20099,7 +20135,17 @@ var NodeInferenceDeviceSchema = object({
20099
20135
  * available per format; this is the stored selection that becomes the
20100
20136
  * default for EVERY camera landing on this accelerator.
20101
20137
  */
20102
- steps: record(string(), DeviceStepConfigSchema).optional()
20138
+ steps: record(string(), DeviceStepConfigSchema).optional(),
20139
+ /**
20140
+ * `null` when the device IS a camera-root candidate on this node; otherwise
20141
+ * the reason the dispatcher drops it. Computed by the SAME
20142
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
20143
+ * never disagree with the election — deriving it in the UI from
20144
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
20145
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
20146
+ * "an accelerator is serving" predicate).
20147
+ */
20148
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
20103
20149
  });
20104
20150
  var NodeInferenceDevicesSchema = object({
20105
20151
  nodeId: string(),
@@ -20604,6 +20650,7 @@ DeviceType.Camera, method(object({
20604
20650
  isBattery: boolean(),
20605
20651
  reason: _enum([
20606
20652
  "disabled",
20653
+ "offline",
20607
20654
  "sleeping",
20608
20655
  "unreachable",
20609
20656
  "waking"
@@ -20649,9 +20696,17 @@ targets: array(object({
20649
20696
  sleeping: boolean(),
20650
20697
  /** Current device state rendered over the cached frame. State images
20651
20698
  * remain authoritative even when their photographic background is
20652
- * old; null means the link must carry a current camera frame. */
20699
+ * old; null means the link must carry a current camera frame.
20700
+ *
20701
+ * **This is the ONLY source a surface may derive an inactive tile
20702
+ * from.** A client that reads a device's `online`/`disabled` flags and
20703
+ * draws its own verdict will disagree with the pixels it is painting
20704
+ * — the frame comes from here, and so must the sentence over it
20705
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
20706
+ * state. */
20653
20707
  stateReason: _enum([
20654
20708
  "disabled",
20709
+ "offline",
20655
20710
  "sleeping",
20656
20711
  "unreachable",
20657
20712
  "waking"
@@ -24949,7 +25004,12 @@ var ListResultSchema = object({
24949
25004
  probedAt: number()
24950
25005
  });
24951
25006
  var PreferredSchema = LocalInterfaceSchema.nullable();
24952
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
25007
+ /**
25008
+ * Candidate base URL for the SDK to race on connect. Order matters —
25009
+ * the SDK should attempt these top-to-bottom with a short per-candidate
25010
+ * timeout (e.g. 1500ms) and cache the winner for the session.
25011
+ */
25012
+ var ConnectionEndpointSchema = object({
24953
25013
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
24954
25014
  label: string(),
24955
25015
  /** Fully-formed base URL with scheme + host + port. */
@@ -24992,7 +25052,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
24992
25052
  * ordering between polls.
24993
25053
  */
24994
25054
  priority: number()
24995
- })).readonly() });
25055
+ });
25056
+ /**
25057
+ * Where the advertised local port came from. Ordered most → least
25058
+ * authoritative, and the whole point of returning it: a client must be able to
25059
+ * tell a FACT about the hub's socket from an echo of its own guess.
25060
+ */
25061
+ var LocalPortSourceEnum = _enum([
25062
+ "server-config",
25063
+ "server-env",
25064
+ "caller-hint",
25065
+ "default"
25066
+ ]);
25067
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
25068
+ var AdvertisedLocalPortSchema = object({
25069
+ port: number().int().min(1).max(65535),
25070
+ source: LocalPortSourceEnum
25071
+ });
25072
+ var GetConnectionEndpointsResultSchema = object({
25073
+ endpoints: array(ConnectionEndpointSchema).readonly(),
25074
+ /**
25075
+ * The port the hub built the LAN/loopback URLs with, and where that number
25076
+ * came from.
25077
+ *
25078
+ * Returned rather than merely applied, because "the URL is right" and "the
25079
+ * client can KNOW the URL is right" are different properties. A client that
25080
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
25081
+ * a hub that echoed the port the client sent, so it cannot decide whether to
25082
+ * race the candidate or discard it. With `source` it can: anything but
25083
+ * `caller-hint` is the hub's own socket.
25084
+ *
25085
+ * Absent on hubs predating this field — a client that finds it missing is
25086
+ * talking to an echoing hub and must degrade exactly as it does for
25087
+ * `caller-hint`.
25088
+ */
25089
+ localPort: AdvertisedLocalPortSchema
25090
+ });
24996
25091
  /**
24997
25092
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
24998
25093
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -25013,8 +25108,13 @@ var AllowedAddressesSchema = object({
25013
25108
  */
25014
25109
  addresses: array(string()).readonly() });
25015
25110
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
25016
- /** Local hub HTTP port to use in base URLs. */
25017
- port: number().int().min(1).max(65535),
25111
+ /**
25112
+ * LEGACY HINT — do not send from new code. Kept optional so clients
25113
+ * written against the echoing contract keep working; the hub uses it
25114
+ * only when it cannot read its own port, and says so via
25115
+ * `localPort.source === 'caller-hint'`.
25116
+ */
25117
+ port: number().int().min(1).max(65535).optional(),
25018
25118
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
25019
25119
  * candidate. Default `true`. */
25020
25120
  includeLoopback: boolean().optional(),
@@ -38202,7 +38302,6 @@ var NC_AUDIO_DEFAULTS = {
38202
38302
  hitPercent: 60,
38203
38303
  samplingSeconds: 10
38204
38304
  };
38205
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
38206
38305
  new Set(["devices", "classes"]);
38207
38306
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
38208
38307
  /**
@@ -38436,6 +38535,39 @@ TimelapseRuleInputSchema.extend({
38436
38535
  createdAt: number(),
38437
38536
  updatedAt: number()
38438
38537
  });
38538
+ Object.freeze(Object.fromEntries([{
38539
+ stepId: "face-embedding",
38540
+ label: "Face recognition model",
38541
+ defaultModelId: "arcface-r100",
38542
+ indexName: "the enrolled face gallery",
38543
+ options: [
38544
+ {
38545
+ id: "arcface-r100",
38546
+ label: "ArcFace (ResNet34)"
38547
+ },
38548
+ {
38549
+ id: "auraface-r100",
38550
+ label: "AuraFace R100"
38551
+ },
38552
+ {
38553
+ id: "inception-resnet-v1",
38554
+ label: "Inception ResNet V1"
38555
+ }
38556
+ ]
38557
+ }, {
38558
+ stepId: "clip-embedding",
38559
+ label: "Semantic search model",
38560
+ defaultModelId: "mobileclip-s1",
38561
+ indexName: "the object semantic-search index",
38562
+ options: [{
38563
+ id: "mobileclip-s1",
38564
+ label: "MobileCLIP S1"
38565
+ }, {
38566
+ id: "mobileclip-s2",
38567
+ label: "MobileCLIP S2"
38568
+ }]
38569
+ }].map((s) => [s.stepId, s.defaultModelId])));
38570
+ string().min(1);
38439
38571
  object({
38440
38572
  /**
38441
38573
  * Fraction of the box's own size added on EACH side before cutting.
@@ -38456,10 +38588,21 @@ object({
38456
38588
  */
38457
38589
  square: boolean()
38458
38590
  });
38459
- ({
38591
+ /**
38592
+ * The convention in force when nobody has configured one — byte-for-byte the
38593
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
38594
+ */
38595
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
38460
38596
  paddingRatio: .15,
38461
38597
  square: false
38462
- }).paddingRatio;
38598
+ };
38599
+ Object.freeze({
38600
+ x: 0,
38601
+ y: 0,
38602
+ w: 1,
38603
+ h: 1
38604
+ });
38605
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
38463
38606
  /**
38464
38607
  * WHICH delivered frames the decode worker retains a native copy of.
38465
38608
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-petkit",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "PetKit smart-feeder device-provider addon for CamStack — wraps the @apocaliss92/nodepetkit PetKit cloud client",
5
5
  "keywords": [
6
6
  "camstack",