@camstack/addon-export-alexa 1.2.109 → 1.2.111

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.
@@ -5387,7 +5387,7 @@ var ZodIssueCode = {
5387
5387
  var ZodFirstPartyTypeKind;
5388
5388
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5389
5389
  //#endregion
5390
- //#region ../types/dist/sleep-DEuj7E3j.mjs
5390
+ //#region ../types/dist/sleep-BDR76Ykr.mjs
5391
5391
  /**
5392
5392
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5393
5393
  * window to float samples (D455).
@@ -7268,6 +7268,28 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7268
7268
  DeviceRole["GenericNotifier"] = "generic-notifier";
7269
7269
  return DeviceRole;
7270
7270
  }({});
7271
+ /**
7272
+ * Identity — preserves literal types for downstream inference.
7273
+ *
7274
+ * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
7275
+ * TypeScript does not widen each entry's literal `kind`/`auth` fields to
7276
+ * the broader unions declared on `CustomActionSpec`'s default generics.
7277
+ * Shape validity is enforced separately by the `customAction(...)` helper
7278
+ * whose return type is already a `CustomActionSpec<...>`.
7279
+ */
7280
+ function defineCustomActions(spec) {
7281
+ return spec;
7282
+ }
7283
+ function customAction(input, output, options) {
7284
+ return {
7285
+ input,
7286
+ output,
7287
+ kind: options?.kind ?? "query",
7288
+ auth: options?.auth ?? "protected",
7289
+ scope: options?.scope ?? { kind: "system" },
7290
+ ...options?.caller ? { caller: "required" } : {}
7291
+ };
7292
+ }
7271
7293
  var StaticDirOutputSchema = object({ staticDir: string() });
7272
7294
  var VersionOutputSchema = object({ version: string() });
7273
7295
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -12726,28 +12748,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
12726
12748
  content: string()
12727
12749
  })) }), { auth: "admin" });
12728
12750
  /**
12729
- * Identity — preserves literal types for downstream inference.
12730
- *
12731
- * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
12732
- * TypeScript does not widen each entry's literal `kind`/`auth` fields to
12733
- * the broader unions declared on `CustomActionSpec`'s default generics.
12734
- * Shape validity is enforced separately by the `customAction(...)` helper
12735
- * whose return type is already a `CustomActionSpec<...>`.
12736
- */
12737
- function defineCustomActions(spec) {
12738
- return spec;
12739
- }
12740
- function customAction(input, output, options) {
12741
- return {
12742
- input,
12743
- output,
12744
- kind: options?.kind ?? "query",
12745
- auth: options?.auth ?? "protected",
12746
- scope: options?.scope ?? { kind: "system" },
12747
- ...options?.caller ? { caller: "required" } : {}
12748
- };
12749
- }
12750
- /**
12751
12751
  * `custom-model-registry` — collection cap exposing operator-registered
12752
12752
  * custom detection models. Each provider (today: `addon-model-studio`)
12753
12753
  * contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
@@ -18187,6 +18187,48 @@ var NcAlarmConfigSchema = object({
18187
18187
  settings: NcAlarmSettingsSchema,
18188
18188
  coverage: array(NcAlarmModeCoverageSchema)
18189
18189
  });
18190
+ /**
18191
+ * ONE rule's demand on ONE camera's clip ring.
18192
+ *
18193
+ * A rule, not a camera: the broker takes the MAX over the asks that reach a
18194
+ * camera, so the answer stays a faithful description of the rules and the
18195
+ * bounding (the byte budget, the broker's own ceiling) stays where the cost
18196
+ * lives. A camera that appears in no ask is being told **nothing** — which is
18197
+ * a different thing from never having been told, and only the envelope
18198
+ * (`rulesLoaded`) can tell those apart.
18199
+ */
18200
+ var NcClipRetentionAskSchema = object({
18201
+ /**
18202
+ * The camera this ask is about. **Absent = every camera**, which is what a
18203
+ * rule with no `conditions.devices` means: it can fire anywhere, so it is
18204
+ * asking everywhere.
18205
+ */
18206
+ deviceId: number().int().nonnegative().optional(),
18207
+ /** Seconds of history the rule needs held for it. Never zero — a rule that
18208
+ * needs nothing produces no ask at all. */
18209
+ seconds: number().min(0).max(60),
18210
+ /** The profile the rule cuts from, when it named one. Absent = the cheapest
18211
+ * assigned, which is what the cut defaults to. */
18212
+ profile: CamProfileSchema.optional(),
18213
+ /** Who is asking — so a retention the operator did not expect names a rule. */
18214
+ ruleId: string(),
18215
+ ruleName: string(),
18216
+ /** Why this many seconds: the rule's own window, or the occupancy ceiling. */
18217
+ reason: _enum(["window", "occupancy"])
18218
+ });
18219
+ /**
18220
+ * The whole answer, with the one bit that keeps absence from reading as zero.
18221
+ *
18222
+ * `rulesLoaded` is false until the rule ledger has completed a load at least
18223
+ * once. A centre that is up but has not read its rules yet answers with an
18224
+ * empty ask list that means nothing at all — applying it would drop every
18225
+ * camera's ring to zero for the first events after a restart, which is the
18226
+ * footage this mechanism exists to keep.
18227
+ */
18228
+ var NcClipRetentionPlanSchema = object({
18229
+ rulesLoaded: boolean(),
18230
+ asks: array(NcClipRetentionAskSchema).readonly()
18231
+ });
18190
18232
  method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
18191
18233
  kind: "mutation",
18192
18234
  auth: "admin",
@@ -18207,7 +18249,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
18207
18249
  }), object({ success: literal(true) }), {
18208
18250
  kind: "mutation",
18209
18251
  auth: "admin"
18210
- }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18252
+ }), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18211
18253
  deviceId: number().int(),
18212
18254
  muted: boolean()
18213
18255
  }), object({ success: literal(true) }), {
@@ -19368,13 +19410,26 @@ var TrackAudioLabelSchema = object({
19368
19410
  * - `audio` — an audio event on the camera itself that was anomalous for
19369
19411
  * THAT camera, loud, and heard while nothing visual was happening (D62).
19370
19412
  *
19413
+ * - `onboard` — the CAMERA's own firmware, paired with a decoded frame in
19414
+ * that frame's pixel space. A real root detector and a SPATIAL one: its
19415
+ * boxes are the same kind of fact `pipeline`'s are, produced by a different
19416
+ * detector.
19417
+ *
19418
+ * `onboard` was missing here while `DetectionSourceSchema` already had it, so
19419
+ * a persisted onboard track failed `safeParse` on read and came back with NO
19420
+ * source at all — which `isSpatialTrack` then admitted by accident, through the
19421
+ * `undefined` arm rather than by anybody's decision, and which `excludeSources`
19422
+ * could not name.
19423
+ *
19371
19424
  * The spatial subsystems (tracker association, occupancy count, re-id /
19372
19425
  * embedding, resurrection) MUST skip every synthetic source. Test for that
19373
- * with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
19374
- * check silently readmits every source added after it was written.
19426
+ * with `isSpatialTrack`, which allow-lists the spatial sources explicitly — a
19427
+ * `!== 'sensor'` check silently readmits every source added after it was
19428
+ * written.
19375
19429
  */
19376
19430
  var TrackSourceSchema = _enum([
19377
19431
  "pipeline",
19432
+ "onboard",
19378
19433
  "sensor",
19379
19434
  "audio"
19380
19435
  ]);
@@ -20757,6 +20812,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
20757
20812
  * solid is this track", cheaper than re-deriving it from a trajectory. */
20758
20813
  framesMatched: number().int()
20759
20814
  })).readonly() });
20815
+ /**
20816
+ * The long-term series a camera produces. A closed union so a typo cannot
20817
+ * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
20818
+ * is an alias of this, not a second list.
20819
+ */
20820
+ var AnalyticsLtsSeriesSchema = _enum([
20821
+ "motion",
20822
+ "audio-dbfs",
20823
+ "battery",
20824
+ "occupancy"
20825
+ ]);
20826
+ /**
20827
+ * One closed 5-minute bucket of a long-term series.
20828
+ *
20829
+ * `samples` is how many readings the bucket folded — it is the ACTIVITY for a
20830
+ * series like `motion`, and the confidence for one like `occupancy`. A bucket
20831
+ * that exists at all was measured; a bucket that is absent was not, which is a
20832
+ * different claim from zero and the reason this is a sparse series.
20833
+ */
20834
+ var LtsBucketSchema = object({
20835
+ scope: string(),
20836
+ bucketStart: number().int(),
20837
+ samples: number().int().nonnegative(),
20838
+ sum: number(),
20839
+ min: number(),
20840
+ max: number()
20841
+ });
20760
20842
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20761
20843
  deviceId: number(),
20762
20844
  trackId: string()
@@ -20803,6 +20885,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20803
20885
  kinds: array(string()).optional(),
20804
20886
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20805
20887
  }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20888
+ deviceId: number(),
20889
+ series: AnalyticsLtsSeriesSchema,
20890
+ /** Omit for EVERY scope of this series in the range. */
20891
+ scope: string().optional(),
20892
+ from: number(),
20893
+ to: number(),
20894
+ limit: number().int().positive().optional()
20895
+ }), array(LtsBucketSchema).readonly()), method(object({
20806
20896
  deviceId: number(),
20807
20897
  since: number(),
20808
20898
  until: number(),
@@ -21421,6 +21511,40 @@ var MotionSourceEnum = _enum([
21421
21511
  */
21422
21512
  var MotionSourcesSchema = array(MotionSourceEnum);
21423
21513
  /**
21514
+ * Which root detector a camera runs — EXACTLY ONE.
21515
+ *
21516
+ * Deliberately the SAME vocabulary post-analysis already tags every detection
21517
+ * with (`DetectionSource`) rather than a second spelling of the same two
21518
+ * ideas: the value an operator picks here is the value that comes back on the
21519
+ * track, the overlay and the debug row.
21520
+ *
21521
+ * ## Why one, and why it is still an array
21522
+ *
21523
+ * Two roots on one camera is a capability nobody asked for and the operator has
21524
+ * since ruled out. It was never free: both planes feed the SAME per-device
21525
+ * stationary registry, both can promote the same parked object, and the two
21526
+ * detectors disagree about the same box by construction — which is the
21527
+ * disagreement D505 had to arbitrate. Removing the case removes the arbitration.
21528
+ *
21529
+ * The ARRAY shape survives because the stored settings and the attach payload
21530
+ * already speak it on every camera of every fleet, and a cap input that
21531
+ * suddenly refuses a stored value makes the camera un-attachable — a fail-closed
21532
+ * in the one direction that costs an operator their cameras. So the wire stays
21533
+ * tolerant and the TYPE is exact: a longer list is truncated to its first
21534
+ * element rather than refused, and `max(1)` is what every consumer can then
21535
+ * rely on.
21536
+ *
21537
+ * AT MOST one, not exactly one. The EMPTY list is a legal, deliberate pick —
21538
+ * a camera an operator left with no root detector — and `planDetectionSources`
21539
+ * has always said so: inventing a root for them is how a default acts without
21540
+ * anybody choosing it. Tightening this to `length(1)` broke that, and the
21541
+ * suite caught it.
21542
+ *
21543
+ * Measured before tightening (2026-09-15, this fleet, 6 h of attaches): 3327
21544
+ * `["pipeline"]` and 2 `["onboard"]`. Not one camera had two.
21545
+ */
21546
+ var DetectionSourcesSchema = preprocess((value) => Array.isArray(value) && value.length > 1 ? value.slice(0, 1) : value, array(DetectionSourceSchema).max(1));
21547
+ /**
21424
21548
  * Input shape for `pipeline-runner.reportMotion` cap method. Exported
21425
21549
  * so cap-side consumers (the orchestrator forward, the runner addon's
21426
21550
  * cap implementation, tests) can reuse the type instead of redeclaring
@@ -21571,6 +21695,35 @@ var RunnerCameraConfigSchema = object({
21571
21695
  * events; the orchestrator forwards to `reportMotion`.
21572
21696
  */
21573
21697
  motionSources: MotionSourcesSchema.default(["analyzer"]),
21698
+ /**
21699
+ * WHICH root detector runs for this camera. The detection counterpart of
21700
+ * {@link motionSources}, same shape and same discipline: a per-camera list
21701
+ * of sources the system understands, vendor-agnostic, owned here.
21702
+ *
21703
+ * - `pipeline` — this runner's own root step on decoded pixels (`steps`).
21704
+ * - `onboard` — the CAMERA's own detector, admitted only when it is named
21705
+ * here. Its boxes reach post-analysis on the same
21706
+ * `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
21707
+ * track gets the same tracker, the same overlay and the same detail
21708
+ * subtree (face / plate / embedding).
21709
+ *
21710
+ * `['onboard']` alone is the REPLACEMENT: this runner's root step does not
21711
+ * run, and that is where a camera with a usable onboard detector buys back
21712
+ * its share of the accelerator. It saves the root inference, not the decode
21713
+ * — the detail subtree still needs pixels to cut crops from.
21714
+ *
21715
+ * A camera whose onboard detections carry no geometry cannot serve as a root
21716
+ * step at all (there is nothing to track), and the cap that knows this says
21717
+ * so on `native-object-detection.getOptions().geometry`. On Reolink that is
21718
+ * every battery camera: the boxes ride a sub-stream a battery camera never
21719
+ * attaches, so such a camera stays on `['pipeline']` and its onboard AI is
21720
+ * worth exactly what it already is — a wake signal with a class.
21721
+ *
21722
+ * Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
21723
+ * vendor never turns a detector on for the operator, and neither does a
21724
+ * default.
21725
+ */
21726
+ detectionSources: DetectionSourcesSchema.default(["pipeline"]),
21574
21727
  pipelineEnabled: boolean().default(true),
21575
21728
  /** Ordered tree of video steps. Absent → runner skips video detection. */
21576
21729
  steps: array(PipelineStepInputSchema).readonly().optional(),
@@ -21752,7 +21905,47 @@ var RunnerLocalMetricsSchema = object({
21752
21905
  queueDepth: number(),
21753
21906
  frameLazy: FrameLazyMetricsSchema.optional()
21754
21907
  });
21908
+ /**
21909
+ * Pipeline Runner capability — runtime detection workhorse.
21910
+ *
21911
+ * One instance per node. Receives camera assignments from
21912
+ * `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
21913
+ * decoded frames, drains motion + detection queues, calls the local
21914
+ * `motion-detection` and `pipeline-executor` capabilities, and emits typed
21915
+ * `pipeline.inference-result` and `detection.motion-analysis` events on
21916
+ * the bus.
21917
+ *
21918
+ * Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
21919
+ * runner has zero knowledge of other agents, no global state, and never
21920
+ * makes assignment decisions itself.
21921
+ */
21922
+ /**
21923
+ * Why a runner declined to look at a camera out of band. Named rather than a
21924
+ * bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
21925
+ * already mid-session and one already bursting are four different facts, and a
21926
+ * caller told only "no" cannot tell a protection from a bug.
21927
+ */
21928
+ var OccupancyBurstRefusalSchema = _enum([
21929
+ "not-attached",
21930
+ "battery-asleep",
21931
+ "not-watching",
21932
+ "already-bursting"
21933
+ ]);
21755
21934
  method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
21935
+ deviceId: number(),
21936
+ /** How many frames to collect. The caller sizes this against what its
21937
+ * own confirmation needs; the runner clamps it to the field's range. */
21938
+ frames: number().int().positive().optional(),
21939
+ /** Why, for the log. A burst nobody can attribute is a cost nobody can
21940
+ * defend — the periodic one at least has a timer to point at. */
21941
+ reason: string()
21942
+ }), object({
21943
+ started: boolean(),
21944
+ refusedBecause: OccupancyBurstRefusalSchema.optional()
21945
+ }), {
21946
+ auth: "admin",
21947
+ kind: "mutation"
21948
+ }), method(object({
21756
21949
  handle: FrameHandleSchema,
21757
21950
  bbox: NativeCropBboxSchema,
21758
21951
  maxWidth: number().int().positive().optional(),
@@ -27221,7 +27414,7 @@ var NativeDetectionSchema = object({
27221
27414
  /** Firmware-provided confidence [0..1]. Reolink pushes don't carry it → undefined. */
27222
27415
  confidence: number().min(0).max(1).optional()
27223
27416
  });
27224
- object({
27417
+ var NativeObjectDetectionStatusSchema = object({
27225
27418
  /**
27226
27419
  * Last observed instance per class. Missing entries mean the class
27227
27420
  * is supported but nothing has been seen since the provider started.
@@ -27238,14 +27431,56 @@ object({
27238
27431
  supportedClasses: array(NativeObjectClassEnum).readonly(),
27239
27432
  /**
27240
27433
  * Whether forwarding of onboard AI detections is enabled for this device.
27241
- * Default FALSE (opt-in, cold-start) — onboard AI pushes are noisy/sparse and
27242
- * churn the tracker, so forwarding stays off until the operator enables it.
27434
+ *
27435
+ * Cold-start OFF on every camera. A vendor does not decide which detector a
27436
+ * camera runs — that is the operator's choice, and it is made where every
27437
+ * other detection choice is made. `geometry` on
27438
+ * {@link NativeObjectDetectionOptionsSchema} is how the form says what the
27439
+ * choice would buy this particular camera.
27243
27440
  */
27244
27441
  enabled: boolean()
27245
- }).extend({
27442
+ });
27443
+ /**
27444
+ * WHEN a camera's onboard detections carry geometry.
27445
+ *
27446
+ * - `boxed` — always. The provider holds a standing channel for the boxes, so
27447
+ * a detection is a trackable subject whenever the camera sees one.
27448
+ * - `while-streaming` — only while some stream of this camera is already open.
27449
+ * The boxes ride the video's own side-channel rather than a feed of their
27450
+ * own, so they cost nothing and they exist only when something is pulling.
27451
+ * On Reolink this is every BATTERY camera: a persistent second feed is real
27452
+ * radio drain, but a camera that is awake is awake precisely because
27453
+ * something is in front of it, and its stream is already being pulled.
27454
+ * - `flag-only` — never. The firmware ships the class and nothing else; the
27455
+ * pipeline needs geometry to make a subject, so such a push reaches the
27456
+ * tracker as motion and no further.
27457
+ *
27458
+ * Reported and not inferred, because the operator's question in front of the
27459
+ * picker is "what do I get", and these are three different answers (D14: the
27460
+ * derived form is only as honest as `getOptions`).
27461
+ */
27462
+ var NativeObjectGeometryEnum = _enum([
27463
+ "boxed",
27464
+ "while-streaming",
27465
+ "flag-only"
27466
+ ]);
27467
+ var NativeObjectDetectionOptionsSchema = object({
27468
+ /** Classes this firmware can detect — the same list the status reports. */
27469
+ supportedClasses: array(NativeObjectClassEnum).readonly(),
27470
+ /** What a detection from this camera carries. */
27471
+ geometry: NativeObjectGeometryEnum
27472
+ });
27473
+ var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
27474
+ NativeObjectDetectionStatusSchema.extend({
27246
27475
  /** Required by createRuntimeStateBridge — epoch ms of last refresh. */
27247
27476
  lastFetchedAt: number() });
27248
- DeviceType.Camera, method(object({
27477
+ DeviceType.Camera, method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema), method(object({
27478
+ deviceId: number(),
27479
+ settings: NativeObjectDetectionSettingsPatchSchema
27480
+ }), _void(), {
27481
+ kind: "mutation",
27482
+ auth: "admin"
27483
+ }), method(object({
27249
27484
  deviceId: number(),
27250
27485
  enabled: boolean()
27251
27486
  }), _void(), {
@@ -31044,7 +31279,28 @@ var PerScopeBreakdownSchema = object({
31044
31279
  /** Total tracked objects in this scope (frame / zone / unzoned). */
31045
31280
  totalObjects: number().int().nonnegative(),
31046
31281
  /** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
31047
- byClass: record(string(), number().int().nonnegative())
31282
+ byClass: record(string(), number().int().nonnegative()),
31283
+ /**
31284
+ * Of `totalObjects`, how many are STANDING — objects the census holds,
31285
+ * present but not going anywhere (a parked car, a bin, a statue).
31286
+ *
31287
+ * The operator's question about a scene has two halves and they answer
31288
+ * different things: "what is parked here" is stable for hours and must not
31289
+ * flap, while "who is walking through" is the thing an alert is about. One
31290
+ * number could only serve one of them, and `totalObjects` served the first
31291
+ * badly — a passer-by moved it and every occupancy rule saw a change.
31292
+ *
31293
+ * `standing + transient === totalObjects`, always. The total keeps its old
31294
+ * meaning exactly, so no rule written against it changes behaviour.
31295
+ *
31296
+ * ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
31297
+ * this field cannot say, and a reader that folds absence in as 0 reports "no
31298
+ * parked objects here" about a camera whose lot is full.
31299
+ */
31300
+ standingObjects: number().int().nonnegative().optional(),
31301
+ /** Of `totalObjects`, how many are passing through. See `standingObjects` —
31302
+ * same absence rule. */
31303
+ transientObjects: number().int().nonnegative().optional()
31048
31304
  });
31049
31305
  var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
31050
31306
  zoneId: string(),
@@ -33983,12 +34239,24 @@ Object.freeze({
33983
34239
  addonId: null,
33984
34240
  access: "create"
33985
34241
  },
34242
+ "nativeObjectDetection.getOptions": {
34243
+ capName: "native-object-detection",
34244
+ capScope: "device",
34245
+ addonId: null,
34246
+ access: "view"
34247
+ },
33986
34248
  "nativeObjectDetection.setEnabled": {
33987
34249
  capName: "native-object-detection",
33988
34250
  capScope: "device",
33989
34251
  addonId: null,
33990
34252
  access: "create"
33991
34253
  },
34254
+ "nativeObjectDetection.setSettings": {
34255
+ capName: "native-object-detection",
34256
+ capScope: "device",
34257
+ addonId: null,
34258
+ access: "create"
34259
+ },
33992
34260
  "navigation.getFeatures": {
33993
34261
  capName: "navigation",
33994
34262
  capScope: "device",
@@ -34253,6 +34521,12 @@ Object.freeze({
34253
34521
  addonId: null,
34254
34522
  access: "view"
34255
34523
  },
34524
+ "notificationRules.getClipRetentionAsks": {
34525
+ capName: "notification-rules",
34526
+ capScope: "system",
34527
+ addonId: null,
34528
+ access: "view"
34529
+ },
34256
34530
  "notificationRules.getConditionCatalog": {
34257
34531
  capName: "notification-rules",
34258
34532
  capScope: "system",
@@ -34763,6 +35037,12 @@ Object.freeze({
34763
35037
  addonId: null,
34764
35038
  access: "create"
34765
35039
  },
35040
+ "pipelineAnalytics.readLongTermSeries": {
35041
+ capName: "pipeline-analytics",
35042
+ capScope: "device",
35043
+ addonId: null,
35044
+ access: "view"
35045
+ },
34766
35046
  "pipelineAnalytics.rebuildObjectEmbeddings": {
34767
35047
  capName: "pipeline-analytics",
34768
35048
  capScope: "device",
@@ -35435,6 +35715,12 @@ Object.freeze({
35435
35715
  addonId: null,
35436
35716
  access: "create"
35437
35717
  },
35718
+ "pipelineRunner.requestOccupancyBurst": {
35719
+ capName: "pipeline-runner",
35720
+ capScope: "system",
35721
+ addonId: null,
35722
+ access: "create"
35723
+ },
35438
35724
  "pipelineRunner.runDetailSubtree": {
35439
35725
  capName: "pipeline-runner",
35440
35726
  capScope: "system",
@@ -38215,11 +38501,21 @@ Object.freeze({
38215
38501
  form: "single",
38216
38502
  optional: false
38217
38503
  }],
38504
+ "nativeObjectDetection.getOptions": [{
38505
+ name: "deviceId",
38506
+ form: "single",
38507
+ optional: false
38508
+ }],
38218
38509
  "nativeObjectDetection.setEnabled": [{
38219
38510
  name: "deviceId",
38220
38511
  form: "single",
38221
38512
  optional: false
38222
38513
  }],
38514
+ "nativeObjectDetection.setSettings": [{
38515
+ name: "deviceId",
38516
+ form: "single",
38517
+ optional: false
38518
+ }],
38223
38519
  "navigation.getFeatures": [{
38224
38520
  name: "deviceId",
38225
38521
  form: "single",
@@ -38569,6 +38865,11 @@ Object.freeze({
38569
38865
  form: "single",
38570
38866
  optional: false
38571
38867
  }],
38868
+ "pipelineAnalytics.readLongTermSeries": [{
38869
+ name: "deviceId",
38870
+ form: "single",
38871
+ optional: false
38872
+ }],
38572
38873
  "pipelineAnalytics.rebuildObjectEmbeddings": [{
38573
38874
  name: "deviceId",
38574
38875
  form: "single",
@@ -38759,6 +39060,11 @@ Object.freeze({
38759
39060
  form: "single",
38760
39061
  optional: false
38761
39062
  }],
39063
+ "pipelineRunner.requestOccupancyBurst": [{
39064
+ name: "deviceId",
39065
+ form: "single",
39066
+ optional: false
39067
+ }],
38762
39068
  "pipelineRunner.runDetailSubtree": [{
38763
39069
  name: "deviceId",
38764
39070
  form: "single",
@@ -5360,7 +5360,7 @@ var ZodIssueCode = {
5360
5360
  var ZodFirstPartyTypeKind;
5361
5361
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5362
5362
  //#endregion
5363
- //#region ../types/dist/sleep-DEuj7E3j.mjs
5363
+ //#region ../types/dist/sleep-BDR76Ykr.mjs
5364
5364
  /**
5365
5365
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5366
5366
  * window to float samples (D455).
@@ -7241,6 +7241,28 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7241
7241
  DeviceRole["GenericNotifier"] = "generic-notifier";
7242
7242
  return DeviceRole;
7243
7243
  }({});
7244
+ /**
7245
+ * Identity — preserves literal types for downstream inference.
7246
+ *
7247
+ * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
7248
+ * TypeScript does not widen each entry's literal `kind`/`auth` fields to
7249
+ * the broader unions declared on `CustomActionSpec`'s default generics.
7250
+ * Shape validity is enforced separately by the `customAction(...)` helper
7251
+ * whose return type is already a `CustomActionSpec<...>`.
7252
+ */
7253
+ function defineCustomActions(spec) {
7254
+ return spec;
7255
+ }
7256
+ function customAction(input, output, options) {
7257
+ return {
7258
+ input,
7259
+ output,
7260
+ kind: options?.kind ?? "query",
7261
+ auth: options?.auth ?? "protected",
7262
+ scope: options?.scope ?? { kind: "system" },
7263
+ ...options?.caller ? { caller: "required" } : {}
7264
+ };
7265
+ }
7244
7266
  var StaticDirOutputSchema = object({ staticDir: string() });
7245
7267
  var VersionOutputSchema = object({ version: string() });
7246
7268
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -12699,28 +12721,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
12699
12721
  content: string()
12700
12722
  })) }), { auth: "admin" });
12701
12723
  /**
12702
- * Identity — preserves literal types for downstream inference.
12703
- *
12704
- * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
12705
- * TypeScript does not widen each entry's literal `kind`/`auth` fields to
12706
- * the broader unions declared on `CustomActionSpec`'s default generics.
12707
- * Shape validity is enforced separately by the `customAction(...)` helper
12708
- * whose return type is already a `CustomActionSpec<...>`.
12709
- */
12710
- function defineCustomActions(spec) {
12711
- return spec;
12712
- }
12713
- function customAction(input, output, options) {
12714
- return {
12715
- input,
12716
- output,
12717
- kind: options?.kind ?? "query",
12718
- auth: options?.auth ?? "protected",
12719
- scope: options?.scope ?? { kind: "system" },
12720
- ...options?.caller ? { caller: "required" } : {}
12721
- };
12722
- }
12723
- /**
12724
12724
  * `custom-model-registry` — collection cap exposing operator-registered
12725
12725
  * custom detection models. Each provider (today: `addon-model-studio`)
12726
12726
  * contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
@@ -18160,6 +18160,48 @@ var NcAlarmConfigSchema = object({
18160
18160
  settings: NcAlarmSettingsSchema,
18161
18161
  coverage: array(NcAlarmModeCoverageSchema)
18162
18162
  });
18163
+ /**
18164
+ * ONE rule's demand on ONE camera's clip ring.
18165
+ *
18166
+ * A rule, not a camera: the broker takes the MAX over the asks that reach a
18167
+ * camera, so the answer stays a faithful description of the rules and the
18168
+ * bounding (the byte budget, the broker's own ceiling) stays where the cost
18169
+ * lives. A camera that appears in no ask is being told **nothing** — which is
18170
+ * a different thing from never having been told, and only the envelope
18171
+ * (`rulesLoaded`) can tell those apart.
18172
+ */
18173
+ var NcClipRetentionAskSchema = object({
18174
+ /**
18175
+ * The camera this ask is about. **Absent = every camera**, which is what a
18176
+ * rule with no `conditions.devices` means: it can fire anywhere, so it is
18177
+ * asking everywhere.
18178
+ */
18179
+ deviceId: number().int().nonnegative().optional(),
18180
+ /** Seconds of history the rule needs held for it. Never zero — a rule that
18181
+ * needs nothing produces no ask at all. */
18182
+ seconds: number().min(0).max(60),
18183
+ /** The profile the rule cuts from, when it named one. Absent = the cheapest
18184
+ * assigned, which is what the cut defaults to. */
18185
+ profile: CamProfileSchema.optional(),
18186
+ /** Who is asking — so a retention the operator did not expect names a rule. */
18187
+ ruleId: string(),
18188
+ ruleName: string(),
18189
+ /** Why this many seconds: the rule's own window, or the occupancy ceiling. */
18190
+ reason: _enum(["window", "occupancy"])
18191
+ });
18192
+ /**
18193
+ * The whole answer, with the one bit that keeps absence from reading as zero.
18194
+ *
18195
+ * `rulesLoaded` is false until the rule ledger has completed a load at least
18196
+ * once. A centre that is up but has not read its rules yet answers with an
18197
+ * empty ask list that means nothing at all — applying it would drop every
18198
+ * camera's ring to zero for the first events after a restart, which is the
18199
+ * footage this mechanism exists to keep.
18200
+ */
18201
+ var NcClipRetentionPlanSchema = object({
18202
+ rulesLoaded: boolean(),
18203
+ asks: array(NcClipRetentionAskSchema).readonly()
18204
+ });
18163
18205
  method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), method(object({ ruleId: string() }), object({ rule: NcRuleSchema.nullable() }), { auth: "admin" }), method(object({ rule: NcRuleInputSchema }), object({ rule: NcRuleSchema }), {
18164
18206
  kind: "mutation",
18165
18207
  auth: "admin",
@@ -18180,7 +18222,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
18180
18222
  }), object({ success: literal(true) }), {
18181
18223
  kind: "mutation",
18182
18224
  auth: "admin"
18183
- }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18225
+ }), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18184
18226
  deviceId: number().int(),
18185
18227
  muted: boolean()
18186
18228
  }), object({ success: literal(true) }), {
@@ -19341,13 +19383,26 @@ var TrackAudioLabelSchema = object({
19341
19383
  * - `audio` — an audio event on the camera itself that was anomalous for
19342
19384
  * THAT camera, loud, and heard while nothing visual was happening (D62).
19343
19385
  *
19386
+ * - `onboard` — the CAMERA's own firmware, paired with a decoded frame in
19387
+ * that frame's pixel space. A real root detector and a SPATIAL one: its
19388
+ * boxes are the same kind of fact `pipeline`'s are, produced by a different
19389
+ * detector.
19390
+ *
19391
+ * `onboard` was missing here while `DetectionSourceSchema` already had it, so
19392
+ * a persisted onboard track failed `safeParse` on read and came back with NO
19393
+ * source at all — which `isSpatialTrack` then admitted by accident, through the
19394
+ * `undefined` arm rather than by anybody's decision, and which `excludeSources`
19395
+ * could not name.
19396
+ *
19344
19397
  * The spatial subsystems (tracker association, occupancy count, re-id /
19345
19398
  * embedding, resurrection) MUST skip every synthetic source. Test for that
19346
- * with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
19347
- * check silently readmits every source added after it was written.
19399
+ * with `isSpatialTrack`, which allow-lists the spatial sources explicitly — a
19400
+ * `!== 'sensor'` check silently readmits every source added after it was
19401
+ * written.
19348
19402
  */
19349
19403
  var TrackSourceSchema = _enum([
19350
19404
  "pipeline",
19405
+ "onboard",
19351
19406
  "sensor",
19352
19407
  "audio"
19353
19408
  ]);
@@ -20730,6 +20785,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
20730
20785
  * solid is this track", cheaper than re-deriving it from a trajectory. */
20731
20786
  framesMatched: number().int()
20732
20787
  })).readonly() });
20788
+ /**
20789
+ * The long-term series a camera produces. A closed union so a typo cannot
20790
+ * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
20791
+ * is an alias of this, not a second list.
20792
+ */
20793
+ var AnalyticsLtsSeriesSchema = _enum([
20794
+ "motion",
20795
+ "audio-dbfs",
20796
+ "battery",
20797
+ "occupancy"
20798
+ ]);
20799
+ /**
20800
+ * One closed 5-minute bucket of a long-term series.
20801
+ *
20802
+ * `samples` is how many readings the bucket folded — it is the ACTIVITY for a
20803
+ * series like `motion`, and the confidence for one like `occupancy`. A bucket
20804
+ * that exists at all was measured; a bucket that is absent was not, which is a
20805
+ * different claim from zero and the reason this is a sparse series.
20806
+ */
20807
+ var LtsBucketSchema = object({
20808
+ scope: string(),
20809
+ bucketStart: number().int(),
20810
+ samples: number().int().nonnegative(),
20811
+ sum: number(),
20812
+ min: number(),
20813
+ max: number()
20814
+ });
20733
20815
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20734
20816
  deviceId: number(),
20735
20817
  trackId: string()
@@ -20776,6 +20858,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20776
20858
  kinds: array(string()).optional(),
20777
20859
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20778
20860
  }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20861
+ deviceId: number(),
20862
+ series: AnalyticsLtsSeriesSchema,
20863
+ /** Omit for EVERY scope of this series in the range. */
20864
+ scope: string().optional(),
20865
+ from: number(),
20866
+ to: number(),
20867
+ limit: number().int().positive().optional()
20868
+ }), array(LtsBucketSchema).readonly()), method(object({
20779
20869
  deviceId: number(),
20780
20870
  since: number(),
20781
20871
  until: number(),
@@ -21394,6 +21484,40 @@ var MotionSourceEnum = _enum([
21394
21484
  */
21395
21485
  var MotionSourcesSchema = array(MotionSourceEnum);
21396
21486
  /**
21487
+ * Which root detector a camera runs — EXACTLY ONE.
21488
+ *
21489
+ * Deliberately the SAME vocabulary post-analysis already tags every detection
21490
+ * with (`DetectionSource`) rather than a second spelling of the same two
21491
+ * ideas: the value an operator picks here is the value that comes back on the
21492
+ * track, the overlay and the debug row.
21493
+ *
21494
+ * ## Why one, and why it is still an array
21495
+ *
21496
+ * Two roots on one camera is a capability nobody asked for and the operator has
21497
+ * since ruled out. It was never free: both planes feed the SAME per-device
21498
+ * stationary registry, both can promote the same parked object, and the two
21499
+ * detectors disagree about the same box by construction — which is the
21500
+ * disagreement D505 had to arbitrate. Removing the case removes the arbitration.
21501
+ *
21502
+ * The ARRAY shape survives because the stored settings and the attach payload
21503
+ * already speak it on every camera of every fleet, and a cap input that
21504
+ * suddenly refuses a stored value makes the camera un-attachable — a fail-closed
21505
+ * in the one direction that costs an operator their cameras. So the wire stays
21506
+ * tolerant and the TYPE is exact: a longer list is truncated to its first
21507
+ * element rather than refused, and `max(1)` is what every consumer can then
21508
+ * rely on.
21509
+ *
21510
+ * AT MOST one, not exactly one. The EMPTY list is a legal, deliberate pick —
21511
+ * a camera an operator left with no root detector — and `planDetectionSources`
21512
+ * has always said so: inventing a root for them is how a default acts without
21513
+ * anybody choosing it. Tightening this to `length(1)` broke that, and the
21514
+ * suite caught it.
21515
+ *
21516
+ * Measured before tightening (2026-09-15, this fleet, 6 h of attaches): 3327
21517
+ * `["pipeline"]` and 2 `["onboard"]`. Not one camera had two.
21518
+ */
21519
+ var DetectionSourcesSchema = preprocess((value) => Array.isArray(value) && value.length > 1 ? value.slice(0, 1) : value, array(DetectionSourceSchema).max(1));
21520
+ /**
21397
21521
  * Input shape for `pipeline-runner.reportMotion` cap method. Exported
21398
21522
  * so cap-side consumers (the orchestrator forward, the runner addon's
21399
21523
  * cap implementation, tests) can reuse the type instead of redeclaring
@@ -21544,6 +21668,35 @@ var RunnerCameraConfigSchema = object({
21544
21668
  * events; the orchestrator forwards to `reportMotion`.
21545
21669
  */
21546
21670
  motionSources: MotionSourcesSchema.default(["analyzer"]),
21671
+ /**
21672
+ * WHICH root detector runs for this camera. The detection counterpart of
21673
+ * {@link motionSources}, same shape and same discipline: a per-camera list
21674
+ * of sources the system understands, vendor-agnostic, owned here.
21675
+ *
21676
+ * - `pipeline` — this runner's own root step on decoded pixels (`steps`).
21677
+ * - `onboard` — the CAMERA's own detector, admitted only when it is named
21678
+ * here. Its boxes reach post-analysis on the same
21679
+ * `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
21680
+ * track gets the same tracker, the same overlay and the same detail
21681
+ * subtree (face / plate / embedding).
21682
+ *
21683
+ * `['onboard']` alone is the REPLACEMENT: this runner's root step does not
21684
+ * run, and that is where a camera with a usable onboard detector buys back
21685
+ * its share of the accelerator. It saves the root inference, not the decode
21686
+ * — the detail subtree still needs pixels to cut crops from.
21687
+ *
21688
+ * A camera whose onboard detections carry no geometry cannot serve as a root
21689
+ * step at all (there is nothing to track), and the cap that knows this says
21690
+ * so on `native-object-detection.getOptions().geometry`. On Reolink that is
21691
+ * every battery camera: the boxes ride a sub-stream a battery camera never
21692
+ * attaches, so such a camera stays on `['pipeline']` and its onboard AI is
21693
+ * worth exactly what it already is — a wake signal with a class.
21694
+ *
21695
+ * Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
21696
+ * vendor never turns a detector on for the operator, and neither does a
21697
+ * default.
21698
+ */
21699
+ detectionSources: DetectionSourcesSchema.default(["pipeline"]),
21547
21700
  pipelineEnabled: boolean().default(true),
21548
21701
  /** Ordered tree of video steps. Absent → runner skips video detection. */
21549
21702
  steps: array(PipelineStepInputSchema).readonly().optional(),
@@ -21725,7 +21878,47 @@ var RunnerLocalMetricsSchema = object({
21725
21878
  queueDepth: number(),
21726
21879
  frameLazy: FrameLazyMetricsSchema.optional()
21727
21880
  });
21881
+ /**
21882
+ * Pipeline Runner capability — runtime detection workhorse.
21883
+ *
21884
+ * One instance per node. Receives camera assignments from
21885
+ * `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
21886
+ * decoded frames, drains motion + detection queues, calls the local
21887
+ * `motion-detection` and `pipeline-executor` capabilities, and emits typed
21888
+ * `pipeline.inference-result` and `detection.motion-analysis` events on
21889
+ * the bus.
21890
+ *
21891
+ * Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
21892
+ * runner has zero knowledge of other agents, no global state, and never
21893
+ * makes assignment decisions itself.
21894
+ */
21895
+ /**
21896
+ * Why a runner declined to look at a camera out of band. Named rather than a
21897
+ * bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
21898
+ * already mid-session and one already bursting are four different facts, and a
21899
+ * caller told only "no" cannot tell a protection from a bug.
21900
+ */
21901
+ var OccupancyBurstRefusalSchema = _enum([
21902
+ "not-attached",
21903
+ "battery-asleep",
21904
+ "not-watching",
21905
+ "already-bursting"
21906
+ ]);
21728
21907
  method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mutation" }), method(object({ deviceId: number() }), object({ success: literal(true) }), { kind: "mutation" }), method(ReportMotionInputSchema, object({ success: literal(true) }), { kind: "mutation" }), method(_void(), RunnerLocalLoadSchema), method(_void(), RunnerLocalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()), method(_void(), array(number()).readonly()), method(object({
21908
+ deviceId: number(),
21909
+ /** How many frames to collect. The caller sizes this against what its
21910
+ * own confirmation needs; the runner clamps it to the field's range. */
21911
+ frames: number().int().positive().optional(),
21912
+ /** Why, for the log. A burst nobody can attribute is a cost nobody can
21913
+ * defend — the periodic one at least has a timer to point at. */
21914
+ reason: string()
21915
+ }), object({
21916
+ started: boolean(),
21917
+ refusedBecause: OccupancyBurstRefusalSchema.optional()
21918
+ }), {
21919
+ auth: "admin",
21920
+ kind: "mutation"
21921
+ }), method(object({
21729
21922
  handle: FrameHandleSchema,
21730
21923
  bbox: NativeCropBboxSchema,
21731
21924
  maxWidth: number().int().positive().optional(),
@@ -27194,7 +27387,7 @@ var NativeDetectionSchema = object({
27194
27387
  /** Firmware-provided confidence [0..1]. Reolink pushes don't carry it → undefined. */
27195
27388
  confidence: number().min(0).max(1).optional()
27196
27389
  });
27197
- object({
27390
+ var NativeObjectDetectionStatusSchema = object({
27198
27391
  /**
27199
27392
  * Last observed instance per class. Missing entries mean the class
27200
27393
  * is supported but nothing has been seen since the provider started.
@@ -27211,14 +27404,56 @@ object({
27211
27404
  supportedClasses: array(NativeObjectClassEnum).readonly(),
27212
27405
  /**
27213
27406
  * Whether forwarding of onboard AI detections is enabled for this device.
27214
- * Default FALSE (opt-in, cold-start) — onboard AI pushes are noisy/sparse and
27215
- * churn the tracker, so forwarding stays off until the operator enables it.
27407
+ *
27408
+ * Cold-start OFF on every camera. A vendor does not decide which detector a
27409
+ * camera runs — that is the operator's choice, and it is made where every
27410
+ * other detection choice is made. `geometry` on
27411
+ * {@link NativeObjectDetectionOptionsSchema} is how the form says what the
27412
+ * choice would buy this particular camera.
27216
27413
  */
27217
27414
  enabled: boolean()
27218
- }).extend({
27415
+ });
27416
+ /**
27417
+ * WHEN a camera's onboard detections carry geometry.
27418
+ *
27419
+ * - `boxed` — always. The provider holds a standing channel for the boxes, so
27420
+ * a detection is a trackable subject whenever the camera sees one.
27421
+ * - `while-streaming` — only while some stream of this camera is already open.
27422
+ * The boxes ride the video's own side-channel rather than a feed of their
27423
+ * own, so they cost nothing and they exist only when something is pulling.
27424
+ * On Reolink this is every BATTERY camera: a persistent second feed is real
27425
+ * radio drain, but a camera that is awake is awake precisely because
27426
+ * something is in front of it, and its stream is already being pulled.
27427
+ * - `flag-only` — never. The firmware ships the class and nothing else; the
27428
+ * pipeline needs geometry to make a subject, so such a push reaches the
27429
+ * tracker as motion and no further.
27430
+ *
27431
+ * Reported and not inferred, because the operator's question in front of the
27432
+ * picker is "what do I get", and these are three different answers (D14: the
27433
+ * derived form is only as honest as `getOptions`).
27434
+ */
27435
+ var NativeObjectGeometryEnum = _enum([
27436
+ "boxed",
27437
+ "while-streaming",
27438
+ "flag-only"
27439
+ ]);
27440
+ var NativeObjectDetectionOptionsSchema = object({
27441
+ /** Classes this firmware can detect — the same list the status reports. */
27442
+ supportedClasses: array(NativeObjectClassEnum).readonly(),
27443
+ /** What a detection from this camera carries. */
27444
+ geometry: NativeObjectGeometryEnum
27445
+ });
27446
+ var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
27447
+ NativeObjectDetectionStatusSchema.extend({
27219
27448
  /** Required by createRuntimeStateBridge — epoch ms of last refresh. */
27220
27449
  lastFetchedAt: number() });
27221
- DeviceType.Camera, method(object({
27450
+ DeviceType.Camera, method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema), method(object({
27451
+ deviceId: number(),
27452
+ settings: NativeObjectDetectionSettingsPatchSchema
27453
+ }), _void(), {
27454
+ kind: "mutation",
27455
+ auth: "admin"
27456
+ }), method(object({
27222
27457
  deviceId: number(),
27223
27458
  enabled: boolean()
27224
27459
  }), _void(), {
@@ -31017,7 +31252,28 @@ var PerScopeBreakdownSchema = object({
31017
31252
  /** Total tracked objects in this scope (frame / zone / unzoned). */
31018
31253
  totalObjects: number().int().nonnegative(),
31019
31254
  /** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
31020
- byClass: record(string(), number().int().nonnegative())
31255
+ byClass: record(string(), number().int().nonnegative()),
31256
+ /**
31257
+ * Of `totalObjects`, how many are STANDING — objects the census holds,
31258
+ * present but not going anywhere (a parked car, a bin, a statue).
31259
+ *
31260
+ * The operator's question about a scene has two halves and they answer
31261
+ * different things: "what is parked here" is stable for hours and must not
31262
+ * flap, while "who is walking through" is the thing an alert is about. One
31263
+ * number could only serve one of them, and `totalObjects` served the first
31264
+ * badly — a passer-by moved it and every occupancy rule saw a change.
31265
+ *
31266
+ * `standing + transient === totalObjects`, always. The total keeps its old
31267
+ * meaning exactly, so no rule written against it changes behaviour.
31268
+ *
31269
+ * ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
31270
+ * this field cannot say, and a reader that folds absence in as 0 reports "no
31271
+ * parked objects here" about a camera whose lot is full.
31272
+ */
31273
+ standingObjects: number().int().nonnegative().optional(),
31274
+ /** Of `totalObjects`, how many are passing through. See `standingObjects` —
31275
+ * same absence rule. */
31276
+ transientObjects: number().int().nonnegative().optional()
31021
31277
  });
31022
31278
  var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
31023
31279
  zoneId: string(),
@@ -33956,12 +34212,24 @@ Object.freeze({
33956
34212
  addonId: null,
33957
34213
  access: "create"
33958
34214
  },
34215
+ "nativeObjectDetection.getOptions": {
34216
+ capName: "native-object-detection",
34217
+ capScope: "device",
34218
+ addonId: null,
34219
+ access: "view"
34220
+ },
33959
34221
  "nativeObjectDetection.setEnabled": {
33960
34222
  capName: "native-object-detection",
33961
34223
  capScope: "device",
33962
34224
  addonId: null,
33963
34225
  access: "create"
33964
34226
  },
34227
+ "nativeObjectDetection.setSettings": {
34228
+ capName: "native-object-detection",
34229
+ capScope: "device",
34230
+ addonId: null,
34231
+ access: "create"
34232
+ },
33965
34233
  "navigation.getFeatures": {
33966
34234
  capName: "navigation",
33967
34235
  capScope: "device",
@@ -34226,6 +34494,12 @@ Object.freeze({
34226
34494
  addonId: null,
34227
34495
  access: "view"
34228
34496
  },
34497
+ "notificationRules.getClipRetentionAsks": {
34498
+ capName: "notification-rules",
34499
+ capScope: "system",
34500
+ addonId: null,
34501
+ access: "view"
34502
+ },
34229
34503
  "notificationRules.getConditionCatalog": {
34230
34504
  capName: "notification-rules",
34231
34505
  capScope: "system",
@@ -34736,6 +35010,12 @@ Object.freeze({
34736
35010
  addonId: null,
34737
35011
  access: "create"
34738
35012
  },
35013
+ "pipelineAnalytics.readLongTermSeries": {
35014
+ capName: "pipeline-analytics",
35015
+ capScope: "device",
35016
+ addonId: null,
35017
+ access: "view"
35018
+ },
34739
35019
  "pipelineAnalytics.rebuildObjectEmbeddings": {
34740
35020
  capName: "pipeline-analytics",
34741
35021
  capScope: "device",
@@ -35408,6 +35688,12 @@ Object.freeze({
35408
35688
  addonId: null,
35409
35689
  access: "create"
35410
35690
  },
35691
+ "pipelineRunner.requestOccupancyBurst": {
35692
+ capName: "pipeline-runner",
35693
+ capScope: "system",
35694
+ addonId: null,
35695
+ access: "create"
35696
+ },
35411
35697
  "pipelineRunner.runDetailSubtree": {
35412
35698
  capName: "pipeline-runner",
35413
35699
  capScope: "system",
@@ -38188,11 +38474,21 @@ Object.freeze({
38188
38474
  form: "single",
38189
38475
  optional: false
38190
38476
  }],
38477
+ "nativeObjectDetection.getOptions": [{
38478
+ name: "deviceId",
38479
+ form: "single",
38480
+ optional: false
38481
+ }],
38191
38482
  "nativeObjectDetection.setEnabled": [{
38192
38483
  name: "deviceId",
38193
38484
  form: "single",
38194
38485
  optional: false
38195
38486
  }],
38487
+ "nativeObjectDetection.setSettings": [{
38488
+ name: "deviceId",
38489
+ form: "single",
38490
+ optional: false
38491
+ }],
38196
38492
  "navigation.getFeatures": [{
38197
38493
  name: "deviceId",
38198
38494
  form: "single",
@@ -38542,6 +38838,11 @@ Object.freeze({
38542
38838
  form: "single",
38543
38839
  optional: false
38544
38840
  }],
38841
+ "pipelineAnalytics.readLongTermSeries": [{
38842
+ name: "deviceId",
38843
+ form: "single",
38844
+ optional: false
38845
+ }],
38545
38846
  "pipelineAnalytics.rebuildObjectEmbeddings": [{
38546
38847
  name: "deviceId",
38547
38848
  form: "single",
@@ -38732,6 +39033,11 @@ Object.freeze({
38732
39033
  form: "single",
38733
39034
  optional: false
38734
39035
  }],
39036
+ "pipelineRunner.requestOccupancyBurst": [{
39037
+ name: "deviceId",
39038
+ form: "single",
39039
+ optional: false
39040
+ }],
38735
39041
  "pipelineRunner.runDetailSubtree": [{
38736
39042
  name: "deviceId",
38737
39043
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-alexa",
3
- "version": "1.2.109",
3
+ "version": "1.2.111",
4
4
  "description": "Alexa Smart Home Skill export — hub-side OAuth provider + directive handler. The companion AWS Lambda forwards Alexa directives to this addon's /addon/export-alexa/directive endpoint.",
5
5
  "keywords": [
6
6
  "camstack",