@camstack/addon-auth 1.2.19 → 1.2.21

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.
@@ -10967,6 +10967,8 @@ var QueryFilterSchema = object({
10967
10967
  where: record(string(), unknown()).optional(),
10968
10968
  whereIn: record(string(), array(unknown())).optional(),
10969
10969
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
10970
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
10971
+ whereNot: record(string(), unknown()).optional(),
10970
10972
  orderBy: object({
10971
10973
  field: string(),
10972
10974
  direction: _enum(["asc", "desc"])
@@ -10986,7 +10988,8 @@ var QueryFilterSchema = object({
10986
10988
  var MutationFilterSchema = object({
10987
10989
  where: record(string(), unknown()).optional(),
10988
10990
  whereIn: record(string(), array(unknown())).optional(),
10989
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
10991
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
10992
+ whereNot: record(string(), unknown()).optional()
10990
10993
  });
10991
10994
  /** A single stored record: `{ id, data }`. */
10992
10995
  var SettingsRecordSchema = object({
@@ -12402,6 +12405,18 @@ var LlmGenerateBaseInputSchema = object({
12402
12405
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
12403
12406
  * watchdog — operator decision #3).
12404
12407
  */
12408
+ /**
12409
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
12410
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
12411
+ * REF rather than looked up at install time, so what the operator approved in
12412
+ * the preview is exactly what the node downloads.
12413
+ */
12414
+ var ManagedModelExtraFileSchema = object({
12415
+ url: string(),
12416
+ filename: string(),
12417
+ sizeBytes: number(),
12418
+ sha256: string().optional()
12419
+ });
12405
12420
  var ManagedModelRefSchema = discriminatedUnion("kind", [
12406
12421
  object({
12407
12422
  kind: literal("catalog"),
@@ -12410,7 +12425,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
12410
12425
  object({
12411
12426
  kind: literal("url"),
12412
12427
  url: string(),
12413
- sha256: string().optional()
12428
+ sha256: string().optional(),
12429
+ /** Picker/status label; the file basename when absent. */
12430
+ label: string().optional(),
12431
+ sizeBytes: number().optional(),
12432
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
12414
12433
  }),
12415
12434
  object({
12416
12435
  kind: literal("path"),
@@ -12471,11 +12490,39 @@ var ManagedRuntimeConfigSchema = object({
12471
12490
  "q4_1",
12472
12491
  "q4_0"
12473
12492
  ]).optional(),
12493
+ /**
12494
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
12495
+ * (which most vision chat templates need and some language-only models
12496
+ * dislike), `--cont-batching`, `--rope-scaling`, …
12497
+ *
12498
+ * It is NOT a second place to set the flags above. A token that collides
12499
+ * with a typed field is REJECTED at start, naming the field that owns it
12500
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
12501
+ * the "two switches that disagree" failure this repo has already shipped
12502
+ * twice (D62).
12503
+ */
12504
+ extraArgs: array(string()).default([]),
12474
12505
  /** Else lazy: first generate boots it. */
12475
12506
  autoStart: boolean().default(false),
12476
12507
  /** 0 = never; frees RAM after quiet periods. */
12477
12508
  idleStopMinutes: number().int().default(30)
12478
12509
  });
12510
+ /**
12511
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
12512
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
12513
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
12514
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
12515
+ */
12516
+ var LlmDownloadProgressSchema = object({
12517
+ phase: _enum(["downloading", "verifying"]),
12518
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
12519
+ file: string(),
12520
+ fileIndex: number().int(),
12521
+ fileCount: number().int(),
12522
+ /** Across the WHOLE install, not the current file. */
12523
+ downloadedBytes: number(),
12524
+ totalBytes: number().optional()
12525
+ });
12479
12526
  var LlmRuntimeStatusSchema = object({
12480
12527
  /** Status is ALWAYS node-qualified. */
12481
12528
  nodeId: string(),
@@ -12492,6 +12539,8 @@ var LlmRuntimeStatusSchema = object({
12492
12539
  modelPath: string().optional(),
12493
12540
  modelId: string().optional(),
12494
12541
  downloadProgress: number().min(0).max(1).optional(),
12542
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
12543
+ download: LlmDownloadProgressSchema.optional(),
12495
12544
  lastError: string().optional(),
12496
12545
  crashesInWindow: number(),
12497
12546
  /** Child RSS (sampled best-effort). */
@@ -12502,7 +12551,14 @@ var LlmNodeModelSchema = object({
12502
12551
  file: string(),
12503
12552
  sizeBytes: number(),
12504
12553
  catalogId: string().optional(),
12505
- installedAt: number().optional()
12554
+ installedAt: number().optional(),
12555
+ /**
12556
+ * Absolute path on the node. Present so a file that is on disk but matches
12557
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
12558
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
12559
+ * it the picker could list such a file and do nothing with it.
12560
+ */
12561
+ path: string().optional()
12506
12562
  });
12507
12563
  var LlmRuntimeDiskUsageSchema = object({
12508
12564
  nodeId: string(),
@@ -12580,9 +12636,12 @@ var LlmProfileSchema = object({
12580
12636
  systemPrompt: string().optional(),
12581
12637
  /** Total generation bound — the only one a unary call has. */
12582
12638
  timeoutMs: number().int().positive().default(6e4),
12583
- /** Wait for response headers only. */
12639
+ /** The TCP handshake only — "is the port even open". NOT the wait for
12640
+ * response headers: on the LM Studio / llama-server wire those are written
12641
+ * once the model has finished loading, so they belong to the bound below. */
12584
12642
  connectTimeoutMs: number().int().positive().default(1e4),
12585
- /** Accepted, but no output yet — a cold GPU load lives here. */
12643
+ /** Accepted, but no output yet — response headers included, because a cold
12644
+ * GPU load is exactly what happens before them. */
12586
12645
  firstTokenTimeoutMs: number().int().positive().default(12e4),
12587
12646
  /** Output started then stopped. */
12588
12647
  idleTimeoutMs: number().int().positive().default(6e4),
@@ -12645,6 +12704,36 @@ var ManagedModelCatalogEntrySchema = object({
12645
12704
  /** Vision models: companion projector file. */
12646
12705
  mmprojUrl: string().optional()
12647
12706
  });
12707
+ /**
12708
+ * The outcome of turning one operator-typed Hugging Face reference into a
12709
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
12710
+ * I will not pick for you" is a normal answer the UI has to render, not an
12711
+ * exception.
12712
+ *
12713
+ * `candidates` is the whole reason the refusal is usable — every string in it
12714
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
12715
+ */
12716
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
12717
+ ok: literal(true),
12718
+ /** Ready to hand to `installModel` unchanged. */
12719
+ model: ManagedModelRefSchema,
12720
+ label: string(),
12721
+ repo: string(),
12722
+ quantization: string(),
12723
+ purpose: _enum(["text", "vision"]),
12724
+ totalBytes: number(),
12725
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
12726
+ * see that 0.9 GB of it is a projector they did not name. */
12727
+ extraFilenames: array(string())
12728
+ }), object({
12729
+ ok: literal(false),
12730
+ code: string(),
12731
+ message: string(),
12732
+ candidates: array(string()).optional(),
12733
+ /** Set when the refusal was only the ceiling: re-calling with
12734
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
12735
+ requiredBytes: number().optional()
12736
+ })]);
12648
12737
  var LlmRuntimeNodeSchema = object({
12649
12738
  nodeId: string(),
12650
12739
  reachable: boolean(),
@@ -12681,6 +12770,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
12681
12770
  consumer: string().optional(),
12682
12771
  profileId: string().optional()
12683
12772
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
12773
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
12774
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
12775
+ ref: string(),
12776
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
12777
+ maxBytes: number().positive().optional()
12778
+ }), HfModelResolutionSchema, {
12779
+ kind: "mutation",
12780
+ auth: "admin"
12781
+ }), method(object({
12684
12782
  nodeId: string(),
12685
12783
  model: ManagedModelRefSchema
12686
12784
  }), _void(), {
@@ -14585,13 +14683,81 @@ var NcRuleActionsSchema = object({
14585
14683
  */
14586
14684
  buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
14587
14685
  });
14686
+ /**
14687
+ * "This rule applies only while `deviceId` is in one of `states`."
14688
+ *
14689
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
14690
+ * `on`/`off` for a switch — not a normalised set, because normalising would
14691
+ * make the condition lie about devices whose states have no equivalent.
14692
+ *
14693
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
14694
+ * condition that fired on "I could not read it" would be worse than no gate.
14695
+ */
14696
+ var NcDeviceStateConditionSchema = object({
14697
+ deviceId: number().int(),
14698
+ /** Any of these matches. */
14699
+ states: array(string().min(1)).min(1)
14700
+ });
14701
+ /**
14702
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
14703
+ *
14704
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
14705
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
14706
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
14707
+ * already has a trigger ("tell me about a person at the front door, but only
14708
+ * while the bin is still out"). That is why it composes with every delivery
14709
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
14710
+ * kind exist for it — see D159.
14711
+ *
14712
+ * ── Identity ───────────────────────────────────────────────────────────────
14713
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
14714
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
14715
+ * carried as a HINT for the editor and for the log line, never as part of the
14716
+ * lookup key: a rule whose hint drifted must still gate correctly.
14717
+ *
14718
+ * ── Which boolean ──────────────────────────────────────────────────────────
14719
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
14720
+ * already declares which boolean drives notification rules, and a second knob
14721
+ * that could disagree with it is exactly the D62 failure. Set it only to
14722
+ * override one rule against the scene's own default.
14723
+ *
14724
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
14725
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
14726
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
14727
+ * evidence, in either direction.
14728
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
14729
+ * The latch is a durable fact about the past ("it has diverged since I armed
14730
+ * it"), so a camera that has gone dark does not clear it — that is the whole
14731
+ * reason the operator asked for a latch.
14732
+ *
14733
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
14734
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
14735
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
14736
+ * said out loud in the log rather than dropped in silence.
14737
+ */
14738
+ var NcSceneConditionSchema = object({
14739
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
14740
+ sceneId: string().min(1),
14741
+ /** The camera the scene lives on. A hint for the editor and the log line. */
14742
+ deviceId: number().int().optional(),
14743
+ /** The state the scene must be in for the rule to fire. */
14744
+ requiredState: _enum(["matched", "diverged"]),
14745
+ /**
14746
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
14747
+ * scene's own `emit` field, which is the only place that decision belongs.
14748
+ */
14749
+ latched: boolean().optional()
14750
+ });
14588
14751
  var NcConditionsSchema = object({
14589
14752
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
14590
- deviceState: object({
14591
- deviceId: number().int(),
14592
- /** Any of these matches. */
14593
- states: array(string().min(1)).min(1)
14594
- }).optional(),
14753
+ deviceState: NcDeviceStateConditionSchema.optional(),
14754
+ /**
14755
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
14756
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
14757
+ * unlike `occupancy`/`audio` it discriminates nothing. See
14758
+ * {@link NcSceneCondition} and D159.
14759
+ */
14760
+ scene: NcSceneConditionSchema.optional(),
14595
14761
  /** Device scope — absent = all devices. */
14596
14762
  devices: array(number()).optional(),
14597
14763
  /** Detector class names (any overlap with the record's class set). */
@@ -15229,6 +15395,7 @@ var NcConditionDescriptorSchema = object({
15229
15395
  "occupancy",
15230
15396
  "audio",
15231
15397
  "deviceState",
15398
+ "scene",
15232
15399
  "systemEvent"
15233
15400
  ]),
15234
15401
  operator: _enum([
@@ -16706,7 +16873,10 @@ var RecentTracksQueryInput = object({
16706
16873
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16707
16874
  cursor: string().optional(),
16708
16875
  /** See {@link TrackProjectionSchema}. Default `full`. */
16709
- projection: TrackProjectionSchema.optional()
16876
+ projection: TrackProjectionSchema.optional(),
16877
+ /** Include stationary-promoted rows (parked objects). Default false: the
16878
+ * feed lists passages; parking records live on the stationary registry. */
16879
+ includeStationary: boolean().optional()
16710
16880
  });
16711
16881
  var RecentTracksPageSchema = object({
16712
16882
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -16924,7 +17094,11 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16924
17094
  zone: TrackZoneFilterSchema.optional(),
16925
17095
  /** See {@link TrackProjectionSchema}. Default `full` (backward
16926
17096
  * compatible — omitting the field keeps today's exact behaviour). */
16927
- projection: TrackProjectionSchema.optional()
17097
+ projection: TrackProjectionSchema.optional(),
17098
+ /** Include stationary-promoted rows (parked objects handed to the
17099
+ * stationary registry). Default false: the timeline lists passages,
17100
+ * not parking records (operator decision, 2026-08-15). */
17101
+ includeStationary: boolean().optional()
16928
17102
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
16929
17103
  kind: "mutation",
16930
17104
  auth: "admin"
@@ -17088,11 +17262,16 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
17088
17262
  auth: "admin"
17089
17263
  }), method(object({
17090
17264
  eventId: string(),
17091
- kind: MediaFileKindEnum.optional()
17265
+ kind: MediaFileKindEnum.optional(),
17266
+ deviceId: number()
17267
+ }), array(MediaFileSchema).readonly()), method(object({
17268
+ trackId: string(),
17269
+ kinds: array(MediaFileKindEnum).optional(),
17270
+ deviceId: number()
17092
17271
  }), array(MediaFileSchema).readonly()), method(object({
17093
17272
  trackId: string(),
17094
- kinds: array(MediaFileKindEnum).optional()
17095
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17273
+ deviceId: number()
17274
+ }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17096
17275
  kind: "mutation",
17097
17276
  auth: "admin"
17098
17277
  }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
@@ -17752,6 +17931,17 @@ var maxSessionHoldMsField = {
17752
17931
  default: 12e4,
17753
17932
  step: 5e3
17754
17933
  };
17934
+ /**
17935
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
17936
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
17937
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
17938
+ */
17939
+ var audioMotionWindowMsField = {
17940
+ min: 5e3,
17941
+ max: 6e5,
17942
+ default: 9e4,
17943
+ step: 5e3
17944
+ };
17755
17945
  var motionFpsField = {
17756
17946
  min: 1,
17757
17947
  max: 30,
@@ -17928,6 +18118,27 @@ var RunnerCameraConfigSchema = object({
17928
18118
  * resolved `CameraDetectionConfig`.
17929
18119
  */
17930
18120
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
18121
+ /**
18122
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
18123
+ * 'on-motion'` audio window, measured from the LAST motion event.
18124
+ *
18125
+ * This exists because the falling edge cannot be relied on. Camera-native
18126
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
18127
+ * its email-push SMTP path both emit `detected: true` and never the
18128
+ * counterpart); only the frame-diff analyzer emits falls. So on an
18129
+ * onboard-only camera a window that closed only on `detected: false` never
18130
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
18131
+ * battery camera, the one failure mode the mode exists to prevent.
18132
+ *
18133
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
18134
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
18135
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
18136
+ *
18137
+ * Not consumed by the runner: carried here so it shares the per-camera
18138
+ * device-settings surface with `motionCooldownMs`, exactly like
18139
+ * `maxSessionHoldMs`.
18140
+ */
18141
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
17931
18142
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
17932
18143
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
17933
18144
  motionStreamId: string(),
@@ -18023,7 +18234,7 @@ var RunnerCameraConfigSchema = object({
18023
18234
  */
18024
18235
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
18025
18236
  });
18026
- 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;
18237
+ 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;
18027
18238
  /**
18028
18239
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
18029
18240
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -19009,7 +19220,31 @@ DeviceType.Camera, method(object({
19009
19220
  lastCapturedAt: number().nullable(),
19010
19221
  cacheAgeMs: number().nullable(),
19011
19222
  etag: string().nullable()
19012
- }))), systemMethod(object({
19223
+ }))), systemMethod(object({ deviceId: number() }), object({
19224
+ /** The battery slice as read, or null when the device has none. */
19225
+ battery: object({
19226
+ sleeping: boolean(),
19227
+ lastUpdated: number(),
19228
+ lastContactAt: number().optional()
19229
+ }).nullable(),
19230
+ /** The resolved snapshot state (what the overlay decision used). */
19231
+ state: object({
19232
+ isBattery: boolean(),
19233
+ reason: _enum([
19234
+ "disabled",
19235
+ "sleeping",
19236
+ "unreachable",
19237
+ "waking"
19238
+ ]).nullable()
19239
+ }),
19240
+ /** The cached frame behind the next paint. */
19241
+ frame: object({
19242
+ capturedAt: number().nullable(),
19243
+ ageMs: number().nullable()
19244
+ }),
19245
+ /** A wake window is currently open (the Waking overlay's source). */
19246
+ waking: boolean()
19247
+ })), systemMethod(object({
19013
19248
  /** The tiles a surface is actually rendering. One entry per (device,
19014
19249
  * width) the caller will paint — the width is snapped to the server's
19015
19250
  * ladder and becomes part of the link's SIGNED identity. */
@@ -19039,7 +19274,16 @@ targets: array(object({
19039
19274
  /** A sleeping battery camera: the frame is deliberately stale and will
19040
19275
  * NOT refresh in the background. A surface should say so rather than
19041
19276
  * present it as current. */
19042
- sleeping: boolean()
19277
+ sleeping: boolean(),
19278
+ /** Current device state rendered over the cached frame. State images
19279
+ * remain authoritative even when their photographic background is
19280
+ * old; null means the link must carry a current camera frame. */
19281
+ stateReason: _enum([
19282
+ "disabled",
19283
+ "sleeping",
19284
+ "unreachable",
19285
+ "waking"
19286
+ ]).nullable()
19043
19287
  })));
19044
19288
  /**
19045
19289
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -20578,6 +20822,25 @@ var BatteryStatusSchema = object({
20578
20822
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
20579
20823
  lastUpdated: number(),
20580
20824
  /**
20825
+ * Ms epoch of the last time the device PROVED it was reachable — a
20826
+ * completed firmware round-trip, an observed wake, or an inbound push
20827
+ * (firmware event, email). `0`/absent = never since this slice was born.
20828
+ *
20829
+ * This is the ONLY input that separates "asleep" from "gone", and it is
20830
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
20831
+ * for the radio, because a poll that confirms reachability is the same
20832
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
20833
+ * single derivation every consumer must use; no surface computes its own.
20834
+ *
20835
+ * It is deliberately NOT a clock in the
20836
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
20837
+ * observation itself, and it is the only thing a 30-hour silence is
20838
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
20839
+ * Reolink provider) so a value that means "recently" cannot cost a
20840
+ * SQLite commit per round-trip.
20841
+ */
20842
+ lastContactAt: number().optional(),
20843
+ /**
20581
20844
  * True when the source is a BINARY low-battery indicator (HA
20582
20845
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
20583
20846
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -24446,6 +24709,33 @@ method(object({
24446
24709
  * as `unknown`, never guessed. A day reference scored against an IR frame
24447
24710
  * collapses the cosine and would latch a false alarm every single night. */
24448
24711
  var SceneConditionSchema = string();
24712
+ /**
24713
+ * What a scene does when the CURRENT light has no reference of its own.
24714
+ *
24715
+ * The lighting variants are not equally likely to exist. Almost every operator
24716
+ * captures daylight and then never stands outside at 22:00 to capture IR, and a
24717
+ * scene that is only ever going to be asked about a daytime question ("is the
24718
+ * bin still on the kerb at 08:00") does not need a night reference at all. The
24719
+ * night half must therefore be OPTIONAL, and optional means the scene keeps
24720
+ * working without it rather than degrading into a permanent complaint.
24721
+ *
24722
+ * - `skip` (default) — the check in that light is not made. Not a verdict, not
24723
+ * an alarm, not even an `unknown`: the live state simply stays whatever the
24724
+ * last covered light left it at, the latch is untouched, and the hysteresis
24725
+ * run is neither spent nor cleared. The scene resumes by itself at first
24726
+ * light. This is the only behaviour under which "I never captured IR" is a
24727
+ * configuration choice instead of a nightly fault.
24728
+ * - `judge-anyway` — score against the OTHER conditions' references. Available
24729
+ * for cameras whose IR frame is close enough to daylight (a floodlit
24730
+ * driveway, an always-white-light doorbell), and wrong for everything else:
24731
+ * cross-condition cosines are not comparable, so a day reference against a
24732
+ * true IR frame collapses and the scene reports a theft at 21:40.
24733
+ *
24734
+ * Never applies when the scene has NO comparable reference at all — that is
24735
+ * "not armed yet", it is reported as `no-reference-for-condition`, and silence
24736
+ * there would hide a scene the operator never finished setting up.
24737
+ */
24738
+ var SceneUncoveredPolicySchema = _enum(["skip", "judge-anyway"]);
24449
24739
  /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
24450
24740
  * `unknown` = we cannot judge (no reference for this condition, encoder model
24451
24741
  * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
@@ -24501,6 +24791,9 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
24501
24791
  hysteresisCount: number().int().positive()
24502
24792
  })]);
24503
24793
  var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
24794
+ /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
24795
+ * out in silence rather than reporting a fault every night. */
24796
+ var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
24504
24797
  /**
24505
24798
  * Vision-model adjudication of a candidate flip. Field names deliberately
24506
24799
  * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
@@ -24567,6 +24860,21 @@ var SceneMonitorSchema = object({
24567
24860
  * automation can react to the bin coming back without the operator's own
24568
24861
  * alarm silently clearing itself. */
24569
24862
  autoRestore: boolean().default(false),
24863
+ /** What to do when the current light has no reference of its own. See
24864
+ * {@link SceneUncoveredPolicySchema} — the default makes night OPTIONAL. */
24865
+ onUncoveredCondition: SceneUncoveredPolicySchema.default(SCENE_DEFAULT_UNCOVERED_POLICY),
24866
+ /**
24867
+ * The light whose checks are currently being SAT OUT under
24868
+ * `onUncoveredCondition: 'skip'` — `null` when the scene is checking normally.
24869
+ *
24870
+ * Engine-reported and advisory only: it moves no verdict, no latch and no
24871
+ * hysteresis. It exists so the card can say *"night (IR) — checks paused,
24872
+ * nothing captured in this light"* in the same calm voice as the coverage
24873
+ * line, because the alternative is a scene that silently stops answering
24874
+ * after sunset with nothing anywhere saying why. A skipped check must never
24875
+ * read as a broken one.
24876
+ */
24877
+ suspendedCondition: SceneConditionSchema.nullable().default(null),
24570
24878
  /** Named cause when `verdict === 'unknown'`. */
24571
24879
  unavailable: SceneUnavailableSchema.nullable(),
24572
24880
  /** Conditions that have at least one comparable reference — the coverage line
@@ -24610,6 +24918,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
24610
24918
  minObservationSpacingSec: number().int().min(0).max(3600).optional(),
24611
24919
  anchorThreshold: number().min(0).max(1).optional(),
24612
24920
  autoRestore: boolean().optional(),
24921
+ onUncoveredCondition: SceneUncoveredPolicySchema.optional(),
24613
24922
  /** `null` clears the vision-model adjudicator. */
24614
24923
  confirm: SceneConfirmSchema.nullable().optional()
24615
24924
  })
@@ -24809,6 +25118,16 @@ var CamStreamDescriptorSchema = object({
24809
25118
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
24810
25119
  metadata: record(string(), unknown()).optional()
24811
25120
  });
25121
+ object({
25122
+ /** The descriptors as last built from a real camera response. Never a guess:
25123
+ * a failed or refused build writes NOTHING, so a restored catalog is always
25124
+ * one the camera itself once produced. */
25125
+ descriptors: array(CamStreamDescriptorSchema),
25126
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
25127
+ * path decide whether the camera's own awake window is worth spending on a
25128
+ * re-read. */
25129
+ lastFetchedAt: number()
25130
+ });
24812
25131
  DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
24813
25132
  /** One of the camera's stream profiles. */
24814
25133
  var StreamProfileSchema = _enum([
@@ -27841,6 +28160,12 @@ Object.freeze({
27841
28160
  addonId: null,
27842
28161
  access: "view"
27843
28162
  },
28163
+ "llm.resolveModelRef": {
28164
+ capName: "llm",
28165
+ capScope: "system",
28166
+ addonId: null,
28167
+ access: "create"
28168
+ },
27844
28169
  "llm.setDefault": {
27845
28170
  capName: "llm",
27846
28171
  capScope: "system",
@@ -30151,6 +30476,12 @@ Object.freeze({
30151
30476
  addonId: null,
30152
30477
  access: "view"
30153
30478
  },
30479
+ "snapshot.getDebugState": {
30480
+ capName: "snapshot",
30481
+ capScope: "device",
30482
+ addonId: null,
30483
+ access: "view"
30484
+ },
30154
30485
  "snapshot.getSnapshot": {
30155
30486
  capName: "snapshot",
30156
30487
  capScope: "device",
@@ -32233,6 +32564,11 @@ Object.freeze({
32233
32564
  form: "single",
32234
32565
  optional: false
32235
32566
  }],
32567
+ "pipelineAnalytics.getEventMedia": [{
32568
+ name: "deviceId",
32569
+ form: "single",
32570
+ optional: false
32571
+ }],
32236
32572
  "pipelineAnalytics.getKeyEvents": [{
32237
32573
  name: "deviceId",
32238
32574
  form: "single",
@@ -32263,6 +32599,11 @@ Object.freeze({
32263
32599
  form: "single",
32264
32600
  optional: false
32265
32601
  }],
32602
+ "pipelineAnalytics.getTrackMedia": [{
32603
+ name: "deviceId",
32604
+ form: "single",
32605
+ optional: false
32606
+ }],
32266
32607
  "pipelineAnalytics.getTrainingExportSummary": [{
32267
32608
  name: "deviceIds",
32268
32609
  form: "array",
@@ -32298,6 +32639,11 @@ Object.freeze({
32298
32639
  form: "array",
32299
32640
  optional: true
32300
32641
  }],
32642
+ "pipelineAnalytics.listTrackMedia": [{
32643
+ name: "deviceId",
32644
+ form: "single",
32645
+ optional: false
32646
+ }],
32301
32647
  "pipelineAnalytics.listTracks": [{
32302
32648
  name: "deviceId",
32303
32649
  form: "single",
@@ -32733,11 +33079,22 @@ Object.freeze({
32733
33079
  form: "single",
32734
33080
  optional: false
32735
33081
  }],
33082
+ "snapshot.getDebugState": [{
33083
+ name: "deviceId",
33084
+ form: "single",
33085
+ optional: false
33086
+ }],
32736
33087
  "snapshot.getSnapshot": [{
32737
33088
  name: "deviceId",
32738
33089
  form: "single",
32739
33090
  optional: false
32740
33091
  }],
33092
+ "snapshot.getSnapshotLinks": [{
33093
+ name: "targets",
33094
+ form: "object-array",
33095
+ optional: false,
33096
+ itemField: "deviceId"
33097
+ }],
32741
33098
  "snapshot.getSnapshotOverview": [{
32742
33099
  name: "deviceIds",
32743
33100
  form: "array",