@camstack/addon-export-google 0.1.66 → 0.1.68

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.
@@ -5438,7 +5438,7 @@ var ZodIssueCode = {
5438
5438
  var ZodFirstPartyTypeKind;
5439
5439
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5440
5440
  //#endregion
5441
- //#region ../types/dist/sleep-DEuj7E3j.mjs
5441
+ //#region ../types/dist/sleep-BDR76Ykr.mjs
5442
5442
  /**
5443
5443
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5444
5444
  * window to float samples (D455).
@@ -7319,6 +7319,28 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7319
7319
  DeviceRole["GenericNotifier"] = "generic-notifier";
7320
7320
  return DeviceRole;
7321
7321
  }({});
7322
+ /**
7323
+ * Identity — preserves literal types for downstream inference.
7324
+ *
7325
+ * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
7326
+ * TypeScript does not widen each entry's literal `kind`/`auth` fields to
7327
+ * the broader unions declared on `CustomActionSpec`'s default generics.
7328
+ * Shape validity is enforced separately by the `customAction(...)` helper
7329
+ * whose return type is already a `CustomActionSpec<...>`.
7330
+ */
7331
+ function defineCustomActions(spec) {
7332
+ return spec;
7333
+ }
7334
+ function customAction(input, output, options) {
7335
+ return {
7336
+ input,
7337
+ output,
7338
+ kind: options?.kind ?? "query",
7339
+ auth: options?.auth ?? "protected",
7340
+ scope: options?.scope ?? { kind: "system" },
7341
+ ...options?.caller ? { caller: "required" } : {}
7342
+ };
7343
+ }
7322
7344
  var StaticDirOutputSchema = object({ staticDir: string() });
7323
7345
  var VersionOutputSchema = object({ version: string() });
7324
7346
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -12637,28 +12659,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
12637
12659
  content: string()
12638
12660
  })) }), { auth: "admin" });
12639
12661
  /**
12640
- * Identity — preserves literal types for downstream inference.
12641
- *
12642
- * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
12643
- * TypeScript does not widen each entry's literal `kind`/`auth` fields to
12644
- * the broader unions declared on `CustomActionSpec`'s default generics.
12645
- * Shape validity is enforced separately by the `customAction(...)` helper
12646
- * whose return type is already a `CustomActionSpec<...>`.
12647
- */
12648
- function defineCustomActions(spec) {
12649
- return spec;
12650
- }
12651
- function customAction(input, output, options) {
12652
- return {
12653
- input,
12654
- output,
12655
- kind: options?.kind ?? "query",
12656
- auth: options?.auth ?? "protected",
12657
- scope: options?.scope ?? { kind: "system" },
12658
- ...options?.caller ? { caller: "required" } : {}
12659
- };
12660
- }
12661
- /**
12662
12662
  * `custom-model-registry` — collection cap exposing operator-registered
12663
12663
  * custom detection models. Each provider (today: `addon-model-studio`)
12664
12664
  * contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
@@ -18098,6 +18098,48 @@ var NcAlarmConfigSchema = object({
18098
18098
  settings: NcAlarmSettingsSchema,
18099
18099
  coverage: array(NcAlarmModeCoverageSchema)
18100
18100
  });
18101
+ /**
18102
+ * ONE rule's demand on ONE camera's clip ring.
18103
+ *
18104
+ * A rule, not a camera: the broker takes the MAX over the asks that reach a
18105
+ * camera, so the answer stays a faithful description of the rules and the
18106
+ * bounding (the byte budget, the broker's own ceiling) stays where the cost
18107
+ * lives. A camera that appears in no ask is being told **nothing** — which is
18108
+ * a different thing from never having been told, and only the envelope
18109
+ * (`rulesLoaded`) can tell those apart.
18110
+ */
18111
+ var NcClipRetentionAskSchema = object({
18112
+ /**
18113
+ * The camera this ask is about. **Absent = every camera**, which is what a
18114
+ * rule with no `conditions.devices` means: it can fire anywhere, so it is
18115
+ * asking everywhere.
18116
+ */
18117
+ deviceId: number().int().nonnegative().optional(),
18118
+ /** Seconds of history the rule needs held for it. Never zero — a rule that
18119
+ * needs nothing produces no ask at all. */
18120
+ seconds: number().min(0).max(60),
18121
+ /** The profile the rule cuts from, when it named one. Absent = the cheapest
18122
+ * assigned, which is what the cut defaults to. */
18123
+ profile: CamProfileSchema.optional(),
18124
+ /** Who is asking — so a retention the operator did not expect names a rule. */
18125
+ ruleId: string(),
18126
+ ruleName: string(),
18127
+ /** Why this many seconds: the rule's own window, or the occupancy ceiling. */
18128
+ reason: _enum(["window", "occupancy"])
18129
+ });
18130
+ /**
18131
+ * The whole answer, with the one bit that keeps absence from reading as zero.
18132
+ *
18133
+ * `rulesLoaded` is false until the rule ledger has completed a load at least
18134
+ * once. A centre that is up but has not read its rules yet answers with an
18135
+ * empty ask list that means nothing at all — applying it would drop every
18136
+ * camera's ring to zero for the first events after a restart, which is the
18137
+ * footage this mechanism exists to keep.
18138
+ */
18139
+ var NcClipRetentionPlanSchema = object({
18140
+ rulesLoaded: boolean(),
18141
+ asks: array(NcClipRetentionAskSchema).readonly()
18142
+ });
18101
18143
  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 }), {
18102
18144
  kind: "mutation",
18103
18145
  auth: "admin",
@@ -18118,7 +18160,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
18118
18160
  }), object({ success: literal(true) }), {
18119
18161
  kind: "mutation",
18120
18162
  auth: "admin"
18121
- }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18163
+ }), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18122
18164
  deviceId: number().int(),
18123
18165
  muted: boolean()
18124
18166
  }), object({ success: literal(true) }), {
@@ -19279,13 +19321,26 @@ var TrackAudioLabelSchema = object({
19279
19321
  * - `audio` — an audio event on the camera itself that was anomalous for
19280
19322
  * THAT camera, loud, and heard while nothing visual was happening (D62).
19281
19323
  *
19324
+ * - `onboard` — the CAMERA's own firmware, paired with a decoded frame in
19325
+ * that frame's pixel space. A real root detector and a SPATIAL one: its
19326
+ * boxes are the same kind of fact `pipeline`'s are, produced by a different
19327
+ * detector.
19328
+ *
19329
+ * `onboard` was missing here while `DetectionSourceSchema` already had it, so
19330
+ * a persisted onboard track failed `safeParse` on read and came back with NO
19331
+ * source at all — which `isSpatialTrack` then admitted by accident, through the
19332
+ * `undefined` arm rather than by anybody's decision, and which `excludeSources`
19333
+ * could not name.
19334
+ *
19282
19335
  * The spatial subsystems (tracker association, occupancy count, re-id /
19283
19336
  * embedding, resurrection) MUST skip every synthetic source. Test for that
19284
- * with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
19285
- * check silently readmits every source added after it was written.
19337
+ * with `isSpatialTrack`, which allow-lists the spatial sources explicitly — a
19338
+ * `!== 'sensor'` check silently readmits every source added after it was
19339
+ * written.
19286
19340
  */
19287
19341
  var TrackSourceSchema = _enum([
19288
19342
  "pipeline",
19343
+ "onboard",
19289
19344
  "sensor",
19290
19345
  "audio"
19291
19346
  ]);
@@ -20668,6 +20723,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
20668
20723
  * solid is this track", cheaper than re-deriving it from a trajectory. */
20669
20724
  framesMatched: number().int()
20670
20725
  })).readonly() });
20726
+ /**
20727
+ * The long-term series a camera produces. A closed union so a typo cannot
20728
+ * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
20729
+ * is an alias of this, not a second list.
20730
+ */
20731
+ var AnalyticsLtsSeriesSchema = _enum([
20732
+ "motion",
20733
+ "audio-dbfs",
20734
+ "battery",
20735
+ "occupancy"
20736
+ ]);
20737
+ /**
20738
+ * One closed 5-minute bucket of a long-term series.
20739
+ *
20740
+ * `samples` is how many readings the bucket folded — it is the ACTIVITY for a
20741
+ * series like `motion`, and the confidence for one like `occupancy`. A bucket
20742
+ * that exists at all was measured; a bucket that is absent was not, which is a
20743
+ * different claim from zero and the reason this is a sparse series.
20744
+ */
20745
+ var LtsBucketSchema = object({
20746
+ scope: string(),
20747
+ bucketStart: number().int(),
20748
+ samples: number().int().nonnegative(),
20749
+ sum: number(),
20750
+ min: number(),
20751
+ max: number()
20752
+ });
20671
20753
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20672
20754
  deviceId: number(),
20673
20755
  trackId: string()
@@ -20714,6 +20796,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20714
20796
  kinds: array(string()).optional(),
20715
20797
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20716
20798
  }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20799
+ deviceId: number(),
20800
+ series: AnalyticsLtsSeriesSchema,
20801
+ /** Omit for EVERY scope of this series in the range. */
20802
+ scope: string().optional(),
20803
+ from: number(),
20804
+ to: number(),
20805
+ limit: number().int().positive().optional()
20806
+ }), array(LtsBucketSchema).readonly()), method(object({
20717
20807
  deviceId: number(),
20718
20808
  since: number(),
20719
20809
  until: number(),
@@ -21332,6 +21422,40 @@ var MotionSourceEnum = _enum([
21332
21422
  */
21333
21423
  var MotionSourcesSchema = array(MotionSourceEnum);
21334
21424
  /**
21425
+ * Which root detector a camera runs — EXACTLY ONE.
21426
+ *
21427
+ * Deliberately the SAME vocabulary post-analysis already tags every detection
21428
+ * with (`DetectionSource`) rather than a second spelling of the same two
21429
+ * ideas: the value an operator picks here is the value that comes back on the
21430
+ * track, the overlay and the debug row.
21431
+ *
21432
+ * ## Why one, and why it is still an array
21433
+ *
21434
+ * Two roots on one camera is a capability nobody asked for and the operator has
21435
+ * since ruled out. It was never free: both planes feed the SAME per-device
21436
+ * stationary registry, both can promote the same parked object, and the two
21437
+ * detectors disagree about the same box by construction — which is the
21438
+ * disagreement D505 had to arbitrate. Removing the case removes the arbitration.
21439
+ *
21440
+ * The ARRAY shape survives because the stored settings and the attach payload
21441
+ * already speak it on every camera of every fleet, and a cap input that
21442
+ * suddenly refuses a stored value makes the camera un-attachable — a fail-closed
21443
+ * in the one direction that costs an operator their cameras. So the wire stays
21444
+ * tolerant and the TYPE is exact: a longer list is truncated to its first
21445
+ * element rather than refused, and `max(1)` is what every consumer can then
21446
+ * rely on.
21447
+ *
21448
+ * AT MOST one, not exactly one. The EMPTY list is a legal, deliberate pick —
21449
+ * a camera an operator left with no root detector — and `planDetectionSources`
21450
+ * has always said so: inventing a root for them is how a default acts without
21451
+ * anybody choosing it. Tightening this to `length(1)` broke that, and the
21452
+ * suite caught it.
21453
+ *
21454
+ * Measured before tightening (2026-09-15, this fleet, 6 h of attaches): 3327
21455
+ * `["pipeline"]` and 2 `["onboard"]`. Not one camera had two.
21456
+ */
21457
+ var DetectionSourcesSchema = preprocess((value) => Array.isArray(value) && value.length > 1 ? value.slice(0, 1) : value, array(DetectionSourceSchema).max(1));
21458
+ /**
21335
21459
  * Input shape for `pipeline-runner.reportMotion` cap method. Exported
21336
21460
  * so cap-side consumers (the orchestrator forward, the runner addon's
21337
21461
  * cap implementation, tests) can reuse the type instead of redeclaring
@@ -21482,6 +21606,35 @@ var RunnerCameraConfigSchema = object({
21482
21606
  * events; the orchestrator forwards to `reportMotion`.
21483
21607
  */
21484
21608
  motionSources: MotionSourcesSchema.default(["analyzer"]),
21609
+ /**
21610
+ * WHICH root detector runs for this camera. The detection counterpart of
21611
+ * {@link motionSources}, same shape and same discipline: a per-camera list
21612
+ * of sources the system understands, vendor-agnostic, owned here.
21613
+ *
21614
+ * - `pipeline` — this runner's own root step on decoded pixels (`steps`).
21615
+ * - `onboard` — the CAMERA's own detector, admitted only when it is named
21616
+ * here. Its boxes reach post-analysis on the same
21617
+ * `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
21618
+ * track gets the same tracker, the same overlay and the same detail
21619
+ * subtree (face / plate / embedding).
21620
+ *
21621
+ * `['onboard']` alone is the REPLACEMENT: this runner's root step does not
21622
+ * run, and that is where a camera with a usable onboard detector buys back
21623
+ * its share of the accelerator. It saves the root inference, not the decode
21624
+ * — the detail subtree still needs pixels to cut crops from.
21625
+ *
21626
+ * A camera whose onboard detections carry no geometry cannot serve as a root
21627
+ * step at all (there is nothing to track), and the cap that knows this says
21628
+ * so on `native-object-detection.getOptions().geometry`. On Reolink that is
21629
+ * every battery camera: the boxes ride a sub-stream a battery camera never
21630
+ * attaches, so such a camera stays on `['pipeline']` and its onboard AI is
21631
+ * worth exactly what it already is — a wake signal with a class.
21632
+ *
21633
+ * Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
21634
+ * vendor never turns a detector on for the operator, and neither does a
21635
+ * default.
21636
+ */
21637
+ detectionSources: DetectionSourcesSchema.default(["pipeline"]),
21485
21638
  pipelineEnabled: boolean().default(true),
21486
21639
  /** Ordered tree of video steps. Absent → runner skips video detection. */
21487
21640
  steps: array(PipelineStepInputSchema).readonly().optional(),
@@ -21663,7 +21816,47 @@ var RunnerLocalMetricsSchema = object({
21663
21816
  queueDepth: number(),
21664
21817
  frameLazy: FrameLazyMetricsSchema.optional()
21665
21818
  });
21819
+ /**
21820
+ * Pipeline Runner capability — runtime detection workhorse.
21821
+ *
21822
+ * One instance per node. Receives camera assignments from
21823
+ * `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
21824
+ * decoded frames, drains motion + detection queues, calls the local
21825
+ * `motion-detection` and `pipeline-executor` capabilities, and emits typed
21826
+ * `pipeline.inference-result` and `detection.motion-analysis` events on
21827
+ * the bus.
21828
+ *
21829
+ * Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
21830
+ * runner has zero knowledge of other agents, no global state, and never
21831
+ * makes assignment decisions itself.
21832
+ */
21833
+ /**
21834
+ * Why a runner declined to look at a camera out of band. Named rather than a
21835
+ * bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
21836
+ * already mid-session and one already bursting are four different facts, and a
21837
+ * caller told only "no" cannot tell a protection from a bug.
21838
+ */
21839
+ var OccupancyBurstRefusalSchema = _enum([
21840
+ "not-attached",
21841
+ "battery-asleep",
21842
+ "not-watching",
21843
+ "already-bursting"
21844
+ ]);
21666
21845
  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({
21846
+ deviceId: number(),
21847
+ /** How many frames to collect. The caller sizes this against what its
21848
+ * own confirmation needs; the runner clamps it to the field's range. */
21849
+ frames: number().int().positive().optional(),
21850
+ /** Why, for the log. A burst nobody can attribute is a cost nobody can
21851
+ * defend — the periodic one at least has a timer to point at. */
21852
+ reason: string()
21853
+ }), object({
21854
+ started: boolean(),
21855
+ refusedBecause: OccupancyBurstRefusalSchema.optional()
21856
+ }), {
21857
+ auth: "admin",
21858
+ kind: "mutation"
21859
+ }), method(object({
21667
21860
  handle: FrameHandleSchema,
21668
21861
  bbox: NativeCropBboxSchema,
21669
21862
  maxWidth: number().int().positive().optional(),
@@ -27144,7 +27337,7 @@ var NativeDetectionSchema = object({
27144
27337
  /** Firmware-provided confidence [0..1]. Reolink pushes don't carry it → undefined. */
27145
27338
  confidence: number().min(0).max(1).optional()
27146
27339
  });
27147
- object({
27340
+ var NativeObjectDetectionStatusSchema = object({
27148
27341
  /**
27149
27342
  * Last observed instance per class. Missing entries mean the class
27150
27343
  * is supported but nothing has been seen since the provider started.
@@ -27161,14 +27354,56 @@ object({
27161
27354
  supportedClasses: array(NativeObjectClassEnum).readonly(),
27162
27355
  /**
27163
27356
  * Whether forwarding of onboard AI detections is enabled for this device.
27164
- * Default FALSE (opt-in, cold-start) — onboard AI pushes are noisy/sparse and
27165
- * churn the tracker, so forwarding stays off until the operator enables it.
27357
+ *
27358
+ * Cold-start OFF on every camera. A vendor does not decide which detector a
27359
+ * camera runs — that is the operator's choice, and it is made where every
27360
+ * other detection choice is made. `geometry` on
27361
+ * {@link NativeObjectDetectionOptionsSchema} is how the form says what the
27362
+ * choice would buy this particular camera.
27166
27363
  */
27167
27364
  enabled: boolean()
27168
- }).extend({
27365
+ });
27366
+ /**
27367
+ * WHEN a camera's onboard detections carry geometry.
27368
+ *
27369
+ * - `boxed` — always. The provider holds a standing channel for the boxes, so
27370
+ * a detection is a trackable subject whenever the camera sees one.
27371
+ * - `while-streaming` — only while some stream of this camera is already open.
27372
+ * The boxes ride the video's own side-channel rather than a feed of their
27373
+ * own, so they cost nothing and they exist only when something is pulling.
27374
+ * On Reolink this is every BATTERY camera: a persistent second feed is real
27375
+ * radio drain, but a camera that is awake is awake precisely because
27376
+ * something is in front of it, and its stream is already being pulled.
27377
+ * - `flag-only` — never. The firmware ships the class and nothing else; the
27378
+ * pipeline needs geometry to make a subject, so such a push reaches the
27379
+ * tracker as motion and no further.
27380
+ *
27381
+ * Reported and not inferred, because the operator's question in front of the
27382
+ * picker is "what do I get", and these are three different answers (D14: the
27383
+ * derived form is only as honest as `getOptions`).
27384
+ */
27385
+ var NativeObjectGeometryEnum = _enum([
27386
+ "boxed",
27387
+ "while-streaming",
27388
+ "flag-only"
27389
+ ]);
27390
+ var NativeObjectDetectionOptionsSchema = object({
27391
+ /** Classes this firmware can detect — the same list the status reports. */
27392
+ supportedClasses: array(NativeObjectClassEnum).readonly(),
27393
+ /** What a detection from this camera carries. */
27394
+ geometry: NativeObjectGeometryEnum
27395
+ });
27396
+ var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
27397
+ NativeObjectDetectionStatusSchema.extend({
27169
27398
  /** Required by createRuntimeStateBridge — epoch ms of last refresh. */
27170
27399
  lastFetchedAt: number() });
27171
- DeviceType.Camera, method(object({
27400
+ DeviceType.Camera, method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema), method(object({
27401
+ deviceId: number(),
27402
+ settings: NativeObjectDetectionSettingsPatchSchema
27403
+ }), _void(), {
27404
+ kind: "mutation",
27405
+ auth: "admin"
27406
+ }), method(object({
27172
27407
  deviceId: number(),
27173
27408
  enabled: boolean()
27174
27409
  }), _void(), {
@@ -30974,7 +31209,28 @@ var PerScopeBreakdownSchema = object({
30974
31209
  /** Total tracked objects in this scope (frame / zone / unzoned). */
30975
31210
  totalObjects: number().int().nonnegative(),
30976
31211
  /** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
30977
- byClass: record(string(), number().int().nonnegative())
31212
+ byClass: record(string(), number().int().nonnegative()),
31213
+ /**
31214
+ * Of `totalObjects`, how many are STANDING — objects the census holds,
31215
+ * present but not going anywhere (a parked car, a bin, a statue).
31216
+ *
31217
+ * The operator's question about a scene has two halves and they answer
31218
+ * different things: "what is parked here" is stable for hours and must not
31219
+ * flap, while "who is walking through" is the thing an alert is about. One
31220
+ * number could only serve one of them, and `totalObjects` served the first
31221
+ * badly — a passer-by moved it and every occupancy rule saw a change.
31222
+ *
31223
+ * `standing + transient === totalObjects`, always. The total keeps its old
31224
+ * meaning exactly, so no rule written against it changes behaviour.
31225
+ *
31226
+ * ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
31227
+ * this field cannot say, and a reader that folds absence in as 0 reports "no
31228
+ * parked objects here" about a camera whose lot is full.
31229
+ */
31230
+ standingObjects: number().int().nonnegative().optional(),
31231
+ /** Of `totalObjects`, how many are passing through. See `standingObjects` —
31232
+ * same absence rule. */
31233
+ transientObjects: number().int().nonnegative().optional()
30978
31234
  });
30979
31235
  var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
30980
31236
  zoneId: string(),
@@ -33913,12 +34169,24 @@ Object.freeze({
33913
34169
  addonId: null,
33914
34170
  access: "create"
33915
34171
  },
34172
+ "nativeObjectDetection.getOptions": {
34173
+ capName: "native-object-detection",
34174
+ capScope: "device",
34175
+ addonId: null,
34176
+ access: "view"
34177
+ },
33916
34178
  "nativeObjectDetection.setEnabled": {
33917
34179
  capName: "native-object-detection",
33918
34180
  capScope: "device",
33919
34181
  addonId: null,
33920
34182
  access: "create"
33921
34183
  },
34184
+ "nativeObjectDetection.setSettings": {
34185
+ capName: "native-object-detection",
34186
+ capScope: "device",
34187
+ addonId: null,
34188
+ access: "create"
34189
+ },
33922
34190
  "navigation.getFeatures": {
33923
34191
  capName: "navigation",
33924
34192
  capScope: "device",
@@ -34183,6 +34451,12 @@ Object.freeze({
34183
34451
  addonId: null,
34184
34452
  access: "view"
34185
34453
  },
34454
+ "notificationRules.getClipRetentionAsks": {
34455
+ capName: "notification-rules",
34456
+ capScope: "system",
34457
+ addonId: null,
34458
+ access: "view"
34459
+ },
34186
34460
  "notificationRules.getConditionCatalog": {
34187
34461
  capName: "notification-rules",
34188
34462
  capScope: "system",
@@ -34693,6 +34967,12 @@ Object.freeze({
34693
34967
  addonId: null,
34694
34968
  access: "create"
34695
34969
  },
34970
+ "pipelineAnalytics.readLongTermSeries": {
34971
+ capName: "pipeline-analytics",
34972
+ capScope: "device",
34973
+ addonId: null,
34974
+ access: "view"
34975
+ },
34696
34976
  "pipelineAnalytics.rebuildObjectEmbeddings": {
34697
34977
  capName: "pipeline-analytics",
34698
34978
  capScope: "device",
@@ -35365,6 +35645,12 @@ Object.freeze({
35365
35645
  addonId: null,
35366
35646
  access: "create"
35367
35647
  },
35648
+ "pipelineRunner.requestOccupancyBurst": {
35649
+ capName: "pipeline-runner",
35650
+ capScope: "system",
35651
+ addonId: null,
35652
+ access: "create"
35653
+ },
35368
35654
  "pipelineRunner.runDetailSubtree": {
35369
35655
  capName: "pipeline-runner",
35370
35656
  capScope: "system",
@@ -38145,11 +38431,21 @@ Object.freeze({
38145
38431
  form: "single",
38146
38432
  optional: false
38147
38433
  }],
38434
+ "nativeObjectDetection.getOptions": [{
38435
+ name: "deviceId",
38436
+ form: "single",
38437
+ optional: false
38438
+ }],
38148
38439
  "nativeObjectDetection.setEnabled": [{
38149
38440
  name: "deviceId",
38150
38441
  form: "single",
38151
38442
  optional: false
38152
38443
  }],
38444
+ "nativeObjectDetection.setSettings": [{
38445
+ name: "deviceId",
38446
+ form: "single",
38447
+ optional: false
38448
+ }],
38153
38449
  "navigation.getFeatures": [{
38154
38450
  name: "deviceId",
38155
38451
  form: "single",
@@ -38499,6 +38795,11 @@ Object.freeze({
38499
38795
  form: "single",
38500
38796
  optional: false
38501
38797
  }],
38798
+ "pipelineAnalytics.readLongTermSeries": [{
38799
+ name: "deviceId",
38800
+ form: "single",
38801
+ optional: false
38802
+ }],
38502
38803
  "pipelineAnalytics.rebuildObjectEmbeddings": [{
38503
38804
  name: "deviceId",
38504
38805
  form: "single",
@@ -38689,6 +38990,11 @@ Object.freeze({
38689
38990
  form: "single",
38690
38991
  optional: false
38691
38992
  }],
38993
+ "pipelineRunner.requestOccupancyBurst": [{
38994
+ name: "deviceId",
38995
+ form: "single",
38996
+ optional: false
38997
+ }],
38692
38998
  "pipelineRunner.runDetailSubtree": [{
38693
38999
  name: "deviceId",
38694
39000
  form: "single",
@@ -5434,7 +5434,7 @@ var ZodIssueCode = {
5434
5434
  var ZodFirstPartyTypeKind;
5435
5435
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5436
5436
  //#endregion
5437
- //#region ../types/dist/sleep-DEuj7E3j.mjs
5437
+ //#region ../types/dist/sleep-BDR76Ykr.mjs
5438
5438
  /**
5439
5439
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5440
5440
  * window to float samples (D455).
@@ -7315,6 +7315,28 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7315
7315
  DeviceRole["GenericNotifier"] = "generic-notifier";
7316
7316
  return DeviceRole;
7317
7317
  }({});
7318
+ /**
7319
+ * Identity — preserves literal types for downstream inference.
7320
+ *
7321
+ * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
7322
+ * TypeScript does not widen each entry's literal `kind`/`auth` fields to
7323
+ * the broader unions declared on `CustomActionSpec`'s default generics.
7324
+ * Shape validity is enforced separately by the `customAction(...)` helper
7325
+ * whose return type is already a `CustomActionSpec<...>`.
7326
+ */
7327
+ function defineCustomActions(spec) {
7328
+ return spec;
7329
+ }
7330
+ function customAction(input, output, options) {
7331
+ return {
7332
+ input,
7333
+ output,
7334
+ kind: options?.kind ?? "query",
7335
+ auth: options?.auth ?? "protected",
7336
+ scope: options?.scope ?? { kind: "system" },
7337
+ ...options?.caller ? { caller: "required" } : {}
7338
+ };
7339
+ }
7318
7340
  var StaticDirOutputSchema = object({ staticDir: string() });
7319
7341
  var VersionOutputSchema = object({ version: string() });
7320
7342
  method(_void(), StaticDirOutputSchema, { auth: "admin" }), method(_void(), VersionOutputSchema, { auth: "admin" });
@@ -12633,28 +12655,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
12633
12655
  content: string()
12634
12656
  })) }), { auth: "admin" });
12635
12657
  /**
12636
- * Identity — preserves literal types for downstream inference.
12637
- *
12638
- * The constraint is `Record<string, unknown>` (not `CustomActionsSpec`) so
12639
- * TypeScript does not widen each entry's literal `kind`/`auth` fields to
12640
- * the broader unions declared on `CustomActionSpec`'s default generics.
12641
- * Shape validity is enforced separately by the `customAction(...)` helper
12642
- * whose return type is already a `CustomActionSpec<...>`.
12643
- */
12644
- function defineCustomActions(spec) {
12645
- return spec;
12646
- }
12647
- function customAction(input, output, options) {
12648
- return {
12649
- input,
12650
- output,
12651
- kind: options?.kind ?? "query",
12652
- auth: options?.auth ?? "protected",
12653
- scope: options?.scope ?? { kind: "system" },
12654
- ...options?.caller ? { caller: "required" } : {}
12655
- };
12656
- }
12657
- /**
12658
12658
  * `custom-model-registry` — collection cap exposing operator-registered
12659
12659
  * custom detection models. Each provider (today: `addon-model-studio`)
12660
12660
  * contributes a list of `CustomModelDescriptor`s; the hub auto-concatenates
@@ -18094,6 +18094,48 @@ var NcAlarmConfigSchema = object({
18094
18094
  settings: NcAlarmSettingsSchema,
18095
18095
  coverage: array(NcAlarmModeCoverageSchema)
18096
18096
  });
18097
+ /**
18098
+ * ONE rule's demand on ONE camera's clip ring.
18099
+ *
18100
+ * A rule, not a camera: the broker takes the MAX over the asks that reach a
18101
+ * camera, so the answer stays a faithful description of the rules and the
18102
+ * bounding (the byte budget, the broker's own ceiling) stays where the cost
18103
+ * lives. A camera that appears in no ask is being told **nothing** — which is
18104
+ * a different thing from never having been told, and only the envelope
18105
+ * (`rulesLoaded`) can tell those apart.
18106
+ */
18107
+ var NcClipRetentionAskSchema = object({
18108
+ /**
18109
+ * The camera this ask is about. **Absent = every camera**, which is what a
18110
+ * rule with no `conditions.devices` means: it can fire anywhere, so it is
18111
+ * asking everywhere.
18112
+ */
18113
+ deviceId: number().int().nonnegative().optional(),
18114
+ /** Seconds of history the rule needs held for it. Never zero — a rule that
18115
+ * needs nothing produces no ask at all. */
18116
+ seconds: number().min(0).max(60),
18117
+ /** The profile the rule cuts from, when it named one. Absent = the cheapest
18118
+ * assigned, which is what the cut defaults to. */
18119
+ profile: CamProfileSchema.optional(),
18120
+ /** Who is asking — so a retention the operator did not expect names a rule. */
18121
+ ruleId: string(),
18122
+ ruleName: string(),
18123
+ /** Why this many seconds: the rule's own window, or the occupancy ceiling. */
18124
+ reason: _enum(["window", "occupancy"])
18125
+ });
18126
+ /**
18127
+ * The whole answer, with the one bit that keeps absence from reading as zero.
18128
+ *
18129
+ * `rulesLoaded` is false until the rule ledger has completed a load at least
18130
+ * once. A centre that is up but has not read its rules yet answers with an
18131
+ * empty ask list that means nothing at all — applying it would drop every
18132
+ * camera's ring to zero for the first events after a restart, which is the
18133
+ * footage this mechanism exists to keep.
18134
+ */
18135
+ var NcClipRetentionPlanSchema = object({
18136
+ rulesLoaded: boolean(),
18137
+ asks: array(NcClipRetentionAskSchema).readonly()
18138
+ });
18097
18139
  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 }), {
18098
18140
  kind: "mutation",
18099
18141
  auth: "admin",
@@ -18114,7 +18156,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
18114
18156
  }), object({ success: literal(true) }), {
18115
18157
  kind: "mutation",
18116
18158
  auth: "admin"
18117
- }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18159
+ }), method(object({}), NcClipRetentionPlanSchema, { auth: "admin" }), method(object({}), object({ mutedDeviceIds: array(number().int()).readonly() }), { auth: "admin" }), method(object({
18118
18160
  deviceId: number().int(),
18119
18161
  muted: boolean()
18120
18162
  }), object({ success: literal(true) }), {
@@ -19275,13 +19317,26 @@ var TrackAudioLabelSchema = object({
19275
19317
  * - `audio` — an audio event on the camera itself that was anomalous for
19276
19318
  * THAT camera, loud, and heard while nothing visual was happening (D62).
19277
19319
  *
19320
+ * - `onboard` — the CAMERA's own firmware, paired with a decoded frame in
19321
+ * that frame's pixel space. A real root detector and a SPATIAL one: its
19322
+ * boxes are the same kind of fact `pipeline`'s are, produced by a different
19323
+ * detector.
19324
+ *
19325
+ * `onboard` was missing here while `DetectionSourceSchema` already had it, so
19326
+ * a persisted onboard track failed `safeParse` on read and came back with NO
19327
+ * source at all — which `isSpatialTrack` then admitted by accident, through the
19328
+ * `undefined` arm rather than by anybody's decision, and which `excludeSources`
19329
+ * could not name.
19330
+ *
19278
19331
  * The spatial subsystems (tracker association, occupancy count, re-id /
19279
19332
  * embedding, resurrection) MUST skip every synthetic source. Test for that
19280
- * with `isSpatialTrack`, which allow-lists `pipeline` — a `!== 'sensor'`
19281
- * check silently readmits every source added after it was written.
19333
+ * with `isSpatialTrack`, which allow-lists the spatial sources explicitly — a
19334
+ * `!== 'sensor'` check silently readmits every source added after it was
19335
+ * written.
19282
19336
  */
19283
19337
  var TrackSourceSchema = _enum([
19284
19338
  "pipeline",
19339
+ "onboard",
19285
19340
  "sensor",
19286
19341
  "audio"
19287
19342
  ]);
@@ -20664,6 +20719,33 @@ var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
20664
20719
  * solid is this track", cheaper than re-deriving it from a trajectory. */
20665
20720
  framesMatched: number().int()
20666
20721
  })).readonly() });
20722
+ /**
20723
+ * The long-term series a camera produces. A closed union so a typo cannot
20724
+ * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
20725
+ * is an alias of this, not a second list.
20726
+ */
20727
+ var AnalyticsLtsSeriesSchema = _enum([
20728
+ "motion",
20729
+ "audio-dbfs",
20730
+ "battery",
20731
+ "occupancy"
20732
+ ]);
20733
+ /**
20734
+ * One closed 5-minute bucket of a long-term series.
20735
+ *
20736
+ * `samples` is how many readings the bucket folded — it is the ACTIVITY for a
20737
+ * series like `motion`, and the confidence for one like `occupancy`. A bucket
20738
+ * that exists at all was measured; a bucket that is absent was not, which is a
20739
+ * different claim from zero and the reason this is a sparse series.
20740
+ */
20741
+ var LtsBucketSchema = object({
20742
+ scope: string(),
20743
+ bucketStart: number().int(),
20744
+ samples: number().int().nonnegative(),
20745
+ sum: number(),
20746
+ min: number(),
20747
+ max: number()
20748
+ });
20667
20749
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
20668
20750
  deviceId: number(),
20669
20751
  trackId: string()
@@ -20710,6 +20792,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20710
20792
  kinds: array(string()).optional(),
20711
20793
  limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
20712
20794
  }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
20795
+ deviceId: number(),
20796
+ series: AnalyticsLtsSeriesSchema,
20797
+ /** Omit for EVERY scope of this series in the range. */
20798
+ scope: string().optional(),
20799
+ from: number(),
20800
+ to: number(),
20801
+ limit: number().int().positive().optional()
20802
+ }), array(LtsBucketSchema).readonly()), method(object({
20713
20803
  deviceId: number(),
20714
20804
  since: number(),
20715
20805
  until: number(),
@@ -21328,6 +21418,40 @@ var MotionSourceEnum = _enum([
21328
21418
  */
21329
21419
  var MotionSourcesSchema = array(MotionSourceEnum);
21330
21420
  /**
21421
+ * Which root detector a camera runs — EXACTLY ONE.
21422
+ *
21423
+ * Deliberately the SAME vocabulary post-analysis already tags every detection
21424
+ * with (`DetectionSource`) rather than a second spelling of the same two
21425
+ * ideas: the value an operator picks here is the value that comes back on the
21426
+ * track, the overlay and the debug row.
21427
+ *
21428
+ * ## Why one, and why it is still an array
21429
+ *
21430
+ * Two roots on one camera is a capability nobody asked for and the operator has
21431
+ * since ruled out. It was never free: both planes feed the SAME per-device
21432
+ * stationary registry, both can promote the same parked object, and the two
21433
+ * detectors disagree about the same box by construction — which is the
21434
+ * disagreement D505 had to arbitrate. Removing the case removes the arbitration.
21435
+ *
21436
+ * The ARRAY shape survives because the stored settings and the attach payload
21437
+ * already speak it on every camera of every fleet, and a cap input that
21438
+ * suddenly refuses a stored value makes the camera un-attachable — a fail-closed
21439
+ * in the one direction that costs an operator their cameras. So the wire stays
21440
+ * tolerant and the TYPE is exact: a longer list is truncated to its first
21441
+ * element rather than refused, and `max(1)` is what every consumer can then
21442
+ * rely on.
21443
+ *
21444
+ * AT MOST one, not exactly one. The EMPTY list is a legal, deliberate pick —
21445
+ * a camera an operator left with no root detector — and `planDetectionSources`
21446
+ * has always said so: inventing a root for them is how a default acts without
21447
+ * anybody choosing it. Tightening this to `length(1)` broke that, and the
21448
+ * suite caught it.
21449
+ *
21450
+ * Measured before tightening (2026-09-15, this fleet, 6 h of attaches): 3327
21451
+ * `["pipeline"]` and 2 `["onboard"]`. Not one camera had two.
21452
+ */
21453
+ var DetectionSourcesSchema = preprocess((value) => Array.isArray(value) && value.length > 1 ? value.slice(0, 1) : value, array(DetectionSourceSchema).max(1));
21454
+ /**
21331
21455
  * Input shape for `pipeline-runner.reportMotion` cap method. Exported
21332
21456
  * so cap-side consumers (the orchestrator forward, the runner addon's
21333
21457
  * cap implementation, tests) can reuse the type instead of redeclaring
@@ -21478,6 +21602,35 @@ var RunnerCameraConfigSchema = object({
21478
21602
  * events; the orchestrator forwards to `reportMotion`.
21479
21603
  */
21480
21604
  motionSources: MotionSourcesSchema.default(["analyzer"]),
21605
+ /**
21606
+ * WHICH root detector runs for this camera. The detection counterpart of
21607
+ * {@link motionSources}, same shape and same discipline: a per-camera list
21608
+ * of sources the system understands, vendor-agnostic, owned here.
21609
+ *
21610
+ * - `pipeline` — this runner's own root step on decoded pixels (`steps`).
21611
+ * - `onboard` — the CAMERA's own detector, admitted only when it is named
21612
+ * here. Its boxes reach post-analysis on the same
21613
+ * `PipelineInferenceResult` payload as the pipeline's, so an onboard-born
21614
+ * track gets the same tracker, the same overlay and the same detail
21615
+ * subtree (face / plate / embedding).
21616
+ *
21617
+ * `['onboard']` alone is the REPLACEMENT: this runner's root step does not
21618
+ * run, and that is where a camera with a usable onboard detector buys back
21619
+ * its share of the accelerator. It saves the root inference, not the decode
21620
+ * — the detail subtree still needs pixels to cut crops from.
21621
+ *
21622
+ * A camera whose onboard detections carry no geometry cannot serve as a root
21623
+ * step at all (there is nothing to track), and the cap that knows this says
21624
+ * so on `native-object-detection.getOptions().geometry`. On Reolink that is
21625
+ * every battery camera: the boxes ride a sub-stream a battery camera never
21626
+ * attaches, so such a camera stays on `['pipeline']` and its onboard AI is
21627
+ * worth exactly what it already is — a wake signal with a class.
21628
+ *
21629
+ * Defaults to `['pipeline']`, which is byte-for-byte today's behaviour: a
21630
+ * vendor never turns a detector on for the operator, and neither does a
21631
+ * default.
21632
+ */
21633
+ detectionSources: DetectionSourcesSchema.default(["pipeline"]),
21481
21634
  pipelineEnabled: boolean().default(true),
21482
21635
  /** Ordered tree of video steps. Absent → runner skips video detection. */
21483
21636
  steps: array(PipelineStepInputSchema).readonly().optional(),
@@ -21659,7 +21812,47 @@ var RunnerLocalMetricsSchema = object({
21659
21812
  queueDepth: number(),
21660
21813
  frameLazy: FrameLazyMetricsSchema.optional()
21661
21814
  });
21815
+ /**
21816
+ * Pipeline Runner capability — runtime detection workhorse.
21817
+ *
21818
+ * One instance per node. Receives camera assignments from
21819
+ * `addon-pipeline-orchestrator`, subscribes to the local stream-broker for
21820
+ * decoded frames, drains motion + detection queues, calls the local
21821
+ * `motion-detection` and `pipeline-executor` capabilities, and emits typed
21822
+ * `pipeline.inference-result` and `detection.motion-analysis` events on
21823
+ * the bus.
21824
+ *
21825
+ * Distinct from `pipeline-orchestrator` (the hub-side load balancer) — the
21826
+ * runner has zero knowledge of other agents, no global state, and never
21827
+ * makes assignment decisions itself.
21828
+ */
21829
+ /**
21830
+ * Why a runner declined to look at a camera out of band. Named rather than a
21831
+ * bare `false`: a camera asleep on battery (D173), one attached elsewhere, one
21832
+ * already mid-session and one already bursting are four different facts, and a
21833
+ * caller told only "no" cannot tell a protection from a bug.
21834
+ */
21835
+ var OccupancyBurstRefusalSchema = _enum([
21836
+ "not-attached",
21837
+ "battery-asleep",
21838
+ "not-watching",
21839
+ "already-bursting"
21840
+ ]);
21662
21841
  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({
21842
+ deviceId: number(),
21843
+ /** How many frames to collect. The caller sizes this against what its
21844
+ * own confirmation needs; the runner clamps it to the field's range. */
21845
+ frames: number().int().positive().optional(),
21846
+ /** Why, for the log. A burst nobody can attribute is a cost nobody can
21847
+ * defend — the periodic one at least has a timer to point at. */
21848
+ reason: string()
21849
+ }), object({
21850
+ started: boolean(),
21851
+ refusedBecause: OccupancyBurstRefusalSchema.optional()
21852
+ }), {
21853
+ auth: "admin",
21854
+ kind: "mutation"
21855
+ }), method(object({
21663
21856
  handle: FrameHandleSchema,
21664
21857
  bbox: NativeCropBboxSchema,
21665
21858
  maxWidth: number().int().positive().optional(),
@@ -27140,7 +27333,7 @@ var NativeDetectionSchema = object({
27140
27333
  /** Firmware-provided confidence [0..1]. Reolink pushes don't carry it → undefined. */
27141
27334
  confidence: number().min(0).max(1).optional()
27142
27335
  });
27143
- object({
27336
+ var NativeObjectDetectionStatusSchema = object({
27144
27337
  /**
27145
27338
  * Last observed instance per class. Missing entries mean the class
27146
27339
  * is supported but nothing has been seen since the provider started.
@@ -27157,14 +27350,56 @@ object({
27157
27350
  supportedClasses: array(NativeObjectClassEnum).readonly(),
27158
27351
  /**
27159
27352
  * Whether forwarding of onboard AI detections is enabled for this device.
27160
- * Default FALSE (opt-in, cold-start) — onboard AI pushes are noisy/sparse and
27161
- * churn the tracker, so forwarding stays off until the operator enables it.
27353
+ *
27354
+ * Cold-start OFF on every camera. A vendor does not decide which detector a
27355
+ * camera runs — that is the operator's choice, and it is made where every
27356
+ * other detection choice is made. `geometry` on
27357
+ * {@link NativeObjectDetectionOptionsSchema} is how the form says what the
27358
+ * choice would buy this particular camera.
27162
27359
  */
27163
27360
  enabled: boolean()
27164
- }).extend({
27361
+ });
27362
+ /**
27363
+ * WHEN a camera's onboard detections carry geometry.
27364
+ *
27365
+ * - `boxed` — always. The provider holds a standing channel for the boxes, so
27366
+ * a detection is a trackable subject whenever the camera sees one.
27367
+ * - `while-streaming` — only while some stream of this camera is already open.
27368
+ * The boxes ride the video's own side-channel rather than a feed of their
27369
+ * own, so they cost nothing and they exist only when something is pulling.
27370
+ * On Reolink this is every BATTERY camera: a persistent second feed is real
27371
+ * radio drain, but a camera that is awake is awake precisely because
27372
+ * something is in front of it, and its stream is already being pulled.
27373
+ * - `flag-only` — never. The firmware ships the class and nothing else; the
27374
+ * pipeline needs geometry to make a subject, so such a push reaches the
27375
+ * tracker as motion and no further.
27376
+ *
27377
+ * Reported and not inferred, because the operator's question in front of the
27378
+ * picker is "what do I get", and these are three different answers (D14: the
27379
+ * derived form is only as honest as `getOptions`).
27380
+ */
27381
+ var NativeObjectGeometryEnum = _enum([
27382
+ "boxed",
27383
+ "while-streaming",
27384
+ "flag-only"
27385
+ ]);
27386
+ var NativeObjectDetectionOptionsSchema = object({
27387
+ /** Classes this firmware can detect — the same list the status reports. */
27388
+ supportedClasses: array(NativeObjectClassEnum).readonly(),
27389
+ /** What a detection from this camera carries. */
27390
+ geometry: NativeObjectGeometryEnum
27391
+ });
27392
+ var NativeObjectDetectionSettingsPatchSchema = object({ enabled: boolean().optional() });
27393
+ NativeObjectDetectionStatusSchema.extend({
27165
27394
  /** Required by createRuntimeStateBridge — epoch ms of last refresh. */
27166
27395
  lastFetchedAt: number() });
27167
- DeviceType.Camera, method(object({
27396
+ DeviceType.Camera, method(object({ deviceId: number() }), NativeObjectDetectionOptionsSchema), method(object({
27397
+ deviceId: number(),
27398
+ settings: NativeObjectDetectionSettingsPatchSchema
27399
+ }), _void(), {
27400
+ kind: "mutation",
27401
+ auth: "admin"
27402
+ }), method(object({
27168
27403
  deviceId: number(),
27169
27404
  enabled: boolean()
27170
27405
  }), _void(), {
@@ -30970,7 +31205,28 @@ var PerScopeBreakdownSchema = object({
30970
31205
  /** Total tracked objects in this scope (frame / zone / unzoned). */
30971
31206
  totalObjects: number().int().nonnegative(),
30972
31207
  /** Per-class count. Keys are macro class names (e.g. `person`, `car`). */
30973
- byClass: record(string(), number().int().nonnegative())
31208
+ byClass: record(string(), number().int().nonnegative()),
31209
+ /**
31210
+ * Of `totalObjects`, how many are STANDING — objects the census holds,
31211
+ * present but not going anywhere (a parked car, a bin, a statue).
31212
+ *
31213
+ * The operator's question about a scene has two halves and they answer
31214
+ * different things: "what is parked here" is stable for hours and must not
31215
+ * flap, while "who is walking through" is the thing an alert is about. One
31216
+ * number could only serve one of them, and `totalObjects` served the first
31217
+ * badly — a passer-by moved it and every occupancy rule saw a change.
31218
+ *
31219
+ * `standing + transient === totalObjects`, always. The total keeps its old
31220
+ * meaning exactly, so no rule written against it changes behaviour.
31221
+ *
31222
+ * ABSENT means UNKNOWN, never zero (D393): a snapshot from a node older than
31223
+ * this field cannot say, and a reader that folds absence in as 0 reports "no
31224
+ * parked objects here" about a camera whose lot is full.
31225
+ */
31226
+ standingObjects: number().int().nonnegative().optional(),
31227
+ /** Of `totalObjects`, how many are passing through. See `standingObjects` —
31228
+ * same absence rule. */
31229
+ transientObjects: number().int().nonnegative().optional()
30974
31230
  });
30975
31231
  var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
30976
31232
  zoneId: string(),
@@ -33909,12 +34165,24 @@ Object.freeze({
33909
34165
  addonId: null,
33910
34166
  access: "create"
33911
34167
  },
34168
+ "nativeObjectDetection.getOptions": {
34169
+ capName: "native-object-detection",
34170
+ capScope: "device",
34171
+ addonId: null,
34172
+ access: "view"
34173
+ },
33912
34174
  "nativeObjectDetection.setEnabled": {
33913
34175
  capName: "native-object-detection",
33914
34176
  capScope: "device",
33915
34177
  addonId: null,
33916
34178
  access: "create"
33917
34179
  },
34180
+ "nativeObjectDetection.setSettings": {
34181
+ capName: "native-object-detection",
34182
+ capScope: "device",
34183
+ addonId: null,
34184
+ access: "create"
34185
+ },
33918
34186
  "navigation.getFeatures": {
33919
34187
  capName: "navigation",
33920
34188
  capScope: "device",
@@ -34179,6 +34447,12 @@ Object.freeze({
34179
34447
  addonId: null,
34180
34448
  access: "view"
34181
34449
  },
34450
+ "notificationRules.getClipRetentionAsks": {
34451
+ capName: "notification-rules",
34452
+ capScope: "system",
34453
+ addonId: null,
34454
+ access: "view"
34455
+ },
34182
34456
  "notificationRules.getConditionCatalog": {
34183
34457
  capName: "notification-rules",
34184
34458
  capScope: "system",
@@ -34689,6 +34963,12 @@ Object.freeze({
34689
34963
  addonId: null,
34690
34964
  access: "create"
34691
34965
  },
34966
+ "pipelineAnalytics.readLongTermSeries": {
34967
+ capName: "pipeline-analytics",
34968
+ capScope: "device",
34969
+ addonId: null,
34970
+ access: "view"
34971
+ },
34692
34972
  "pipelineAnalytics.rebuildObjectEmbeddings": {
34693
34973
  capName: "pipeline-analytics",
34694
34974
  capScope: "device",
@@ -35361,6 +35641,12 @@ Object.freeze({
35361
35641
  addonId: null,
35362
35642
  access: "create"
35363
35643
  },
35644
+ "pipelineRunner.requestOccupancyBurst": {
35645
+ capName: "pipeline-runner",
35646
+ capScope: "system",
35647
+ addonId: null,
35648
+ access: "create"
35649
+ },
35364
35650
  "pipelineRunner.runDetailSubtree": {
35365
35651
  capName: "pipeline-runner",
35366
35652
  capScope: "system",
@@ -38141,11 +38427,21 @@ Object.freeze({
38141
38427
  form: "single",
38142
38428
  optional: false
38143
38429
  }],
38430
+ "nativeObjectDetection.getOptions": [{
38431
+ name: "deviceId",
38432
+ form: "single",
38433
+ optional: false
38434
+ }],
38144
38435
  "nativeObjectDetection.setEnabled": [{
38145
38436
  name: "deviceId",
38146
38437
  form: "single",
38147
38438
  optional: false
38148
38439
  }],
38440
+ "nativeObjectDetection.setSettings": [{
38441
+ name: "deviceId",
38442
+ form: "single",
38443
+ optional: false
38444
+ }],
38149
38445
  "navigation.getFeatures": [{
38150
38446
  name: "deviceId",
38151
38447
  form: "single",
@@ -38495,6 +38791,11 @@ Object.freeze({
38495
38791
  form: "single",
38496
38792
  optional: false
38497
38793
  }],
38794
+ "pipelineAnalytics.readLongTermSeries": [{
38795
+ name: "deviceId",
38796
+ form: "single",
38797
+ optional: false
38798
+ }],
38498
38799
  "pipelineAnalytics.rebuildObjectEmbeddings": [{
38499
38800
  name: "deviceId",
38500
38801
  form: "single",
@@ -38685,6 +38986,11 @@ Object.freeze({
38685
38986
  form: "single",
38686
38987
  optional: false
38687
38988
  }],
38989
+ "pipelineRunner.requestOccupancyBurst": [{
38990
+ name: "deviceId",
38991
+ form: "single",
38992
+ optional: false
38993
+ }],
38688
38994
  "pipelineRunner.runDetailSubtree": [{
38689
38995
  name: "deviceId",
38690
38996
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-google",
3
- "version": "0.1.66",
3
+ "version": "0.1.68",
4
4
  "description": "Google Home export — hub-side smart-home fulfillment (SYNC / QUERY / EXECUTE / DISCONNECT) for the non-camera fleet, served over the hub's own OAuth account link. No Google credential is stored, sent or required.",
5
5
  "keywords": [
6
6
  "camstack",