@camstack/addon-export-alexa 1.2.20 → 1.2.22

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.
@@ -1,5 +1,5 @@
1
1
  import * as crypto$1 from "node:crypto";
2
- //#region ../types/dist/event-category-Cv9dO26A.mjs
2
+ //#region ../types/dist/event-category-Bxo5yJjt.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -206,6 +206,33 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
206
206
  EventCategory["PipelineCameraAssigned"] = "pipeline.camera-assigned";
207
207
  EventCategory["PipelineCameraUnassigned"] = "pipeline.camera-unassigned";
208
208
  /**
209
+ * A node the orchestrator would otherwise place cameras on has NO usable
210
+ * inference device: the operator enabled one or more accelerators there and
211
+ * the live probe reports every one of them unavailable. Emitted once per
212
+ * TRANSITION into that state (never per dispatch), and the node is dropped
213
+ * from the placement candidate set for as long as it holds.
214
+ *
215
+ * This exists because the state was previously invisible: little-unraid
216
+ * absorbed 283k inference errors in a day while still being handed cameras,
217
+ * and nothing in the system said so.
218
+ *
219
+ * A node with no accelerators configured at all is NOT this — its devices
220
+ * are `disabled`, not `unavailable`, and the runner's default CPU pool
221
+ * serves it exactly as before.
222
+ */
223
+ EventCategory["PipelineNodeInferenceUnavailable"] = "pipeline.node-inference-unavailable";
224
+ /**
225
+ * A camera has an OPEN detection session and has produced no detection at
226
+ * all for longer than the blind threshold — the camera is being decoded and
227
+ * inferred and is returning nothing. Emitted once per transition into blind,
228
+ * per camera.
229
+ *
230
+ * The failure it reports: a 1h43 detection blackout on the entrance camera
231
+ * that nobody noticed, because "a camera that detects nothing" and "a quiet
232
+ * camera" produce byte-identical silence.
233
+ */
234
+ EventCategory["PipelineDetectionBlind"] = "pipeline.detection-blind";
235
+ /**
209
236
  * Per-camera pipeline config was mutated by the orchestrator
210
237
  * (3-level settings change via `setAgentAddonDefaults` /
211
238
  * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
@@ -2997,6 +3024,9 @@ function handlePipeResult(left, next, ctx) {
2997
3024
  fallback: left.fallback
2998
3025
  }, ctx);
2999
3026
  }
3027
+ var $ZodPreprocess = /*@__PURE__*/ $constructor("$ZodPreprocess", (inst, def) => {
3028
+ $ZodPipe.init(inst, def);
3029
+ });
3000
3030
  var $ZodReadonly = /*@__PURE__*/ $constructor("$ZodReadonly", (inst, def) => {
3001
3031
  $ZodType.init(inst, def);
3002
3032
  defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
@@ -5182,6 +5212,10 @@ function pipe(in_, out) {
5182
5212
  out
5183
5213
  });
5184
5214
  }
5215
+ var ZodPreprocess = /*@__PURE__*/ $constructor("ZodPreprocess", (inst, def) => {
5216
+ ZodPipe.init(inst, def);
5217
+ $ZodPreprocess.init(inst, def);
5218
+ });
5185
5219
  var ZodReadonly = /*@__PURE__*/ $constructor("ZodReadonly", (inst, def) => {
5186
5220
  $ZodReadonly.init(inst, def);
5187
5221
  ZodType.init(inst, def);
@@ -5240,6 +5274,13 @@ function _instanceof(cls, params = {}) {
5240
5274
  };
5241
5275
  return inst;
5242
5276
  }
5277
+ function preprocess(fn, schema) {
5278
+ return new ZodPreprocess({
5279
+ type: "pipe",
5280
+ in: transform(fn),
5281
+ out: schema
5282
+ });
5283
+ }
5243
5284
  //#endregion
5244
5285
  //#region ../../node_modules/zod/v4/classic/compat.js
5245
5286
  /** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
@@ -12418,6 +12459,17 @@ var LlmImageSchema = object({
12418
12459
  bytes: _instanceof(Uint8Array),
12419
12460
  mimeType: string()
12420
12461
  });
12462
+ /**
12463
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
12464
+ * the flag is what a consumer table flips, the count is what the operator tunes.
12465
+ * A retry doubles the wall time of a call, so the two gates that run inside a
12466
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
12467
+ */
12468
+ var LlmRetryPolicySchema = object({
12469
+ enabled: boolean().default(false),
12470
+ /** Total attempts INCLUDING the first. 1 = no retry. */
12471
+ maxAttempts: number().int().min(1).max(5).default(1)
12472
+ });
12421
12473
  var LlmGenerateBaseInputSchema = object({
12422
12474
  /** Collection routing (the notification-output posture). */
12423
12475
  addonId: string().optional(),
@@ -12432,7 +12484,28 @@ var LlmGenerateBaseInputSchema = object({
12432
12484
  jsonSchema: record(string(), unknown()).optional(),
12433
12485
  /** Per-call override of the profile default. */
12434
12486
  maxTokens: number().int().positive().optional(),
12435
- temperature: number().optional()
12487
+ temperature: number().optional(),
12488
+ /** Per-call override of the profile default (nucleus sampling). */
12489
+ topP: number().min(0).max(1).optional(),
12490
+ /** Per-call override of the profile default (top-k sampling). */
12491
+ topK: number().int().positive().optional(),
12492
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
12493
+ timeoutMs: number().int().positive().optional(),
12494
+ /** Per-call override; beats both the consumer table and the profile. */
12495
+ retry: LlmRetryPolicySchema.optional(),
12496
+ /**
12497
+ * Caller-minted id that makes this generation CANCELLABLE.
12498
+ *
12499
+ * Without it a caller that stops waiting cannot stop the work: the gates race
12500
+ * the call against 8 s and free their own slot when the timer wins, while the
12501
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
12502
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
12503
+ * not generations, and the real load is unbounded.
12504
+ *
12505
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
12506
+ * `llm.cancel({ requestId })` tears the socket down.
12507
+ */
12508
+ requestId: string().optional()
12436
12509
  });
12437
12510
  /**
12438
12511
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -12445,6 +12518,18 @@ var LlmGenerateBaseInputSchema = object({
12445
12518
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
12446
12519
  * watchdog — operator decision #3).
12447
12520
  */
12521
+ /**
12522
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
12523
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
12524
+ * REF rather than looked up at install time, so what the operator approved in
12525
+ * the preview is exactly what the node downloads.
12526
+ */
12527
+ var ManagedModelExtraFileSchema = object({
12528
+ url: string(),
12529
+ filename: string(),
12530
+ sizeBytes: number(),
12531
+ sha256: string().optional()
12532
+ });
12448
12533
  var ManagedModelRefSchema = discriminatedUnion("kind", [
12449
12534
  object({
12450
12535
  kind: literal("catalog"),
@@ -12453,7 +12538,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
12453
12538
  object({
12454
12539
  kind: literal("url"),
12455
12540
  url: string(),
12456
- sha256: string().optional()
12541
+ sha256: string().optional(),
12542
+ /** Picker/status label; the file basename when absent. */
12543
+ label: string().optional(),
12544
+ sizeBytes: number().optional(),
12545
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
12457
12546
  }),
12458
12547
  object({
12459
12548
  kind: literal("path"),
@@ -12471,13 +12560,82 @@ var ManagedRuntimeConfigSchema = object({
12471
12560
  gpuLayers: number().int().default(0),
12472
12561
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
12473
12562
  threads: number().int().optional(),
12474
- /** Concurrent slots. */
12563
+ /** Concurrent slots (`--parallel`). */
12475
12564
  parallel: number().int().default(1),
12565
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
12566
+ batchSize: number().int().positive().optional(),
12567
+ /** Physical batch / micro-batch (`-ub`). */
12568
+ ubatchSize: number().int().positive().optional(),
12569
+ /**
12570
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
12571
+ * is a no-op elsewhere, so it is offered rather than assumed.
12572
+ */
12573
+ flashAttention: boolean().default(false),
12574
+ /**
12575
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
12576
+ * inference. Costs the full model size in resident memory — which is exactly
12577
+ * what the RAM budget is counting.
12578
+ */
12579
+ mlock: boolean().default(false),
12580
+ /**
12581
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
12582
+ * start, but avoids the page-fault stalls a network or spinning-disk model
12583
+ * store produces on every first token.
12584
+ */
12585
+ noMmap: boolean().default(false),
12586
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
12587
+ * cheapest way to fit a longer context in the same RAM. */
12588
+ cacheTypeK: _enum([
12589
+ "f32",
12590
+ "f16",
12591
+ "q8_0",
12592
+ "q5_1",
12593
+ "q5_0",
12594
+ "q4_1",
12595
+ "q4_0"
12596
+ ]).optional(),
12597
+ cacheTypeV: _enum([
12598
+ "f32",
12599
+ "f16",
12600
+ "q8_0",
12601
+ "q5_1",
12602
+ "q5_0",
12603
+ "q4_1",
12604
+ "q4_0"
12605
+ ]).optional(),
12606
+ /**
12607
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
12608
+ * (which most vision chat templates need and some language-only models
12609
+ * dislike), `--cont-batching`, `--rope-scaling`, …
12610
+ *
12611
+ * It is NOT a second place to set the flags above. A token that collides
12612
+ * with a typed field is REJECTED at start, naming the field that owns it
12613
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
12614
+ * the "two switches that disagree" failure this repo has already shipped
12615
+ * twice (D62).
12616
+ */
12617
+ extraArgs: array(string()).default([]),
12476
12618
  /** Else lazy: first generate boots it. */
12477
12619
  autoStart: boolean().default(false),
12478
12620
  /** 0 = never; frees RAM after quiet periods. */
12479
12621
  idleStopMinutes: number().int().default(30)
12480
12622
  });
12623
+ /**
12624
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
12625
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
12626
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
12627
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
12628
+ */
12629
+ var LlmDownloadProgressSchema = object({
12630
+ phase: _enum(["downloading", "verifying"]),
12631
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
12632
+ file: string(),
12633
+ fileIndex: number().int(),
12634
+ fileCount: number().int(),
12635
+ /** Across the WHOLE install, not the current file. */
12636
+ downloadedBytes: number(),
12637
+ totalBytes: number().optional()
12638
+ });
12481
12639
  var LlmRuntimeStatusSchema = object({
12482
12640
  /** Status is ALWAYS node-qualified. */
12483
12641
  nodeId: string(),
@@ -12494,6 +12652,8 @@ var LlmRuntimeStatusSchema = object({
12494
12652
  modelPath: string().optional(),
12495
12653
  modelId: string().optional(),
12496
12654
  downloadProgress: number().min(0).max(1).optional(),
12655
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
12656
+ download: LlmDownloadProgressSchema.optional(),
12497
12657
  lastError: string().optional(),
12498
12658
  crashesInWindow: number(),
12499
12659
  /** Child RSS (sampled best-effort). */
@@ -12504,7 +12664,14 @@ var LlmNodeModelSchema = object({
12504
12664
  file: string(),
12505
12665
  sizeBytes: number(),
12506
12666
  catalogId: string().optional(),
12507
- installedAt: number().optional()
12667
+ installedAt: number().optional(),
12668
+ /**
12669
+ * Absolute path on the node. Present so a file that is on disk but matches
12670
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
12671
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
12672
+ * it the picker could list such a file and do nothing with it.
12673
+ */
12674
+ path: string().optional()
12508
12675
  });
12509
12676
  var LlmRuntimeDiskUsageSchema = object({
12510
12677
  nodeId: string(),
@@ -12560,10 +12727,47 @@ var LlmProfileSchema = object({
12560
12727
  baseUrl: string().optional(),
12561
12728
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
12562
12729
  apiKey: string().optional(),
12730
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
12731
+ * degraded to text — that shipped once and produced a confident answer to a
12732
+ * question about a picture nobody sent. */
12563
12733
  supportsVision: boolean(),
12564
12734
  temperature: number().min(0).max(2).optional(),
12735
+ /** Nucleus sampling. Every wire we speak has it. */
12736
+ topP: number().min(0).max(1).optional(),
12737
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
12738
+ * wire does, and the client drops it there (measured: the request body gets
12739
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
12740
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
12741
+ topK: number().int().positive().optional(),
12565
12742
  maxTokens: number().int().positive().optional(),
12743
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
12744
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
12745
+ * the model with, so it is the one field that changes a PROCESS. */
12746
+ contextLength: number().int().positive().optional(),
12747
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
12748
+ * two system prompts fighting is worse than either alone). */
12749
+ systemPrompt: string().optional(),
12750
+ /** Total generation bound — the only one a unary call has. */
12566
12751
  timeoutMs: number().int().positive().default(6e4),
12752
+ /** The TCP handshake only — "is the port even open". NOT the wait for
12753
+ * response headers: on the LM Studio / llama-server wire those are written
12754
+ * once the model has finished loading, so they belong to the bound below. */
12755
+ connectTimeoutMs: number().int().positive().default(1e4),
12756
+ /** Accepted, but no output yet — response headers included, because a cold
12757
+ * GPU load is exactly what happens before them. */
12758
+ firstTokenTimeoutMs: number().int().positive().default(12e4),
12759
+ /** Output started then stopped. */
12760
+ idleTimeoutMs: number().int().positive().default(6e4),
12761
+ /** Profile-level default. The per-consumer table and a per-call override
12762
+ * both beat it — see `resolveRetryPolicy`. */
12763
+ retry: LlmRetryPolicySchema.default({
12764
+ enabled: false,
12765
+ maxAttempts: 1
12766
+ }),
12767
+ /** Whether this profile may use tools. The tool-call plumbing rides the
12768
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
12769
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
12770
+ toolsEnabled: boolean().default(false),
12567
12771
  extraHeaders: record(string(), string()).optional(),
12568
12772
  /** kind === 'managed-local' only (spec §4). */
12569
12773
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -12613,6 +12817,36 @@ var ManagedModelCatalogEntrySchema = object({
12613
12817
  /** Vision models: companion projector file. */
12614
12818
  mmprojUrl: string().optional()
12615
12819
  });
12820
+ /**
12821
+ * The outcome of turning one operator-typed Hugging Face reference into a
12822
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
12823
+ * I will not pick for you" is a normal answer the UI has to render, not an
12824
+ * exception.
12825
+ *
12826
+ * `candidates` is the whole reason the refusal is usable — every string in it
12827
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
12828
+ */
12829
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
12830
+ ok: literal(true),
12831
+ /** Ready to hand to `installModel` unchanged. */
12832
+ model: ManagedModelRefSchema,
12833
+ label: string(),
12834
+ repo: string(),
12835
+ quantization: string(),
12836
+ purpose: _enum(["text", "vision"]),
12837
+ totalBytes: number(),
12838
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
12839
+ * see that 0.9 GB of it is a projector they did not name. */
12840
+ extraFilenames: array(string())
12841
+ }), object({
12842
+ ok: literal(false),
12843
+ code: string(),
12844
+ message: string(),
12845
+ candidates: array(string()).optional(),
12846
+ /** Set when the refusal was only the ceiling: re-calling with
12847
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
12848
+ requiredBytes: number().optional()
12849
+ })]);
12616
12850
  var LlmRuntimeNodeSchema = object({
12617
12851
  nodeId: string(),
12618
12852
  reachable: boolean(),
@@ -12625,7 +12859,10 @@ var ProfileRefInputSchema = object({
12625
12859
  addonId: string(),
12626
12860
  profileId: string()
12627
12861
  });
12628
- method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12862
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
12863
+ addonId: string().optional(),
12864
+ requestId: string()
12865
+ }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12629
12866
  kind: "mutation",
12630
12867
  auth: "admin"
12631
12868
  }), method(ProfileRefInputSchema, _void(), {
@@ -12646,6 +12883,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
12646
12883
  consumer: string().optional(),
12647
12884
  profileId: string().optional()
12648
12885
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
12886
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
12887
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
12888
+ ref: string(),
12889
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
12890
+ maxBytes: number().positive().optional()
12891
+ }), HfModelResolutionSchema, {
12892
+ kind: "mutation",
12893
+ auth: "admin"
12894
+ }), method(object({
12649
12895
  nodeId: string(),
12650
12896
  model: ManagedModelRefSchema
12651
12897
  }), _void(), {
@@ -13205,11 +13451,33 @@ var NotificationFormatSchema = _enum([
13205
13451
  * Named by INTENT, never by glyph. "check" would tie the vocabulary to one
13206
13452
  * renderer's icon set; "acknowledge" survives an adapter that draws it
13207
13453
  * differently.
13454
+ *
13455
+ * ── A TOKEN IS NOT A WIRE VALUE ─────────────────────────────────────
13456
+ *
13457
+ * These names are for US. **No adapter may forward one verbatim.** Each maps
13458
+ * the whole set onto its own renderer's vocabulary through a
13459
+ * `Record<NotificationActionIcon, string>` — a Record, never a lookup with a
13460
+ * fallback, so adding a member here fails every adapter's build until someone
13461
+ * decides its glyph, which is the only place that decision can be made
13462
+ * honestly.
13463
+ *
13464
+ * This paragraph is the bug. Zentik declared `actionIcons: true` and passed
13465
+ * `disarm` straight through; iOS feeds that string to
13466
+ * `UNNotificationActionIcon(systemImageName:)`, `disarm` is not an SF Symbol,
13467
+ * and every snooze and alarm button arrived BLANK. A pass-through is not a
13468
+ * mapping, and "the field is documented" is not "the value renders".
13469
+ *
13470
+ * Adding a member is TRAIN-BOUND. The enum lives in the published
13471
+ * `@camstack/server` closure and the cap seam validates against the HUB's copy,
13472
+ * so an addon that emits a token the running hub does not know does not lose an
13473
+ * icon — its whole `send` fails Zod validation and the notification never
13474
+ * arrives. Never emit a new token from an addon before the train carrying it.
13208
13475
  */
13209
13476
  var NotificationActionIconSchema = _enum([
13210
13477
  "acknowledge",
13211
13478
  "dismiss",
13212
13479
  "silence",
13480
+ "snooze",
13213
13481
  "view",
13214
13482
  "play",
13215
13483
  "open",
@@ -13217,9 +13485,13 @@ var NotificationActionIconSchema = _enum([
13217
13485
  "lock",
13218
13486
  "unlock",
13219
13487
  "arm",
13488
+ "arm-home",
13489
+ "arm-away",
13490
+ "arm-night",
13220
13491
  "disarm",
13221
13492
  "light",
13222
- "alert"
13493
+ "alert",
13494
+ "camera"
13223
13495
  ]);
13224
13496
  /** A single tap-through action button. */
13225
13497
  var NotificationActionSchema = object({
@@ -13419,6 +13691,24 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
13419
13691
  targetId: string(),
13420
13692
  enabled: boolean()
13421
13693
  }), _void(), { kind: "mutation" });
13694
+ new Set([
13695
+ {
13696
+ id: "person",
13697
+ name: "Person"
13698
+ },
13699
+ {
13700
+ id: "vehicle",
13701
+ name: "Vehicle"
13702
+ },
13703
+ {
13704
+ id: "animal",
13705
+ name: "Animal"
13706
+ },
13707
+ {
13708
+ id: "package",
13709
+ name: "Package"
13710
+ }
13711
+ ].map((l) => l.id));
13422
13712
  var COCO_TO_MACRO = {
13423
13713
  mapping: {
13424
13714
  person: "person",
@@ -14211,11 +14501,15 @@ var NcSystemEventKindSchema = _enum([
14211
14501
  "stream-offline",
14212
14502
  "node-online",
14213
14503
  "node-offline",
14504
+ "node-inference-unavailable",
14505
+ "detection-blind",
14214
14506
  "addon-update-available",
14215
14507
  "server-update-available",
14216
14508
  "alarm-triggered",
14217
14509
  "alarm-armed",
14218
14510
  "alarm-disarmed",
14511
+ "alarm-arming",
14512
+ "alarm-arm-refused",
14219
14513
  "camera-online",
14220
14514
  "camera-offline",
14221
14515
  "camera-disabled",
@@ -14270,7 +14564,16 @@ var NcScheduleSchema = object({
14270
14564
  });
14271
14565
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
14272
14566
  var NcPlateMatcherSchema = object({
14273
- values: array(string().min(1)).min(1),
14567
+ /**
14568
+ * Plate texts (or gallery vehicle names) to match. EMPTY = **any plate the
14569
+ * pipeline could read** — the plate half of "no selection = no narrowing",
14570
+ * and the switch that says this rule is about vehicles that were IDENTIFIED
14571
+ * rather than merely seen. A subject carrying no plate still fails.
14572
+ *
14573
+ * The `.min(1)` this used to carry made that state unauthorable; nothing has
14574
+ * ever persisted an empty list, so widening it cannot change an existing rule.
14575
+ */
14576
+ values: array(string().min(1)),
14274
14577
  /** Max Levenshtein distance after normalization (uppercase alphanumeric). */
14275
14578
  maxDistance: number().int().min(0).max(3).default(1)
14276
14579
  });
@@ -14304,28 +14607,36 @@ var NcOccupancyConditionSchema = object({
14304
14607
  /**
14305
14608
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
14306
14609
  *
14307
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
14308
- * reference notifier uses, so an operator moving between them re-uses what
14309
- * they already know): a rule matches when, over a sampling window of
14310
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
14311
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
14312
- *
14313
- * - `dbThreshold` — its level is at or above this many dBFS (see
14314
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
14315
- * - `labels` — the classifier put at least one of these labels on it.
14316
- *
14317
- * Both are OPTIONAL and independent, which is the point of the shape: a
14318
- * loudness rule ("something loud at 3am") needs no model to be right, and a
14319
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
14320
- * is given** — a window in which every sample is trivially a hit would fire on
14321
- * silence, so the engine refuses such a condition rather than notifying on
14322
- * nothing (the schema cannot express "at least one of" without becoming a
14323
- * ZodEffects the cap path would have to special-case).
14324
- *
14325
- * `hitPercent` is over the samples the window actually HOLDS, and the window
14326
- * must be FULL before it can match — a window that has been open for two
14327
- * seconds of its ten is 100% of nothing, and firing on it would make
14328
- * `samplingSeconds` decorative.
14610
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
14611
+ * rule is in is not a stored field — it is WHICH FILTER the rule carries, so
14612
+ * there is no second switch that can disagree with the first and every rule
14613
+ * authored before the decision migrates for free (`audioModeOf`):
14614
+ *
14615
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14616
+ * classifier labels with one of them. No window, no percentage:
14617
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14618
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
14619
+ * the analyzer's (`classificationMinScore`, per device) — a label only
14620
+ * reaches this condition if the classifier was already confident enough.
14621
+ * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14622
+ * the condition: at least `hitPercent`% of the samples over
14623
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
14624
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
14625
+ * must be FULL before it can match — a window open for two of its ten
14626
+ * seconds is 100% of nothing.
14627
+ *
14628
+ * **Why label mode has no window.** It had one, and it never fired: the
14629
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
14630
+ * of them per episode, even through continuous crying. The measured maximum
14631
+ * `hitPercent` over the whole live history was 40 — under the shipped default
14632
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
14633
+ * the wrong question to ask of a sparse classifier.
14634
+ *
14635
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
14636
+ * and the rule would fire on silence. The schema cannot express "exactly one
14637
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
14638
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
14639
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
14329
14640
  *
14330
14641
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
14331
14642
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -14333,13 +14644,13 @@ var NcOccupancyConditionSchema = object({
14333
14644
  * an operator who typed `dog` mean the same thing.
14334
14645
  */
14335
14646
  var NcAudioConditionSchema = object({
14336
- /** Audio macro labels; absent = any sound (level-only rule). */
14647
+ /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14337
14648
  labels: array(string().min(1)).min(1).optional(),
14338
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
14649
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14339
14650
  dbThreshold: number().min(-96).max(0).optional(),
14340
- /** Percentage of the window's samples that must be hits (1–100). */
14651
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14341
14652
  hitPercent: number().int().min(1).max(100).default(60),
14342
- /** Length of the sampling window in seconds. */
14653
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14343
14654
  samplingSeconds: number().int().min(1).max(300).default(10)
14344
14655
  });
14345
14656
  /**
@@ -14477,13 +14788,81 @@ var NcRuleActionsSchema = object({
14477
14788
  */
14478
14789
  buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
14479
14790
  });
14791
+ /**
14792
+ * "This rule applies only while `deviceId` is in one of `states`."
14793
+ *
14794
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
14795
+ * `on`/`off` for a switch — not a normalised set, because normalising would
14796
+ * make the condition lie about devices whose states have no equivalent.
14797
+ *
14798
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
14799
+ * condition that fired on "I could not read it" would be worse than no gate.
14800
+ */
14801
+ var NcDeviceStateConditionSchema = object({
14802
+ deviceId: number().int(),
14803
+ /** Any of these matches. */
14804
+ states: array(string().min(1)).min(1)
14805
+ });
14806
+ /**
14807
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
14808
+ *
14809
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
14810
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
14811
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
14812
+ * already has a trigger ("tell me about a person at the front door, but only
14813
+ * while the bin is still out"). That is why it composes with every delivery
14814
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
14815
+ * kind exist for it — see D159.
14816
+ *
14817
+ * ── Identity ───────────────────────────────────────────────────────────────
14818
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
14819
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
14820
+ * carried as a HINT for the editor and for the log line, never as part of the
14821
+ * lookup key: a rule whose hint drifted must still gate correctly.
14822
+ *
14823
+ * ── Which boolean ──────────────────────────────────────────────────────────
14824
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
14825
+ * already declares which boolean drives notification rules, and a second knob
14826
+ * that could disagree with it is exactly the D62 failure. Set it only to
14827
+ * override one rule against the scene's own default.
14828
+ *
14829
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
14830
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
14831
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
14832
+ * evidence, in either direction.
14833
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
14834
+ * The latch is a durable fact about the past ("it has diverged since I armed
14835
+ * it"), so a camera that has gone dark does not clear it — that is the whole
14836
+ * reason the operator asked for a latch.
14837
+ *
14838
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
14839
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
14840
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
14841
+ * said out loud in the log rather than dropped in silence.
14842
+ */
14843
+ var NcSceneConditionSchema = object({
14844
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
14845
+ sceneId: string().min(1),
14846
+ /** The camera the scene lives on. A hint for the editor and the log line. */
14847
+ deviceId: number().int().optional(),
14848
+ /** The state the scene must be in for the rule to fire. */
14849
+ requiredState: _enum(["matched", "diverged"]),
14850
+ /**
14851
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
14852
+ * scene's own `emit` field, which is the only place that decision belongs.
14853
+ */
14854
+ latched: boolean().optional()
14855
+ });
14480
14856
  var NcConditionsSchema = object({
14481
14857
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
14482
- deviceState: object({
14483
- deviceId: number().int(),
14484
- /** Any of these matches. */
14485
- states: array(string().min(1)).min(1)
14486
- }).optional(),
14858
+ deviceState: NcDeviceStateConditionSchema.optional(),
14859
+ /**
14860
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
14861
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
14862
+ * unlike `occupancy`/`audio` it discriminates nothing. See
14863
+ * {@link NcSceneCondition} and D159.
14864
+ */
14865
+ scene: NcSceneConditionSchema.optional(),
14487
14866
  /** Device scope — absent = all devices. */
14488
14867
  devices: array(number()).optional(),
14489
14868
  /** Detector class names (any overlap with the record's class set). */
@@ -14509,18 +14888,47 @@ var NcConditionsSchema = object({
14509
14888
  */
14510
14889
  labelEquals: array(string().min(1)).optional(),
14511
14890
  /**
14512
- * Identity matcher. P1 boundary: matched against the record's collapsed
14513
- * `label` (the identity display name propagated by the face pipeline) —
14514
- * identity-ID matching rides in P2 when identity ids reach the record.
14891
+ * KNOWN FACES — the rule's identity scope, and the switch that says the rule
14892
+ * is about recognised people at all.
14893
+ *
14894
+ * Three states, and the empty one is the point:
14895
+ *
14896
+ * | value | meaning |
14897
+ * | --- | --- |
14898
+ * | absent | the rule does not care who it is; an unrecognised person matches |
14899
+ * | `[]` | **only known faces** — any identity in the gallery, nobody in particular |
14900
+ * | a list | only these identities |
14901
+ *
14902
+ * `[]` is the repo-wide "no selection = no narrowing" reading (an absent
14903
+ * `devices` list is every device), applied one level down: the operator has
14904
+ * turned the face scope ON and narrowed it to nothing, which is every known
14905
+ * face. No second field states the same thing — a switch that can disagree
14906
+ * with the list under it is worse than no switch (D62).
14907
+ *
14908
+ * MEMBERS ARE FACE-GALLERY `Identity.id`s (uuid), not display names. A name is
14909
+ * renameable, and a rule authored on "Gianluca" went silently dark the moment
14910
+ * the operator fixed the spelling. The id reaches the record on
14911
+ * `LabelAttribution.identityId`; the name is what the editor shows and what
14912
+ * `{{label}}` renders.
14913
+ *
14914
+ * Rules written before this carry NAMES, and are resolved to ids lazily at
14915
+ * load (`NcRuleStore.load`) against the live gallery — a name nothing answers
14916
+ * for is left as it stands and reported, never dropped. The engine also
14917
+ * accepts a display-name hit as a compatibility leg, so a rule whose
14918
+ * migration could not resolve keeps matching exactly what it matched before.
14515
14919
  */
14516
14920
  identities: array(string().min(1)).optional(),
14517
- /** Fuzzy plate matcher against the record's `label` (plate text). */
14921
+ /**
14922
+ * KNOWN PLATES / VEHICLES — the plate mirror of {@link identities}, including
14923
+ * the empty-list reading: `values: []` is "any plate the OCR could read",
14924
+ * a non-empty list is those plates (fuzzily). See {@link NcPlateMatcherSchema}.
14925
+ */
14518
14926
  plates: NcPlateMatcherSchema.optional(),
14519
14927
  /**
14520
- * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics.
14521
- * Same P1 boundary: matched against the record's collapsed `label` (the
14522
- * identity display name). A record with NO label passes (nothing to
14523
- * exclude), unlike the include variant which fails on an absent label.
14928
+ * Identity EXCLUDE — mirror of {@link identities} with `notIn` semantics, and
14929
+ * the same id members and the same lazy name→id migration. A record with NO
14930
+ * identity passes (nothing to exclude), unlike the include variant which
14931
+ * fails on an unrecognised subject. An EMPTY list excludes nobody.
14524
14932
  */
14525
14933
  identitiesExclude: array(string().min(1)).optional(),
14526
14934
  /**
@@ -14912,7 +15320,80 @@ var NcRuleInputSchema = object({
14912
15320
  * a rule that predates the gate must keep delivering byte-for-byte as it
14913
15321
  * did, and absent is the only way to say that without a migration.
14914
15322
  */
14915
- confirm: NcConfirmSchema.optional()
15323
+ confirm: NcConfirmSchema.optional(),
15324
+ /**
15325
+ * WAIT for face/plate recognition before saying anything.
15326
+ *
15327
+ * A notification's TEXT is frozen at enqueue and its media is re-resolved at
15328
+ * send; the identity is neither. A face is confirmed after `confirmFrames`
15329
+ * agreeing observations — p50 **11.4 s** after the track was first seen,
15330
+ * measured on this hub — and an `immediate` rule enqueues on the first object
15331
+ * event, seconds before that. So "Gianluca è arrivato" is unsayable on the
15332
+ * immediate path, and no amount of media re-resolution fixes a sentence.
15333
+ *
15334
+ * Only two honest answers exist, and this flag picks between them. It has
15335
+ * effect ONLY on a rule that declares a recognition scope
15336
+ * ({@link NcConditions.identities} or {@link NcConditions.plates}) — on any
15337
+ * other rule there is nothing to wait for and the flag is inert.
15338
+ *
15339
+ * | value | what happens |
15340
+ * | --- | --- |
15341
+ * | `true` | the rule stops firing on the object event and fires at TRACK CLOSE instead, once, with the name — later, and complete |
15342
+ * | absent / `false` | it fires at once WITHOUT the name, and if recognition lands before the track closes a SECOND, "…is Gianluca" notification follows (one per track, per rule, per target) |
15343
+ *
15344
+ * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
15345
+ * on the addon cap path, and absent has to keep meaning exactly what every
15346
+ * rule authored before this field meant.
15347
+ *
15348
+ * The cost of `true` is stated here because the editor states it too: a rule
15349
+ * that waits also inherits track-close SEMANTICS — its `zones` condition
15350
+ * tests every zone the track visited and a `crossing` condition can no longer
15351
+ * be satisfied, because a closed track carries no crossing.
15352
+ */
15353
+ waitForEnhancement: boolean().optional(),
15354
+ /**
15355
+ * GROUP a burst of subjects into ONE notification that grows.
15356
+ *
15357
+ * Seconds of quiet after the last matching subject before the burst is
15358
+ * considered over. While it is open, the first subject enqueues immediately —
15359
+ * **exactly as today, with no added latency** — and every real growth (a new
15360
+ * subject, or a name confirmed on one already in it) REPLACES that
15361
+ * notification with an updated one naming everybody. The push carries the
15362
+ * group's own coalescing tag, so the phone replaces rather than stacks.
15363
+ *
15364
+ * `0` / absent = off, and off is today's behaviour byte for byte.
15365
+ *
15366
+ * ### Why an idle cutoff and not a window
15367
+ *
15368
+ * The measured seven-person arrival on device 590 spans 110 s with every
15369
+ * internal gap under 30 s. A 12 s fixed window cuts it into three groups; an
15370
+ * idle cutoff holds it as one and ends it when the arrival actually ends.
15371
+ * 30 is Frigate's shipped value for the same decision.
15372
+ *
15373
+ * ### What it replaces
15374
+ *
15375
+ * The blind cooldown, which collapses a burst by DISCARDING it. Measured on
15376
+ * device 615 / *Persona su Uscio* over six days: 116 qualifying tracks → 74
15377
+ * notifications, **44 (37.9%) suppressed outright**, 23 of them overlapping a
15378
+ * track that did fire and 7 carrying a confirmed identity nobody heard about.
15379
+ * A group collapses the same volume by MERGING, so the cooldown becomes a
15380
+ * budget over GROUPS — which is what it always meant — and a growth is never
15381
+ * throttled by the window its own first member spent.
15382
+ *
15383
+ * ### Interaction with {@link waitForEnhancement}
15384
+ *
15385
+ * They compose, and the order matters. `waitForEnhancement` defers the rule to
15386
+ * TRACK CLOSE, so with both set the group is opened by the first member to
15387
+ * CLOSE — already carrying its name — and grows as later members close. That
15388
+ * is later, and complete. With grouping alone the group opens on the first
15389
+ * object event and picks up names as they are confirmed, through the growth
15390
+ * path. Neither combination fires twice for one subject.
15391
+ *
15392
+ * `.optional()` and deliberately NOT `.default()`: a Zod default does not run
15393
+ * on the addon cap path, so absent must keep meaning what it meant before this
15394
+ * field existed.
15395
+ */
15396
+ groupIdleSec: number().int().min(0).max(600).optional()
14916
15397
  });
14917
15398
  /**
14918
15399
  * Partial patch for `updateRule` — any subset of the input fields, plus the
@@ -15019,6 +15500,7 @@ var NcConditionDescriptorSchema = object({
15019
15500
  "occupancy",
15020
15501
  "audio",
15021
15502
  "deviceState",
15503
+ "scene",
15022
15504
  "systemEvent"
15023
15505
  ]),
15024
15506
  operator: _enum([
@@ -15424,7 +15906,87 @@ var MethodAccessSchema = _enum([
15424
15906
  var AllowedProviderSchema = union([literal("*"), array(string())]);
15425
15907
  var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
15426
15908
  var CapScopeSchema = _enum(["device", "system"]);
15427
- var TokenScopeSchema = discriminatedUnion("type", [
15909
+ /**
15910
+ * DeviceSelector (scope model v3 — 2026-08-12).
15911
+ *
15912
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
15913
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
15914
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
15915
+ * AFTER the grant was minted — no re-grant, no re-login.
15916
+ *
15917
+ * - `all` — every device in the deployment. The broad viewer/operator
15918
+ * lever without a `category` grant (a `category` grant also covers device
15919
+ * caps that carry no deviceId; `all` is specifically the device set).
15920
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
15921
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
15922
+ * is NOT covered until the grant is edited.
15923
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
15924
+ * DYNAMIC. A device that changes type, or a new device of the type,
15925
+ * re-resolves on the next request.
15926
+ * - `locations` — every device whose operator-assigned `location` label is
15927
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
15928
+ * null/unset location matches NO `locations` selector.
15929
+ */
15930
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
15931
+ object({ kind: literal("all") }),
15932
+ object({
15933
+ kind: literal("ids"),
15934
+ ids: array(number().int()).min(1)
15935
+ }),
15936
+ object({
15937
+ kind: literal("types"),
15938
+ types: array(_enum(DeviceType)).min(1)
15939
+ }),
15940
+ object({
15941
+ kind: literal("locations"),
15942
+ locations: array(string().min(1)).min(1)
15943
+ })
15944
+ ]);
15945
+ var DeviceTokenScopeSchema = object({
15946
+ type: literal("device"),
15947
+ /** The device SET this grant covers — resolved against the live fleet. */
15948
+ selector: DeviceSelectorSchema,
15949
+ access: array(MethodAccessSchema).min(1),
15950
+ /**
15951
+ * Whether a grant on a PARENT device transparently covers its accessory
15952
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
15953
+ * Direction is parent → children ONLY.
15954
+ *
15955
+ * Absent → the matcher DERIVES it from the access flavour: `view`
15956
+ * inherits (a camera viewer sees the camera's accessories), `create` /
15957
+ * `delete` do NOT (actuating/removing a child is an explicit act the
15958
+ * operator must grant on the child, not inherit from the parent). Set it
15959
+ * explicitly to override that default per grant.
15960
+ */
15961
+ includeLinked: boolean().optional()
15962
+ });
15963
+ /**
15964
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
15965
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
15966
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
15967
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
15968
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
15969
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
15970
+ * migration cannot reach a JWT already in a client's hands; parse-time
15971
+ * migration covers both without a flag day. No cast — the raw object is read
15972
+ * through `Reflect.get` (its static type is `unknown`).
15973
+ */
15974
+ function migrateLegacyTokenScope(raw) {
15975
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
15976
+ if (Reflect.get(raw, "type") !== "device") return raw;
15977
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
15978
+ const targets = Reflect.get(raw, "targets");
15979
+ if (!Array.isArray(targets)) return raw;
15980
+ return {
15981
+ type: "device",
15982
+ selector: {
15983
+ kind: "ids",
15984
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
15985
+ },
15986
+ access: Reflect.get(raw, "access")
15987
+ };
15988
+ }
15989
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
15428
15990
  object({
15429
15991
  type: literal("category"),
15430
15992
  target: CapScopeSchema,
@@ -15440,18 +16002,8 @@ var TokenScopeSchema = discriminatedUnion("type", [
15440
16002
  target: string(),
15441
16003
  access: array(MethodAccessSchema).min(1)
15442
16004
  }),
15443
- object({
15444
- type: literal("device"),
15445
- /**
15446
- * One or more deviceIds (serialised as strings for wire-format
15447
- * consistency with the rest of the union). Matcher accepts if
15448
- * `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
15449
- * of one scope-per-device when granting access to a set of cameras.
15450
- */
15451
- targets: array(string()).min(1),
15452
- access: array(MethodAccessSchema).min(1)
15453
- })
15454
- ]);
16005
+ DeviceTokenScopeSchema
16006
+ ]));
15455
16007
  object({
15456
16008
  id: string(),
15457
16009
  username: string(),
@@ -15774,7 +16326,7 @@ var TrackEnvelopeSchema = object({
15774
16326
  * `snapshots[]` references — megabytes across a page of tracks. `slim`
15775
16327
  * keeps every scalar the list surfaces actually render (ids, class(es),
15776
16328
  * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
15777
- * zonesVisited, bestEventId, envelope, hasFace) and returns `positions` /
16329
+ * zonesVisited, bestEventId, envelope, hasFace, hasRider) and returns `positions` /
15778
16330
  * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
15779
16331
  * `getTrack`. Mirrors the event-store `projection` convention
15780
16332
  * (`getObjectEvents` et al.).
@@ -15910,7 +16462,21 @@ union([literal(1), literal(2)]);
15910
16462
  var LabelAttributionSchema = object({
15911
16463
  stepId: string(),
15912
16464
  modelId: string().optional(),
15913
- decidedAt: number()
16465
+ decidedAt: number(),
16466
+ /**
16467
+ * The GALLERY id behind a recognised tier-2 label — a face-gallery
16468
+ * `Identity.id` or a plate-gallery `Vehicle.id` (both `randomUUID`).
16469
+ *
16470
+ * The text alone is a DISPLAY NAME, and a display name is renameable: a
16471
+ * notification rule authored on "Gianluca" stopped matching the moment the
16472
+ * operator fixed the spelling in the gallery, and nothing said so. The id is
16473
+ * the thing that does not move, so it is what a rule matches on
16474
+ * (`NcConditions.identities`) and the text is what a human is shown.
16475
+ *
16476
+ * Absent when the label names no gallery row — a plate the OCR read but no
16477
+ * vehicle claims, a sub-class, a species, any tier-1 value.
16478
+ */
16479
+ identityId: string().optional()
15914
16480
  });
15915
16481
  /**
15916
16482
  * The TIERED label model (roadmap 4g), spread into `TrackSchema` and
@@ -16047,6 +16613,28 @@ var TrackSchema = object({
16047
16613
  * `=== true` and render nothing otherwise, never infer "no face".
16048
16614
  */
16049
16615
  hasFace: boolean().optional(),
16616
+ /**
16617
+ * This subject CONTAINS a folded rider — a person the rider-pairing step
16618
+ * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
16619
+ * so the passage is tracked once and as a VEHICLE.
16620
+ *
16621
+ * It exists because the fold's record was dishonest. D34 and the code both
16622
+ * said "the person is not lost — it is reported so both entities stay on the
16623
+ * record"; in fact the pair went into a per-processor RAM field behind an
16624
+ * accessor nobody called, and every durable surface said `vehicle`, full
16625
+ * stop. This is the composition note that makes the row true.
16626
+ *
16627
+ * A COMPOSITION, never a class and never a label. "This vehicle contains a
16628
+ * person" is not an answer to "what is this" — both label tiers would refuse
16629
+ * a macro token anyway (D89), and correctly. Nothing here changes what the
16630
+ * subject IS: a cyclist stays one vehicle track, occupancy still counts one,
16631
+ * and a `person` rule still does not fire for someone cycling past.
16632
+ *
16633
+ * **Absent ≠ false**, exactly like {@link hasFace}: every row written before
16634
+ * the column, and every hub that predates the field, omits it. Test
16635
+ * `=== true` and render nothing otherwise — never infer "no rider".
16636
+ */
16637
+ hasRider: boolean().optional(),
16050
16638
  ...TrackFlagFields,
16051
16639
  ...TrackRetrainFields
16052
16640
  });
@@ -17442,6 +18030,17 @@ var maxSessionHoldMsField = {
17442
18030
  default: 12e4,
17443
18031
  step: 5e3
17444
18032
  };
18033
+ /**
18034
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
18035
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
18036
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
18037
+ */
18038
+ var audioMotionWindowMsField = {
18039
+ min: 5e3,
18040
+ max: 6e5,
18041
+ default: 9e4,
18042
+ step: 5e3
18043
+ };
17445
18044
  var motionFpsField = {
17446
18045
  min: 1,
17447
18046
  max: 30,
@@ -17454,10 +18053,26 @@ var detectionFpsField = {
17454
18053
  default: 10,
17455
18054
  step: 1
17456
18055
  };
18056
+ /**
18057
+ * The occupancy re-check interval. DEFAULT 300 s (2026-08-13 — was 30 s).
18058
+ *
18059
+ * The recheck is now on by default (a parked car is invisible to occupancy
18060
+ * rules until the stationary registry has been rebuilt by motion, which after a
18061
+ * restart may be never on a quiet camera). Each cycle re-subscribes a detection
18062
+ * session — an RTSP re-dial — so the switch is only affordable at a WIDE
18063
+ * interval: 300 s is ~12 re-dials an hour per camera, against 120 at the old
18064
+ * 30 s. A parked car is therefore counted within 5 minutes of a restart.
18065
+ *
18066
+ * Why not wider: `max` is 300 and raising it is TRAIN-BOUND, not addon-bound —
18067
+ * the host validates `attachCamera` against ITS copy of this schema, so a
18068
+ * runner asked for 600 would be rejected by the hub until a `@camstack/server`
18069
+ * carrying the wider bound is installed everywhere. 300 is the widest value
18070
+ * that ships with an addon deploy.
18071
+ */
17457
18072
  var occupancyRecheckSecField = {
17458
18073
  min: 0,
17459
18074
  max: 300,
17460
- default: 30,
18075
+ default: 300,
17461
18076
  step: 5
17462
18077
  };
17463
18078
  var occupancyRecheckFramesField = {
@@ -17602,6 +18217,27 @@ var RunnerCameraConfigSchema = object({
17602
18217
  * resolved `CameraDetectionConfig`.
17603
18218
  */
17604
18219
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
18220
+ /**
18221
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
18222
+ * 'on-motion'` audio window, measured from the LAST motion event.
18223
+ *
18224
+ * This exists because the falling edge cannot be relied on. Camera-native
18225
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
18226
+ * its email-push SMTP path both emit `detected: true` and never the
18227
+ * counterpart); only the frame-diff analyzer emits falls. So on an
18228
+ * onboard-only camera a window that closed only on `detected: false` never
18229
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
18230
+ * battery camera, the one failure mode the mode exists to prevent.
18231
+ *
18232
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
18233
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
18234
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
18235
+ *
18236
+ * Not consumed by the runner: carried here so it shares the per-camera
18237
+ * device-settings surface with `motionCooldownMs`, exactly like
18238
+ * `maxSessionHoldMs`.
18239
+ */
18240
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
17605
18241
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
17606
18242
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
17607
18243
  motionStreamId: string(),
@@ -17655,15 +18291,21 @@ var RunnerCameraConfigSchema = object({
17655
18291
  */
17656
18292
  onboardMotionDrivesAnalyzer: boolean().default(true),
17657
18293
  /**
17658
- * Master toggle for the occupancy re-check. When `false` (DEFAULT) the runner
17659
- * never arms the periodic recheck timer, regardless of `occupancyRecheckSec` —
17660
- * this is off by default because the recheck re-subscribes a detection session
17661
- * every N seconds while `watching`, a major source of pull-decoder re-dial
17662
- * churn (each cycle creates+tears a session → RTSP re-dial → latency). The
18294
+ * Master toggle for the occupancy re-check. When `false` the runner never arms
18295
+ * the periodic recheck timer, regardless of `occupancyRecheckSec`; the
17663
18296
  * `occupancyRecheckSec` / `occupancyRecheckFrames` sliders only take effect
17664
18297
  * (and only render) when this is enabled.
17665
- */
17666
- occupancyRecheckEnabled: boolean().default(false),
18298
+ *
18299
+ * DEFAULT `true` since 2026-08-13 (was `false`). It was off because the
18300
+ * recheck re-subscribes a detection session every N seconds while `watching`
18301
+ * — each cycle creates+tears a session ⇒ an RTSP re-dial ⇒ latency, a major
18302
+ * pull-decoder churn source. What that bought was a blind spot: a STATIONARY
18303
+ * object is counted only while the stationary registry holds it, and the
18304
+ * registry rebuilds from motion, so after a restart a parked car was invisible
18305
+ * to every occupancy rule until something moved in front of it. The churn is
18306
+ * now paid on the interval instead — see `occupancyRecheckSecField`.
18307
+ */
18308
+ occupancyRecheckEnabled: boolean().default(true),
17667
18309
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
17668
18310
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
17669
18311
  /**
@@ -17691,7 +18333,7 @@ var RunnerCameraConfigSchema = object({
17691
18333
  */
17692
18334
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
17693
18335
  });
17694
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
18336
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
17695
18337
  /**
17696
18338
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
17697
18339
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -20229,6 +20871,25 @@ var BatteryStatusSchema = object({
20229
20871
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
20230
20872
  lastUpdated: number(),
20231
20873
  /**
20874
+ * Ms epoch of the last time the device PROVED it was reachable — a
20875
+ * completed firmware round-trip, an observed wake, or an inbound push
20876
+ * (firmware event, email). `0`/absent = never since this slice was born.
20877
+ *
20878
+ * This is the ONLY input that separates "asleep" from "gone", and it is
20879
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
20880
+ * for the radio, because a poll that confirms reachability is the same
20881
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
20882
+ * single derivation every consumer must use; no surface computes its own.
20883
+ *
20884
+ * It is deliberately NOT a clock in the
20885
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
20886
+ * observation itself, and it is the only thing a 30-hour silence is
20887
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
20888
+ * Reolink provider) so a value that means "recently" cannot cost a
20889
+ * SQLite commit per round-trip.
20890
+ */
20891
+ lastContactAt: number().optional(),
20892
+ /**
20232
20893
  * True when the source is a BINARY low-battery indicator (HA
20233
20894
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
20234
20895
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -23766,7 +24427,7 @@ method(object({
23766
24427
  toMs: number()
23767
24428
  }), RecordingAvailabilitySchema, {
23768
24429
  kind: "query",
23769
- auth: "admin"
24430
+ auth: "protected"
23770
24431
  }), method(object({
23771
24432
  deviceId: number(),
23772
24433
  fromMs: number(),
@@ -23774,14 +24435,14 @@ method(object({
23774
24435
  tzOffsetMinutes: number()
23775
24436
  }), RecordingDaysSchema, {
23776
24437
  kind: "query",
23777
- auth: "admin"
24438
+ auth: "protected"
23778
24439
  }), method(object({
23779
24440
  deviceId: number(),
23780
24441
  fromMs: number(),
23781
24442
  toMs: number()
23782
24443
  }), RecordingManifestSchema, {
23783
24444
  kind: "query",
23784
- auth: "admin"
24445
+ auth: "protected"
23785
24446
  }), method(object({}), RecordingStorageUsageSchema, {
23786
24447
  kind: "query",
23787
24448
  auth: "admin"
@@ -24071,14 +24732,77 @@ method(object({
24071
24732
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
24072
24733
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
24073
24734
  *
24074
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
24075
- * derives the device-detail contribution; the provider carries NO hand-written
24076
- * settings-contribution methods. `status.kind:'push'` — the engine pushes on
24077
- * every hysteresis flip / availability change; consumers never poll.
24078
- */
24079
- /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
24080
- * be added without a wire break (matching falls back to any-condition refs). */
24735
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
24736
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
24737
+ * for the thing: a scene is a standing question about the property ("is the bin
24738
+ * still out"), and the operator's question is "which of my scenes have tripped",
24739
+ * across every camera at once — not "what does camera 617 think". Buried one
24740
+ * camera deep it also could not be found. The surface is now a top-level admin
24741
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
24742
+ * picks the camera inside the create flow, the same shape Events and Faces have.
24743
+ *
24744
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
24745
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
24746
+ * directions, so a registration nobody declares fails exactly as loudly as a
24747
+ * declaration nobody registers. The editor is imported directly by the page.
24748
+ *
24749
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
24750
+ * availability change; consumers never poll.
24751
+ */
24752
+ /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
24753
+ * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
24754
+ * Open by design so more can be added without a wire break.
24755
+ *
24756
+ * Matching does NOT fall back across conditions: cross-condition cosines are
24757
+ * not comparable, so "I have never seen this scene in this light" is reported
24758
+ * as `unknown`, never guessed. A day reference scored against an IR frame
24759
+ * collapses the cosine and would latch a false alarm every single night. */
24081
24760
  var SceneConditionSchema = string();
24761
+ /**
24762
+ * What a scene does when the CURRENT light has no reference of its own.
24763
+ *
24764
+ * The lighting variants are not equally likely to exist. Almost every operator
24765
+ * captures daylight and then never stands outside at 22:00 to capture IR, and a
24766
+ * scene that is only ever going to be asked about a daytime question ("is the
24767
+ * bin still on the kerb at 08:00") does not need a night reference at all. The
24768
+ * night half must therefore be OPTIONAL, and optional means the scene keeps
24769
+ * working without it rather than degrading into a permanent complaint.
24770
+ *
24771
+ * - `skip` (default) — the check in that light is not made. Not a verdict, not
24772
+ * an alarm, not even an `unknown`: the live state simply stays whatever the
24773
+ * last covered light left it at, the latch is untouched, and the hysteresis
24774
+ * run is neither spent nor cleared. The scene resumes by itself at first
24775
+ * light. This is the only behaviour under which "I never captured IR" is a
24776
+ * configuration choice instead of a nightly fault.
24777
+ * - `judge-anyway` — score against the OTHER conditions' references. Available
24778
+ * for cameras whose IR frame is close enough to daylight (a floodlit
24779
+ * driveway, an always-white-light doorbell), and wrong for everything else:
24780
+ * cross-condition cosines are not comparable, so a day reference against a
24781
+ * true IR frame collapses and the scene reports a theft at 21:40.
24782
+ *
24783
+ * Never applies when the scene has NO comparable reference at all — that is
24784
+ * "not armed yet", it is reported as `no-reference-for-condition`, and silence
24785
+ * there would hide a scene the operator never finished setting up.
24786
+ */
24787
+ var SceneUncoveredPolicySchema = _enum(["skip", "judge-anyway"]);
24788
+ /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
24789
+ * `unknown` = we cannot judge (no reference for this condition, encoder model
24790
+ * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
24791
+ * and never counts toward hysteresis in either direction. */
24792
+ var SceneVerdictSchema = _enum([
24793
+ "matched",
24794
+ "diverged",
24795
+ "unknown"
24796
+ ]);
24797
+ /** Why a scene cannot judge. Named, because this feature's failure mode is
24798
+ * silence that reads as "nothing has happened". */
24799
+ var SceneUnavailableSchema = _enum([
24800
+ "no-reference-for-condition",
24801
+ "view-shifted",
24802
+ "no-vision-profile",
24803
+ "encoder-model-changed",
24804
+ "no-snapshot"
24805
+ ]);
24082
24806
  /** One captured reference — condition-tagged, model-version-gated. `embedding`
24083
24807
  * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
24084
24808
  var SceneReferenceSchema = object({
@@ -24086,7 +24810,14 @@ var SceneReferenceSchema = object({
24086
24810
  modelId: string(),
24087
24811
  condition: SceneConditionSchema,
24088
24812
  capturedAt: number(),
24089
- thumbnailMediaId: string().optional()
24813
+ thumbnailMediaId: string().optional(),
24814
+ /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
24815
+ * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
24816
+ * normalized rect frame a different piece of world, and the scene would
24817
+ * diverge forever with a perfectly plausible cosine. Checked LAZILY, only
24818
+ * when hysteresis is about to flip — one extra encode per candidate
24819
+ * transition, not per poll. */
24820
+ anchorEmbedding: array(number()).optional()
24090
24821
  });
24091
24822
  var SceneMonitorStateSchema = object({
24092
24823
  id: string(),
@@ -24108,6 +24839,28 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
24108
24839
  profileId: string().optional(),
24109
24840
  hysteresisCount: number().int().positive()
24110
24841
  })]);
24842
+ var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
24843
+ /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
24844
+ * out in silence rather than reporting a fault every night. */
24845
+ var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
24846
+ /**
24847
+ * Vision-model adjudication of a candidate flip. Field names deliberately
24848
+ * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
24849
+ *
24850
+ * `onTimeout` defaults to **'hold'**, the OPPOSITE of `NcConfirmGate`'s
24851
+ * fail-open: a notification suppressed is the worse error there, but a vision
24852
+ * model that timed out has not told us the bin is gone, and a latch is a
24853
+ * stateful claim that costs the operator a trip to reset.
24854
+ */
24855
+ var SceneConfirmSchema = object({
24856
+ enabled: boolean().default(false),
24857
+ prompt: string().min(1).max(1e3),
24858
+ profileId: string().optional(),
24859
+ timeoutMs: number().int().min(1e3).max(2e4).default(8e3),
24860
+ maxImagePx: number().int().min(64).max(2048).default(448),
24861
+ /** What a timeout / unavailable model means for the PENDING flip. */
24862
+ onTimeout: _enum(["flip", "hold"]).default("hold")
24863
+ });
24111
24864
  var SceneMonitorSchema = object({
24112
24865
  id: string(),
24113
24866
  label: string(),
@@ -24126,7 +24879,56 @@ var SceneMonitorSchema = object({
24126
24879
  lastConfidence: number().nullable(),
24127
24880
  currentCondition: SceneConditionSchema.nullable(),
24128
24881
  availability: _enum(["ok", "unavailable"]),
24129
- unavailableReason: string().nullable()
24882
+ unavailableReason: string().nullable(),
24883
+ /** Which state is "the initial screen". `null` until the first capture. */
24884
+ baselineStateId: string().nullable(),
24885
+ /** Which boolean drives notification rules and any export. */
24886
+ emit: _enum(["latched", "live"]).default("latched"),
24887
+ /** Live: does the region match the baseline RIGHT NOW. */
24888
+ verdict: SceneVerdictSchema,
24889
+ /** Has it been `diverged` at least once since `armedAt` — the operator's boolean. */
24890
+ latched: boolean(),
24891
+ /** Last reset (or creation). */
24892
+ armedAt: number(),
24893
+ divergedAt: number().nullable(),
24894
+ restoredAt: number().nullable(),
24895
+ /** A check is only COUNTED when the device has been quiet this long. Motion
24896
+ * during the window DISCARDS the observation — a car pulling up in front of
24897
+ * the bin must not be able to spend hysteresis credit. */
24898
+ quietSeconds: number().int().min(0).max(3600).default(60),
24899
+ /** An observation only advances the pending count when it is at least this
24900
+ * far from the previously counted one, so N agreeing checks span real time
24901
+ * rather than N adjacent polls inside one occlusion. */
24902
+ minObservationSpacingSec: number().int().min(0).max(3600).default(120),
24903
+ /** Vision-model adjudication of a candidate flip. Similarity primary only. */
24904
+ confirm: SceneConfirmSchema.optional(),
24905
+ /** Whole-frame anchor cosine below which a flip is REFUSED as `view-shifted`. */
24906
+ anchorThreshold: number().min(0).max(1).default(SCENE_DEFAULT_ANCHOR_THRESHOLD),
24907
+ /** Clear the latch on its own when the scene matches again? Default false —
24908
+ * `restoredAt` and the `scene-restored` edge are recorded regardless, so an
24909
+ * automation can react to the bin coming back without the operator's own
24910
+ * alarm silently clearing itself. */
24911
+ autoRestore: boolean().default(false),
24912
+ /** What to do when the current light has no reference of its own. See
24913
+ * {@link SceneUncoveredPolicySchema} — the default makes night OPTIONAL. */
24914
+ onUncoveredCondition: SceneUncoveredPolicySchema.default(SCENE_DEFAULT_UNCOVERED_POLICY),
24915
+ /**
24916
+ * The light whose checks are currently being SAT OUT under
24917
+ * `onUncoveredCondition: 'skip'` — `null` when the scene is checking normally.
24918
+ *
24919
+ * Engine-reported and advisory only: it moves no verdict, no latch and no
24920
+ * hysteresis. It exists so the card can say *"night (IR) — checks paused,
24921
+ * nothing captured in this light"* in the same calm voice as the coverage
24922
+ * line, because the alternative is a scene that silently stops answering
24923
+ * after sunset with nothing anywhere saying why. A skipped check must never
24924
+ * read as a broken one.
24925
+ */
24926
+ suspendedCondition: SceneConditionSchema.nullable().default(null),
24927
+ /** Named cause when `verdict === 'unknown'`. */
24928
+ unavailable: SceneUnavailableSchema.nullable(),
24929
+ /** Conditions that have at least one comparable reference — the coverage line
24930
+ * ("day ✓ · ir ✓ · dusk ✗") that turns a silent fallback into a visible fact. */
24931
+ coveredConditions: array(SceneConditionSchema)
24130
24932
  });
24131
24933
  var SceneMonitorStatusSchema = object({
24132
24934
  monitors: array(SceneMonitorSchema),
@@ -24159,7 +24961,15 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
24159
24961
  "both"
24160
24962
  ]).optional(),
24161
24963
  checkIntervalSec: number().optional(),
24162
- check: SceneCheckSchema.optional()
24964
+ check: SceneCheckSchema.optional(),
24965
+ emit: _enum(["latched", "live"]).optional(),
24966
+ quietSeconds: number().int().min(0).max(3600).optional(),
24967
+ minObservationSpacingSec: number().int().min(0).max(3600).optional(),
24968
+ anchorThreshold: number().min(0).max(1).optional(),
24969
+ autoRestore: boolean().optional(),
24970
+ onUncoveredCondition: SceneUncoveredPolicySchema.optional(),
24971
+ /** `null` clears the vision-model adjudicator. */
24972
+ confirm: SceneConfirmSchema.nullable().optional()
24163
24973
  })
24164
24974
  }), _void(), {
24165
24975
  kind: "mutation",
@@ -24196,6 +25006,14 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
24196
25006
  }), _void(), {
24197
25007
  kind: "mutation",
24198
25008
  auth: "admin"
25009
+ }), method(object({
25010
+ deviceId: number(),
25011
+ monitorId: string(),
25012
+ /** Defaults to TRUE at the provider seam — see `SCENE_RESET_RECAPTURES`. */
25013
+ recapture: boolean().optional()
25014
+ }), _void(), {
25015
+ kind: "mutation",
25016
+ auth: "admin"
24199
25017
  });
24200
25018
  /**
24201
25019
  * Per-stage gating mode applied to the zones a rule references.
@@ -24349,6 +25167,16 @@ var CamStreamDescriptorSchema = object({
24349
25167
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
24350
25168
  metadata: record(string(), unknown()).optional()
24351
25169
  });
25170
+ object({
25171
+ /** The descriptors as last built from a real camera response. Never a guess:
25172
+ * a failed or refused build writes NOTHING, so a restored catalog is always
25173
+ * one the camera itself once produced. */
25174
+ descriptors: array(CamStreamDescriptorSchema),
25175
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
25176
+ * path decide whether the camera's own awake window is worth spending on a
25177
+ * re-read. */
25178
+ lastFetchedAt: number()
25179
+ });
24352
25180
  DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
24353
25181
  /** One of the camera's stream profiles. */
24354
25182
  var StreamProfileSchema = _enum([
@@ -24504,12 +25332,64 @@ var NetworkAddressSchema = object({
24504
25332
  family: string(),
24505
25333
  internal: boolean()
24506
25334
  });
25335
+ /**
25336
+ * Provenance of the site coordinates, and the whole reason this is not just two
25337
+ * numbers.
25338
+ *
25339
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
25340
+ * nothing overwrites it.
25341
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
25342
+ * default that is right to a few kilometres beats the coarse UTC clock split
25343
+ * the sun-times consumers otherwise fall back to.
25344
+ *
25345
+ * The UI shows which one it is. An operator who cannot tell a guess from their
25346
+ * own input will eventually trust the guess.
25347
+ */
25348
+ var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
25349
+ /**
25350
+ * The read shape: the location plus the honest state of the one-shot derivation.
25351
+ *
25352
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
25353
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
25354
+ * failed; the hub will NOT try again on its own — the fallback is declared
25355
+ * (consumers degrade to their own last resort) and the operator either types the
25356
+ * coordinates or presses detect.
25357
+ */
25358
+ var SiteLocationStatusSchema = object({
25359
+ location: object({
25360
+ /** WGS84 decimal degrees. */
25361
+ latitude: number().min(-90).max(90),
25362
+ longitude: number().min(-180).max(180),
25363
+ source: SiteLocationSourceSchema,
25364
+ /** Epoch ms the value was last written. */
25365
+ updatedAt: number(),
25366
+ /**
25367
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
25368
+ * only — never parsed, never matched on. Absent for an operator-typed value.
25369
+ */
25370
+ label: string().optional()
25371
+ }).nullable(),
25372
+ derivationAttemptedAt: number().nullable(),
25373
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
25374
+ derivationError: string().nullable()
25375
+ });
25376
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
25377
+ var SetSiteLocationInputSchema = object({
25378
+ latitude: number().min(-90).max(90),
25379
+ longitude: number().min(-180).max(180)
25380
+ }).nullable();
24507
25381
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
24508
25382
  kind: "mutation",
24509
25383
  auth: "admin"
24510
25384
  }), method(_void(), _void(), {
24511
25385
  kind: "mutation",
24512
25386
  auth: "admin"
25387
+ }), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
25388
+ kind: "mutation",
25389
+ auth: "admin"
25390
+ }), method(_void(), SiteLocationStatusSchema, {
25391
+ kind: "mutation",
25392
+ auth: "admin"
24513
25393
  });
24514
25394
  object({
24515
25395
  /** True when the device's tamper switch / case-open contact is
@@ -27239,6 +28119,12 @@ Object.freeze({
27239
28119
  addonId: null,
27240
28120
  access: "create"
27241
28121
  },
28122
+ "llm.cancel": {
28123
+ capName: "llm",
28124
+ capScope: "system",
28125
+ addonId: null,
28126
+ access: "create"
28127
+ },
27242
28128
  "llm.deleteModel": {
27243
28129
  capName: "llm",
27244
28130
  capScope: "system",
@@ -27323,6 +28209,12 @@ Object.freeze({
27323
28209
  addonId: null,
27324
28210
  access: "view"
27325
28211
  },
28212
+ "llm.resolveModelRef": {
28213
+ capName: "llm",
28214
+ capScope: "system",
28215
+ addonId: null,
28216
+ access: "create"
28217
+ },
27326
28218
  "llm.setDefault": {
27327
28219
  capName: "llm",
27328
28220
  capScope: "system",
@@ -29489,6 +30381,12 @@ Object.freeze({
29489
30381
  addonId: null,
29490
30382
  access: "create"
29491
30383
  },
30384
+ "sceneMonitor.resetScene": {
30385
+ capName: "scene-monitor",
30386
+ capScope: "device",
30387
+ addonId: null,
30388
+ access: "delete"
30389
+ },
29492
30390
  "sceneMonitor.updateScene": {
29493
30391
  capName: "scene-monitor",
29494
30392
  capScope: "device",
@@ -30167,6 +31065,12 @@ Object.freeze({
30167
31065
  addonId: null,
30168
31066
  access: "create"
30169
31067
  },
31068
+ "system.detectSiteLocation": {
31069
+ capName: "system",
31070
+ capScope: "system",
31071
+ addonId: null,
31072
+ access: "create"
31073
+ },
30170
31074
  "system.featureFlags": {
30171
31075
  capName: "system",
30172
31076
  capScope: "system",
@@ -30185,6 +31089,12 @@ Object.freeze({
30185
31089
  addonId: null,
30186
31090
  access: "view"
30187
31091
  },
31092
+ "system.getSiteLocation": {
31093
+ capName: "system",
31094
+ capScope: "system",
31095
+ addonId: null,
31096
+ access: "view"
31097
+ },
30188
31098
  "system.health": {
30189
31099
  capName: "system",
30190
31100
  capScope: "system",
@@ -30209,6 +31119,12 @@ Object.freeze({
30209
31119
  addonId: null,
30210
31120
  access: "create"
30211
31121
  },
31122
+ "system.setSiteLocation": {
31123
+ capName: "system",
31124
+ capScope: "system",
31125
+ addonId: null,
31126
+ access: "create"
31127
+ },
30212
31128
  "terminalSession.adoptLegacyMonitor": {
30213
31129
  capName: "terminal-session",
30214
31130
  capScope: "system",
@@ -30780,6 +31696,1683 @@ Object.freeze({
30780
31696
  access: "create"
30781
31697
  }
30782
31698
  });
31699
+ Object.freeze({
31700
+ "accessories.setChildHidden": [{
31701
+ name: "childDeviceId",
31702
+ form: "single",
31703
+ optional: false
31704
+ }, {
31705
+ name: "deviceId",
31706
+ form: "single",
31707
+ optional: false
31708
+ }],
31709
+ "addonSettings.getDeviceSettings": [{
31710
+ name: "deviceId",
31711
+ form: "single",
31712
+ optional: false
31713
+ }],
31714
+ "addonSettings.updateDeviceSettings": [{
31715
+ name: "deviceId",
31716
+ form: "single",
31717
+ optional: false
31718
+ }],
31719
+ "alarmPanel.arm": [{
31720
+ name: "deviceId",
31721
+ form: "single",
31722
+ optional: false
31723
+ }],
31724
+ "alarmPanel.disarm": [{
31725
+ name: "deviceId",
31726
+ form: "single",
31727
+ optional: false
31728
+ }],
31729
+ "alarmPanel.trigger": [{
31730
+ name: "deviceId",
31731
+ form: "single",
31732
+ optional: false
31733
+ }],
31734
+ "audioAnalysis.resolveDeviceSettings": [{
31735
+ name: "deviceId",
31736
+ form: "single",
31737
+ optional: false
31738
+ }],
31739
+ "audioAnalyzer.classify": [{
31740
+ name: "deviceId",
31741
+ form: "single",
31742
+ optional: true
31743
+ }],
31744
+ "audioMetrics.getCurrentSnapshot": [{
31745
+ name: "deviceId",
31746
+ form: "single",
31747
+ optional: false
31748
+ }],
31749
+ "audioMetrics.getHistory": [{
31750
+ name: "deviceId",
31751
+ form: "single",
31752
+ optional: false
31753
+ }],
31754
+ "automationControl.disable": [{
31755
+ name: "deviceId",
31756
+ form: "single",
31757
+ optional: false
31758
+ }],
31759
+ "automationControl.enable": [{
31760
+ name: "deviceId",
31761
+ form: "single",
31762
+ optional: false
31763
+ }],
31764
+ "automationControl.trigger": [{
31765
+ name: "deviceId",
31766
+ form: "single",
31767
+ optional: false
31768
+ }],
31769
+ "battery.wakeForStream": [{
31770
+ name: "deviceId",
31771
+ form: "single",
31772
+ optional: false
31773
+ }],
31774
+ "brightness.setBrightness": [{
31775
+ name: "deviceId",
31776
+ form: "single",
31777
+ optional: false
31778
+ }],
31779
+ "button.press": [{
31780
+ name: "deviceId",
31781
+ form: "single",
31782
+ optional: false
31783
+ }],
31784
+ "cameraCredentials.getCredentials": [{
31785
+ name: "deviceId",
31786
+ form: "single",
31787
+ optional: false
31788
+ }],
31789
+ "cameraStreams.getBrokerStreams": [{
31790
+ name: "deviceId",
31791
+ form: "single",
31792
+ optional: false
31793
+ }],
31794
+ "cameraStreams.getCameraStreams": [{
31795
+ name: "deviceId",
31796
+ form: "single",
31797
+ optional: false
31798
+ }],
31799
+ "cameraStreams.getProfileRtspEntries": [{
31800
+ name: "deviceId",
31801
+ form: "single",
31802
+ optional: false
31803
+ }],
31804
+ "cameraStreams.getRtspEntries": [{
31805
+ name: "deviceId",
31806
+ form: "single",
31807
+ optional: false
31808
+ }],
31809
+ "cameraStreams.pickStream": [{
31810
+ name: "deviceId",
31811
+ form: "single",
31812
+ optional: false
31813
+ }],
31814
+ "climateControl.setFanMode": [{
31815
+ name: "deviceId",
31816
+ form: "single",
31817
+ optional: false
31818
+ }],
31819
+ "climateControl.setMode": [{
31820
+ name: "deviceId",
31821
+ form: "single",
31822
+ optional: false
31823
+ }],
31824
+ "climateControl.setPreset": [{
31825
+ name: "deviceId",
31826
+ form: "single",
31827
+ optional: false
31828
+ }],
31829
+ "climateControl.setSwingHorizontal": [{
31830
+ name: "deviceId",
31831
+ form: "single",
31832
+ optional: false
31833
+ }],
31834
+ "climateControl.setSwingVertical": [{
31835
+ name: "deviceId",
31836
+ form: "single",
31837
+ optional: false
31838
+ }],
31839
+ "climateControl.setTarget": [{
31840
+ name: "deviceId",
31841
+ form: "single",
31842
+ optional: false
31843
+ }],
31844
+ "climateControl.setTargetHumidity": [{
31845
+ name: "deviceId",
31846
+ form: "single",
31847
+ optional: false
31848
+ }],
31849
+ "climateControl.setTargetRange": [{
31850
+ name: "deviceId",
31851
+ form: "single",
31852
+ optional: false
31853
+ }],
31854
+ "color.setColor": [{
31855
+ name: "deviceId",
31856
+ form: "single",
31857
+ optional: false
31858
+ }],
31859
+ "consumables.reset": [{
31860
+ name: "deviceId",
31861
+ form: "single",
31862
+ optional: false
31863
+ }],
31864
+ "control.setValue": [{
31865
+ name: "deviceId",
31866
+ form: "single",
31867
+ optional: false
31868
+ }],
31869
+ "cover.close": [{
31870
+ name: "deviceId",
31871
+ form: "single",
31872
+ optional: false
31873
+ }],
31874
+ "cover.open": [{
31875
+ name: "deviceId",
31876
+ form: "single",
31877
+ optional: false
31878
+ }],
31879
+ "cover.setPosition": [{
31880
+ name: "deviceId",
31881
+ form: "single",
31882
+ optional: false
31883
+ }],
31884
+ "cover.setTiltPosition": [{
31885
+ name: "deviceId",
31886
+ form: "single",
31887
+ optional: false
31888
+ }],
31889
+ "cover.stop": [{
31890
+ name: "deviceId",
31891
+ form: "single",
31892
+ optional: false
31893
+ }],
31894
+ "dayNight.getOptions": [{
31895
+ name: "deviceId",
31896
+ form: "single",
31897
+ optional: false
31898
+ }],
31899
+ "dayNight.setSettings": [{
31900
+ name: "deviceId",
31901
+ form: "single",
31902
+ optional: false
31903
+ }],
31904
+ "decoder.createSession": [{
31905
+ name: "deviceId",
31906
+ form: "single",
31907
+ optional: true
31908
+ }],
31909
+ "deviceAdoption.release": [{
31910
+ name: "camDeviceId",
31911
+ form: "single",
31912
+ optional: false
31913
+ }],
31914
+ "deviceAdoption.resync": [{
31915
+ name: "camDeviceId",
31916
+ form: "single",
31917
+ optional: false
31918
+ }],
31919
+ "deviceDiscovery.adoptDevice": [{
31920
+ name: "deviceId",
31921
+ form: "single",
31922
+ optional: false
31923
+ }],
31924
+ "deviceDiscovery.listDiscovered": [{
31925
+ name: "deviceId",
31926
+ form: "single",
31927
+ optional: false
31928
+ }],
31929
+ "deviceDiscovery.refreshDiscovery": [{
31930
+ name: "deviceId",
31931
+ form: "single",
31932
+ optional: false
31933
+ }],
31934
+ "deviceDiscovery.releaseDevice": [{
31935
+ name: "childDeviceId",
31936
+ form: "single",
31937
+ optional: false
31938
+ }, {
31939
+ name: "deviceId",
31940
+ form: "single",
31941
+ optional: false
31942
+ }],
31943
+ "deviceManager.adoptionRelease": [{
31944
+ name: "camDeviceId",
31945
+ form: "single",
31946
+ optional: false
31947
+ }],
31948
+ "deviceManager.adoptionResync": [{
31949
+ name: "camDeviceId",
31950
+ form: "single",
31951
+ optional: false
31952
+ }],
31953
+ "deviceManager.applyInitialMeta": [{
31954
+ name: "deviceId",
31955
+ form: "single",
31956
+ optional: false
31957
+ }, {
31958
+ name: "linkDeviceId",
31959
+ form: "single",
31960
+ optional: true
31961
+ }],
31962
+ "deviceManager.disable": [{
31963
+ name: "deviceId",
31964
+ form: "single",
31965
+ optional: false
31966
+ }],
31967
+ "deviceManager.enable": [{
31968
+ name: "deviceId",
31969
+ form: "single",
31970
+ optional: false
31971
+ }],
31972
+ "deviceManager.getBindings": [{
31973
+ name: "deviceId",
31974
+ form: "single",
31975
+ optional: false
31976
+ }],
31977
+ "deviceManager.getChildren": [{
31978
+ name: "parentDeviceId",
31979
+ form: "single",
31980
+ optional: false
31981
+ }],
31982
+ "deviceManager.getConfigSchema": [{
31983
+ name: "deviceId",
31984
+ form: "single",
31985
+ optional: false
31986
+ }],
31987
+ "deviceManager.getDevice": [{
31988
+ name: "deviceId",
31989
+ form: "single",
31990
+ optional: false
31991
+ }],
31992
+ "deviceManager.getDeviceAggregate": [{
31993
+ name: "deviceId",
31994
+ form: "single",
31995
+ optional: false
31996
+ }],
31997
+ "deviceManager.getDeviceLiveInfoAggregate": [{
31998
+ name: "deviceId",
31999
+ form: "single",
32000
+ optional: false
32001
+ }],
32002
+ "deviceManager.getDeviceSettingsAggregate": [{
32003
+ name: "deviceId",
32004
+ form: "single",
32005
+ optional: false
32006
+ }],
32007
+ "deviceManager.getDeviceStatusAggregate": [{
32008
+ name: "deviceId",
32009
+ form: "single",
32010
+ optional: false
32011
+ }],
32012
+ "deviceManager.getDeviceStatusAggregateBatch": [{
32013
+ name: "deviceIds",
32014
+ form: "array",
32015
+ optional: false
32016
+ }],
32017
+ "deviceManager.getLinkedDevices": [{
32018
+ name: "deviceId",
32019
+ form: "single",
32020
+ optional: false
32021
+ }],
32022
+ "deviceManager.getSettingsSchema": [{
32023
+ name: "deviceId",
32024
+ form: "single",
32025
+ optional: false
32026
+ }],
32027
+ "deviceManager.getStreamProfileMap": [{
32028
+ name: "deviceId",
32029
+ form: "single",
32030
+ optional: false
32031
+ }],
32032
+ "deviceManager.getStreamSources": [{
32033
+ name: "deviceId",
32034
+ form: "single",
32035
+ optional: false
32036
+ }],
32037
+ "deviceManager.getWireableFields": [{
32038
+ name: "deviceId",
32039
+ form: "single",
32040
+ optional: false
32041
+ }],
32042
+ "deviceManager.loadConfig": [{
32043
+ name: "deviceId",
32044
+ form: "single",
32045
+ optional: false
32046
+ }],
32047
+ "deviceManager.loadMeta": [{
32048
+ name: "deviceId",
32049
+ form: "single",
32050
+ optional: false
32051
+ }],
32052
+ "deviceManager.loadRuntimeState": [{
32053
+ name: "deviceId",
32054
+ form: "single",
32055
+ optional: false
32056
+ }],
32057
+ "deviceManager.persistConfig": [{
32058
+ name: "deviceId",
32059
+ form: "single",
32060
+ optional: false
32061
+ }],
32062
+ "deviceManager.probeStreams": [{
32063
+ name: "deviceId",
32064
+ form: "single",
32065
+ optional: false
32066
+ }],
32067
+ "deviceManager.registerDevice": [{
32068
+ name: "parentDeviceId",
32069
+ form: "single",
32070
+ optional: true
32071
+ }],
32072
+ "deviceManager.remove": [{
32073
+ name: "deviceId",
32074
+ form: "single",
32075
+ optional: false
32076
+ }],
32077
+ "deviceManager.removeDevice": [{
32078
+ name: "deviceId",
32079
+ form: "single",
32080
+ optional: false
32081
+ }],
32082
+ "deviceManager.runDeviceAction": [{
32083
+ name: "deviceId",
32084
+ form: "single",
32085
+ optional: false
32086
+ }],
32087
+ "deviceManager.setChildLayout": [{
32088
+ name: "deviceId",
32089
+ form: "single",
32090
+ optional: false
32091
+ }],
32092
+ "deviceManager.setDisabled": [{
32093
+ name: "deviceId",
32094
+ form: "single",
32095
+ optional: false
32096
+ }],
32097
+ "deviceManager.setDisplay": [{
32098
+ name: "deviceId",
32099
+ form: "single",
32100
+ optional: false
32101
+ }],
32102
+ "deviceManager.setIntegrationId": [{
32103
+ name: "deviceId",
32104
+ form: "single",
32105
+ optional: false
32106
+ }],
32107
+ "deviceManager.setLinkDeviceId": [{
32108
+ name: "deviceId",
32109
+ form: "single",
32110
+ optional: false
32111
+ }, {
32112
+ name: "linkDeviceId",
32113
+ form: "single",
32114
+ optional: true
32115
+ }],
32116
+ "deviceManager.setLocation": [{
32117
+ name: "deviceId",
32118
+ form: "single",
32119
+ optional: false
32120
+ }],
32121
+ "deviceManager.setMetadata": [{
32122
+ name: "deviceId",
32123
+ form: "single",
32124
+ optional: false
32125
+ }],
32126
+ "deviceManager.setName": [{
32127
+ name: "deviceId",
32128
+ form: "single",
32129
+ optional: false
32130
+ }],
32131
+ "deviceManager.setPrimaryChildEntityId": [{
32132
+ name: "deviceId",
32133
+ form: "single",
32134
+ optional: false
32135
+ }],
32136
+ "deviceManager.setRole": [{
32137
+ name: "deviceId",
32138
+ form: "single",
32139
+ optional: false
32140
+ }],
32141
+ "deviceManager.setStreamProfileMap": [{
32142
+ name: "deviceId",
32143
+ form: "single",
32144
+ optional: false
32145
+ }],
32146
+ "deviceManager.setType": [{
32147
+ name: "deviceId",
32148
+ form: "single",
32149
+ optional: false
32150
+ }],
32151
+ "deviceManager.setWrapperActive": [{
32152
+ name: "deviceId",
32153
+ form: "single",
32154
+ optional: false
32155
+ }],
32156
+ "deviceManager.testField": [{
32157
+ name: "deviceId",
32158
+ form: "single",
32159
+ optional: false
32160
+ }],
32161
+ "deviceManager.updateConfig": [{
32162
+ name: "deviceId",
32163
+ form: "single",
32164
+ optional: false
32165
+ }],
32166
+ "deviceManager.updateDeviceField": [{
32167
+ name: "deviceId",
32168
+ form: "single",
32169
+ optional: false
32170
+ }],
32171
+ "deviceManager.updateDeviceFieldsBatch": [{
32172
+ name: "deviceId",
32173
+ form: "single",
32174
+ optional: false
32175
+ }],
32176
+ "deviceOps.getConfigEntries": [{
32177
+ name: "deviceId",
32178
+ form: "single",
32179
+ optional: false
32180
+ }],
32181
+ "deviceOps.getRawState": [{
32182
+ name: "deviceId",
32183
+ form: "single",
32184
+ optional: false
32185
+ }],
32186
+ "deviceOps.getSettingsSchema": [{
32187
+ name: "deviceId",
32188
+ form: "single",
32189
+ optional: false
32190
+ }],
32191
+ "deviceOps.getStreamSources": [{
32192
+ name: "deviceId",
32193
+ form: "single",
32194
+ optional: false
32195
+ }],
32196
+ "deviceOps.removeDevice": [{
32197
+ name: "deviceId",
32198
+ form: "single",
32199
+ optional: false
32200
+ }],
32201
+ "deviceOps.runAction": [{
32202
+ name: "deviceId",
32203
+ form: "single",
32204
+ optional: false
32205
+ }],
32206
+ "deviceOps.setConfig": [{
32207
+ name: "deviceId",
32208
+ form: "single",
32209
+ optional: false
32210
+ }],
32211
+ "deviceState.getCapSlice": [{
32212
+ name: "deviceId",
32213
+ form: "single",
32214
+ optional: false
32215
+ }],
32216
+ "deviceState.getSnapshot": [{
32217
+ name: "deviceId",
32218
+ form: "single",
32219
+ optional: false
32220
+ }],
32221
+ "deviceState.setCapSlice": [{
32222
+ name: "deviceId",
32223
+ form: "single",
32224
+ optional: false
32225
+ }],
32226
+ "events.getEventClipUrl": [{
32227
+ name: "deviceId",
32228
+ form: "single",
32229
+ optional: false
32230
+ }],
32231
+ "events.getEvents": [{
32232
+ name: "deviceId",
32233
+ form: "single",
32234
+ optional: false
32235
+ }],
32236
+ "events.getEventThumbnail": [{
32237
+ name: "deviceId",
32238
+ form: "single",
32239
+ optional: false
32240
+ }],
32241
+ "faceGallery.getFaceByTrack": [{
32242
+ name: "deviceId",
32243
+ form: "single",
32244
+ optional: false
32245
+ }],
32246
+ "faceGallery.listRecentFaces": [{
32247
+ name: "deviceId",
32248
+ form: "single",
32249
+ optional: true
32250
+ }],
32251
+ "fanControl.setDirection": [{
32252
+ name: "deviceId",
32253
+ form: "single",
32254
+ optional: false
32255
+ }],
32256
+ "fanControl.setOscillating": [{
32257
+ name: "deviceId",
32258
+ form: "single",
32259
+ optional: false
32260
+ }],
32261
+ "fanControl.setPercentage": [{
32262
+ name: "deviceId",
32263
+ form: "single",
32264
+ optional: false
32265
+ }],
32266
+ "fanControl.setPreset": [{
32267
+ name: "deviceId",
32268
+ form: "single",
32269
+ optional: false
32270
+ }],
32271
+ "humidifier.setMode": [{
32272
+ name: "deviceId",
32273
+ form: "single",
32274
+ optional: false
32275
+ }],
32276
+ "humidifier.setOn": [{
32277
+ name: "deviceId",
32278
+ form: "single",
32279
+ optional: false
32280
+ }],
32281
+ "humidifier.setTargetHumidity": [{
32282
+ name: "deviceId",
32283
+ form: "single",
32284
+ optional: false
32285
+ }],
32286
+ "imageSettings.getOptions": [{
32287
+ name: "deviceId",
32288
+ form: "single",
32289
+ optional: false
32290
+ }],
32291
+ "imageSettings.setSettings": [{
32292
+ name: "deviceId",
32293
+ form: "single",
32294
+ optional: false
32295
+ }],
32296
+ "intercom.endTalkSession": [{
32297
+ name: "deviceId",
32298
+ form: "single",
32299
+ optional: false
32300
+ }],
32301
+ "intercom.handleAnswer": [{
32302
+ name: "deviceId",
32303
+ form: "single",
32304
+ optional: false
32305
+ }],
32306
+ "intercom.pushTalkAudio": [{
32307
+ name: "deviceId",
32308
+ form: "single",
32309
+ optional: false
32310
+ }],
32311
+ "intercom.startSession": [{
32312
+ name: "deviceId",
32313
+ form: "single",
32314
+ optional: false
32315
+ }],
32316
+ "intercom.startTalkSession": [{
32317
+ name: "deviceId",
32318
+ form: "single",
32319
+ optional: false
32320
+ }],
32321
+ "intercom.stopSession": [{
32322
+ name: "deviceId",
32323
+ form: "single",
32324
+ optional: false
32325
+ }],
32326
+ "lawnMowerControl.dock": [{
32327
+ name: "deviceId",
32328
+ form: "single",
32329
+ optional: false
32330
+ }],
32331
+ "lawnMowerControl.pause": [{
32332
+ name: "deviceId",
32333
+ form: "single",
32334
+ optional: false
32335
+ }],
32336
+ "lawnMowerControl.startMowing": [{
32337
+ name: "deviceId",
32338
+ form: "single",
32339
+ optional: false
32340
+ }],
32341
+ "lockControl.lock": [{
32342
+ name: "deviceId",
32343
+ form: "single",
32344
+ optional: false
32345
+ }],
32346
+ "lockControl.open": [{
32347
+ name: "deviceId",
32348
+ form: "single",
32349
+ optional: false
32350
+ }],
32351
+ "lockControl.unlock": [{
32352
+ name: "deviceId",
32353
+ form: "single",
32354
+ optional: false
32355
+ }],
32356
+ "mediaPlayer.next": [{
32357
+ name: "deviceId",
32358
+ form: "single",
32359
+ optional: false
32360
+ }],
32361
+ "mediaPlayer.pause": [{
32362
+ name: "deviceId",
32363
+ form: "single",
32364
+ optional: false
32365
+ }],
32366
+ "mediaPlayer.play": [{
32367
+ name: "deviceId",
32368
+ form: "single",
32369
+ optional: false
32370
+ }],
32371
+ "mediaPlayer.playMedia": [{
32372
+ name: "deviceId",
32373
+ form: "single",
32374
+ optional: false
32375
+ }],
32376
+ "mediaPlayer.previous": [{
32377
+ name: "deviceId",
32378
+ form: "single",
32379
+ optional: false
32380
+ }],
32381
+ "mediaPlayer.seek": [{
32382
+ name: "deviceId",
32383
+ form: "single",
32384
+ optional: false
32385
+ }],
32386
+ "mediaPlayer.selectSource": [{
32387
+ name: "deviceId",
32388
+ form: "single",
32389
+ optional: false
32390
+ }],
32391
+ "mediaPlayer.setMute": [{
32392
+ name: "deviceId",
32393
+ form: "single",
32394
+ optional: false
32395
+ }],
32396
+ "mediaPlayer.setRepeat": [{
32397
+ name: "deviceId",
32398
+ form: "single",
32399
+ optional: false
32400
+ }],
32401
+ "mediaPlayer.setShuffle": [{
32402
+ name: "deviceId",
32403
+ form: "single",
32404
+ optional: false
32405
+ }],
32406
+ "mediaPlayer.setVolume": [{
32407
+ name: "deviceId",
32408
+ form: "single",
32409
+ optional: false
32410
+ }],
32411
+ "mediaPlayer.stop": [{
32412
+ name: "deviceId",
32413
+ form: "single",
32414
+ optional: false
32415
+ }],
32416
+ "motion.isDetected": [{
32417
+ name: "deviceId",
32418
+ form: "single",
32419
+ optional: false
32420
+ }],
32421
+ "motionDetection.analyze": [{
32422
+ name: "deviceId",
32423
+ form: "single",
32424
+ optional: false
32425
+ }],
32426
+ "motionDetection.removeCamera": [{
32427
+ name: "deviceId",
32428
+ form: "single",
32429
+ optional: false
32430
+ }],
32431
+ "motionTrigger.setMotionTrigger": [{
32432
+ name: "deviceId",
32433
+ form: "single",
32434
+ optional: false
32435
+ }],
32436
+ "motionZones.getOptions": [{
32437
+ name: "deviceId",
32438
+ form: "single",
32439
+ optional: false
32440
+ }],
32441
+ "motionZones.setZone": [{
32442
+ name: "deviceId",
32443
+ form: "single",
32444
+ optional: false
32445
+ }],
32446
+ "nativeObjectDetection.setEnabled": [{
32447
+ name: "deviceId",
32448
+ form: "single",
32449
+ optional: false
32450
+ }],
32451
+ "networkQuality.getDeviceStats": [{
32452
+ name: "deviceId",
32453
+ form: "single",
32454
+ optional: false
32455
+ }],
32456
+ "networkQuality.reportClientStats": [{
32457
+ name: "deviceId",
32458
+ form: "single",
32459
+ optional: false
32460
+ }],
32461
+ "notificationRules.setDeviceMuted": [{
32462
+ name: "deviceId",
32463
+ form: "single",
32464
+ optional: false
32465
+ }],
32466
+ "notifier.cancel": [{
32467
+ name: "deviceId",
32468
+ form: "single",
32469
+ optional: false
32470
+ }],
32471
+ "notifier.send": [{
32472
+ name: "deviceId",
32473
+ form: "single",
32474
+ optional: false
32475
+ }],
32476
+ "osd.setOverlay": [{
32477
+ name: "deviceId",
32478
+ form: "single",
32479
+ optional: false
32480
+ }],
32481
+ "osdManager.clearSlotBinding": [{
32482
+ name: "deviceId",
32483
+ form: "single",
32484
+ optional: false
32485
+ }],
32486
+ "osdManager.copyDeviceConfiguration": [{
32487
+ name: "sourceDeviceId",
32488
+ form: "single",
32489
+ optional: false
32490
+ }, {
32491
+ name: "targetDeviceId",
32492
+ form: "single",
32493
+ optional: false
32494
+ }],
32495
+ "osdManager.getDeviceOsd": [{
32496
+ name: "deviceId",
32497
+ form: "single",
32498
+ optional: false
32499
+ }],
32500
+ "osdManager.getSourceCatalog": [{
32501
+ name: "deviceId",
32502
+ form: "single",
32503
+ optional: false
32504
+ }],
32505
+ "osdManager.previewSlot": [{
32506
+ name: "deviceId",
32507
+ form: "single",
32508
+ optional: false
32509
+ }],
32510
+ "osdManager.renderDevice": [{
32511
+ name: "deviceId",
32512
+ form: "single",
32513
+ optional: false
32514
+ }],
32515
+ "osdManager.setSlotBinding": [{
32516
+ name: "deviceId",
32517
+ form: "single",
32518
+ optional: false
32519
+ }],
32520
+ "petFeeder.callPet": [{
32521
+ name: "deviceId",
32522
+ form: "single",
32523
+ optional: false
32524
+ }],
32525
+ "petFeeder.cancelFeed": [{
32526
+ name: "deviceId",
32527
+ form: "single",
32528
+ optional: false
32529
+ }],
32530
+ "petFeeder.feed": [{
32531
+ name: "deviceId",
32532
+ form: "single",
32533
+ optional: false
32534
+ }],
32535
+ "petFeeder.markFoodReplenished": [{
32536
+ name: "deviceId",
32537
+ form: "single",
32538
+ optional: false
32539
+ }],
32540
+ "petFeeder.playSound": [{
32541
+ name: "deviceId",
32542
+ form: "single",
32543
+ optional: false
32544
+ }],
32545
+ "petFeeder.resetDesiccant": [{
32546
+ name: "deviceId",
32547
+ form: "single",
32548
+ optional: false
32549
+ }],
32550
+ "petFeeder.setChildLock": [{
32551
+ name: "deviceId",
32552
+ form: "single",
32553
+ optional: false
32554
+ }],
32555
+ "petFeeder.setFeedSound": [{
32556
+ name: "deviceId",
32557
+ form: "single",
32558
+ optional: false
32559
+ }],
32560
+ "petFeeder.setIndicatorLight": [{
32561
+ name: "deviceId",
32562
+ form: "single",
32563
+ optional: false
32564
+ }],
32565
+ "petFeeder.setVolume": [{
32566
+ name: "deviceId",
32567
+ form: "single",
32568
+ optional: false
32569
+ }],
32570
+ "pipelineAnalytics.clearTracks": [{
32571
+ name: "deviceId",
32572
+ form: "single",
32573
+ optional: false
32574
+ }],
32575
+ "pipelineAnalytics.completeRetrainTrack": [{
32576
+ name: "deviceId",
32577
+ form: "single",
32578
+ optional: false
32579
+ }],
32580
+ "pipelineAnalytics.deleteDeviceEvents": [{
32581
+ name: "deviceId",
32582
+ form: "single",
32583
+ optional: false
32584
+ }],
32585
+ "pipelineAnalytics.deleteTracks": [{
32586
+ name: "deviceId",
32587
+ form: "single",
32588
+ optional: false
32589
+ }],
32590
+ "pipelineAnalytics.deselectRetrainFrame": [{
32591
+ name: "deviceId",
32592
+ form: "single",
32593
+ optional: false
32594
+ }],
32595
+ "pipelineAnalytics.getActiveTracks": [{
32596
+ name: "deviceId",
32597
+ form: "single",
32598
+ optional: false
32599
+ }],
32600
+ "pipelineAnalytics.getAudioEvents": [{
32601
+ name: "deviceId",
32602
+ form: "single",
32603
+ optional: false
32604
+ }],
32605
+ "pipelineAnalytics.getEventDensity": [{
32606
+ name: "deviceId",
32607
+ form: "single",
32608
+ optional: false
32609
+ }],
32610
+ "pipelineAnalytics.getKeyEvents": [{
32611
+ name: "deviceId",
32612
+ form: "single",
32613
+ optional: false
32614
+ }],
32615
+ "pipelineAnalytics.getMotionEvents": [{
32616
+ name: "deviceId",
32617
+ form: "single",
32618
+ optional: false
32619
+ }],
32620
+ "pipelineAnalytics.getObjectEvents": [{
32621
+ name: "deviceId",
32622
+ form: "single",
32623
+ optional: false
32624
+ }],
32625
+ "pipelineAnalytics.getRetrainExportUrl": [{
32626
+ name: "deviceIds",
32627
+ form: "array",
32628
+ optional: true
32629
+ }],
32630
+ "pipelineAnalytics.getSensorEvents": [{
32631
+ name: "deviceId",
32632
+ form: "single",
32633
+ optional: false
32634
+ }],
32635
+ "pipelineAnalytics.getTrack": [{
32636
+ name: "deviceId",
32637
+ form: "single",
32638
+ optional: false
32639
+ }],
32640
+ "pipelineAnalytics.getTrainingExportSummary": [{
32641
+ name: "deviceIds",
32642
+ form: "array",
32643
+ optional: true
32644
+ }],
32645
+ "pipelineAnalytics.getTrainingExportUrl": [{
32646
+ name: "deviceIds",
32647
+ form: "array",
32648
+ optional: true
32649
+ }],
32650
+ "pipelineAnalytics.listEventKinds": [{
32651
+ name: "deviceId",
32652
+ form: "single",
32653
+ optional: false
32654
+ }],
32655
+ "pipelineAnalytics.listEventKindsBatch": [{
32656
+ name: "deviceIds",
32657
+ form: "array",
32658
+ optional: false
32659
+ }],
32660
+ "pipelineAnalytics.listOpsLog": [{
32661
+ name: "deviceId",
32662
+ form: "single",
32663
+ optional: true
32664
+ }],
32665
+ "pipelineAnalytics.listRecentTracks": [{
32666
+ name: "deviceIds",
32667
+ form: "array",
32668
+ optional: false
32669
+ }],
32670
+ "pipelineAnalytics.listRetrainStaging": [{
32671
+ name: "deviceIds",
32672
+ form: "array",
32673
+ optional: true
32674
+ }],
32675
+ "pipelineAnalytics.listTracks": [{
32676
+ name: "deviceId",
32677
+ form: "single",
32678
+ optional: false
32679
+ }],
32680
+ "pipelineAnalytics.proposeRetrainAnnotations": [{
32681
+ name: "deviceId",
32682
+ form: "single",
32683
+ optional: false
32684
+ }],
32685
+ "pipelineAnalytics.pruneEventsBefore": [{
32686
+ name: "deviceId",
32687
+ form: "single",
32688
+ optional: false
32689
+ }],
32690
+ "pipelineAnalytics.pruneTracksBefore": [{
32691
+ name: "deviceId",
32692
+ form: "single",
32693
+ optional: false
32694
+ }],
32695
+ "pipelineAnalytics.rebuildObjectEmbeddings": [{
32696
+ name: "deviceId",
32697
+ form: "single",
32698
+ optional: true
32699
+ }],
32700
+ "pipelineAnalytics.restageRetrainTrack": [{
32701
+ name: "deviceId",
32702
+ form: "single",
32703
+ optional: false
32704
+ }],
32705
+ "pipelineAnalytics.saveRetrainAnnotations": [{
32706
+ name: "deviceId",
32707
+ form: "single",
32708
+ optional: false
32709
+ }],
32710
+ "pipelineAnalytics.searchObjectEvents": [{
32711
+ name: "deviceId",
32712
+ form: "single",
32713
+ optional: true
32714
+ }],
32715
+ "pipelineAnalytics.selectRetrainFrames": [{
32716
+ name: "deviceId",
32717
+ form: "single",
32718
+ optional: false
32719
+ }],
32720
+ "pipelineAnalytics.setTrackFlags": [{
32721
+ name: "deviceId",
32722
+ form: "single",
32723
+ optional: false
32724
+ }],
32725
+ "pipelineAnalytics.wipeAllAnalytics": [{
32726
+ name: "deviceId",
32727
+ form: "single",
32728
+ optional: false
32729
+ }],
32730
+ "pipelineExecutor.runPipeline": [{
32731
+ name: "deviceId",
32732
+ form: "single",
32733
+ optional: true
32734
+ }],
32735
+ "pipelineExecutor.runPipelineBatch": [{
32736
+ name: "deviceId",
32737
+ form: "single",
32738
+ optional: true
32739
+ }],
32740
+ "pipelineOrchestrator.assignAudio": [{
32741
+ name: "deviceId",
32742
+ form: "single",
32743
+ optional: false
32744
+ }],
32745
+ "pipelineOrchestrator.assignPipeline": [{
32746
+ name: "deviceId",
32747
+ form: "single",
32748
+ optional: false
32749
+ }],
32750
+ "pipelineOrchestrator.getAudioAssignment": [{
32751
+ name: "deviceId",
32752
+ form: "single",
32753
+ optional: false
32754
+ }],
32755
+ "pipelineOrchestrator.getCameraMetrics": [{
32756
+ name: "deviceId",
32757
+ form: "single",
32758
+ optional: false
32759
+ }],
32760
+ "pipelineOrchestrator.getCameraSettings": [{
32761
+ name: "deviceId",
32762
+ form: "single",
32763
+ optional: false
32764
+ }],
32765
+ "pipelineOrchestrator.getCameraStatus": [{
32766
+ name: "deviceId",
32767
+ form: "single",
32768
+ optional: false
32769
+ }],
32770
+ "pipelineOrchestrator.getCameraStatuses": [{
32771
+ name: "deviceIds",
32772
+ form: "array",
32773
+ optional: true
32774
+ }],
32775
+ "pipelineOrchestrator.getCameraStepOverrides": [{
32776
+ name: "deviceId",
32777
+ form: "single",
32778
+ optional: false
32779
+ }],
32780
+ "pipelineOrchestrator.getCameraSwitches": [{
32781
+ name: "deviceId",
32782
+ form: "single",
32783
+ optional: false
32784
+ }],
32785
+ "pipelineOrchestrator.getPipelineAssignment": [{
32786
+ name: "deviceId",
32787
+ form: "single",
32788
+ optional: false
32789
+ }],
32790
+ "pipelineOrchestrator.getPipelineDevicePin": [{
32791
+ name: "deviceId",
32792
+ form: "single",
32793
+ optional: false
32794
+ }],
32795
+ "pipelineOrchestrator.resolvePipeline": [{
32796
+ name: "deviceId",
32797
+ form: "single",
32798
+ optional: false
32799
+ }],
32800
+ "pipelineOrchestrator.setCameraPipelineForAgent": [{
32801
+ name: "deviceId",
32802
+ form: "single",
32803
+ optional: false
32804
+ }],
32805
+ "pipelineOrchestrator.setCameraStepOverride": [{
32806
+ name: "deviceId",
32807
+ form: "single",
32808
+ optional: false
32809
+ }],
32810
+ "pipelineOrchestrator.setCameraStepToggle": [{
32811
+ name: "deviceId",
32812
+ form: "single",
32813
+ optional: false
32814
+ }],
32815
+ "pipelineOrchestrator.setCameraSwitch": [{
32816
+ name: "deviceId",
32817
+ form: "single",
32818
+ optional: false
32819
+ }],
32820
+ "pipelineOrchestrator.setPipelineDevicePin": [{
32821
+ name: "deviceId",
32822
+ form: "single",
32823
+ optional: false
32824
+ }],
32825
+ "pipelineOrchestrator.unassignAudio": [{
32826
+ name: "deviceId",
32827
+ form: "single",
32828
+ optional: false
32829
+ }],
32830
+ "pipelineOrchestrator.unassignPipeline": [{
32831
+ name: "deviceId",
32832
+ form: "single",
32833
+ optional: false
32834
+ }],
32835
+ "pipelineRunner.attachCamera": [{
32836
+ name: "deviceId",
32837
+ form: "single",
32838
+ optional: false
32839
+ }],
32840
+ "pipelineRunner.detachCamera": [{
32841
+ name: "deviceId",
32842
+ form: "single",
32843
+ optional: false
32844
+ }],
32845
+ "pipelineRunner.getCameraMetrics": [{
32846
+ name: "deviceId",
32847
+ form: "single",
32848
+ optional: false
32849
+ }],
32850
+ "pipelineRunner.reportMotion": [{
32851
+ name: "deviceId",
32852
+ form: "single",
32853
+ optional: false
32854
+ }],
32855
+ "pipelineRunner.runDetailSubtree": [{
32856
+ name: "deviceId",
32857
+ form: "single",
32858
+ optional: false
32859
+ }],
32860
+ "pipelineRunner.runStatelessStep": [{
32861
+ name: "sourceDeviceId",
32862
+ form: "single",
32863
+ optional: false
32864
+ }],
32865
+ "plateGallery.getPlateByTrack": [{
32866
+ name: "deviceId",
32867
+ form: "single",
32868
+ optional: false
32869
+ }],
32870
+ "plateGallery.listPlates": [{
32871
+ name: "deviceId",
32872
+ form: "single",
32873
+ optional: true
32874
+ }],
32875
+ "privacyMask.getOptions": [{
32876
+ name: "deviceId",
32877
+ form: "single",
32878
+ optional: false
32879
+ }],
32880
+ "privacyMask.setAudioEnabled": [{
32881
+ name: "deviceId",
32882
+ form: "single",
32883
+ optional: false
32884
+ }],
32885
+ "privacyMask.setMask": [{
32886
+ name: "deviceId",
32887
+ form: "single",
32888
+ optional: false
32889
+ }],
32890
+ "ptz.continuousMove": [{
32891
+ name: "deviceId",
32892
+ form: "single",
32893
+ optional: false
32894
+ }],
32895
+ "ptz.deletePreset": [{
32896
+ name: "deviceId",
32897
+ form: "single",
32898
+ optional: false
32899
+ }],
32900
+ "ptz.getOptions": [{
32901
+ name: "deviceId",
32902
+ form: "single",
32903
+ optional: false
32904
+ }],
32905
+ "ptz.getPosition": [{
32906
+ name: "deviceId",
32907
+ form: "single",
32908
+ optional: false
32909
+ }],
32910
+ "ptz.getPresets": [{
32911
+ name: "deviceId",
32912
+ form: "single",
32913
+ optional: false
32914
+ }],
32915
+ "ptz.goHome": [{
32916
+ name: "deviceId",
32917
+ form: "single",
32918
+ optional: false
32919
+ }],
32920
+ "ptz.goToPreset": [{
32921
+ name: "deviceId",
32922
+ form: "single",
32923
+ optional: false
32924
+ }],
32925
+ "ptz.move": [{
32926
+ name: "deviceId",
32927
+ form: "single",
32928
+ optional: false
32929
+ }],
32930
+ "ptz.savePreset": [{
32931
+ name: "deviceId",
32932
+ form: "single",
32933
+ optional: false
32934
+ }],
32935
+ "ptz.setAutofocus": [{
32936
+ name: "deviceId",
32937
+ form: "single",
32938
+ optional: false
32939
+ }],
32940
+ "ptz.stop": [{
32941
+ name: "deviceId",
32942
+ form: "single",
32943
+ optional: false
32944
+ }],
32945
+ "ptzAutotrack.getSettings": [{
32946
+ name: "deviceId",
32947
+ form: "single",
32948
+ optional: false
32949
+ }],
32950
+ "ptzAutotrack.getStatus": [{
32951
+ name: "deviceId",
32952
+ form: "single",
32953
+ optional: false
32954
+ }],
32955
+ "ptzAutotrack.setEnabled": [{
32956
+ name: "deviceId",
32957
+ form: "single",
32958
+ optional: false
32959
+ }],
32960
+ "ptzAutotrack.setSettings": [{
32961
+ name: "deviceId",
32962
+ form: "single",
32963
+ optional: false
32964
+ }],
32965
+ "reboot.reboot": [{
32966
+ name: "deviceId",
32967
+ form: "single",
32968
+ optional: false
32969
+ }],
32970
+ "recording.deleteFootprint": [{
32971
+ name: "deviceId",
32972
+ form: "single",
32973
+ optional: false
32974
+ }],
32975
+ "recording.getAvailability": [{
32976
+ name: "deviceId",
32977
+ form: "single",
32978
+ optional: false
32979
+ }],
32980
+ "recording.getDaysWithRecordings": [{
32981
+ name: "deviceId",
32982
+ form: "single",
32983
+ optional: false
32984
+ }],
32985
+ "recording.getDeviceConfig": [{
32986
+ name: "deviceId",
32987
+ form: "single",
32988
+ optional: false
32989
+ }],
32990
+ "recording.getPlaybackManifest": [{
32991
+ name: "deviceId",
32992
+ form: "single",
32993
+ optional: false
32994
+ }],
32995
+ "recording.listOpsLog": [{
32996
+ name: "deviceId",
32997
+ form: "single",
32998
+ optional: true
32999
+ }],
33000
+ "recording.locateSegment": [{
33001
+ name: "deviceId",
33002
+ form: "single",
33003
+ optional: false
33004
+ }],
33005
+ "recording.pruneFootage": [{
33006
+ name: "deviceId",
33007
+ form: "single",
33008
+ optional: false
33009
+ }],
33010
+ "recording.readGopBytes": [{
33011
+ name: "deviceId",
33012
+ form: "single",
33013
+ optional: false
33014
+ }],
33015
+ "recording.readSegmentBytes": [{
33016
+ name: "deviceId",
33017
+ form: "single",
33018
+ optional: false
33019
+ }],
33020
+ "recording.relocateFootage": [{
33021
+ name: "deviceId",
33022
+ form: "single",
33023
+ optional: true
33024
+ }],
33025
+ "recording.renderClip": [{
33026
+ name: "deviceId",
33027
+ form: "single",
33028
+ optional: false
33029
+ }],
33030
+ "recording.renderGif": [{
33031
+ name: "deviceId",
33032
+ form: "single",
33033
+ optional: false
33034
+ }],
33035
+ "recording.rescanStorage": [{
33036
+ name: "deviceId",
33037
+ form: "single",
33038
+ optional: false
33039
+ }],
33040
+ "recording.setDeviceConfig": [{
33041
+ name: "deviceId",
33042
+ form: "single",
33043
+ optional: false
33044
+ }],
33045
+ "recording.startStorageMigrationMove": [{
33046
+ name: "deviceId",
33047
+ form: "single",
33048
+ optional: true
33049
+ }],
33050
+ "recordingExport.createExport": [{
33051
+ name: "deviceId",
33052
+ form: "single",
33053
+ optional: false
33054
+ }],
33055
+ "recordingExport.listExports": [{
33056
+ name: "deviceId",
33057
+ form: "single",
33058
+ optional: true
33059
+ }],
33060
+ "sceneMonitor.captureReference": [{
33061
+ name: "deviceId",
33062
+ form: "single",
33063
+ optional: false
33064
+ }],
33065
+ "sceneMonitor.createScene": [{
33066
+ name: "deviceId",
33067
+ form: "single",
33068
+ optional: false
33069
+ }],
33070
+ "sceneMonitor.deleteReference": [{
33071
+ name: "deviceId",
33072
+ form: "single",
33073
+ optional: false
33074
+ }],
33075
+ "sceneMonitor.deleteScene": [{
33076
+ name: "deviceId",
33077
+ form: "single",
33078
+ optional: false
33079
+ }],
33080
+ "sceneMonitor.listScenes": [{
33081
+ name: "deviceId",
33082
+ form: "single",
33083
+ optional: false
33084
+ }],
33085
+ "sceneMonitor.recheckNow": [{
33086
+ name: "deviceId",
33087
+ form: "single",
33088
+ optional: false
33089
+ }],
33090
+ "sceneMonitor.resetScene": [{
33091
+ name: "deviceId",
33092
+ form: "single",
33093
+ optional: false
33094
+ }],
33095
+ "sceneMonitor.updateScene": [{
33096
+ name: "deviceId",
33097
+ form: "single",
33098
+ optional: false
33099
+ }],
33100
+ "scriptRunner.run": [{
33101
+ name: "deviceId",
33102
+ form: "single",
33103
+ optional: false
33104
+ }],
33105
+ "scriptRunner.stop": [{
33106
+ name: "deviceId",
33107
+ form: "single",
33108
+ optional: false
33109
+ }],
33110
+ "snapshot.getSnapshot": [{
33111
+ name: "deviceId",
33112
+ form: "single",
33113
+ optional: false
33114
+ }],
33115
+ "snapshot.getSnapshotOverview": [{
33116
+ name: "deviceIds",
33117
+ form: "array",
33118
+ optional: false
33119
+ }],
33120
+ "snapshot.invalidateCache": [{
33121
+ name: "deviceId",
33122
+ form: "single",
33123
+ optional: false
33124
+ }],
33125
+ "streamBroker.acquireEgressTranscode": [{
33126
+ name: "deviceId",
33127
+ form: "single",
33128
+ optional: false
33129
+ }],
33130
+ "streamBroker.assignProfile": [{
33131
+ name: "deviceId",
33132
+ form: "single",
33133
+ optional: false
33134
+ }],
33135
+ "streamBroker.getDeviceAudioMute": [{
33136
+ name: "deviceId",
33137
+ form: "single",
33138
+ optional: false
33139
+ }],
33140
+ "streamBroker.getStreamWithCodec": [{
33141
+ name: "deviceId",
33142
+ form: "single",
33143
+ optional: false
33144
+ }],
33145
+ "streamBroker.produceEventMedia": [{
33146
+ name: "deviceId",
33147
+ form: "single",
33148
+ optional: false
33149
+ }],
33150
+ "streamBroker.publishCameraStream": [{
33151
+ name: "deviceId",
33152
+ form: "single",
33153
+ optional: false
33154
+ }],
33155
+ "streamBroker.renderPreBufferClip": [{
33156
+ name: "deviceId",
33157
+ form: "single",
33158
+ optional: false
33159
+ }],
33160
+ "streamBroker.restartProfile": [{
33161
+ name: "deviceId",
33162
+ form: "single",
33163
+ optional: false
33164
+ }],
33165
+ "streamBroker.retractCameraStream": [{
33166
+ name: "deviceId",
33167
+ form: "single",
33168
+ optional: false
33169
+ }],
33170
+ "streamBroker.setDeviceAudioMute": [{
33171
+ name: "deviceId",
33172
+ form: "single",
33173
+ optional: false
33174
+ }],
33175
+ "streamBroker.unassignProfile": [{
33176
+ name: "deviceId",
33177
+ form: "single",
33178
+ optional: false
33179
+ }],
33180
+ "streamCatalog.getCatalog": [{
33181
+ name: "deviceId",
33182
+ form: "single",
33183
+ optional: false
33184
+ }],
33185
+ "streamParams.getConfigSchema": [{
33186
+ name: "deviceId",
33187
+ form: "single",
33188
+ optional: false
33189
+ }],
33190
+ "streamParams.getOptions": [{
33191
+ name: "deviceId",
33192
+ form: "single",
33193
+ optional: false
33194
+ }],
33195
+ "streamParams.setProfile": [{
33196
+ name: "deviceId",
33197
+ form: "single",
33198
+ optional: false
33199
+ }],
33200
+ "switch.setState": [{
33201
+ name: "deviceId",
33202
+ form: "single",
33203
+ optional: false
33204
+ }],
33205
+ "vacuumControl.locate": [{
33206
+ name: "deviceId",
33207
+ form: "single",
33208
+ optional: false
33209
+ }],
33210
+ "vacuumControl.pause": [{
33211
+ name: "deviceId",
33212
+ form: "single",
33213
+ optional: false
33214
+ }],
33215
+ "vacuumControl.returnToBase": [{
33216
+ name: "deviceId",
33217
+ form: "single",
33218
+ optional: false
33219
+ }],
33220
+ "vacuumControl.setFanSpeed": [{
33221
+ name: "deviceId",
33222
+ form: "single",
33223
+ optional: false
33224
+ }],
33225
+ "vacuumControl.start": [{
33226
+ name: "deviceId",
33227
+ form: "single",
33228
+ optional: false
33229
+ }],
33230
+ "vacuumControl.stop": [{
33231
+ name: "deviceId",
33232
+ form: "single",
33233
+ optional: false
33234
+ }],
33235
+ "valve.close": [{
33236
+ name: "deviceId",
33237
+ form: "single",
33238
+ optional: false
33239
+ }],
33240
+ "valve.open": [{
33241
+ name: "deviceId",
33242
+ form: "single",
33243
+ optional: false
33244
+ }],
33245
+ "valve.setPosition": [{
33246
+ name: "deviceId",
33247
+ form: "single",
33248
+ optional: false
33249
+ }],
33250
+ "valve.stop": [{
33251
+ name: "deviceId",
33252
+ form: "single",
33253
+ optional: false
33254
+ }],
33255
+ "videoclips.getClipPlayback": [{
33256
+ name: "deviceId",
33257
+ form: "single",
33258
+ optional: false
33259
+ }],
33260
+ "videoclips.listClips": [{
33261
+ name: "deviceId",
33262
+ form: "single",
33263
+ optional: false
33264
+ }],
33265
+ "waterHeater.setAway": [{
33266
+ name: "deviceId",
33267
+ form: "single",
33268
+ optional: false
33269
+ }],
33270
+ "waterHeater.setOperationMode": [{
33271
+ name: "deviceId",
33272
+ form: "single",
33273
+ optional: false
33274
+ }],
33275
+ "waterHeater.setTargetTemp": [{
33276
+ name: "deviceId",
33277
+ form: "single",
33278
+ optional: false
33279
+ }],
33280
+ "webrtcSession.addIceCandidate": [{
33281
+ name: "deviceId",
33282
+ form: "single",
33283
+ optional: false
33284
+ }],
33285
+ "webrtcSession.closeSession": [{
33286
+ name: "deviceId",
33287
+ form: "single",
33288
+ optional: false
33289
+ }],
33290
+ "webrtcSession.createSession": [{
33291
+ name: "deviceId",
33292
+ form: "single",
33293
+ optional: false
33294
+ }],
33295
+ "webrtcSession.getIceCandidates": [{
33296
+ name: "deviceId",
33297
+ form: "single",
33298
+ optional: false
33299
+ }],
33300
+ "webrtcSession.getSessionState": [{
33301
+ name: "deviceId",
33302
+ form: "single",
33303
+ optional: false
33304
+ }],
33305
+ "webrtcSession.handleAnswer": [{
33306
+ name: "deviceId",
33307
+ form: "single",
33308
+ optional: false
33309
+ }],
33310
+ "webrtcSession.handleOffer": [{
33311
+ name: "deviceId",
33312
+ form: "single",
33313
+ optional: false
33314
+ }],
33315
+ "webrtcSession.hasAdaptiveBitrate": [{
33316
+ name: "deviceId",
33317
+ form: "single",
33318
+ optional: false
33319
+ }],
33320
+ "webrtcSession.listStreams": [{
33321
+ name: "deviceId",
33322
+ form: "single",
33323
+ optional: false
33324
+ }],
33325
+ "zoneAnalytics.getCameraHistory": [{
33326
+ name: "deviceId",
33327
+ form: "single",
33328
+ optional: false
33329
+ }],
33330
+ "zoneAnalytics.getCurrentSnapshot": [{
33331
+ name: "deviceId",
33332
+ form: "single",
33333
+ optional: false
33334
+ }],
33335
+ "zoneAnalytics.getUnzonedHistory": [{
33336
+ name: "deviceId",
33337
+ form: "single",
33338
+ optional: false
33339
+ }],
33340
+ "zoneAnalytics.getZoneHistory": [{
33341
+ name: "deviceId",
33342
+ form: "single",
33343
+ optional: false
33344
+ }],
33345
+ "zoneRules.listRules": [{
33346
+ name: "deviceId",
33347
+ form: "single",
33348
+ optional: false
33349
+ }],
33350
+ "zoneRules.setRules": [{
33351
+ name: "deviceId",
33352
+ form: "single",
33353
+ optional: false
33354
+ }],
33355
+ "zones.addZone": [{
33356
+ name: "deviceId",
33357
+ form: "single",
33358
+ optional: false
33359
+ }],
33360
+ "zones.listZones": [{
33361
+ name: "deviceId",
33362
+ form: "single",
33363
+ optional: false
33364
+ }],
33365
+ "zones.removeZone": [{
33366
+ name: "deviceId",
33367
+ form: "single",
33368
+ optional: false
33369
+ }],
33370
+ "zones.updateZone": [{
33371
+ name: "deviceId",
33372
+ form: "single",
33373
+ optional: false
33374
+ }]
33375
+ });
30783
33376
  Object.freeze({
30784
33377
  "broker": "broker",
30785
33378
  "device-export": "device-export",
@@ -32068,28 +34661,6 @@ async function handleInitiateSessionWithOffer(header, endpointRaw, payloadRaw, d
32068
34661
  if (pref === "low" || pref === "mid" || pref === "high") return pref;
32069
34662
  })();
32070
34663
  const sourceProfile = explicitSourceProfile ?? mappedFromPreference ?? "high";
32071
- try {
32072
- if (isSleepingBatteryStatus(null)) {
32073
- const wakeStart = Date.now();
32074
- const wake = await api.battery.wakeForStream.mutate({
32075
- deviceId,
32076
- timeoutMs: 8e3
32077
- }).catch(() => ({
32078
- awoke: false,
32079
- durationMs: 0
32080
- }));
32081
- log.info("alexa rtc-session: battery pre-wake", { meta: {
32082
- awoke: wake.awoke,
32083
- waitedMs: wake.durationMs || Date.now() - wakeStart
32084
- } });
32085
- }
32086
- } catch (err) {
32087
- log.debug("alexa rtc-session: pre-wake skipped", { meta: { error: err instanceof Error ? err.message : String(err) } });
32088
- }
32089
- function isSleepingBatteryStatus(value) {
32090
- if (typeof value !== "object" || value === null) return false;
32091
- return value["sleeping"] === true;
32092
- }
32093
34664
  let derivedTarget;
32094
34665
  /**
32095
34666
  * The primitive's release handle, when this session acquired one. Recorded so