@camstack/addon-decoder-ffmpeg 1.2.22 → 1.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/index.js +204 -61
  2. package/dist/index.mjs +204 -61
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7567,7 +7567,7 @@ var RecordingBandSchema = object({
7567
7567
  postBufferSec: number().min(0).optional()
7568
7568
  });
7569
7569
  ({
7570
- preBufferSec: 10,
7570
+ preBufferSec: 15,
7571
7571
  postBufferSec: 30
7572
7572
  }).postBufferSec * 1e3;
7573
7573
  /**
@@ -13636,128 +13636,103 @@ var COCO_TO_MACRO = {
13636
13636
  var AUDIO_MACRO_LABELS = [
13637
13637
  {
13638
13638
  id: "speech",
13639
- name: "Speech",
13640
- icon: "🗣️"
13639
+ name: "Speech"
13641
13640
  },
13642
13641
  {
13643
13642
  id: "scream",
13644
- name: "Scream / Shout",
13645
- icon: "😱"
13643
+ name: "Scream / Shout"
13646
13644
  },
13647
13645
  {
13648
13646
  id: "crying",
13649
- name: "Crying / Baby",
13650
- icon: "😢"
13647
+ name: "Crying / Baby"
13651
13648
  },
13652
13649
  {
13653
13650
  id: "laughter",
13654
- name: "Laughter",
13655
- icon: "😂"
13651
+ name: "Laughter"
13656
13652
  },
13657
13653
  {
13658
13654
  id: "music",
13659
- name: "Music",
13660
- icon: "🎵"
13655
+ name: "Music"
13661
13656
  },
13662
13657
  {
13663
13658
  id: "dog",
13664
- name: "Dog",
13665
- icon: "🐕"
13659
+ name: "Dog"
13666
13660
  },
13667
13661
  {
13668
13662
  id: "cat",
13669
- name: "Cat",
13670
- icon: "🐈"
13663
+ name: "Cat"
13671
13664
  },
13672
13665
  {
13673
13666
  id: "bird",
13674
- name: "Bird",
13675
- icon: "🐦"
13667
+ name: "Bird"
13676
13668
  },
13677
13669
  {
13678
13670
  id: "animal",
13679
- name: "Animal (other)",
13680
- icon: "🐾"
13671
+ name: "Animal (other)"
13681
13672
  },
13682
13673
  {
13683
13674
  id: "alarm",
13684
- name: "Alarm / Siren",
13685
- icon: "🚨"
13675
+ name: "Alarm / Siren"
13686
13676
  },
13687
13677
  {
13688
13678
  id: "doorbell",
13689
- name: "Doorbell / Knock",
13690
- icon: "🔔"
13679
+ name: "Doorbell / Knock"
13691
13680
  },
13692
13681
  {
13693
13682
  id: "glass_breaking",
13694
- name: "Glass Breaking",
13695
- icon: "💥"
13683
+ name: "Glass Breaking"
13696
13684
  },
13697
13685
  {
13698
13686
  id: "gunshot",
13699
- name: "Gunshot / Explosion",
13700
- icon: "💣"
13687
+ name: "Gunshot / Explosion"
13701
13688
  },
13702
13689
  {
13703
13690
  id: "vehicle",
13704
- name: "Vehicle",
13705
- icon: "🚗"
13691
+ name: "Vehicle"
13706
13692
  },
13707
13693
  {
13708
13694
  id: "siren",
13709
- name: "Emergency Siren",
13710
- icon: "🚑"
13695
+ name: "Emergency Siren"
13711
13696
  },
13712
13697
  {
13713
13698
  id: "fire",
13714
- name: "Fire / Smoke",
13715
- icon: "🔥"
13699
+ name: "Fire / Smoke"
13716
13700
  },
13717
13701
  {
13718
13702
  id: "water",
13719
- name: "Water",
13720
- icon: "💧"
13703
+ name: "Water"
13721
13704
  },
13722
13705
  {
13723
13706
  id: "wind",
13724
- name: "Wind / Weather",
13725
- icon: "🌬️"
13707
+ name: "Wind / Weather"
13726
13708
  },
13727
13709
  {
13728
13710
  id: "door",
13729
- name: "Door",
13730
- icon: "🚪"
13711
+ name: "Door"
13731
13712
  },
13732
13713
  {
13733
13714
  id: "footsteps",
13734
- name: "Footsteps",
13735
- icon: "👣"
13715
+ name: "Footsteps"
13736
13716
  },
13737
13717
  {
13738
13718
  id: "crowd",
13739
- name: "Crowd / Chatter",
13740
- icon: "👥"
13719
+ name: "Crowd / Chatter"
13741
13720
  },
13742
13721
  {
13743
13722
  id: "telephone",
13744
- name: "Telephone",
13745
- icon: "📞"
13723
+ name: "Telephone"
13746
13724
  },
13747
13725
  {
13748
13726
  id: "engine",
13749
- name: "Engine / Motor",
13750
- icon: "⚙️"
13727
+ name: "Engine / Motor"
13751
13728
  },
13752
13729
  {
13753
13730
  id: "tools",
13754
- name: "Tools / Construction",
13755
- icon: "🔨"
13731
+ name: "Tools / Construction"
13756
13732
  },
13757
13733
  {
13758
13734
  id: "silence",
13759
- name: "Silence",
13760
- icon: "🤫"
13735
+ name: "Silence"
13761
13736
  }
13762
13737
  ];
13763
13738
  var YAMNET_TO_MACRO = {
@@ -16221,7 +16196,7 @@ var TrackEnvelopeSchema = object({
16221
16196
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
16222
16197
  * keeps every scalar the list surfaces actually render (ids, class(es),
16223
16198
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
16224
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
16199
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
16225
16200
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
16226
16201
  * `getTrack`. Mirrors the event-store `projection` convention
16227
16202
  * (`getObjectEvents` et al.).
@@ -16516,6 +16491,35 @@ var TrackSchema = object({
16516
16491
  */
16517
16492
  hasFace: boolean().optional(),
16518
16493
  /**
16494
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
16495
+ * face an operator could ASSIGN to an identity.
16496
+ *
16497
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
16498
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
16499
+ * branch, before every gate, and means no more than "a face detector produced
16500
+ * a face detail". This one is stamped at the single moment the gallery row
16501
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
16502
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
16503
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
16504
+ * the crop-store drop. Everything between the detector and that insert can
16505
+ * legitimately refuse the face, so a flag written any earlier promises the
16506
+ * operator something to assign and delivers nothing.
16507
+ *
16508
+ * **Independent of recognition.** A face collected but never auto-matched is
16509
+ * still assignable — it is in fact the face an operator most wants to reach —
16510
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
16511
+ * `subLabel`; this says only that the raw material exists.
16512
+ *
16513
+ * **Set once, never cleared.** A track that produced a gallery row produced
16514
+ * one; deleting the row later is the gallery's business, not this flag's.
16515
+ *
16516
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
16517
+ * before the column omits it, and so does every server that predates the
16518
+ * field. A consumer must test `=== true` and render nothing otherwise —
16519
+ * never infer "no assignable face".
16520
+ */
16521
+ hasEmbeddedFace: boolean().optional(),
16522
+ /**
16519
16523
  * This subject CONTAINS a folded rider — a person the rider-pairing step
16520
16524
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16521
16525
  * so the passage is tracked once and as a VEHICLE.
@@ -17865,6 +17869,32 @@ var DetailResultSchema = object({
17865
17869
  /** FRAME-space bbox (already mapped back from crop space). */
17866
17870
  bbox: NativeCropBboxSchema.optional(),
17867
17871
  embedding: string().optional(),
17872
+ /**
17873
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
17874
+ * so an older runner that does not send it degrades to "unmeasurable", which
17875
+ * the consuming gate treats as accept — never as reject.
17876
+ */
17877
+ embeddingMagnitude: number().optional(),
17878
+ /**
17879
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
17880
+ * `label`, and for the same reason: the producing node is the only side that
17881
+ * knows which model actually ran, and re-deriving it downstream is exactly
17882
+ * the inference this model exists to forbid.
17883
+ *
17884
+ * Additive and optional, so a runner that predates it degrades to
17885
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
17886
+ * its previous assumption instead of refusing.
17887
+ *
17888
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
17889
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
17890
+ * with nothing on the wire it took the id from the GALLERY, so the gate
17891
+ * compared the gallery against itself and could never fire. That turned the
17892
+ * one guard protecting the face feature space into a tautology, and it is
17893
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
17894
+ * gallery as the catalog comment predicts — it would have scored the new
17895
+ * model's vectors against the old model's samples in one cosine space.
17896
+ */
17897
+ embeddingModelId: string().optional(),
17868
17898
  label: string().optional(),
17869
17899
  /**
17870
17900
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -18740,6 +18770,12 @@ var CameraStatusSchema = object({
18740
18770
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
18741
18771
  fetchedAt: number()
18742
18772
  });
18773
+ var InferenceDeviceExclusionReasonSchema = _enum([
18774
+ "disabled",
18775
+ "unavailable",
18776
+ "cannot-host-camera-root",
18777
+ "accelerator-preferred"
18778
+ ]);
18743
18779
  var NodeInferenceDeviceSchema = object({
18744
18780
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
18745
18781
  key: string(),
@@ -18770,7 +18806,17 @@ var NodeInferenceDeviceSchema = object({
18770
18806
  * available per format; this is the stored selection that becomes the
18771
18807
  * default for EVERY camera landing on this accelerator.
18772
18808
  */
18773
- steps: record(string(), DeviceStepConfigSchema).optional()
18809
+ steps: record(string(), DeviceStepConfigSchema).optional(),
18810
+ /**
18811
+ * `null` when the device IS a camera-root candidate on this node; otherwise
18812
+ * the reason the dispatcher drops it. Computed by the SAME
18813
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
18814
+ * never disagree with the election — deriving it in the UI from
18815
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
18816
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
18817
+ * "an accelerator is serving" predicate).
18818
+ */
18819
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
18774
18820
  });
18775
18821
  var NodeInferenceDevicesSchema = object({
18776
18822
  nodeId: string(),
@@ -19275,6 +19321,7 @@ DeviceType.Camera, method(object({
19275
19321
  isBattery: boolean(),
19276
19322
  reason: _enum([
19277
19323
  "disabled",
19324
+ "offline",
19278
19325
  "sleeping",
19279
19326
  "unreachable",
19280
19327
  "waking"
@@ -19320,9 +19367,17 @@ targets: array(object({
19320
19367
  sleeping: boolean(),
19321
19368
  /** Current device state rendered over the cached frame. State images
19322
19369
  * remain authoritative even when their photographic background is
19323
- * old; null means the link must carry a current camera frame. */
19370
+ * old; null means the link must carry a current camera frame.
19371
+ *
19372
+ * **This is the ONLY source a surface may derive an inactive tile
19373
+ * from.** A client that reads a device's `online`/`disabled` flags and
19374
+ * draws its own verdict will disagree with the pixels it is painting
19375
+ * — the frame comes from here, and so must the sentence over it
19376
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
19377
+ * state. */
19324
19378
  stateReason: _enum([
19325
19379
  "disabled",
19380
+ "offline",
19326
19381
  "sleeping",
19327
19382
  "unreachable",
19328
19383
  "waking"
@@ -22519,7 +22574,12 @@ var ListResultSchema = object({
22519
22574
  probedAt: number()
22520
22575
  });
22521
22576
  var PreferredSchema = LocalInterfaceSchema.nullable();
22522
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
22577
+ /**
22578
+ * Candidate base URL for the SDK to race on connect. Order matters —
22579
+ * the SDK should attempt these top-to-bottom with a short per-candidate
22580
+ * timeout (e.g. 1500ms) and cache the winner for the session.
22581
+ */
22582
+ var ConnectionEndpointSchema = object({
22523
22583
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
22524
22584
  label: string(),
22525
22585
  /** Fully-formed base URL with scheme + host + port. */
@@ -22562,7 +22622,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
22562
22622
  * ordering between polls.
22563
22623
  */
22564
22624
  priority: number()
22565
- })).readonly() });
22625
+ });
22626
+ /**
22627
+ * Where the advertised local port came from. Ordered most → least
22628
+ * authoritative, and the whole point of returning it: a client must be able to
22629
+ * tell a FACT about the hub's socket from an echo of its own guess.
22630
+ */
22631
+ var LocalPortSourceEnum = _enum([
22632
+ "server-config",
22633
+ "server-env",
22634
+ "caller-hint",
22635
+ "default"
22636
+ ]);
22637
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
22638
+ var AdvertisedLocalPortSchema = object({
22639
+ port: number().int().min(1).max(65535),
22640
+ source: LocalPortSourceEnum
22641
+ });
22642
+ var GetConnectionEndpointsResultSchema = object({
22643
+ endpoints: array(ConnectionEndpointSchema).readonly(),
22644
+ /**
22645
+ * The port the hub built the LAN/loopback URLs with, and where that number
22646
+ * came from.
22647
+ *
22648
+ * Returned rather than merely applied, because "the URL is right" and "the
22649
+ * client can KNOW the URL is right" are different properties. A client that
22650
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
22651
+ * a hub that echoed the port the client sent, so it cannot decide whether to
22652
+ * race the candidate or discard it. With `source` it can: anything but
22653
+ * `caller-hint` is the hub's own socket.
22654
+ *
22655
+ * Absent on hubs predating this field — a client that finds it missing is
22656
+ * talking to an echoing hub and must degrade exactly as it does for
22657
+ * `caller-hint`.
22658
+ */
22659
+ localPort: AdvertisedLocalPortSchema
22660
+ });
22566
22661
  /**
22567
22662
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
22568
22663
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -22583,8 +22678,13 @@ var AllowedAddressesSchema = object({
22583
22678
  */
22584
22679
  addresses: array(string()).readonly() });
22585
22680
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22586
- /** Local hub HTTP port to use in base URLs. */
22587
- port: number().int().min(1).max(65535),
22681
+ /**
22682
+ * LEGACY HINT — do not send from new code. Kept optional so clients
22683
+ * written against the echoing contract keep working; the hub uses it
22684
+ * only when it cannot read its own port, and says so via
22685
+ * `localPort.source === 'caller-hint'`.
22686
+ */
22687
+ port: number().int().min(1).max(65535).optional(),
22588
22688
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22589
22689
  * candidate. Default `true`. */
22590
22690
  includeLoopback: boolean().optional(),
@@ -33508,7 +33608,6 @@ var NC_AUDIO_DEFAULTS = {
33508
33608
  hitPercent: 60,
33509
33609
  samplingSeconds: 10
33510
33610
  };
33511
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
33512
33611
  new Set(["devices", "classes"]);
33513
33612
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
33514
33613
  /**
@@ -33742,6 +33841,39 @@ TimelapseRuleInputSchema.extend({
33742
33841
  createdAt: number(),
33743
33842
  updatedAt: number()
33744
33843
  });
33844
+ Object.freeze(Object.fromEntries([{
33845
+ stepId: "face-embedding",
33846
+ label: "Face recognition model",
33847
+ defaultModelId: "arcface-r100",
33848
+ indexName: "the enrolled face gallery",
33849
+ options: [
33850
+ {
33851
+ id: "arcface-r100",
33852
+ label: "ArcFace (ResNet34)"
33853
+ },
33854
+ {
33855
+ id: "auraface-r100",
33856
+ label: "AuraFace R100"
33857
+ },
33858
+ {
33859
+ id: "inception-resnet-v1",
33860
+ label: "Inception ResNet V1"
33861
+ }
33862
+ ]
33863
+ }, {
33864
+ stepId: "clip-embedding",
33865
+ label: "Semantic search model",
33866
+ defaultModelId: "mobileclip-s1",
33867
+ indexName: "the object semantic-search index",
33868
+ options: [{
33869
+ id: "mobileclip-s1",
33870
+ label: "MobileCLIP S1"
33871
+ }, {
33872
+ id: "mobileclip-s2",
33873
+ label: "MobileCLIP S2"
33874
+ }]
33875
+ }].map((s) => [s.stepId, s.defaultModelId])));
33876
+ string().min(1);
33745
33877
  object({
33746
33878
  /**
33747
33879
  * Fraction of the box's own size added on EACH side before cutting.
@@ -33762,10 +33894,21 @@ object({
33762
33894
  */
33763
33895
  square: boolean()
33764
33896
  });
33765
- ({
33897
+ /**
33898
+ * The convention in force when nobody has configured one — byte-for-byte the
33899
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
33900
+ */
33901
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
33766
33902
  paddingRatio: .15,
33767
33903
  square: false
33768
- }).paddingRatio;
33904
+ };
33905
+ Object.freeze({
33906
+ x: 0,
33907
+ y: 0,
33908
+ w: 1,
33909
+ h: 1
33910
+ });
33911
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
33769
33912
  /**
33770
33913
  * WHICH delivered frames the decode worker retains a native copy of.
33771
33914
  *
package/dist/index.mjs CHANGED
@@ -7563,7 +7563,7 @@ var RecordingBandSchema = object({
7563
7563
  postBufferSec: number().min(0).optional()
7564
7564
  });
7565
7565
  ({
7566
- preBufferSec: 10,
7566
+ preBufferSec: 15,
7567
7567
  postBufferSec: 30
7568
7568
  }).postBufferSec * 1e3;
7569
7569
  /**
@@ -13632,128 +13632,103 @@ var COCO_TO_MACRO = {
13632
13632
  var AUDIO_MACRO_LABELS = [
13633
13633
  {
13634
13634
  id: "speech",
13635
- name: "Speech",
13636
- icon: "🗣️"
13635
+ name: "Speech"
13637
13636
  },
13638
13637
  {
13639
13638
  id: "scream",
13640
- name: "Scream / Shout",
13641
- icon: "😱"
13639
+ name: "Scream / Shout"
13642
13640
  },
13643
13641
  {
13644
13642
  id: "crying",
13645
- name: "Crying / Baby",
13646
- icon: "😢"
13643
+ name: "Crying / Baby"
13647
13644
  },
13648
13645
  {
13649
13646
  id: "laughter",
13650
- name: "Laughter",
13651
- icon: "😂"
13647
+ name: "Laughter"
13652
13648
  },
13653
13649
  {
13654
13650
  id: "music",
13655
- name: "Music",
13656
- icon: "🎵"
13651
+ name: "Music"
13657
13652
  },
13658
13653
  {
13659
13654
  id: "dog",
13660
- name: "Dog",
13661
- icon: "🐕"
13655
+ name: "Dog"
13662
13656
  },
13663
13657
  {
13664
13658
  id: "cat",
13665
- name: "Cat",
13666
- icon: "🐈"
13659
+ name: "Cat"
13667
13660
  },
13668
13661
  {
13669
13662
  id: "bird",
13670
- name: "Bird",
13671
- icon: "🐦"
13663
+ name: "Bird"
13672
13664
  },
13673
13665
  {
13674
13666
  id: "animal",
13675
- name: "Animal (other)",
13676
- icon: "🐾"
13667
+ name: "Animal (other)"
13677
13668
  },
13678
13669
  {
13679
13670
  id: "alarm",
13680
- name: "Alarm / Siren",
13681
- icon: "🚨"
13671
+ name: "Alarm / Siren"
13682
13672
  },
13683
13673
  {
13684
13674
  id: "doorbell",
13685
- name: "Doorbell / Knock",
13686
- icon: "🔔"
13675
+ name: "Doorbell / Knock"
13687
13676
  },
13688
13677
  {
13689
13678
  id: "glass_breaking",
13690
- name: "Glass Breaking",
13691
- icon: "💥"
13679
+ name: "Glass Breaking"
13692
13680
  },
13693
13681
  {
13694
13682
  id: "gunshot",
13695
- name: "Gunshot / Explosion",
13696
- icon: "💣"
13683
+ name: "Gunshot / Explosion"
13697
13684
  },
13698
13685
  {
13699
13686
  id: "vehicle",
13700
- name: "Vehicle",
13701
- icon: "🚗"
13687
+ name: "Vehicle"
13702
13688
  },
13703
13689
  {
13704
13690
  id: "siren",
13705
- name: "Emergency Siren",
13706
- icon: "🚑"
13691
+ name: "Emergency Siren"
13707
13692
  },
13708
13693
  {
13709
13694
  id: "fire",
13710
- name: "Fire / Smoke",
13711
- icon: "🔥"
13695
+ name: "Fire / Smoke"
13712
13696
  },
13713
13697
  {
13714
13698
  id: "water",
13715
- name: "Water",
13716
- icon: "💧"
13699
+ name: "Water"
13717
13700
  },
13718
13701
  {
13719
13702
  id: "wind",
13720
- name: "Wind / Weather",
13721
- icon: "🌬️"
13703
+ name: "Wind / Weather"
13722
13704
  },
13723
13705
  {
13724
13706
  id: "door",
13725
- name: "Door",
13726
- icon: "🚪"
13707
+ name: "Door"
13727
13708
  },
13728
13709
  {
13729
13710
  id: "footsteps",
13730
- name: "Footsteps",
13731
- icon: "👣"
13711
+ name: "Footsteps"
13732
13712
  },
13733
13713
  {
13734
13714
  id: "crowd",
13735
- name: "Crowd / Chatter",
13736
- icon: "👥"
13715
+ name: "Crowd / Chatter"
13737
13716
  },
13738
13717
  {
13739
13718
  id: "telephone",
13740
- name: "Telephone",
13741
- icon: "📞"
13719
+ name: "Telephone"
13742
13720
  },
13743
13721
  {
13744
13722
  id: "engine",
13745
- name: "Engine / Motor",
13746
- icon: "⚙️"
13723
+ name: "Engine / Motor"
13747
13724
  },
13748
13725
  {
13749
13726
  id: "tools",
13750
- name: "Tools / Construction",
13751
- icon: "🔨"
13727
+ name: "Tools / Construction"
13752
13728
  },
13753
13729
  {
13754
13730
  id: "silence",
13755
- name: "Silence",
13756
- icon: "🤫"
13731
+ name: "Silence"
13757
13732
  }
13758
13733
  ];
13759
13734
  var YAMNET_TO_MACRO = {
@@ -16217,7 +16192,7 @@ var TrackEnvelopeSchema = object({
16217
16192
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
16218
16193
  * keeps every scalar the list surfaces actually render (ids, class(es),
16219
16194
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
16220
- * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
16195
+ * zonesVisited, bestEventId, envelope, hasFace, hasEmbeddedFace, hasRider) and returns `positions` /
16221
16196
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
16222
16197
  * `getTrack`. Mirrors the event-store `projection` convention
16223
16198
  * (`getObjectEvents` et al.).
@@ -16512,6 +16487,35 @@ var TrackSchema = object({
16512
16487
  */
16513
16488
  hasFace: boolean().optional(),
16514
16489
  /**
16490
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
16491
+ * face an operator could ASSIGN to an identity.
16492
+ *
16493
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
16494
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
16495
+ * branch, before every gate, and means no more than "a face detector produced
16496
+ * a face detail". This one is stamped at the single moment the gallery row
16497
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
16498
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
16499
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
16500
+ * the crop-store drop. Everything between the detector and that insert can
16501
+ * legitimately refuse the face, so a flag written any earlier promises the
16502
+ * operator something to assign and delivers nothing.
16503
+ *
16504
+ * **Independent of recognition.** A face collected but never auto-matched is
16505
+ * still assignable — it is in fact the face an operator most wants to reach —
16506
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
16507
+ * `subLabel`; this says only that the raw material exists.
16508
+ *
16509
+ * **Set once, never cleared.** A track that produced a gallery row produced
16510
+ * one; deleting the row later is the gallery's business, not this flag's.
16511
+ *
16512
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
16513
+ * before the column omits it, and so does every server that predates the
16514
+ * field. A consumer must test `=== true` and render nothing otherwise —
16515
+ * never infer "no assignable face".
16516
+ */
16517
+ hasEmbeddedFace: boolean().optional(),
16518
+ /**
16515
16519
  * This subject CONTAINS a folded rider — a person the rider-pairing step
16516
16520
  * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16517
16521
  * so the passage is tracked once and as a VEHICLE.
@@ -17861,6 +17865,32 @@ var DetailResultSchema = object({
17861
17865
  /** FRAME-space bbox (already mapped back from crop space). */
17862
17866
  bbox: NativeCropBboxSchema.optional(),
17863
17867
  embedding: string().optional(),
17868
+ /**
17869
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
17870
+ * so an older runner that does not send it degrades to "unmeasurable", which
17871
+ * the consuming gate treats as accept — never as reject.
17872
+ */
17873
+ embeddingMagnitude: number().optional(),
17874
+ /**
17875
+ * Model that produced `embedding` — the SAME contract `labelModelId` has for
17876
+ * `label`, and for the same reason: the producing node is the only side that
17877
+ * knows which model actually ran, and re-deriving it downstream is exactly
17878
+ * the inference this model exists to forbid.
17879
+ *
17880
+ * Additive and optional, so a runner that predates it degrades to
17881
+ * "unmeasurable" rather than to a wrong answer — the consumer falls back to
17882
+ * its previous assumption instead of refusing.
17883
+ *
17884
+ * Its absence was not cosmetic. `FaceRecognizer` needs the probe's model id
17885
+ * to use `face-matcher`'s same-model gate (`sample.modelId !== probe.modelId`);
17886
+ * with nothing on the wire it took the id from the GALLERY, so the gate
17887
+ * compared the gallery against itself and could never fire. That turned the
17888
+ * one guard protecting the face feature space into a tautology, and it is
17889
+ * why swapping `face-embedding`'s model would NOT have stranded the enrolled
17890
+ * gallery as the catalog comment predicts — it would have scored the new
17891
+ * model's vectors against the old model's samples in one cosine space.
17892
+ */
17893
+ embeddingModelId: string().optional(),
17864
17894
  label: string().optional(),
17865
17895
  /**
17866
17896
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -18736,6 +18766,12 @@ var CameraStatusSchema = object({
18736
18766
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
18737
18767
  fetchedAt: number()
18738
18768
  });
18769
+ var InferenceDeviceExclusionReasonSchema = _enum([
18770
+ "disabled",
18771
+ "unavailable",
18772
+ "cannot-host-camera-root",
18773
+ "accelerator-preferred"
18774
+ ]);
18739
18775
  var NodeInferenceDeviceSchema = object({
18740
18776
  /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
18741
18777
  key: string(),
@@ -18766,7 +18802,17 @@ var NodeInferenceDeviceSchema = object({
18766
18802
  * available per format; this is the stored selection that becomes the
18767
18803
  * default for EVERY camera landing on this accelerator.
18768
18804
  */
18769
- steps: record(string(), DeviceStepConfigSchema).optional()
18805
+ steps: record(string(), DeviceStepConfigSchema).optional(),
18806
+ /**
18807
+ * `null` when the device IS a camera-root candidate on this node; otherwise
18808
+ * the reason the dispatcher drops it. Computed by the SAME
18809
+ * `resolveInferenceDeviceEligibility` the dispatcher runs, so this view can
18810
+ * never disagree with the election — deriving it in the UI from
18811
+ * `enabled`/`available` would silently miss `cannot-host-camera-root` (needs
18812
+ * the node's model catalog) and `accelerator-preferred` (needs the node-wide
18813
+ * "an accelerator is serving" predicate).
18814
+ */
18815
+ exclusion: InferenceDeviceExclusionReasonSchema.nullable()
18770
18816
  });
18771
18817
  var NodeInferenceDevicesSchema = object({
18772
18818
  nodeId: string(),
@@ -19271,6 +19317,7 @@ DeviceType.Camera, method(object({
19271
19317
  isBattery: boolean(),
19272
19318
  reason: _enum([
19273
19319
  "disabled",
19320
+ "offline",
19274
19321
  "sleeping",
19275
19322
  "unreachable",
19276
19323
  "waking"
@@ -19316,9 +19363,17 @@ targets: array(object({
19316
19363
  sleeping: boolean(),
19317
19364
  /** Current device state rendered over the cached frame. State images
19318
19365
  * remain authoritative even when their photographic background is
19319
- * old; null means the link must carry a current camera frame. */
19366
+ * old; null means the link must carry a current camera frame.
19367
+ *
19368
+ * **This is the ONLY source a surface may derive an inactive tile
19369
+ * from.** A client that reads a device's `online`/`disabled` flags and
19370
+ * draws its own verdict will disagree with the pixels it is painting
19371
+ * — the frame comes from here, and so must the sentence over it
19372
+ * (D190). Pair with `ageMs` for the age of the photograph behind the
19373
+ * state. */
19320
19374
  stateReason: _enum([
19321
19375
  "disabled",
19376
+ "offline",
19322
19377
  "sleeping",
19323
19378
  "unreachable",
19324
19379
  "waking"
@@ -22515,7 +22570,12 @@ var ListResultSchema = object({
22515
22570
  probedAt: number()
22516
22571
  });
22517
22572
  var PreferredSchema = LocalInterfaceSchema.nullable();
22518
- var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
22573
+ /**
22574
+ * Candidate base URL for the SDK to race on connect. Order matters —
22575
+ * the SDK should attempt these top-to-bottom with a short per-candidate
22576
+ * timeout (e.g. 1500ms) and cache the winner for the session.
22577
+ */
22578
+ var ConnectionEndpointSchema = object({
22519
22579
  /** Operator-facing label (e.g. "LAN — en0", "Public tunnel"). */
22520
22580
  label: string(),
22521
22581
  /** Fully-formed base URL with scheme + host + port. */
@@ -22558,7 +22618,42 @@ var GetConnectionEndpointsResultSchema = object({ endpoints: array(object({
22558
22618
  * ordering between polls.
22559
22619
  */
22560
22620
  priority: number()
22561
- })).readonly() });
22621
+ });
22622
+ /**
22623
+ * Where the advertised local port came from. Ordered most → least
22624
+ * authoritative, and the whole point of returning it: a client must be able to
22625
+ * tell a FACT about the hub's socket from an echo of its own guess.
22626
+ */
22627
+ var LocalPortSourceEnum = _enum([
22628
+ "server-config",
22629
+ "server-env",
22630
+ "caller-hint",
22631
+ "default"
22632
+ ]);
22633
+ /** The port every LAN/loopback `baseUrl` in the same result was built with. */
22634
+ var AdvertisedLocalPortSchema = object({
22635
+ port: number().int().min(1).max(65535),
22636
+ source: LocalPortSourceEnum
22637
+ });
22638
+ var GetConnectionEndpointsResultSchema = object({
22639
+ endpoints: array(ConnectionEndpointSchema).readonly(),
22640
+ /**
22641
+ * The port the hub built the LAN/loopback URLs with, and where that number
22642
+ * came from.
22643
+ *
22644
+ * Returned rather than merely applied, because "the URL is right" and "the
22645
+ * client can KNOW the URL is right" are different properties. A client that
22646
+ * only sees a corrected URL cannot distinguish a hub that fixed the port from
22647
+ * a hub that echoed the port the client sent, so it cannot decide whether to
22648
+ * race the candidate or discard it. With `source` it can: anything but
22649
+ * `caller-hint` is the hub's own socket.
22650
+ *
22651
+ * Absent on hubs predating this field — a client that finds it missing is
22652
+ * talking to an echoing hub and must degrade exactly as it does for
22653
+ * `caller-hint`.
22654
+ */
22655
+ localPort: AdvertisedLocalPortSchema
22656
+ });
22562
22657
  /**
22563
22658
  * The chosen outbound endpoint for notification artifacts. `baseUrl: null` =
22564
22659
  * AUTO (resolved from the candidate ranking at send time); `resolved` reports
@@ -22579,8 +22674,13 @@ var AllowedAddressesSchema = object({
22579
22674
  */
22580
22675
  addresses: array(string()).readonly() });
22581
22676
  method(_void(), ListResultSchema), method(_void(), PreferredSchema), method(object({
22582
- /** Local hub HTTP port to use in base URLs. */
22583
- port: number().int().min(1).max(65535),
22677
+ /**
22678
+ * LEGACY HINT — do not send from new code. Kept optional so clients
22679
+ * written against the echoing contract keep working; the hub uses it
22680
+ * only when it cannot read its own port, and says so via
22681
+ * `localPort.source === 'caller-hint'`.
22682
+ */
22683
+ port: number().int().min(1).max(65535).optional(),
22584
22684
  /** Include `http(s)://127.0.0.1:<port>` as the lowest-priority
22585
22685
  * candidate. Default `true`. */
22586
22686
  includeLoopback: boolean().optional(),
@@ -33504,7 +33604,6 @@ var NC_AUDIO_DEFAULTS = {
33504
33604
  hitPercent: 60,
33505
33605
  samplingSeconds: 10
33506
33606
  };
33507
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
33508
33607
  new Set(["devices", "classes"]);
33509
33608
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
33510
33609
  /**
@@ -33738,6 +33837,39 @@ TimelapseRuleInputSchema.extend({
33738
33837
  createdAt: number(),
33739
33838
  updatedAt: number()
33740
33839
  });
33840
+ Object.freeze(Object.fromEntries([{
33841
+ stepId: "face-embedding",
33842
+ label: "Face recognition model",
33843
+ defaultModelId: "arcface-r100",
33844
+ indexName: "the enrolled face gallery",
33845
+ options: [
33846
+ {
33847
+ id: "arcface-r100",
33848
+ label: "ArcFace (ResNet34)"
33849
+ },
33850
+ {
33851
+ id: "auraface-r100",
33852
+ label: "AuraFace R100"
33853
+ },
33854
+ {
33855
+ id: "inception-resnet-v1",
33856
+ label: "Inception ResNet V1"
33857
+ }
33858
+ ]
33859
+ }, {
33860
+ stepId: "clip-embedding",
33861
+ label: "Semantic search model",
33862
+ defaultModelId: "mobileclip-s1",
33863
+ indexName: "the object semantic-search index",
33864
+ options: [{
33865
+ id: "mobileclip-s1",
33866
+ label: "MobileCLIP S1"
33867
+ }, {
33868
+ id: "mobileclip-s2",
33869
+ label: "MobileCLIP S2"
33870
+ }]
33871
+ }].map((s) => [s.stepId, s.defaultModelId])));
33872
+ string().min(1);
33741
33873
  object({
33742
33874
  /**
33743
33875
  * Fraction of the box's own size added on EACH side before cutting.
@@ -33758,10 +33890,21 @@ object({
33758
33890
  */
33759
33891
  square: boolean()
33760
33892
  });
33761
- ({
33893
+ /**
33894
+ * The convention in force when nobody has configured one — byte-for-byte the
33895
+ * behaviour of the pre-unification LIVE path (`DETAIL_CROP_PADDING_RATIO`).
33896
+ */
33897
+ var DEFAULT_DETAIL_CROP_CONVENTION = {
33762
33898
  paddingRatio: .15,
33763
33899
  square: false
33764
- }).paddingRatio;
33900
+ };
33901
+ Object.freeze({
33902
+ x: 0,
33903
+ y: 0,
33904
+ w: 1,
33905
+ h: 1
33906
+ });
33907
+ DEFAULT_DETAIL_CROP_CONVENTION.paddingRatio;
33765
33908
  /**
33766
33909
  * WHICH delivered frames the decode worker retains a native copy of.
33767
33910
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-decoder-ffmpeg",
3
- "version": "1.2.22",
3
+ "version": "1.2.24",
4
4
  "description": "Standalone ffmpeg-subprocess decoder fallback addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",