@camstack/addon-pipeline 1.2.79 → 1.2.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/{addon-utils-BZQxAJ1G.js → addon-utils-CQFlZXSn.js} +1 -1
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +1 -1
  4. package/dist/detection-pipeline/index.js +4 -4
  5. package/dist/detection-pipeline/index.mjs +2 -2
  6. package/dist/{dist-BUIpqu3E.js → dist-B57MztMq.js} +276 -14
  7. package/dist/{dist-BfTf6doa.mjs → dist-DGp0dRiq.mjs} +259 -15
  8. package/dist/{event-loop-stall-monitor-CmrHj0i9.mjs → event-loop-stall-monitor-8vYH44kg.mjs} +1 -1
  9. package/dist/{event-loop-stall-monitor-CGLQP0by.js → event-loop-stall-monitor-DPA6Pu_h.js} +1 -1
  10. package/dist/{lazy-sharp-BZVhL9Lw.js → lazy-sharp-BMgRSV9x.js} +1 -1
  11. package/dist/motion-wasm/index.js +2 -2
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/pipeline-runner/index.js +26 -4
  14. package/dist/pipeline-runner/index.mjs +25 -3
  15. package/dist/recorder/index.js +62 -5
  16. package/dist/recorder/index.mjs +61 -4
  17. package/dist/session-decode/decode-worker-child.js +2 -2
  18. package/dist/session-decode/decode-worker-child.mjs +1 -1
  19. package/dist/stream-broker/_stub.js +2 -2
  20. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BohtrVou.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Cb8ynph1.mjs} +2 -2
  21. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B2scqOH7.mjs +26 -0
  22. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dxr14lIu.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DEps-9qh.mjs} +1 -1
  23. package/dist/stream-broker/{hostInit-pDANS0x0.mjs → hostInit-GJipMxBe.mjs} +2 -2
  24. package/dist/stream-broker/index.js +224 -1
  25. package/dist/stream-broker/index.mjs +224 -1
  26. package/dist/stream-broker/remoteEntry.js +1 -1
  27. package/dist/{worker-protocol-BaLcYcOo.js → worker-protocol-4L4SbSAo.js} +1 -1
  28. package/dist/{worker-protocol-BdOv9Ujl.mjs → worker-protocol-_e84lGuR.mjs} +1 -1
  29. package/package.json +1 -1
  30. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-y8ehJPoY.mjs +0 -26
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-BUIpqu3E.js");
1
+ const require_dist = require("./dist-B57MztMq.js");
2
2
  require("node:crypto");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_dist.__toESM(node_path, 1);
@@ -2,9 +2,9 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BUIpqu3E.js");
5
+ const require_dist = require("../dist-B57MztMq.js");
6
6
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
7
- const require_addon_utils = require("../addon-utils-BZQxAJ1G.js");
7
+ const require_addon_utils = require("../addon-utils-CQFlZXSn.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_dist.__toESM(node_path);
10
10
  let node_fs = require("node:fs");
@@ -1,5 +1,5 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { B as mapAudioLabelToMacro, E as audioAnalyzerCapability, T as audioAnalysisCapability, ft as hydrateSchema, m as HF_BASE_URL, n as AUDIO_BACKEND_CHOICES, o as DEFAULT_AUDIO_ANALYZER_CONFIG, st as BaseAddon, tt as errMsg } from "../dist-BfTf6doa.mjs";
2
+ import { D as audioAnalyzerCapability, E as audioAnalysisCapability, U as mapAudioLabelToMacro, h as HF_BASE_URL, ht as hydrateSchema, it as errMsg, n as AUDIO_BACKEND_CHOICES, s as DEFAULT_AUDIO_ANALYZER_CONFIG, ut as BaseAddon } from "../dist-DGp0dRiq.mjs";
3
3
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
4
4
  import { i as downloadFile } from "../addon-utils-A2S9D7pu.mjs";
5
5
  import * as path$1 from "node:path";
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BUIpqu3E.js");
6
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-CGLQP0by.js");
7
- const require_lazy_sharp = require("../lazy-sharp-BZVhL9Lw.js");
5
+ const require_dist = require("../dist-B57MztMq.js");
6
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DPA6Pu_h.js");
7
+ const require_lazy_sharp = require("../lazy-sharp-BMgRSV9x.js");
8
8
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
9
- const require_addon_utils = require("../addon-utils-BZQxAJ1G.js");
9
+ const require_addon_utils = require("../addon-utils-CQFlZXSn.js");
10
10
  let sharp = require("sharp");
11
11
  sharp = require_dist.__toESM(sharp);
12
12
  let node_child_process = require("node:child_process");
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $ as supportedRuntimes$1, A as defaultDeviceFor$1, At as string, C as YAMNET_TO_MACRO, G as pipelineExecutorCapability, L as enumerateInferenceDevices, Mt as EventCategory, Ot as object, P as detectionPipelineCapability, R as evaluateZoneRules, St as array, X as runtimeDevices$1, _t as parseJsonUnknown, bt as sleep, dt as createEvent, ft as hydrateSchema, gt as nodePin, jt as union, st as BaseAddon, t as APPLE_SA_TO_MACRO, tt as errMsg, u as DEVICE_BACKEND_TO_FORMAT } from "../dist-BfTf6doa.mjs";
3
- import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-CmrHj0i9.mjs";
2
+ import { $ as runtimeDevices$1, B as enumerateInferenceDevices, Ct as sleep, Ft as EventCategory, J as pipelineExecutorCapability, L as detectionPipelineCapability, M as defaultDeviceFor$1, Nt as string, Pt as union, Tt as array, V as evaluateZoneRules, bt as parseJsonUnknown, d as DEVICE_BACKEND_TO_FORMAT, ht as hydrateSchema, it as errMsg, jt as object, mt as createEvent, nt as supportedRuntimes$1, t as APPLE_SA_TO_MACRO, ut as BaseAddon, w as YAMNET_TO_MACRO, yt as nodePin } from "../dist-DGp0dRiq.mjs";
3
+ import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, n as startEventLoopStallMonitor, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep } from "../event-loop-stall-monitor-8vYH44kg.mjs";
4
4
  import { t as getSharp } from "../lazy-sharp-6oymT_yf.mjs";
5
5
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
6
6
  import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../addon-utils-A2S9D7pu.mjs";
@@ -13727,6 +13727,18 @@ var LlmGenerateBaseInputSchema = object({
13727
13727
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
13728
13728
  * watchdog — operator decision #3).
13729
13729
  */
13730
+ /**
13731
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
13732
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
13733
+ * REF rather than looked up at install time, so what the operator approved in
13734
+ * the preview is exactly what the node downloads.
13735
+ */
13736
+ var ManagedModelExtraFileSchema = object({
13737
+ url: string(),
13738
+ filename: string(),
13739
+ sizeBytes: number(),
13740
+ sha256: string().optional()
13741
+ });
13730
13742
  var ManagedModelRefSchema = discriminatedUnion("kind", [
13731
13743
  object({
13732
13744
  kind: literal("catalog"),
@@ -13735,7 +13747,11 @@ var ManagedModelRefSchema = discriminatedUnion("kind", [
13735
13747
  object({
13736
13748
  kind: literal("url"),
13737
13749
  url: string(),
13738
- sha256: string().optional()
13750
+ sha256: string().optional(),
13751
+ /** Picker/status label; the file basename when absent. */
13752
+ label: string().optional(),
13753
+ sizeBytes: number().optional(),
13754
+ extraFiles: array(ManagedModelExtraFileSchema).optional()
13739
13755
  }),
13740
13756
  object({
13741
13757
  kind: literal("path"),
@@ -13796,11 +13812,39 @@ var ManagedRuntimeConfigSchema = object({
13796
13812
  "q4_1",
13797
13813
  "q4_0"
13798
13814
  ]).optional(),
13815
+ /**
13816
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
13817
+ * (which most vision chat templates need and some language-only models
13818
+ * dislike), `--cont-batching`, `--rope-scaling`, …
13819
+ *
13820
+ * It is NOT a second place to set the flags above. A token that collides
13821
+ * with a typed field is REJECTED at start, naming the field that owns it
13822
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
13823
+ * the "two switches that disagree" failure this repo has already shipped
13824
+ * twice (D62).
13825
+ */
13826
+ extraArgs: array(string()).default([]),
13799
13827
  /** Else lazy: first generate boots it. */
13800
13828
  autoStart: boolean().default(false),
13801
13829
  /** 0 = never; frees RAM after quiet periods. */
13802
13830
  idleStopMinutes: number().int().default(30)
13803
13831
  });
13832
+ /**
13833
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
13834
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
13835
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
13836
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
13837
+ */
13838
+ var LlmDownloadProgressSchema = object({
13839
+ phase: _enum(["downloading", "verifying"]),
13840
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
13841
+ file: string(),
13842
+ fileIndex: number().int(),
13843
+ fileCount: number().int(),
13844
+ /** Across the WHOLE install, not the current file. */
13845
+ downloadedBytes: number(),
13846
+ totalBytes: number().optional()
13847
+ });
13804
13848
  var LlmRuntimeStatusSchema = object({
13805
13849
  /** Status is ALWAYS node-qualified. */
13806
13850
  nodeId: string(),
@@ -13817,6 +13861,8 @@ var LlmRuntimeStatusSchema = object({
13817
13861
  modelPath: string().optional(),
13818
13862
  modelId: string().optional(),
13819
13863
  downloadProgress: number().min(0).max(1).optional(),
13864
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
13865
+ download: LlmDownloadProgressSchema.optional(),
13820
13866
  lastError: string().optional(),
13821
13867
  crashesInWindow: number(),
13822
13868
  /** Child RSS (sampled best-effort). */
@@ -13827,7 +13873,14 @@ var LlmNodeModelSchema = object({
13827
13873
  file: string(),
13828
13874
  sizeBytes: number(),
13829
13875
  catalogId: string().optional(),
13830
- installedAt: number().optional()
13876
+ installedAt: number().optional(),
13877
+ /**
13878
+ * Absolute path on the node. Present so a file that is on disk but matches
13879
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
13880
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
13881
+ * it the picker could list such a file and do nothing with it.
13882
+ */
13883
+ path: string().optional()
13831
13884
  });
13832
13885
  var LlmRuntimeDiskUsageSchema = object({
13833
13886
  nodeId: string(),
@@ -13973,6 +14026,36 @@ var ManagedModelCatalogEntrySchema = object({
13973
14026
  /** Vision models: companion projector file. */
13974
14027
  mmprojUrl: string().optional()
13975
14028
  });
14029
+ /**
14030
+ * The outcome of turning one operator-typed Hugging Face reference into a
14031
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
14032
+ * I will not pick for you" is a normal answer the UI has to render, not an
14033
+ * exception.
14034
+ *
14035
+ * `candidates` is the whole reason the refusal is usable — every string in it
14036
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
14037
+ */
14038
+ var HfModelResolutionSchema = discriminatedUnion("ok", [object({
14039
+ ok: literal(true),
14040
+ /** Ready to hand to `installModel` unchanged. */
14041
+ model: ManagedModelRefSchema,
14042
+ label: string(),
14043
+ repo: string(),
14044
+ quantization: string(),
14045
+ purpose: _enum(["text", "vision"]),
14046
+ totalBytes: number(),
14047
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
14048
+ * see that 0.9 GB of it is a projector they did not name. */
14049
+ extraFilenames: array(string())
14050
+ }), object({
14051
+ ok: literal(false),
14052
+ code: string(),
14053
+ message: string(),
14054
+ candidates: array(string()).optional(),
14055
+ /** Set when the refusal was only the ceiling: re-calling with
14056
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
14057
+ requiredBytes: number().optional()
14058
+ })]);
13976
14059
  var LlmRuntimeNodeSchema = object({
13977
14060
  nodeId: string(),
13978
14061
  reachable: boolean(),
@@ -14009,6 +14092,15 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
14009
14092
  consumer: string().optional(),
14010
14093
  profileId: string().optional()
14011
14094
  }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
14095
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
14096
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
14097
+ ref: string(),
14098
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
14099
+ maxBytes: number().positive().optional()
14100
+ }), HfModelResolutionSchema, {
14101
+ kind: "mutation",
14102
+ auth: "admin"
14103
+ }), method(object({
14012
14104
  nodeId: string(),
14013
14105
  model: ManagedModelRefSchema
14014
14106
  }), _void(), {
@@ -19636,6 +19728,17 @@ var maxSessionHoldMsField = {
19636
19728
  default: 12e4,
19637
19729
  step: 5e3
19638
19730
  };
19731
+ /**
19732
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
19733
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
19734
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
19735
+ */
19736
+ var audioMotionWindowMsField = {
19737
+ min: 5e3,
19738
+ max: 6e5,
19739
+ default: 9e4,
19740
+ step: 5e3
19741
+ };
19639
19742
  var motionFpsField = {
19640
19743
  min: 1,
19641
19744
  max: 30,
@@ -19812,6 +19915,27 @@ var RunnerCameraConfigSchema = object({
19812
19915
  * resolved `CameraDetectionConfig`.
19813
19916
  */
19814
19917
  maxSessionHoldMs: number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
19918
+ /**
19919
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
19920
+ * 'on-motion'` audio window, measured from the LAST motion event.
19921
+ *
19922
+ * This exists because the falling edge cannot be relied on. Camera-native
19923
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
19924
+ * its email-push SMTP path both emit `detected: true` and never the
19925
+ * counterpart); only the frame-diff analyzer emits falls. So on an
19926
+ * onboard-only camera a window that closed only on `detected: false` never
19927
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
19928
+ * battery camera, the one failure mode the mode exists to prevent.
19929
+ *
19930
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
19931
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
19932
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
19933
+ *
19934
+ * Not consumed by the runner: carried here so it shares the per-camera
19935
+ * device-settings surface with `motionCooldownMs`, exactly like
19936
+ * `maxSessionHoldMs`.
19937
+ */
19938
+ audioMotionWindowMs: number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
19815
19939
  motionFps: number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
19816
19940
  detectionFps: number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
19817
19941
  motionStreamId: string(),
@@ -19907,7 +20031,7 @@ var RunnerCameraConfigSchema = object({
19907
20031
  */
19908
20032
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
19909
20033
  });
19910
- 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;
20034
+ 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;
19911
20035
  /**
19912
20036
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
19913
20037
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -22682,6 +22806,25 @@ var BatteryStatusSchema = object({
22682
22806
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
22683
22807
  lastUpdated: number(),
22684
22808
  /**
22809
+ * Ms epoch of the last time the device PROVED it was reachable — a
22810
+ * completed firmware round-trip, an observed wake, or an inbound push
22811
+ * (firmware event, email). `0`/absent = never since this slice was born.
22812
+ *
22813
+ * This is the ONLY input that separates "asleep" from "gone", and it is
22814
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
22815
+ * for the radio, because a poll that confirms reachability is the same
22816
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
22817
+ * single derivation every consumer must use; no surface computes its own.
22818
+ *
22819
+ * It is deliberately NOT a clock in the
22820
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
22821
+ * observation itself, and it is the only thing a 30-hour silence is
22822
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
22823
+ * Reolink provider) so a value that means "recently" cannot cost a
22824
+ * SQLite commit per round-trip.
22825
+ */
22826
+ lastContactAt: number().optional(),
22827
+ /**
22685
22828
  * True when the source is a BINARY low-battery indicator (HA
22686
22829
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
22687
22830
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -22690,17 +22833,84 @@ var BatteryStatusSchema = object({
22690
22833
  */
22691
22834
  binary: boolean().optional()
22692
22835
  });
22693
- DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, method(object({
22694
- deviceId: number(),
22695
- /** Bound on the wait. Sensible range 3000–10000ms. */
22696
- timeoutMs: number().int().min(500).max(3e4).default(8e3)
22697
- }), object({
22698
- awoke: boolean(),
22699
- durationMs: number()
22700
- }), { kind: "mutation" }), object({
22701
- deviceId: number(),
22702
- status: BatteryStatusSchema
22703
- });
22836
+ var batteryCapability = {
22837
+ name: "battery",
22838
+ scope: "device",
22839
+ deviceNative: true,
22840
+ mode: "singleton",
22841
+ deviceTypes: [
22842
+ DeviceType.Camera,
22843
+ DeviceType.Sensor,
22844
+ DeviceType.Button,
22845
+ DeviceType.Switch
22846
+ ],
22847
+ methods: {
22848
+ /**
22849
+ * Explicitly wake the camera from low-power sleep ahead of a
22850
+ * streaming session start. Consumers that initiate a stream
22851
+ * against a sleeping battery cam (HomeKit Secure Video, Alexa
22852
+ * RTCSession, snapshot wrappers) call this with a short timeout
22853
+ * before establishing the media pipeline — the broker's own
22854
+ * passive wake-on-dial works but adds 5–7 seconds to first-frame,
22855
+ * during which the consumer renders a black screen. Pre-waking
22856
+ * compresses that gap.
22857
+ *
22858
+ * Returns `awoke: true` when the firmware acknowledged the wake
22859
+ * before `timeoutMs`. Returns `awoke: false` when it timed out OR
22860
+ * the cap surface is unavailable (no Baichuan / firmware
22861
+ * channel); the caller should still attempt the stream — the
22862
+ * passive broker wake remains as fallback.
22863
+ */
22864
+ wakeForStream: method(object({
22865
+ deviceId: number(),
22866
+ /** Bound on the wait. Sensible range 3000–10000ms. */
22867
+ timeoutMs: number().int().min(500).max(3e4).default(8e3)
22868
+ }), object({
22869
+ awoke: boolean(),
22870
+ durationMs: number()
22871
+ }), { kind: "mutation" }) },
22872
+ events: {
22873
+ /**
22874
+ * Emitted whenever the cached status changes (firmware push OR
22875
+ * poll observes a delta). The DeviceEventPropagator mirrors this
22876
+ * event on the parent chain — subscribing to a camera's source
22877
+ * receives battery events from child accessories automatically.
22878
+ */
22879
+ onStatusChanged: { data: object({
22880
+ deviceId: number(),
22881
+ status: BatteryStatusSchema
22882
+ }) } },
22883
+ status: {
22884
+ schema: BatteryStatusSchema,
22885
+ kind: "push",
22886
+ empty: {
22887
+ percentage: 0,
22888
+ charging: "none",
22889
+ sleeping: false,
22890
+ lastUpdated: 0
22891
+ }
22892
+ },
22893
+ /**
22894
+ * Runtime-state slice — every provider that registers this cap
22895
+ * stores the same shape under `device.runtimeState[battery]`.
22896
+ * Cross-provider uniformity: a Reolink Argus, a Frigate sensor
22897
+ * proxy, an ONVIF battery cam all read/write the same keys.
22898
+ * Consumers (BatteryBadge, snapshot wrapper sleep gate) read once
22899
+ * via `device.runtimeState.getCapState('battery')` regardless of
22900
+ * the underlying driver.
22901
+ */
22902
+ runtimeState: BatteryStatusSchema,
22903
+ /**
22904
+ * Runtime-state durability: **restored** — a sleeping battery camera may not report for hours; the restored percentage is the only thing the UI and the sleep gate have. Zero churn once `lastUpdated` is excluded — 526 writes, 0 value changes, in 25 minutes.
22905
+ *
22906
+ * See `RuntimeStateDurability`. Enforced by
22907
+ * `scripts/check-runtime-state-durability.ts`.
22908
+ */
22909
+ durability: "restored",
22910
+ /** Clock fields: written, but excluded from the compare that decides
22911
+ * whether persisting is worth a SQLite commit. */
22912
+ volatileStateFields: ["lastUpdated"]
22913
+ };
22704
22914
  object({
22705
22915
  on: boolean(),
22706
22916
  /** Ms epoch of the last transition. 0 if never observed. */
@@ -27111,6 +27321,16 @@ var CamStreamDescriptorSchema = object({
27111
27321
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
27112
27322
  metadata: record(string(), unknown()).optional()
27113
27323
  });
27324
+ object({
27325
+ /** The descriptors as last built from a real camera response. Never a guess:
27326
+ * a failed or refused build writes NOTHING, so a restored catalog is always
27327
+ * one the camera itself once produced. */
27328
+ descriptors: array(CamStreamDescriptorSchema),
27329
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
27330
+ * path decide whether the camera's own awake window is worth spending on a
27331
+ * re-read. */
27332
+ lastFetchedAt: number()
27333
+ });
27114
27334
  DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
27115
27335
  /** One of the camera's stream profiles. */
27116
27336
  var StreamProfileSchema = _enum([
@@ -27995,6 +28215,24 @@ AccessoryKind.Siren, AccessoryKind.Floodlight, AccessoryKind.Spotlight, Accessor
27995
28215
  DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
27996
28216
  new Set(Object.values(DeviceType));
27997
28217
  DeviceFeature.BatteryOperated;
28218
+ /**
28219
+ * THE derivation. One crop rectangle, one presence verdict — see D52 for why
28220
+ * this repo insists a derived value has exactly one implementation.
28221
+ *
28222
+ * `undefined` status ⇒ `sleeping`. A device with no slice has made no claim,
28223
+ * and the caller decides separately whether it is even battery-operated
28224
+ * (`DeviceFeature.BatteryOperated`); this function never answers that
28225
+ * question, only what a battery device is doing.
28226
+ */
28227
+ function deriveBatteryPresence(input) {
28228
+ const status = input.status;
28229
+ if (!status) return "sleeping";
28230
+ if (status.sleeping !== true) return "awake";
28231
+ const lastContactAt = status.lastContactAt;
28232
+ if (typeof lastContactAt !== "number" || lastContactAt <= 0) return "sleeping";
28233
+ const budget = input.unreachableAfterMs ?? 216e5;
28234
+ return input.nowMs - lastContactAt > budget ? "unreachable" : "sleeping";
28235
+ }
27998
28236
  Object.freeze({
27999
28237
  "accessories.setChildHidden": {
28000
28238
  capName: "accessories",
@@ -30144,6 +30382,12 @@ Object.freeze({
30144
30382
  addonId: null,
30145
30383
  access: "view"
30146
30384
  },
30385
+ "llm.resolveModelRef": {
30386
+ capName: "llm",
30387
+ capScope: "system",
30388
+ addonId: null,
30389
+ access: "create"
30390
+ },
30147
30391
  "llm.setDefault": {
30148
30392
  capName: "llm",
30149
30393
  capScope: "system",
@@ -36330,6 +36574,12 @@ Object.defineProperty(exports, "BaseAddon", {
36330
36574
  return BaseAddon;
36331
36575
  }
36332
36576
  });
36577
+ Object.defineProperty(exports, "BatteryStatusSchema", {
36578
+ enumerable: true,
36579
+ get: function() {
36580
+ return BatteryStatusSchema;
36581
+ }
36582
+ });
36333
36583
  Object.defineProperty(exports, "CAM_PROFILE_ORDER", {
36334
36584
  enumerable: true,
36335
36585
  get: function() {
@@ -36522,6 +36772,12 @@ Object.defineProperty(exports, "audioAnalyzerCapability", {
36522
36772
  return audioAnalyzerCapability;
36523
36773
  }
36524
36774
  });
36775
+ Object.defineProperty(exports, "batteryCapability", {
36776
+ enumerable: true,
36777
+ get: function() {
36778
+ return batteryCapability;
36779
+ }
36780
+ });
36525
36781
  Object.defineProperty(exports, "boolean", {
36526
36782
  enumerable: true,
36527
36783
  get: function() {
@@ -36570,6 +36826,12 @@ Object.defineProperty(exports, "defineCustomActions", {
36570
36826
  return defineCustomActions;
36571
36827
  }
36572
36828
  });
36829
+ Object.defineProperty(exports, "deriveBatteryPresence", {
36830
+ enumerable: true,
36831
+ get: function() {
36832
+ return deriveBatteryPresence;
36833
+ }
36834
+ });
36573
36835
  Object.defineProperty(exports, "deriveDetailCropRect", {
36574
36836
  enumerable: true,
36575
36837
  get: function() {