@camstack/types 1.2.64 → 1.2.66

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 (79) hide show
  1. package/dist/addon.js +1 -1
  2. package/dist/addon.mjs +1 -1
  3. package/dist/capabilities/air-quality-sensor.cap.d.ts +10 -0
  4. package/dist/capabilities/alarm-panel.cap.d.ts +10 -0
  5. package/dist/capabilities/ambient-light-sensor.cap.d.ts +10 -0
  6. package/dist/capabilities/audio-metrics.cap.d.ts +7 -0
  7. package/dist/capabilities/automation-control.cap.d.ts +7 -0
  8. package/dist/capabilities/battery.cap.d.ts +10 -0
  9. package/dist/capabilities/binary.cap.d.ts +10 -0
  10. package/dist/capabilities/brightness.cap.d.ts +7 -0
  11. package/dist/capabilities/camera-streams.cap.d.ts +7 -0
  12. package/dist/capabilities/capability-definition.d.ts +58 -0
  13. package/dist/capabilities/carbon-monoxide.cap.d.ts +10 -0
  14. package/dist/capabilities/climate-control.cap.d.ts +7 -0
  15. package/dist/capabilities/color.cap.d.ts +7 -0
  16. package/dist/capabilities/connectivity.cap.d.ts +10 -0
  17. package/dist/capabilities/consumables.cap.d.ts +7 -0
  18. package/dist/capabilities/contact.cap.d.ts +10 -0
  19. package/dist/capabilities/control.cap.d.ts +7 -0
  20. package/dist/capabilities/cover.cap.d.ts +10 -0
  21. package/dist/capabilities/day-night.cap.d.ts +10 -0
  22. package/dist/capabilities/device-discovery.cap.d.ts +7 -0
  23. package/dist/capabilities/device-status.cap.d.ts +10 -0
  24. package/dist/capabilities/doorbell.cap.d.ts +7 -0
  25. package/dist/capabilities/enum-sensor.cap.d.ts +10 -0
  26. package/dist/capabilities/event-emitter.cap.d.ts +7 -0
  27. package/dist/capabilities/fan-control.cap.d.ts +7 -0
  28. package/dist/capabilities/feature-probe.cap.d.ts +7 -0
  29. package/dist/capabilities/flood.cap.d.ts +10 -0
  30. package/dist/capabilities/gas.cap.d.ts +10 -0
  31. package/dist/capabilities/humidifier.cap.d.ts +7 -0
  32. package/dist/capabilities/humidity-sensor.cap.d.ts +10 -0
  33. package/dist/capabilities/image-settings.cap.d.ts +10 -0
  34. package/dist/capabilities/image.cap.d.ts +7 -0
  35. package/dist/capabilities/index.d.ts +3 -3
  36. package/dist/capabilities/lawn-mower-control.cap.d.ts +7 -0
  37. package/dist/capabilities/lock-control.cap.d.ts +10 -0
  38. package/dist/capabilities/media-player.cap.d.ts +7 -0
  39. package/dist/capabilities/motion-trigger.cap.d.ts +7 -0
  40. package/dist/capabilities/motion-zones.cap.d.ts +10 -0
  41. package/dist/capabilities/motion.cap.d.ts +7 -0
  42. package/dist/capabilities/native-object-detection.cap.d.ts +10 -0
  43. package/dist/capabilities/notification-output.cap.d.ts +16 -0
  44. package/dist/capabilities/notification-rules.cap.d.ts +35 -3
  45. package/dist/capabilities/notifier.cap.d.ts +7 -0
  46. package/dist/capabilities/numeric-sensor.cap.d.ts +10 -0
  47. package/dist/capabilities/osd-manager.cap.d.ts +2 -0
  48. package/dist/capabilities/pet-feeder.cap.d.ts +7 -0
  49. package/dist/capabilities/power-meter.cap.d.ts +10 -0
  50. package/dist/capabilities/presence.cap.d.ts +10 -0
  51. package/dist/capabilities/pressure-sensor.cap.d.ts +10 -0
  52. package/dist/capabilities/privacy-mask.cap.d.ts +10 -0
  53. package/dist/capabilities/ptz-autotrack.cap.d.ts +7 -0
  54. package/dist/capabilities/recording-export.cap.d.ts +17 -6
  55. package/dist/capabilities/scene-monitor.cap.d.ts +7 -0
  56. package/dist/capabilities/script-runner.cap.d.ts +7 -0
  57. package/dist/capabilities/smoke.cap.d.ts +10 -0
  58. package/dist/capabilities/stream-params.cap.d.ts +10 -0
  59. package/dist/capabilities/switch.cap.d.ts +10 -0
  60. package/dist/capabilities/tamper.cap.d.ts +10 -0
  61. package/dist/capabilities/temperature-sensor.cap.d.ts +10 -0
  62. package/dist/capabilities/update.cap.d.ts +7 -0
  63. package/dist/capabilities/vacuum-control.cap.d.ts +7 -0
  64. package/dist/capabilities/valve.cap.d.ts +7 -0
  65. package/dist/capabilities/vibration.cap.d.ts +10 -0
  66. package/dist/capabilities/water-heater.cap.d.ts +7 -0
  67. package/dist/capabilities/weather.cap.d.ts +7 -0
  68. package/dist/capabilities/zone-analytics.cap.d.ts +7 -0
  69. package/dist/capabilities/zone-rules.cap.d.ts +7 -0
  70. package/dist/capabilities/zones.cap.d.ts +7 -0
  71. package/dist/generated/runtime-state-durability.d.ts +31 -0
  72. package/dist/index.d.ts +4 -1
  73. package/dist/index.js +1292 -118
  74. package/dist/index.mjs +1267 -117
  75. package/dist/notification/audio-condition.d.ts +118 -0
  76. package/dist/pipeline/native-lease.d.ts +36 -19
  77. package/dist/{sleep-BxjBLp3L.mjs → sleep-CSgK0rNX.mjs} +3 -1
  78. package/dist/{sleep-BszXLEvP.js → sleep-Dh1EJSqF.js} +8 -0
  79. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-DxZbWydC.js");
3
- const require_sleep = require("./sleep-BszXLEvP.js");
3
+ const require_sleep = require("./sleep-Dh1EJSqF.js");
4
4
  const require_canonical_hash = require("./canonical-hash-DNV8S5ET.js");
5
5
  const require_enums = require("./enums.js");
6
6
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -6664,7 +6664,14 @@ var cameraStreamsCapability = {
6664
6664
  low: zod.z.string().optional()
6665
6665
  }),
6666
6666
  lastChangedAt: zod.z.number()
6667
- })
6667
+ }),
6668
+ /**
6669
+ * Runtime-state durability: **session** — a restored `slotStatuses: streaming` for a camera that has been dark for two hours is a lie the UI renders as truth.
6670
+ *
6671
+ * See `RuntimeStateDurability`. Enforced by
6672
+ * `scripts/check-runtime-state-durability.ts`.
6673
+ */
6674
+ durability: "session"
6668
6675
  };
6669
6676
  //#endregion
6670
6677
  //#region src/capabilities/cap-router-predicates.ts
@@ -7831,6 +7838,13 @@ var deviceDiscoveryCapability = {
7831
7838
  kind: "poll"
7832
7839
  },
7833
7840
  runtimeState: DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: zod.z.number().int().nonnegative() }),
7841
+ /**
7842
+ * Runtime-state durability: **session** — 5.7 KB of scan output on the largest device, fully re-derivable by re-scanning.
7843
+ *
7844
+ * See `RuntimeStateDurability`. Enforced by
7845
+ * `scripts/check-runtime-state-durability.ts`.
7846
+ */
7847
+ durability: "session",
7834
7848
  methods: {
7835
7849
  /**
7836
7850
  * Snapshot of the current `discovered` list. Returns the
@@ -10483,7 +10497,23 @@ var NotificationActionSchema = zod.z.object({
10483
10497
  * else — see `notification-center/action-token.ts` for what that does and
10484
10498
  * does not buy.
10485
10499
  */
10486
- destructive: zod.z.boolean().optional()
10500
+ destructive: zod.z.boolean().optional(),
10501
+ /**
10502
+ * How the tap should REACH the url.
10503
+ *
10504
+ * `navigate` (absent, and every button authored before this field) opens it:
10505
+ * the phone leaves the notification and shows whatever the callback returns.
10506
+ * That is right for a button whose answer the operator wants to read.
10507
+ *
10508
+ * `background` fires it as a POST and stays put. It exists for the buttons
10509
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
10510
+ * an answer to the notification, and being thrown into a browser tab to
10511
+ * confirm it costs more attention than the notification did. A backend that
10512
+ * cannot do a background call renders it as an ordinary link (the adapters
10513
+ * fall back rather than dropping the button), so this is a preference, never
10514
+ * a requirement.
10515
+ */
10516
+ mode: zod.z.enum(["navigate", "background"]).optional()
10487
10517
  });
10488
10518
  /**
10489
10519
  * The canonical notification. `body` is the only hard field (Apprise model).
@@ -11726,7 +11756,17 @@ var alarmPanelCapability = {
11726
11756
  * full slice; renders an arm button per `availableModes` entry and
11727
11757
  * a PIN field iff `requiresCode === true`.
11728
11758
  */
11729
- runtimeState: AlarmPanelStatusSchema
11759
+ runtimeState: AlarmPanelStatusSchema,
11760
+ /**
11761
+ * Runtime-state durability: **restored** — armed state is the one thing a panel must not lose across a restart.
11762
+ *
11763
+ * See `RuntimeStateDurability`. Enforced by
11764
+ * `scripts/check-runtime-state-durability.ts`.
11765
+ */
11766
+ durability: "restored",
11767
+ /** Clock fields: written, but excluded from the compare that decides
11768
+ * whether persisting is worth a SQLite commit. */
11769
+ volatileStateFields: ["lastChangedAt"]
11730
11770
  };
11731
11771
  //#endregion
11732
11772
  //#region src/capabilities/mask-shape.ts
@@ -11934,6 +11974,26 @@ var NcSystemEventConditionSchema = zod.z.object({
11934
11974
  * unique key `(ruleId, trackId, targetId)`. Not a rule field in P1.
11935
11975
  */
11936
11976
  var NC_MAX_PER_TRACK_IMMEDIATE = 1;
11977
+ /** Hard ceiling on a window (24h). A snooze that could not expire would be an
11978
+ * outage the operator asked for once and forgot. */
11979
+ var NC_SNOOZE_MAX_MINUTES = 1440;
11980
+ /**
11981
+ * The durations a notification offers when its rule does not say otherwise.
11982
+ *
11983
+ * MINUTES, and short ones: a snooze button is answered on a lock screen, and
11984
+ * the three choices that fit there are "the next few minutes", "the next half
11985
+ * hour" and "the next hour". Anything longer is a decision somebody sits down
11986
+ * to make, which is what the app's own snooze surface is for.
11987
+ *
11988
+ * Resolved in CODE (`snoozeChoices`), never as a Zod default — see
11989
+ * {@link NcRuleInputSchema.shape.snoozeOptions} for why the distinction is
11990
+ * load-bearing.
11991
+ */
11992
+ var NC_DEFAULT_SNOOZE_MINUTES = [
11993
+ 10,
11994
+ 30,
11995
+ 60
11996
+ ];
11937
11997
  /** One weekly activation window. `startMinute > endMinute` crosses midnight. */
11938
11998
  var NcScheduleWindowSchema = zod.z.object({
11939
11999
  /** Days of week the window STARTS on (0 = Sunday … 6 = Saturday). */
@@ -12335,15 +12395,15 @@ var NcConditionsSchema = zod.z.object({
12335
12395
  * (an `immediate` rule naming an `audio-*` class, one notification per
12336
12396
  * classified sample) stays exactly as it was for rules that already use it.
12337
12397
  *
12338
- * NOT in {@link NC_CONDITION_CATALOG} yet, and that is the sequencing rule
12339
- * rather than an oversight: the viewer mirrors the descriptor enums BY HAND
12340
- * (`camstack/src/data/notification-center.ts`, guarded by
12341
- * `scripts/check-viewer-condition-mirror.ts`) and its rule editor STRIPS the
12398
+ * In {@link NC_CONDITION_CATALOG} since P2, and the ORDER it got there is the
12399
+ * rule rather than an accident: the viewer mirrors the descriptor enums BY
12400
+ * HAND (`camstack/src/data/notification-center.ts`, guarded by
12401
+ * `scripts/check-viewer-condition-mirror.ts`) and its rule editor strips the
12342
12402
  * condition fields it does not know when a rule is saved from the phone.
12343
12403
  * Publishing an editor for a condition the app cannot round-trip is how an
12344
- * operator loses a rule's conditions by opening it — so the descriptor, the
12345
- * admin widget and the viewer mirror land together (P2 + P3), and only then
12346
- * does an audio rule become authorable.
12404
+ * operator loses a rule's conditions by opening it — so the viewer mirror
12405
+ * (P3, shipped) went FIRST, and the descriptor an editor renders from
12406
+ * follows here.
12347
12407
  */
12348
12408
  audio: NcAudioConditionSchema.optional()
12349
12409
  });
@@ -12580,6 +12640,30 @@ var NcRuleInputSchema = zod.z.object({
12580
12640
  */
12581
12641
  snoozeAllowGlobal: zod.z.boolean().optional(),
12582
12642
  /**
12643
+ * The snooze durations THIS rule's notification offers as buttons, in
12644
+ * minutes.
12645
+ *
12646
+ * Three states, and all three are distinct — which is exactly why this is
12647
+ * `.optional()` and never `.default()`. A Zod default does not run on the
12648
+ * addon cap path (three production failures in one day), so a schema default
12649
+ * would collapse the first two:
12650
+ *
12651
+ * | value | meaning |
12652
+ * | --- | --- |
12653
+ * | absent | the operator never said ⇒ {@link NC_DEFAULT_SNOOZE_MINUTES} |
12654
+ * | `[]` | **no snooze buttons on this rule** — the explicit override |
12655
+ * | a list | these choices, de-duplicated and sorted, at most four |
12656
+ *
12657
+ * `.max(4)` because the notifier's own action budget is small (ntfy allows
12658
+ * three buttons in total) and a rule that spent it all on snooze choices
12659
+ * would push its own tap-through actions off the notification.
12660
+ *
12661
+ * An empty list is NOT an alarm exemption: a rule the alarm is about, or
12662
+ * that arms the panel, is exempt automatically and cannot be silenced by a
12663
+ * window from anywhere (D133).
12664
+ */
12665
+ snoozeOptions: zod.z.array(zod.z.number().int().min(1).max(NC_SNOOZE_MAX_MINUTES)).max(4).optional(),
12666
+ /**
12583
12667
  * Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
12584
12668
  *
12585
12669
  * This is what makes the rule set the alarm's trigger set without the alarm
@@ -12679,6 +12763,7 @@ var NcConditionDescriptorSchema = zod.z.object({
12679
12763
  "device",
12680
12764
  "package",
12681
12765
  "occupancy",
12766
+ "audio",
12682
12767
  "system"
12683
12768
  ]),
12684
12769
  label: zod.z.string(),
@@ -12697,6 +12782,7 @@ var NcConditionDescriptorSchema = zod.z.object({
12697
12782
  "crossingSelect",
12698
12783
  "polygonDraw",
12699
12784
  "occupancy",
12785
+ "audio",
12700
12786
  "deviceState",
12701
12787
  "systemEvent"
12702
12788
  ]),
@@ -13094,6 +13180,16 @@ var NC_CONDITION_CATALOG = [
13094
13180
  phase: "P1",
13095
13181
  description: "ZoneAnalytics occupancy edge (optionally zone/class-scoped): count crosses the threshold and holds for sustainSeconds. Fail-closed on a missing snapshot."
13096
13182
  },
13183
+ {
13184
+ id: "audio",
13185
+ group: "audio",
13186
+ label: "Sound",
13187
+ valueType: "audio",
13188
+ operator: "anyOf",
13189
+ appliesTo: ["immediate"],
13190
+ phase: "P2",
13191
+ description: "Fires when at least hitPercent% of the samples in a samplingSeconds window are hits — a hit clears the dBFS floor AND carries one of the chosen sounds. Both filters are optional and independent, but naming NEITHER never matches: every sample would be a hit, so the engine refuses rather than notifying on silence. dBFS is negative-going (0 = full scale, -96 = silence)."
13192
+ },
13097
13193
  {
13098
13194
  id: "customZones",
13099
13195
  group: "zones",
@@ -13229,9 +13325,6 @@ var NcSnoozeScopeSchema = zod.z.enum([
13229
13325
  "device",
13230
13326
  "all"
13231
13327
  ]);
13232
- /** Hard ceiling on a window (24h). A snooze that could not expire would be an
13233
- * outage the operator asked for once and forgot. */
13234
- var NC_SNOOZE_MAX_MINUTES = 1440;
13235
13328
  /**
13236
13329
  * Client-authored snooze. The server stamps `userId`, `startedAt` and
13237
13330
  * `expiresAt` — a DURATION is sent rather than an instant so a client with a
@@ -13244,6 +13337,19 @@ var NcSnoozeInputSchema = zod.z.object({
13244
13337
  ruleId: zod.z.string().optional(),
13245
13338
  /** Required when `scope: 'device'`. */
13246
13339
  deviceId: zod.z.number().int().optional(),
13340
+ /**
13341
+ * Narrow the window to these subject classes — "the cat, not the person".
13342
+ *
13343
+ * ORTHOGONAL to `scope`, deliberately, and absent means EVERY class: that is
13344
+ * what every window authored before this field meant, so no persisted row
13345
+ * changes meaning and no client has to learn anything to keep working.
13346
+ *
13347
+ * It is what makes the window's real key `(deviceId, classes[])` and lets it
13348
+ * cross rules (D133): the operator points at a camera and a kind of thing,
13349
+ * not at whichever of their four rules happened to produce the notification
13350
+ * they are dismissing.
13351
+ */
13352
+ classes: zod.z.array(zod.z.string().min(1)).min(1).optional(),
13247
13353
  durationMinutes: zod.z.number().int().min(1).max(NC_SNOOZE_MAX_MINUTES),
13248
13354
  /**
13249
13355
  * Silence this for EVERY recipient, not just the caller. Permission is
@@ -13269,6 +13375,10 @@ var NcSnoozeSchema = zod.z.object({
13269
13375
  scope: NcSnoozeScopeSchema,
13270
13376
  ruleId: zod.z.string().optional(),
13271
13377
  deviceId: zod.z.number().int().optional(),
13378
+ /** Subject classes this window covers. ABSENT = every class — see
13379
+ * {@link NcSnoozeInputSchema.shape.classes}. Lives in the JSON blob and has
13380
+ * no SQLite column: nothing queries a window by class. */
13381
+ classes: zod.z.array(zod.z.string().min(1)).min(1).optional(),
13272
13382
  startedAt: zod.z.number(),
13273
13383
  /** Exclusive: at exactly this instant the snooze is over. Expiry is a
13274
13384
  * COMPARISON, not a job — no sweeper can leave the operator silenced. */
@@ -16061,7 +16171,14 @@ var zonesCapability = {
16061
16171
  * handle. Slice shape is `{ zones: Zone[] }` so future extensions
16062
16172
  * (e.g. zone groupings) can sit alongside the polygon list.
16063
16173
  */
16064
- runtimeState: zod.z.object({ zones: zod.z.array(ZoneSchema).readonly() })
16174
+ runtimeState: zod.z.object({ zones: zod.z.array(ZoneSchema).readonly() }),
16175
+ /**
16176
+ * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
16177
+ *
16178
+ * See `RuntimeStateDurability`. Enforced by
16179
+ * `scripts/check-runtime-state-durability.ts`.
16180
+ */
16181
+ durability: "restored"
16065
16182
  };
16066
16183
  //#endregion
16067
16184
  //#region src/capabilities/pipeline-runner.cap.ts
@@ -20268,7 +20385,17 @@ var airQualitySensorCapability = {
20268
20385
  schema: AirQualitySensorStatusSchema,
20269
20386
  kind: "push"
20270
20387
  },
20271
- runtimeState: AirQualitySensorStatusSchema
20388
+ runtimeState: AirQualitySensorStatusSchema,
20389
+ /**
20390
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
20391
+ *
20392
+ * See `RuntimeStateDurability`. Enforced by
20393
+ * `scripts/check-runtime-state-durability.ts`.
20394
+ */
20395
+ durability: "restored",
20396
+ /** Clock fields: written, but excluded from the compare that decides
20397
+ * whether persisting is worth a SQLite commit. */
20398
+ volatileStateFields: ["lastFetchedAt"]
20272
20399
  };
20273
20400
  //#endregion
20274
20401
  //#region src/capabilities/ambient-light-sensor.cap.ts
@@ -20302,7 +20429,17 @@ var ambientLightSensorCapability = {
20302
20429
  schema: AmbientLightSensorStatusSchema,
20303
20430
  kind: "push"
20304
20431
  },
20305
- runtimeState: AmbientLightSensorStatusSchema
20432
+ runtimeState: AmbientLightSensorStatusSchema,
20433
+ /**
20434
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
20435
+ *
20436
+ * See `RuntimeStateDurability`. Enforced by
20437
+ * `scripts/check-runtime-state-durability.ts`.
20438
+ */
20439
+ durability: "restored",
20440
+ /** Clock fields: written, but excluded from the compare that decides
20441
+ * whether persisting is worth a SQLite commit. */
20442
+ volatileStateFields: ["lastFetchedAt"]
20306
20443
  };
20307
20444
  //#endregion
20308
20445
  //#region src/capabilities/audio-metrics.cap.ts
@@ -20431,7 +20568,14 @@ var audioMetricsCapability = {
20431
20568
  }), AudioMetricsHistorySchema)
20432
20569
  },
20433
20570
  /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
20434
- runtimeState: AudioMetricsSnapshotSchema
20571
+ runtimeState: AudioMetricsSnapshotSchema,
20572
+ /**
20573
+ * Runtime-state durability: **session** — 1 Hz per camera at the mirror and ~63 % of the fleet's whole runtime-state write rate. `avgDbfs` is a rolling 60 s mean that genuinely moves every second, so no equality fix reclaims it — and a two-hour-old dB reading rendered as current is worse than no reading.
20574
+ *
20575
+ * See `RuntimeStateDurability`. Enforced by
20576
+ * `scripts/check-runtime-state-durability.ts`.
20577
+ */
20578
+ durability: "session"
20435
20579
  };
20436
20580
  //#endregion
20437
20581
  //#region src/capabilities/automation-control.cap.ts
@@ -20495,7 +20639,14 @@ var automationControlCapability = {
20495
20639
  * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
20496
20640
  * (badge) directly.
20497
20641
  */
20498
- runtimeState: AutomationControlStatusSchema
20642
+ runtimeState: AutomationControlStatusSchema,
20643
+ /**
20644
+ * Runtime-state durability: **session** — the authority for an automation being enabled is the automation store; a restored `isRunning` would be a lie. D62: one authority per switch.
20645
+ *
20646
+ * See `RuntimeStateDurability`. Enforced by
20647
+ * `scripts/check-runtime-state-durability.ts`.
20648
+ */
20649
+ durability: "session"
20499
20650
  };
20500
20651
  //#endregion
20501
20652
  //#region src/capabilities/battery.cap.ts
@@ -20603,7 +20754,17 @@ onStatusChanged: { data: zod.z.object({
20603
20754
  * via `device.runtimeState.getCapState('battery')` regardless of
20604
20755
  * the underlying driver.
20605
20756
  */
20606
- runtimeState: BatteryStatusSchema
20757
+ runtimeState: BatteryStatusSchema,
20758
+ /**
20759
+ * 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.
20760
+ *
20761
+ * See `RuntimeStateDurability`. Enforced by
20762
+ * `scripts/check-runtime-state-durability.ts`.
20763
+ */
20764
+ durability: "restored",
20765
+ /** Clock fields: written, but excluded from the compare that decides
20766
+ * whether persisting is worth a SQLite commit. */
20767
+ volatileStateFields: ["lastUpdated"]
20607
20768
  };
20608
20769
  //#endregion
20609
20770
  //#region src/capabilities/binary.cap.ts
@@ -20634,7 +20795,17 @@ var binaryCapability = {
20634
20795
  schema: BinaryStatusSchema,
20635
20796
  kind: "push"
20636
20797
  },
20637
- runtimeState: BinaryStatusSchema
20798
+ runtimeState: BinaryStatusSchema,
20799
+ /**
20800
+ * Runtime-state durability: **restored** — transition-driven sensor state; the restored value gives the boot comparison.
20801
+ *
20802
+ * See `RuntimeStateDurability`. Enforced by
20803
+ * `scripts/check-runtime-state-durability.ts`.
20804
+ */
20805
+ durability: "restored",
20806
+ /** Clock fields: written, but excluded from the compare that decides
20807
+ * whether persisting is worth a SQLite commit. */
20808
+ volatileStateFields: ["lastChangedAt"]
20638
20809
  };
20639
20810
  //#endregion
20640
20811
  //#region src/capabilities/brightness.cap.ts
@@ -20689,7 +20860,14 @@ onBrightnessChanged: { data: zod.z.object({
20689
20860
  * by the kernel. Read via `device.state.brightness.value` so UI
20690
20861
  * sliders surface the current level without polling the provider.
20691
20862
  */
20692
- runtimeState: BrightnessStatusSchema
20863
+ runtimeState: BrightnessStatusSchema,
20864
+ /**
20865
+ * Runtime-state durability: **session** — live lamp state, re-published by the provider on connect.
20866
+ *
20867
+ * See `RuntimeStateDurability`. Enforced by
20868
+ * `scripts/check-runtime-state-durability.ts`.
20869
+ */
20870
+ durability: "session"
20693
20871
  };
20694
20872
  //#endregion
20695
20873
  //#region src/capabilities/button.cap.ts
@@ -20824,7 +21002,17 @@ var carbonMonoxideCapability = {
20824
21002
  schema: CarbonMonoxideStatusSchema,
20825
21003
  kind: "push"
20826
21004
  },
20827
- runtimeState: CarbonMonoxideStatusSchema
21005
+ runtimeState: CarbonMonoxideStatusSchema,
21006
+ /**
21007
+ * Runtime-state durability: **restored** — as `smoke`.
21008
+ *
21009
+ * See `RuntimeStateDurability`. Enforced by
21010
+ * `scripts/check-runtime-state-durability.ts`.
21011
+ */
21012
+ durability: "restored",
21013
+ /** Clock fields: written, but excluded from the compare that decides
21014
+ * whether persisting is worth a SQLite commit. */
21015
+ volatileStateFields: ["lastChangedAt"]
20828
21016
  };
20829
21017
  //#endregion
20830
21018
  //#region src/capabilities/climate-control.cap.ts
@@ -20986,7 +21174,14 @@ var climateControlCapability = {
20986
21174
  * the full slice via `device.state.climate-control.value` and refresh
20987
21175
  * on every push without re-querying the provider.
20988
21176
  */
20989
- runtimeState: ClimateControlStatusSchema
21177
+ runtimeState: ClimateControlStatusSchema,
21178
+ /**
21179
+ * Runtime-state durability: **session** — as `brightness`; `currentTemp` moves continuously and is re-published on connect.
21180
+ *
21181
+ * See `RuntimeStateDurability`. Enforced by
21182
+ * `scripts/check-runtime-state-durability.ts`.
21183
+ */
21184
+ durability: "session"
20990
21185
  };
20991
21186
  //#endregion
20992
21187
  //#region src/capabilities/color.cap.ts
@@ -21098,7 +21293,14 @@ onColorChanged: { data: zod.z.object({
21098
21293
  * kernel. Read via `device.state.color.value` so UI pickers surface
21099
21294
  * the current chromaticity without polling the provider.
21100
21295
  */
21101
- runtimeState: ColorStatusSchema
21296
+ runtimeState: ColorStatusSchema,
21297
+ /**
21298
+ * Runtime-state durability: **session** — as `brightness`.
21299
+ *
21300
+ * See `RuntimeStateDurability`. Enforced by
21301
+ * `scripts/check-runtime-state-durability.ts`.
21302
+ */
21303
+ durability: "session"
21102
21304
  };
21103
21305
  //#endregion
21104
21306
  //#region src/capabilities/connection-test.cap.ts
@@ -21222,7 +21424,17 @@ var connectivityCapability = {
21222
21424
  schema: ConnectivityStatusSchema,
21223
21425
  kind: "push"
21224
21426
  },
21225
- runtimeState: ConnectivityStatusSchema
21427
+ runtimeState: ConnectivityStatusSchema,
21428
+ /**
21429
+ * Runtime-state durability: **restored** — same shape and same argument as `device-status`, for links rather than devices.
21430
+ *
21431
+ * See `RuntimeStateDurability`. Enforced by
21432
+ * `scripts/check-runtime-state-durability.ts`.
21433
+ */
21434
+ durability: "restored",
21435
+ /** Clock fields: written, but excluded from the compare that decides
21436
+ * whether persisting is worth a SQLite commit. */
21437
+ volatileStateFields: ["lastChangedAt"]
21226
21438
  };
21227
21439
  //#endregion
21228
21440
  //#region src/capabilities/consumables.cap.ts
@@ -21306,7 +21518,14 @@ reset: require_sleep.method(zod.z.object({
21306
21518
  }
21307
21519
  }
21308
21520
  },
21309
- runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: zod.z.number() })
21521
+ runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: zod.z.number() }),
21522
+ /**
21523
+ * Runtime-state durability: **session** — the authority is the appliance; the provider re-reads the whole item array on connect.
21524
+ *
21525
+ * See `RuntimeStateDurability`. Enforced by
21526
+ * `scripts/check-runtime-state-durability.ts`.
21527
+ */
21528
+ durability: "session"
21310
21529
  };
21311
21530
  //#endregion
21312
21531
  //#region src/capabilities/contact.cap.ts
@@ -21339,7 +21558,17 @@ var contactCapability = {
21339
21558
  schema: ContactStatusSchema,
21340
21559
  kind: "push"
21341
21560
  },
21342
- runtimeState: ContactStatusSchema
21561
+ runtimeState: ContactStatusSchema,
21562
+ /**
21563
+ * Runtime-state durability: **restored** — a door left open across a restart must still read open.
21564
+ *
21565
+ * See `RuntimeStateDurability`. Enforced by
21566
+ * `scripts/check-runtime-state-durability.ts`.
21567
+ */
21568
+ durability: "restored",
21569
+ /** Clock fields: written, but excluded from the compare that decides
21570
+ * whether persisting is worth a SQLite commit. */
21571
+ volatileStateFields: ["lastChangedAt"]
21343
21572
  };
21344
21573
  //#endregion
21345
21574
  //#region src/capabilities/control.cap.ts
@@ -21471,7 +21700,14 @@ var controlCapability = {
21471
21700
  * dropdown / text field / date picker) read the slice's discriminant
21472
21701
  * and value directly without polling the provider.
21473
21702
  */
21474
- runtimeState: ControlStatusSchema
21703
+ runtimeState: ControlStatusSchema,
21704
+ /**
21705
+ * Runtime-state durability: **session** — a generic control mirrors an external entity that re-publishes on connect; the options array is re-derived with it.
21706
+ *
21707
+ * See `RuntimeStateDurability`. Enforced by
21708
+ * `scripts/check-runtime-state-durability.ts`.
21709
+ */
21710
+ durability: "session"
21475
21711
  };
21476
21712
  //#endregion
21477
21713
  //#region src/capabilities/cover.cap.ts
@@ -21556,7 +21792,17 @@ var coverCapability = {
21556
21792
  * Runtime-state slice — mirrored by the kernel. UI controls watch
21557
21793
  * the slice for live position changes during a move.
21558
21794
  */
21559
- runtimeState: CoverStatusSchema
21795
+ runtimeState: CoverStatusSchema,
21796
+ /**
21797
+ * Runtime-state durability: **restored** — position survives a restart on the device; the mirror should agree at boot rather than read blank.
21798
+ *
21799
+ * See `RuntimeStateDurability`. Enforced by
21800
+ * `scripts/check-runtime-state-durability.ts`.
21801
+ */
21802
+ durability: "restored",
21803
+ /** Clock fields: written, but excluded from the compare that decides
21804
+ * whether persisting is worth a SQLite commit. */
21805
+ volatileStateFields: ["lastChangedAt"]
21560
21806
  };
21561
21807
  //#endregion
21562
21808
  //#region src/capabilities/day-night.cap.ts
@@ -21652,7 +21898,17 @@ var dayNightCapability = {
21652
21898
  schema: DayNightStatusSchema,
21653
21899
  kind: "poll"
21654
21900
  },
21655
- runtimeState: DayNightStatusSchema
21901
+ runtimeState: DayNightStatusSchema,
21902
+ /**
21903
+ * Runtime-state durability: **restored** — operator-set IR-cut behaviour; mutation-driven.
21904
+ *
21905
+ * See `RuntimeStateDurability`. Enforced by
21906
+ * `scripts/check-runtime-state-durability.ts`.
21907
+ */
21908
+ durability: "restored",
21909
+ /** Clock fields: written, but excluded from the compare that decides
21910
+ * whether persisting is worth a SQLite commit. */
21911
+ volatileStateFields: ["lastFetchedAt"]
21656
21912
  };
21657
21913
  //#endregion
21658
21914
  //#region src/capabilities/device-status.cap.ts
@@ -21701,7 +21957,17 @@ onStatusChanged: { data: zod.z.object({
21701
21957
  schema: DeviceStatusSchema,
21702
21958
  kind: "push"
21703
21959
  },
21704
- runtimeState: DeviceStatusSchema
21960
+ runtimeState: DeviceStatusSchema,
21961
+ /**
21962
+ * Runtime-state durability: **restored** — the previous observation is what makes the first reading after a restart a COMPARISON instead of a phantom transition (D130). 32 real flips across 16 devices in 25 min — the busiest slice in the cold half.
21963
+ *
21964
+ * See `RuntimeStateDurability`. Enforced by
21965
+ * `scripts/check-runtime-state-durability.ts`.
21966
+ */
21967
+ durability: "restored",
21968
+ /** Clock fields: written, but excluded from the compare that decides
21969
+ * whether persisting is worth a SQLite commit. */
21970
+ volatileStateFields: ["lastChangedAt"]
21705
21971
  };
21706
21972
  //#endregion
21707
21973
  //#region src/capabilities/doorbell.cap.ts
@@ -21765,7 +22031,14 @@ onPressed: { data: DoorbellPressEventSchema } },
21765
22031
  * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
21766
22032
  * without subscribing.
21767
22033
  */
21768
- runtimeState: DoorbellStatusSchema
22034
+ runtimeState: DoorbellStatusSchema,
22035
+ /**
22036
+ * Runtime-state durability: **restored** — a monotonic accumulator: the slice IS the record. `lastPressedAt` is content here, not a clock, so it is deliberately NOT volatile.
22037
+ *
22038
+ * See `RuntimeStateDurability`. Enforced by
22039
+ * `scripts/check-runtime-state-durability.ts`.
22040
+ */
22041
+ durability: "restored"
21769
22042
  };
21770
22043
  //#endregion
21771
22044
  //#region src/capabilities/enum-sensor.cap.ts
@@ -21807,7 +22080,17 @@ var enumSensorCapability = {
21807
22080
  schema: EnumSensorStatusSchema,
21808
22081
  kind: "push"
21809
22082
  },
21810
- runtimeState: EnumSensorStatusSchema
22083
+ runtimeState: EnumSensorStatusSchema,
22084
+ /**
22085
+ * Runtime-state durability: **restored** — as `numeric-sensor`; 80 devices.
22086
+ *
22087
+ * See `RuntimeStateDurability`. Enforced by
22088
+ * `scripts/check-runtime-state-durability.ts`.
22089
+ */
22090
+ durability: "restored",
22091
+ /** Clock fields: written, but excluded from the compare that decides
22092
+ * whether persisting is worth a SQLite commit. */
22093
+ volatileStateFields: ["lastFetchedAt"]
21811
22094
  };
21812
22095
  //#endregion
21813
22096
  //#region src/capabilities/event-emitter.cap.ts
@@ -21845,7 +22128,14 @@ var eventEmitterCapability = {
21845
22128
  schema: EventEmitterStatusSchema,
21846
22129
  kind: "push"
21847
22130
  },
21848
- runtimeState: EventEmitterStatusSchema
22131
+ runtimeState: EventEmitterStatusSchema,
22132
+ /**
22133
+ * Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
22134
+ *
22135
+ * See `RuntimeStateDurability`. Enforced by
22136
+ * `scripts/check-runtime-state-durability.ts`.
22137
+ */
22138
+ durability: "session"
21849
22139
  };
21850
22140
  //#endregion
21851
22141
  //#region src/capabilities/events.cap.ts
@@ -22168,7 +22458,14 @@ var fanControlCapability = {
22168
22458
  * Runtime-state slice — mirrored by the kernel. UI fan speed
22169
22459
  * sliders read `percentage` for live updates.
22170
22460
  */
22171
- runtimeState: FanControlStatusSchema
22461
+ runtimeState: FanControlStatusSchema,
22462
+ /**
22463
+ * Runtime-state durability: **session** — as `brightness`.
22464
+ *
22465
+ * See `RuntimeStateDurability`. Enforced by
22466
+ * `scripts/check-runtime-state-durability.ts`.
22467
+ */
22468
+ durability: "session"
22172
22469
  };
22173
22470
  //#endregion
22174
22471
  //#region src/capabilities/feature-probe.cap.ts
@@ -22246,7 +22543,14 @@ onProbeChanged: { data: zod.z.object({
22246
22543
  schema: FeatureProbeStatusSchema,
22247
22544
  kind: "push"
22248
22545
  },
22249
- runtimeState: FeatureProbeStatusSchema
22546
+ runtimeState: FeatureProbeStatusSchema,
22547
+ /**
22548
+ * Runtime-state durability: **session** — per-session by definition — `lastProbedAt` means "this worker completed a probe THIS session", which is why the mirror seed already blanks it. Persisting it only creates something to blank.
22549
+ *
22550
+ * See `RuntimeStateDurability`. Enforced by
22551
+ * `scripts/check-runtime-state-durability.ts`.
22552
+ */
22553
+ durability: "session"
22250
22554
  };
22251
22555
  //#endregion
22252
22556
  //#region src/capabilities/flood.cap.ts
@@ -22275,7 +22579,17 @@ var floodCapability = {
22275
22579
  schema: FloodStatusSchema,
22276
22580
  kind: "push"
22277
22581
  },
22278
- runtimeState: FloodStatusSchema
22582
+ runtimeState: FloodStatusSchema,
22583
+ /**
22584
+ * Runtime-state durability: **restored** — as `smoke`.
22585
+ *
22586
+ * See `RuntimeStateDurability`. Enforced by
22587
+ * `scripts/check-runtime-state-durability.ts`.
22588
+ */
22589
+ durability: "restored",
22590
+ /** Clock fields: written, but excluded from the compare that decides
22591
+ * whether persisting is worth a SQLite commit. */
22592
+ volatileStateFields: ["lastChangedAt"]
22279
22593
  };
22280
22594
  //#endregion
22281
22595
  //#region src/capabilities/gas.cap.ts
@@ -22300,7 +22614,17 @@ var gasCapability = {
22300
22614
  schema: GasStatusSchema,
22301
22615
  kind: "push"
22302
22616
  },
22303
- runtimeState: GasStatusSchema
22617
+ runtimeState: GasStatusSchema,
22618
+ /**
22619
+ * Runtime-state durability: **restored** — as `smoke`.
22620
+ *
22621
+ * See `RuntimeStateDurability`. Enforced by
22622
+ * `scripts/check-runtime-state-durability.ts`.
22623
+ */
22624
+ durability: "restored",
22625
+ /** Clock fields: written, but excluded from the compare that decides
22626
+ * whether persisting is worth a SQLite commit. */
22627
+ volatileStateFields: ["lastChangedAt"]
22304
22628
  };
22305
22629
  //#endregion
22306
22630
  //#region src/capabilities/humidifier.cap.ts
@@ -22378,7 +22702,14 @@ var humidifierCapability = {
22378
22702
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
22379
22703
  * slice for live humidity / mode changes.
22380
22704
  */
22381
- runtimeState: HumidifierStatusSchema
22705
+ runtimeState: HumidifierStatusSchema,
22706
+ /**
22707
+ * Runtime-state durability: **session** — as `climate-control`.
22708
+ *
22709
+ * See `RuntimeStateDurability`. Enforced by
22710
+ * `scripts/check-runtime-state-durability.ts`.
22711
+ */
22712
+ durability: "session"
22382
22713
  };
22383
22714
  //#endregion
22384
22715
  //#region src/capabilities/humidity-sensor.cap.ts
@@ -22416,7 +22747,17 @@ var humiditySensorCapability = {
22416
22747
  schema: HumiditySensorStatusSchema,
22417
22748
  kind: "push"
22418
22749
  },
22419
- runtimeState: HumiditySensorStatusSchema
22750
+ runtimeState: HumiditySensorStatusSchema,
22751
+ /**
22752
+ * Runtime-state durability: **restored** — as `numeric-sensor` (67 of 75 writes were the clock alone).
22753
+ *
22754
+ * See `RuntimeStateDurability`. Enforced by
22755
+ * `scripts/check-runtime-state-durability.ts`.
22756
+ */
22757
+ durability: "restored",
22758
+ /** Clock fields: written, but excluded from the compare that decides
22759
+ * whether persisting is worth a SQLite commit. */
22760
+ volatileStateFields: ["lastFetchedAt"]
22420
22761
  };
22421
22762
  //#endregion
22422
22763
  //#region src/capabilities/image.cap.ts
@@ -22456,7 +22797,14 @@ var imageCapability = {
22456
22797
  * Runtime-state slice — mirrored by the kernel. The UI reads `url`
22457
22798
  * directly and renders the still image.
22458
22799
  */
22459
- runtimeState: ImageStatusSchema
22800
+ runtimeState: ImageStatusSchema,
22801
+ /**
22802
+ * Runtime-state durability: **session** — a snapshot URL is a session-scoped handle; a restored one points at nothing.
22803
+ *
22804
+ * See `RuntimeStateDurability`. Enforced by
22805
+ * `scripts/check-runtime-state-durability.ts`.
22806
+ */
22807
+ durability: "session"
22460
22808
  };
22461
22809
  //#endregion
22462
22810
  //#region src/capabilities/image-settings.cap.ts
@@ -22607,7 +22955,17 @@ var imageSettingsCapability = {
22607
22955
  schema: ImageSettingsStatusSchema,
22608
22956
  kind: "poll"
22609
22957
  },
22610
- runtimeState: ImageSettingsStatusSchema
22958
+ runtimeState: ImageSettingsStatusSchema,
22959
+ /**
22960
+ * Runtime-state durability: **restored** — operator-set camera imaging; mutation-driven.
22961
+ *
22962
+ * See `RuntimeStateDurability`. Enforced by
22963
+ * `scripts/check-runtime-state-durability.ts`.
22964
+ */
22965
+ durability: "restored",
22966
+ /** Clock fields: written, but excluded from the compare that decides
22967
+ * whether persisting is worth a SQLite commit. */
22968
+ volatileStateFields: ["lastFetchedAt"]
22611
22969
  };
22612
22970
  //#endregion
22613
22971
  //#region src/capabilities/integrations.cap.ts
@@ -23022,7 +23380,14 @@ var lawnMowerControlCapability = {
23022
23380
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
23023
23381
  * slice for live activity + battery changes.
23024
23382
  */
23025
- runtimeState: LawnMowerControlStatusSchema
23383
+ runtimeState: LawnMowerControlStatusSchema,
23384
+ /**
23385
+ * Runtime-state durability: **session** — as `vacuum-control`.
23386
+ *
23387
+ * See `RuntimeStateDurability`. Enforced by
23388
+ * `scripts/check-runtime-state-durability.ts`.
23389
+ */
23390
+ durability: "session"
23026
23391
  };
23027
23392
  //#endregion
23028
23393
  //#region src/capabilities/local-network.cap.ts
@@ -23329,7 +23694,17 @@ var lockControlCapability = {
23329
23694
  * read `state` and disable themselves during `locking`/`unlocking`
23330
23695
  * transitions.
23331
23696
  */
23332
- runtimeState: LockControlStatusSchema
23697
+ runtimeState: LockControlStatusSchema,
23698
+ /**
23699
+ * Runtime-state durability: **restored** — a lock left locked must still read locked.
23700
+ *
23701
+ * See `RuntimeStateDurability`. Enforced by
23702
+ * `scripts/check-runtime-state-durability.ts`.
23703
+ */
23704
+ durability: "restored",
23705
+ /** Clock fields: written, but excluded from the compare that decides
23706
+ * whether persisting is worth a SQLite commit. */
23707
+ volatileStateFields: ["lastChangedAt"]
23333
23708
  };
23334
23709
  //#endregion
23335
23710
  //#region src/capabilities/media-player.cap.ts
@@ -23493,7 +23868,14 @@ var mediaPlayerCapability = {
23493
23868
  * full slice for live now-playing, volume, and progress updates
23494
23869
  * without polling.
23495
23870
  */
23496
- runtimeState: MediaPlayerStatusSchema
23871
+ runtimeState: MediaPlayerStatusSchema,
23872
+ /**
23873
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
23874
+ *
23875
+ * See `RuntimeStateDurability`. Enforced by
23876
+ * `scripts/check-runtime-state-durability.ts`.
23877
+ */
23878
+ durability: "session"
23497
23879
  };
23498
23880
  //#endregion
23499
23881
  //#region src/capabilities/mesh-network.cap.ts
@@ -23822,7 +24204,14 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
23822
24204
  * `device.state.motion.value`. Reads never invoke the provider, so
23823
24205
  * UIs and other addons can poll the cached state safely.
23824
24206
  */
23825
- runtimeState: MotionStatusSchema
24207
+ runtimeState: MotionStatusSchema,
24208
+ /**
24209
+ * Runtime-state durability: **session** — self-clearing by construction (`autoClearAfterMs`); a restored `detected: true` is a frozen event, and the next frame re-publishes the real one.
24210
+ *
24211
+ * See `RuntimeStateDurability`. Enforced by
24212
+ * `scripts/check-runtime-state-durability.ts`.
24213
+ */
24214
+ durability: "session"
23826
24215
  };
23827
24216
  //#endregion
23828
24217
  //#region src/capabilities/motion-trigger.cap.ts
@@ -23889,7 +24278,14 @@ var motionTriggerCapability = {
23889
24278
  schema: MotionTriggerStatusSchema,
23890
24279
  kind: "command-driven"
23891
24280
  },
23892
- runtimeState: MotionTriggerRuntimeStateSchema
24281
+ runtimeState: MotionTriggerRuntimeStateSchema,
24282
+ /**
24283
+ * Runtime-state durability: **session** — the authority for motion-trigger enablement is the provider's own config; the slice is a mirror of it, re-published on connect.
24284
+ *
24285
+ * See `RuntimeStateDurability`. Enforced by
24286
+ * `scripts/check-runtime-state-durability.ts`.
24287
+ */
24288
+ durability: "session"
23893
24289
  };
23894
24290
  //#endregion
23895
24291
  //#region src/capabilities/motion-zones.cap.ts
@@ -23958,7 +24354,17 @@ var motionZonesCapability = {
23958
24354
  schema: MotionZoneStatusSchema,
23959
24355
  kind: "poll"
23960
24356
  },
23961
- runtimeState: MotionZoneStatusSchema
24357
+ runtimeState: MotionZoneStatusSchema,
24358
+ /**
24359
+ * Runtime-state durability: **restored** — the 14 KB polygon list is the single largest slice on the fleet and has not changed since the operator drew it. Highest value per byte in the table.
24360
+ *
24361
+ * See `RuntimeStateDurability`. Enforced by
24362
+ * `scripts/check-runtime-state-durability.ts`.
24363
+ */
24364
+ durability: "restored",
24365
+ /** Clock fields: written, but excluded from the compare that decides
24366
+ * whether persisting is worth a SQLite commit. */
24367
+ volatileStateFields: ["lastFetchedAt"]
23962
24368
  };
23963
24369
  //#endregion
23964
24370
  //#region src/capabilities/native-object-detection.cap.ts
@@ -24034,7 +24440,17 @@ var nativeObjectDetectionCapability = {
24034
24440
  schema: NativeObjectDetectionStatusSchema,
24035
24441
  kind: "push"
24036
24442
  },
24037
- runtimeState: NativeObjectDetectionRuntimeStateSchema
24443
+ runtimeState: NativeObjectDetectionRuntimeStateSchema,
24444
+ /**
24445
+ * Runtime-state durability: **restored** — `enabled` is an operator toggle on a camera whose refresh is a no-op and whose staleMs is Infinity. There is no hardware value to re-read — losing it loses the setting.
24446
+ *
24447
+ * See `RuntimeStateDurability`. Enforced by
24448
+ * `scripts/check-runtime-state-durability.ts`.
24449
+ */
24450
+ durability: "restored",
24451
+ /** Clock fields: written, but excluded from the compare that decides
24452
+ * whether persisting is worth a SQLite commit. */
24453
+ volatileStateFields: ["lastFetchedAt"]
24038
24454
  };
24039
24455
  //#endregion
24040
24456
  //#region src/capabilities/network-quality.cap.ts
@@ -24427,7 +24843,14 @@ onSent: { data: zod.z.object({
24427
24843
  * form reads `supports` to gate optional fields; history pane reads
24428
24844
  * `lastSentAt` / `lastError` / `queueDepth`.
24429
24845
  */
24430
- runtimeState: NotifierStatusSchema
24846
+ runtimeState: NotifierStatusSchema,
24847
+ /**
24848
+ * Runtime-state durability: **session** — live queue depth and last-send state; a restored queue depth describes a queue that no longer exists.
24849
+ *
24850
+ * See `RuntimeStateDurability`. Enforced by
24851
+ * `scripts/check-runtime-state-durability.ts`.
24852
+ */
24853
+ durability: "session"
24431
24854
  };
24432
24855
  //#endregion
24433
24856
  //#region src/capabilities/numeric-sensor.cap.ts
@@ -24472,7 +24895,17 @@ var numericSensorCapability = {
24472
24895
  schema: NumericSensorStatusSchema,
24473
24896
  kind: "push"
24474
24897
  },
24475
- runtimeState: NumericSensorStatusSchema
24898
+ runtimeState: NumericSensorStatusSchema,
24899
+ /**
24900
+ * Runtime-state durability: **restored** — polled value, low churn once the clock is excluded (251 of 669 writes were the clock alone); restoring it removes the cold window before the first poll.
24901
+ *
24902
+ * See `RuntimeStateDurability`. Enforced by
24903
+ * `scripts/check-runtime-state-durability.ts`.
24904
+ */
24905
+ durability: "restored",
24906
+ /** Clock fields: written, but excluded from the compare that decides
24907
+ * whether persisting is worth a SQLite commit. */
24908
+ volatileStateFields: ["lastFetchedAt"]
24476
24909
  };
24477
24910
  //#endregion
24478
24911
  //#region src/capabilities/osd.cap.ts
@@ -24989,7 +25422,14 @@ var petFeederCapability = {
24989
25422
  * the full slice via `device.state.petFeeder.value` and refresh on
24990
25423
  * every poll without re-querying the provider.
24991
25424
  */
24992
- runtimeState: PetFeederStatusSchema
25425
+ runtimeState: PetFeederStatusSchema,
25426
+ /**
25427
+ * Runtime-state durability: **session** — live appliance state re-published on connect.
25428
+ *
25429
+ * See `RuntimeStateDurability`. Enforced by
25430
+ * `scripts/check-runtime-state-durability.ts`.
25431
+ */
25432
+ durability: "session"
24993
25433
  };
24994
25434
  //#endregion
24995
25435
  //#region src/capabilities/plate-gallery.cap.ts
@@ -25352,7 +25792,17 @@ var powerMeterCapability = {
25352
25792
  schema: PowerMeterStatusSchema,
25353
25793
  kind: "push"
25354
25794
  },
25355
- runtimeState: PowerMeterStatusSchema
25795
+ runtimeState: PowerMeterStatusSchema,
25796
+ /**
25797
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
25798
+ *
25799
+ * See `RuntimeStateDurability`. Enforced by
25800
+ * `scripts/check-runtime-state-durability.ts`.
25801
+ */
25802
+ durability: "restored",
25803
+ /** Clock fields: written, but excluded from the compare that decides
25804
+ * whether persisting is worth a SQLite commit. */
25805
+ volatileStateFields: ["lastFetchedAt"]
25356
25806
  };
25357
25807
  //#endregion
25358
25808
  //#region src/capabilities/presence.cap.ts
@@ -25411,7 +25861,17 @@ var presenceCapability = {
25411
25861
  * the map pin is rendered (use `DeviceFeature.PresenceGps` for the
25412
25862
  * pre-fetch fast-path check).
25413
25863
  */
25414
- runtimeState: PresenceStatusSchema
25864
+ runtimeState: PresenceStatusSchema,
25865
+ /**
25866
+ * Runtime-state durability: **restored** — occupancy-relevant: the restored state is what an occupancy rule compares the first post-restart observation against.
25867
+ *
25868
+ * See `RuntimeStateDurability`. Enforced by
25869
+ * `scripts/check-runtime-state-durability.ts`.
25870
+ */
25871
+ durability: "restored",
25872
+ /** Clock fields: written, but excluded from the compare that decides
25873
+ * whether persisting is worth a SQLite commit. */
25874
+ volatileStateFields: ["lastChangedAt"]
25415
25875
  };
25416
25876
  //#endregion
25417
25877
  //#region src/capabilities/pressure-sensor.cap.ts
@@ -25449,7 +25909,17 @@ var pressureSensorCapability = {
25449
25909
  schema: PressureSensorStatusSchema,
25450
25910
  kind: "push"
25451
25911
  },
25452
- runtimeState: PressureSensorStatusSchema
25912
+ runtimeState: PressureSensorStatusSchema,
25913
+ /**
25914
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
25915
+ *
25916
+ * See `RuntimeStateDurability`. Enforced by
25917
+ * `scripts/check-runtime-state-durability.ts`.
25918
+ */
25919
+ durability: "restored",
25920
+ /** Clock fields: written, but excluded from the compare that decides
25921
+ * whether persisting is worth a SQLite commit. */
25922
+ volatileStateFields: ["lastFetchedAt"]
25453
25923
  };
25454
25924
  //#endregion
25455
25925
  //#region src/capabilities/privacy-mask.cap.ts
@@ -25581,7 +26051,17 @@ var privacyMaskCapability = {
25581
26051
  schema: PrivacyMaskStatusSchema,
25582
26052
  kind: "poll"
25583
26053
  },
25584
- runtimeState: PrivacyMaskStatusSchema
26054
+ runtimeState: PrivacyMaskStatusSchema,
26055
+ /**
26056
+ * Runtime-state durability: **restored** — operator-drawn regions, zero real churn — 22 writes in 25 minutes, every one of them the clock.
26057
+ *
26058
+ * See `RuntimeStateDurability`. Enforced by
26059
+ * `scripts/check-runtime-state-durability.ts`.
26060
+ */
26061
+ durability: "restored",
26062
+ /** Clock fields: written, but excluded from the compare that decides
26063
+ * whether persisting is worth a SQLite commit. */
26064
+ volatileStateFields: ["lastFetchedAt"]
25585
26065
  };
25586
26066
  /**
25587
26067
  * Collapse a camera's PER-PROFILE audio flags into the one answer
@@ -25848,7 +26328,14 @@ var ptzAutotrackCapability = {
25848
26328
  * fetch / cache / fallback logic out of the four cap methods —
25849
26329
  * they become trampolines over `runtimeState`.
25850
26330
  */
25851
- runtimeState: PtzAutotrackRuntimeStateSchema
26331
+ runtimeState: PtzAutotrackRuntimeStateSchema,
26332
+ /**
26333
+ * Runtime-state durability: **session** — mirrors the camera's own autotrack config, re-read on connect.
26334
+ *
26335
+ * See `RuntimeStateDurability`. Enforced by
26336
+ * `scripts/check-runtime-state-durability.ts`.
26337
+ */
26338
+ durability: "session"
25852
26339
  };
25853
26340
  //#endregion
25854
26341
  //#region src/capabilities/reboot.cap.ts
@@ -26324,13 +26811,24 @@ var recordingCapability = {
26324
26811
  /** Playback-speed multiplier for the render (1 = realtime). */
26325
26812
  var ExportSpeedSchema = zod.z.number().min(.25).max(32);
26326
26813
  /**
26327
- * One dense interval, in SECONDS FROM THE EXPORT'S OWN `fromMs`.
26814
+ * One dense interval, in WALL-CLOCK SECONDS FROM THE EXPORT'S OWN `fromMs`.
26328
26815
  *
26329
- * Relative and not absolute epoch on purpose: the renderer's frame-select
26330
- * expression sees ffmpeg's `t`, which starts at 0 for the export's source
26331
- * playlist. Handing it absolute epochs would make every call site responsible
26332
- * for the same subtraction, and the one that forgot would emit a filter that
26333
- * selects nothing silently, as a uniform timelapse.
26816
+ * **Wall clock, not ffmpeg's `t`** and the recorder translates. A caller
26817
+ * derives these bounds from things that happened at a TIME (a track's
26818
+ * `firstSeen`), while `t` runs over the source playlist: the concatenation of
26819
+ * every segment present for the range, with each recording GAP removed. The
26820
+ * two agree only on a window that recorded without one interruption, and only
26821
+ * the render side knows the segments, so the translation lives there
26822
+ * (`export-dense-map.ts`, addon-pipeline).
26823
+ *
26824
+ * It was not always so. These seconds were fed to `between(t,…)` verbatim, and
26825
+ * on a 10 h window holding 29,393 s of footage every range landed late by the
26826
+ * gap accumulated before it — up to 6,607 s, well past EOF. Nothing matched,
26827
+ * the video was a uniform timelapse, and the log line reported the five ranges
26828
+ * that had been ASKED for (2026-08-13, export `57d14363`, camera 615).
26829
+ *
26830
+ * Relative and not absolute epoch, because an absolute epoch would make every
26831
+ * call site responsible for the same subtraction.
26334
26832
  */
26335
26833
  var ExportDenseRangeSchema = zod.z.object({
26336
26834
  fromSec: zod.z.number().nonnegative(),
@@ -26682,7 +27180,14 @@ var sceneMonitorCapability = {
26682
27180
  schema: SceneMonitorStatusSchema,
26683
27181
  kind: "push"
26684
27182
  },
26685
- runtimeState: SceneMonitorStatusSchema
27183
+ runtimeState: SceneMonitorStatusSchema,
27184
+ /**
27185
+ * Runtime-state durability: **session** — re-derived from the current scene on the next evaluation.
27186
+ *
27187
+ * See `RuntimeStateDurability`. Enforced by
27188
+ * `scripts/check-runtime-state-durability.ts`.
27189
+ */
27190
+ durability: "session"
26686
27191
  };
26687
27192
  //#endregion
26688
27193
  //#region src/capabilities/schemas/zone-rule.ts
@@ -26834,7 +27339,14 @@ var scriptRunnerCapability = {
26834
27339
  * `isRunning` to render a spinner during execution and surfaces
26835
27340
  * `lastError` / `lastRunSuccess` in the recent-runs panel.
26836
27341
  */
26837
- runtimeState: ScriptRunnerStatusSchema
27342
+ runtimeState: ScriptRunnerStatusSchema,
27343
+ /**
27344
+ * Runtime-state durability: **session** — a restored `isRunning: true` describes a process that died with the previous hub.
27345
+ *
27346
+ * See `RuntimeStateDurability`. Enforced by
27347
+ * `scripts/check-runtime-state-durability.ts`.
27348
+ */
27349
+ durability: "session"
26838
27350
  };
26839
27351
  //#endregion
26840
27352
  //#region src/capabilities/smoke.cap.ts
@@ -26863,7 +27375,17 @@ var smokeCapability = {
26863
27375
  schema: SmokeStatusSchema,
26864
27376
  kind: "push"
26865
27377
  },
26866
- runtimeState: SmokeStatusSchema
27378
+ runtimeState: SmokeStatusSchema,
27379
+ /**
27380
+ * Runtime-state durability: **restored** — a safety sensor must not read "clear" merely because the hub restarted.
27381
+ *
27382
+ * See `RuntimeStateDurability`. Enforced by
27383
+ * `scripts/check-runtime-state-durability.ts`.
27384
+ */
27385
+ durability: "restored",
27386
+ /** Clock fields: written, but excluded from the compare that decides
27387
+ * whether persisting is worth a SQLite commit. */
27388
+ volatileStateFields: ["lastChangedAt"]
26867
27389
  };
26868
27390
  //#endregion
26869
27391
  //#region src/capabilities/stream-catalog.cap.ts
@@ -27053,7 +27575,17 @@ var streamParamsCapability = {
27053
27575
  schema: StreamParamsStatusSchema,
27054
27576
  kind: "poll"
27055
27577
  },
27056
- runtimeState: StreamParamsStatusSchema
27578
+ runtimeState: StreamParamsStatusSchema,
27579
+ /**
27580
+ * Runtime-state durability: **restored** — operator-set encoder profile; mutation-driven.
27581
+ *
27582
+ * See `RuntimeStateDurability`. Enforced by
27583
+ * `scripts/check-runtime-state-durability.ts`.
27584
+ */
27585
+ durability: "restored",
27586
+ /** Clock fields: written, but excluded from the compare that decides
27587
+ * whether persisting is worth a SQLite commit. */
27588
+ volatileStateFields: ["lastFetchedAt"]
27057
27589
  };
27058
27590
  //#endregion
27059
27591
  //#region src/capabilities/stream-params-config-schema.ts
@@ -27360,6 +27892,16 @@ var switchCapability = {
27360
27892
  * not need to re-query the provider after a setState mutation.
27361
27893
  */
27362
27894
  runtimeState: SwitchStatusSchema,
27895
+ /**
27896
+ * Runtime-state durability: **restored** — device state an operator reads as authoritative; 55 devices, transition-driven.
27897
+ *
27898
+ * See `RuntimeStateDurability`. Enforced by
27899
+ * `scripts/check-runtime-state-durability.ts`.
27900
+ */
27901
+ durability: "restored",
27902
+ /** Clock fields: written, but excluded from the compare that decides
27903
+ * whether persisting is worth a SQLite commit. */
27904
+ volatileStateFields: ["lastChangedAt"],
27363
27905
  settings: { bindings: [{
27364
27906
  kind: "scalar",
27365
27907
  statusPath: "on",
@@ -27458,7 +28000,17 @@ var tamperCapability = {
27458
28000
  schema: TamperStatusSchema,
27459
28001
  kind: "push"
27460
28002
  },
27461
- runtimeState: TamperStatusSchema
28003
+ runtimeState: TamperStatusSchema,
28004
+ /**
28005
+ * Runtime-state durability: **restored** — as `smoke`.
28006
+ *
28007
+ * See `RuntimeStateDurability`. Enforced by
28008
+ * `scripts/check-runtime-state-durability.ts`.
28009
+ */
28010
+ durability: "restored",
28011
+ /** Clock fields: written, but excluded from the compare that decides
28012
+ * whether persisting is worth a SQLite commit. */
28013
+ volatileStateFields: ["lastChangedAt"]
27462
28014
  };
27463
28015
  //#endregion
27464
28016
  //#region src/capabilities/temperature-sensor.cap.ts
@@ -27505,7 +28057,17 @@ var temperatureSensorCapability = {
27505
28057
  schema: TemperatureSensorStatusSchema,
27506
28058
  kind: "push"
27507
28059
  },
27508
- runtimeState: TemperatureSensorStatusSchema
28060
+ runtimeState: TemperatureSensorStatusSchema,
28061
+ /**
28062
+ * Runtime-state durability: **restored** — as `numeric-sensor` (69 of 125 writes were the clock alone).
28063
+ *
28064
+ * See `RuntimeStateDurability`. Enforced by
28065
+ * `scripts/check-runtime-state-durability.ts`.
28066
+ */
28067
+ durability: "restored",
28068
+ /** Clock fields: written, but excluded from the compare that decides
28069
+ * whether persisting is worth a SQLite commit. */
28070
+ volatileStateFields: ["lastFetchedAt"]
27509
28071
  };
27510
28072
  //#endregion
27511
28073
  //#region src/capabilities/toast.cap.ts
@@ -27585,7 +28147,14 @@ var updateCapability = {
27585
28147
  schema: UpdateStatusSchema,
27586
28148
  kind: "poll"
27587
28149
  },
27588
- runtimeState: UpdateStatusSchema
28150
+ runtimeState: UpdateStatusSchema,
28151
+ /**
28152
+ * Runtime-state durability: **session** — a restored `inProgress: true` describes an update that is no longer running; versions are re-probed at boot.
28153
+ *
28154
+ * See `RuntimeStateDurability`. Enforced by
28155
+ * `scripts/check-runtime-state-durability.ts`.
28156
+ */
28157
+ durability: "session"
27589
28158
  };
27590
28159
  //#endregion
27591
28160
  //#region src/capabilities/user-management.cap.ts
@@ -27965,7 +28534,14 @@ var vacuumControlCapability = {
27965
28534
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
27966
28535
  * slice for live state + battery + fan-speed changes.
27967
28536
  */
27968
- runtimeState: VacuumControlStatusSchema
28537
+ runtimeState: VacuumControlStatusSchema,
28538
+ /**
28539
+ * Runtime-state durability: **session** — as `media-player` — a restored `state: cleaning` is a robot that is not cleaning.
28540
+ *
28541
+ * See `RuntimeStateDurability`. Enforced by
28542
+ * `scripts/check-runtime-state-durability.ts`.
28543
+ */
28544
+ durability: "session"
27969
28545
  };
27970
28546
  //#endregion
27971
28547
  //#region src/capabilities/valve.cap.ts
@@ -28037,7 +28613,14 @@ var valveCapability = {
28037
28613
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
28038
28614
  * slice for live position changes during a move.
28039
28615
  */
28040
- runtimeState: ValveStatusSchema
28616
+ runtimeState: ValveStatusSchema,
28617
+ /**
28618
+ * Runtime-state durability: **session** — as `brightness`.
28619
+ *
28620
+ * See `RuntimeStateDurability`. Enforced by
28621
+ * `scripts/check-runtime-state-durability.ts`.
28622
+ */
28623
+ durability: "session"
28041
28624
  };
28042
28625
  //#endregion
28043
28626
  //#region src/capabilities/vibration.cap.ts
@@ -28061,7 +28644,17 @@ var vibrationCapability = {
28061
28644
  schema: VibrationStatusSchema,
28062
28645
  kind: "push"
28063
28646
  },
28064
- runtimeState: VibrationStatusSchema
28647
+ runtimeState: VibrationStatusSchema,
28648
+ /**
28649
+ * Runtime-state durability: **restored** — as `smoke`.
28650
+ *
28651
+ * See `RuntimeStateDurability`. Enforced by
28652
+ * `scripts/check-runtime-state-durability.ts`.
28653
+ */
28654
+ durability: "restored",
28655
+ /** Clock fields: written, but excluded from the compare that decides
28656
+ * whether persisting is worth a SQLite commit. */
28657
+ volatileStateFields: ["lastChangedAt"]
28065
28658
  };
28066
28659
  //#endregion
28067
28660
  //#region src/capabilities/water-heater.cap.ts
@@ -28137,7 +28730,14 @@ var waterHeaterCapability = {
28137
28730
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
28138
28731
  * slice for live temperature / mode / away changes.
28139
28732
  */
28140
- runtimeState: WaterHeaterStatusSchema
28733
+ runtimeState: WaterHeaterStatusSchema,
28734
+ /**
28735
+ * Runtime-state durability: **session** — as `climate-control`.
28736
+ *
28737
+ * See `RuntimeStateDurability`. Enforced by
28738
+ * `scripts/check-runtime-state-durability.ts`.
28739
+ */
28740
+ durability: "session"
28141
28741
  };
28142
28742
  //#endregion
28143
28743
  //#region src/capabilities/weather.cap.ts
@@ -28198,7 +28798,14 @@ var weatherCapability = {
28198
28798
  * Runtime-state slice — mirrored by the kernel. The UI reads the
28199
28799
  * current conditions directly from the slice on each weather push.
28200
28800
  */
28201
- runtimeState: WeatherStatusSchema
28801
+ runtimeState: WeatherStatusSchema,
28802
+ /**
28803
+ * Runtime-state durability: **session** — a forecast is stale the moment the hub is down; the provider re-fetches on connect.
28804
+ *
28805
+ * See `RuntimeStateDurability`. Enforced by
28806
+ * `scripts/check-runtime-state-durability.ts`.
28807
+ */
28808
+ durability: "session"
28202
28809
  };
28203
28810
  //#endregion
28204
28811
  //#region src/capabilities/zone-analytics.cap.ts
@@ -28362,7 +28969,14 @@ var zoneAnalyticsCapability = {
28362
28969
  * automatically; the explicit `getCurrentSnapshot` cap method is
28363
28970
  * still useful for one-off polls without a subscription.
28364
28971
  */
28365
- runtimeState: CameraOccupancySnapshotSchema
28972
+ runtimeState: CameraOccupancySnapshotSchema,
28973
+ /**
28974
+ * Runtime-state durability: **session** — per-frame analytics; with `audio-metrics` it is ~90 % of the offered write rate. Re-derived on the next frame.
28975
+ *
28976
+ * See `RuntimeStateDurability`. Enforced by
28977
+ * `scripts/check-runtime-state-durability.ts`.
28978
+ */
28979
+ durability: "session"
28366
28980
  };
28367
28981
  //#endregion
28368
28982
  //#region src/capabilities/zone-rules.cap.ts
@@ -28442,7 +29056,14 @@ var zoneRulesCapability = {
28442
29056
  motion: zod.z.array(ZoneRuleSchema).readonly(),
28443
29057
  detection: zod.z.array(ZoneRuleSchema).readonly(),
28444
29058
  package: zod.z.array(ZoneRuleSchema).readonly()
28445
- })
29059
+ }),
29060
+ /**
29061
+ * Runtime-state durability: **restored** — operator intent, mutation-only, same argument as `zones`.
29062
+ *
29063
+ * See `RuntimeStateDurability`. Enforced by
29064
+ * `scripts/check-runtime-state-durability.ts`.
29065
+ */
29066
+ durability: "restored"
28446
29067
  };
28447
29068
  //#endregion
28448
29069
  //#region src/capabilities/index.ts
@@ -38060,6 +38681,320 @@ function getCapsByProviderKind(kind) {
38060
38681
  return out;
38061
38682
  }
38062
38683
  //#endregion
38684
+ //#region src/generated/runtime-state-durability.ts
38685
+ /**
38686
+ * Per-cap runtime-state policy, projected from every `*.cap.ts` that declares
38687
+ * `runtimeState`. 62 caps: 33 `restored`, 29 `session`.
38688
+ *
38689
+ * A cap absent from this map has no runtime-state slice at all. A cap PRESENT
38690
+ * with `durability: 'session'` has one and has decided not to keep it.
38691
+ */
38692
+ var RUNTIME_STATE_POLICY = {
38693
+ "air-quality-sensor": {
38694
+ durability: "restored",
38695
+ volatileFields: ["lastFetchedAt"]
38696
+ },
38697
+ "alarm-panel": {
38698
+ durability: "restored",
38699
+ volatileFields: ["lastChangedAt"]
38700
+ },
38701
+ "ambient-light-sensor": {
38702
+ durability: "restored",
38703
+ volatileFields: ["lastFetchedAt"]
38704
+ },
38705
+ "audio-metrics": {
38706
+ durability: "session",
38707
+ volatileFields: []
38708
+ },
38709
+ "automation-control": {
38710
+ durability: "session",
38711
+ volatileFields: []
38712
+ },
38713
+ "battery": {
38714
+ durability: "restored",
38715
+ volatileFields: ["lastUpdated"]
38716
+ },
38717
+ "binary": {
38718
+ durability: "restored",
38719
+ volatileFields: ["lastChangedAt"]
38720
+ },
38721
+ "brightness": {
38722
+ durability: "session",
38723
+ volatileFields: []
38724
+ },
38725
+ "camera-streams": {
38726
+ durability: "session",
38727
+ volatileFields: []
38728
+ },
38729
+ "carbon-monoxide": {
38730
+ durability: "restored",
38731
+ volatileFields: ["lastChangedAt"]
38732
+ },
38733
+ "climate-control": {
38734
+ durability: "session",
38735
+ volatileFields: []
38736
+ },
38737
+ "color": {
38738
+ durability: "session",
38739
+ volatileFields: []
38740
+ },
38741
+ "connectivity": {
38742
+ durability: "restored",
38743
+ volatileFields: ["lastChangedAt"]
38744
+ },
38745
+ "consumables": {
38746
+ durability: "session",
38747
+ volatileFields: []
38748
+ },
38749
+ "contact": {
38750
+ durability: "restored",
38751
+ volatileFields: ["lastChangedAt"]
38752
+ },
38753
+ "control": {
38754
+ durability: "session",
38755
+ volatileFields: []
38756
+ },
38757
+ "cover": {
38758
+ durability: "restored",
38759
+ volatileFields: ["lastChangedAt"]
38760
+ },
38761
+ "day-night": {
38762
+ durability: "restored",
38763
+ volatileFields: ["lastFetchedAt"]
38764
+ },
38765
+ "device-discovery": {
38766
+ durability: "session",
38767
+ volatileFields: []
38768
+ },
38769
+ "device-status": {
38770
+ durability: "restored",
38771
+ volatileFields: ["lastChangedAt"]
38772
+ },
38773
+ "doorbell": {
38774
+ durability: "restored",
38775
+ volatileFields: []
38776
+ },
38777
+ "enum-sensor": {
38778
+ durability: "restored",
38779
+ volatileFields: ["lastFetchedAt"]
38780
+ },
38781
+ "event-emitter": {
38782
+ durability: "session",
38783
+ volatileFields: []
38784
+ },
38785
+ "fan-control": {
38786
+ durability: "session",
38787
+ volatileFields: []
38788
+ },
38789
+ "feature-probe": {
38790
+ durability: "session",
38791
+ volatileFields: []
38792
+ },
38793
+ "flood": {
38794
+ durability: "restored",
38795
+ volatileFields: ["lastChangedAt"]
38796
+ },
38797
+ "gas": {
38798
+ durability: "restored",
38799
+ volatileFields: ["lastChangedAt"]
38800
+ },
38801
+ "humidifier": {
38802
+ durability: "session",
38803
+ volatileFields: []
38804
+ },
38805
+ "humidity-sensor": {
38806
+ durability: "restored",
38807
+ volatileFields: ["lastFetchedAt"]
38808
+ },
38809
+ "image": {
38810
+ durability: "session",
38811
+ volatileFields: []
38812
+ },
38813
+ "image-settings": {
38814
+ durability: "restored",
38815
+ volatileFields: ["lastFetchedAt"]
38816
+ },
38817
+ "lawn-mower-control": {
38818
+ durability: "session",
38819
+ volatileFields: []
38820
+ },
38821
+ "lock-control": {
38822
+ durability: "restored",
38823
+ volatileFields: ["lastChangedAt"]
38824
+ },
38825
+ "media-player": {
38826
+ durability: "session",
38827
+ volatileFields: []
38828
+ },
38829
+ "motion": {
38830
+ durability: "session",
38831
+ volatileFields: []
38832
+ },
38833
+ "motion-trigger": {
38834
+ durability: "session",
38835
+ volatileFields: []
38836
+ },
38837
+ "motion-zones": {
38838
+ durability: "restored",
38839
+ volatileFields: ["lastFetchedAt"]
38840
+ },
38841
+ "native-object-detection": {
38842
+ durability: "restored",
38843
+ volatileFields: ["lastFetchedAt"]
38844
+ },
38845
+ "notifier": {
38846
+ durability: "session",
38847
+ volatileFields: []
38848
+ },
38849
+ "numeric-sensor": {
38850
+ durability: "restored",
38851
+ volatileFields: ["lastFetchedAt"]
38852
+ },
38853
+ "pet-feeder": {
38854
+ durability: "session",
38855
+ volatileFields: []
38856
+ },
38857
+ "power-meter": {
38858
+ durability: "restored",
38859
+ volatileFields: ["lastFetchedAt"]
38860
+ },
38861
+ "presence": {
38862
+ durability: "restored",
38863
+ volatileFields: ["lastChangedAt"]
38864
+ },
38865
+ "pressure-sensor": {
38866
+ durability: "restored",
38867
+ volatileFields: ["lastFetchedAt"]
38868
+ },
38869
+ "privacy-mask": {
38870
+ durability: "restored",
38871
+ volatileFields: ["lastFetchedAt"]
38872
+ },
38873
+ "ptz-autotrack": {
38874
+ durability: "session",
38875
+ volatileFields: []
38876
+ },
38877
+ "scene-monitor": {
38878
+ durability: "session",
38879
+ volatileFields: []
38880
+ },
38881
+ "script-runner": {
38882
+ durability: "session",
38883
+ volatileFields: []
38884
+ },
38885
+ "smoke": {
38886
+ durability: "restored",
38887
+ volatileFields: ["lastChangedAt"]
38888
+ },
38889
+ "stream-params": {
38890
+ durability: "restored",
38891
+ volatileFields: ["lastFetchedAt"]
38892
+ },
38893
+ "switch": {
38894
+ durability: "restored",
38895
+ volatileFields: ["lastChangedAt"]
38896
+ },
38897
+ "tamper": {
38898
+ durability: "restored",
38899
+ volatileFields: ["lastChangedAt"]
38900
+ },
38901
+ "temperature-sensor": {
38902
+ durability: "restored",
38903
+ volatileFields: ["lastFetchedAt"]
38904
+ },
38905
+ "update": {
38906
+ durability: "session",
38907
+ volatileFields: []
38908
+ },
38909
+ "vacuum-control": {
38910
+ durability: "session",
38911
+ volatileFields: []
38912
+ },
38913
+ "valve": {
38914
+ durability: "session",
38915
+ volatileFields: []
38916
+ },
38917
+ "vibration": {
38918
+ durability: "restored",
38919
+ volatileFields: ["lastChangedAt"]
38920
+ },
38921
+ "water-heater": {
38922
+ durability: "session",
38923
+ volatileFields: []
38924
+ },
38925
+ "weather": {
38926
+ durability: "session",
38927
+ volatileFields: []
38928
+ },
38929
+ "zone-analytics": {
38930
+ durability: "session",
38931
+ volatileFields: []
38932
+ },
38933
+ "zone-rules": {
38934
+ durability: "restored",
38935
+ volatileFields: []
38936
+ },
38937
+ "zones": {
38938
+ durability: "restored",
38939
+ volatileFields: []
38940
+ }
38941
+ };
38942
+ /**
38943
+ * The policy for `capName`. An UNKNOWN cap resolves to `'session'` — an
38944
+ * addon-private slice the codegen has never seen is not persisted until its
38945
+ * capability says so, which is the safe direction: the cost of a lost session
38946
+ * value is a cold window, the cost of an unasked-for persist is a commit per
38947
+ * second on the fleet's busiest write path.
38948
+ */
38949
+ function runtimeStatePolicyFor(capName) {
38950
+ return RUNTIME_STATE_POLICY[capName] ?? SESSION_ONLY_POLICY;
38951
+ }
38952
+ /** Shared fallback instance — see {@link runtimeStatePolicyFor}. */
38953
+ var SESSION_ONLY_POLICY = {
38954
+ durability: "session",
38955
+ volatileFields: []
38956
+ };
38957
+ /** True when this cap's slice is written to disk. */
38958
+ function isRestoredCap(capName) {
38959
+ return runtimeStatePolicyFor(capName).durability === "restored";
38960
+ }
38961
+ /** Cap names whose slice survives a restart, sorted. */
38962
+ var RESTORED_CAP_NAMES = [
38963
+ "air-quality-sensor",
38964
+ "alarm-panel",
38965
+ "ambient-light-sensor",
38966
+ "battery",
38967
+ "binary",
38968
+ "carbon-monoxide",
38969
+ "connectivity",
38970
+ "contact",
38971
+ "cover",
38972
+ "day-night",
38973
+ "device-status",
38974
+ "doorbell",
38975
+ "enum-sensor",
38976
+ "flood",
38977
+ "gas",
38978
+ "humidity-sensor",
38979
+ "image-settings",
38980
+ "lock-control",
38981
+ "motion-zones",
38982
+ "native-object-detection",
38983
+ "numeric-sensor",
38984
+ "power-meter",
38985
+ "presence",
38986
+ "pressure-sensor",
38987
+ "privacy-mask",
38988
+ "smoke",
38989
+ "stream-params",
38990
+ "switch",
38991
+ "tamper",
38992
+ "temperature-sensor",
38993
+ "vibration",
38994
+ "zone-rules",
38995
+ "zones"
38996
+ ];
38997
+ //#endregion
38063
38998
  //#region src/generated/scope-presets.ts
38064
38999
  var SCOPE_PRESETS = [
38065
39000
  {
@@ -38722,6 +39657,177 @@ function createSystemProxy(api) {
38722
39657
  };
38723
39658
  }
38724
39659
  //#endregion
39660
+ //#region src/notification/audio-condition.ts
39661
+ /**
39662
+ * audio-condition — the authoring logic behind the sustained-sound condition
39663
+ * (`NcConditions.audio`), pure and UI-framework-free.
39664
+ *
39665
+ * WHY IT LIVES HERE
39666
+ * ─────────────────
39667
+ * Same reason as `condition-taxonomy.ts` next door: two editors author this
39668
+ * condition (the admin UI and the viewer), the vocabulary it offers is already
39669
+ * in this package (`AUDIO_MACRO_LABELS`), and a second copy of "what a cleared
39670
+ * threshold writes" is a mirror free to drift. The viewer keeps its own copy
39671
+ * because it is a separate app with its own toolchain; every surface that CAN
39672
+ * import `@camstack/types` uses this one.
39673
+ *
39674
+ * Four behaviours here are not cosmetic:
39675
+ *
39676
+ * 1. **dBFS is negative-going.** `0` is full scale; digital silence reads
39677
+ * {@link NC_AUDIO_DBFS_FLOOR}. A control that let an operator author `60`
39678
+ * meaning "loud" would write a threshold no sample can reach, and the rule
39679
+ * would look broken rather than mis-configured. Every write is clamped.
39680
+ * 2. **Neither filter given = the engine REFUSES.** With no level floor and no
39681
+ * labels every sample is trivially a hit, so the window would fire on
39682
+ * silence. {@link audioIsFailClosed} is how an editor says that BEFORE the
39683
+ * save instead of shipping a rule that never notifies and never says why.
39684
+ * 3. **Both label spellings are the same label.** The hub's taxonomy serves
39685
+ * `audio-dog`; the macro catalog and an operator both say `dog`, and the
39686
+ * matcher normalizes the prefix away on both sides. Selection therefore
39687
+ * compares NORMALIZED ids — a rule authored in the admin UI must not read
39688
+ * as "nothing selected" on the phone.
39689
+ * 4. **A stored label the build does not know stays visible.** It is offered
39690
+ * as an explicitly-unknown, removable choice, never silently dropped:
39691
+ * rewriting the rule the operator is looking at is worse than showing a
39692
+ * stale id.
39693
+ */
39694
+ /** The `audio-` namespace the NC taxonomy uses for these same macro ids. */
39695
+ var AUDIO_KIND_PREFIX = "audio-";
39696
+ /** Strip the taxonomy's `audio-` namespace: `audio-dog` and `dog` are one label. */
39697
+ function normalizeAudioLabel(value) {
39698
+ return value.startsWith(AUDIO_KIND_PREFIX) ? value.slice(6) : value;
39699
+ }
39700
+ /** Level bounds, in dBFS. Negative-going: the ceiling is full scale. */
39701
+ var NC_AUDIO_DB_MIN = -96;
39702
+ var NC_AUDIO_DB_MAX = 0;
39703
+ /** Step of the level control — 3 dB is one perceptual notch, not one unit. */
39704
+ var NC_AUDIO_DB_STEP = 3;
39705
+ /**
39706
+ * Offered when the operator turns the level filter ON. NOT a schema default:
39707
+ * an absent `dbThreshold` means "any level" and stays absent until asked for,
39708
+ * and a stepper seeded at the -96 floor would need thirty taps to reach a
39709
+ * threshold that can actually fire.
39710
+ */
39711
+ var NC_AUDIO_DB_OFFERED = -30;
39712
+ var NC_AUDIO_HIT_PERCENT_MIN = 1;
39713
+ var NC_AUDIO_HIT_PERCENT_MAX = 100;
39714
+ var NC_AUDIO_SAMPLING_MIN_SEC = 1;
39715
+ var NC_AUDIO_SAMPLING_MAX_SEC = 300;
39716
+ /** Schema defaults — an untouched sub-field must author exactly these. */
39717
+ var NC_AUDIO_DEFAULTS = {
39718
+ hitPercent: 60,
39719
+ samplingSeconds: 10
39720
+ };
39721
+ /** What an editor SHOWS for an unset condition — without authoring it. */
39722
+ function audioOrDefaults(value) {
39723
+ return value ?? NC_AUDIO_DEFAULTS;
39724
+ }
39725
+ /**
39726
+ * True when the condition names NEITHER a level floor NOR any label. The engine
39727
+ * refuses such a rule (every sample is a hit, so the window fires on silence) —
39728
+ * an editor warns instead of letting the operator ship a rule that never
39729
+ * notifies and says nothing about why.
39730
+ *
39731
+ * An ABSENT condition is not flagged: a rule with no `audio` key is not an
39732
+ * audio rule at all.
39733
+ */
39734
+ function audioIsFailClosed(value) {
39735
+ if (value === void 0) return false;
39736
+ return value.dbThreshold === void 0 && (value.labels === void 0 || value.labels.length === 0);
39737
+ }
39738
+ function clampInt(value, min, max) {
39739
+ if (!Number.isFinite(value)) return min;
39740
+ return Math.min(max, Math.max(min, Math.round(value)));
39741
+ }
39742
+ function has(patch, key) {
39743
+ return Object.prototype.hasOwnProperty.call(patch, key);
39744
+ }
39745
+ /**
39746
+ * Apply a sub-field edit, seeding from the defaults when nothing is stored yet.
39747
+ * The result is always in-bounds for `NcAudioConditionSchema`, and a cleared
39748
+ * filter is OMITTED rather than written as an explicit `undefined` (which
39749
+ * survives a merge patch as a deliberate erase and reads as authored).
39750
+ */
39751
+ function patchAudio(current, patch) {
39752
+ const base = audioOrDefaults(current);
39753
+ const labels = has(patch, "labels") ? patch.labels : base.labels;
39754
+ const dbThreshold = has(patch, "dbThreshold") ? patch.dbThreshold : base.dbThreshold;
39755
+ return {
39756
+ ...labels !== void 0 && labels.length > 0 ? { labels: [...labels] } : {},
39757
+ ...dbThreshold !== void 0 ? { dbThreshold: clampInt(dbThreshold, NC_AUDIO_DB_MIN, 0) } : {},
39758
+ hitPercent: clampInt(patch.hitPercent ?? base.hitPercent, 1, 100),
39759
+ samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300)
39760
+ };
39761
+ }
39762
+ /** The icon glyph for a stored label the catalogue does not know. */
39763
+ var UNKNOWN_LABEL_ICON = "🔈";
39764
+ var ICON_BY_ID = new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
39765
+ /**
39766
+ * The label choices: the hub's `audioKinds` when it serves them (its WORDS, the
39767
+ * local EMOJI, keyed by the normalized id), else the local macro catalog — plus
39768
+ * any stored label neither side knows, marked unknown.
39769
+ *
39770
+ * The hub's list wins on membership because it knows which macros this cluster
39771
+ * actually classifies; the icons stay local because the taxonomy carries none.
39772
+ */
39773
+ function audioLabelChoices(taxonomy, selected) {
39774
+ const fromHub = taxonomy?.audioKinds ?? [];
39775
+ const choices = fromHub.length > 0 ? fromHub.map((entry) => {
39776
+ const id = normalizeAudioLabel(entry.kind);
39777
+ return {
39778
+ value: id,
39779
+ label: entry.label,
39780
+ icon: ICON_BY_ID.get(id) ?? UNKNOWN_LABEL_ICON,
39781
+ unknown: false
39782
+ };
39783
+ }) : AUDIO_MACRO_LABELS.map((macro) => ({
39784
+ value: macro.id,
39785
+ label: macro.name,
39786
+ icon: macro.icon ?? UNKNOWN_LABEL_ICON,
39787
+ unknown: false
39788
+ }));
39789
+ const known = new Set(choices.map((choice) => choice.value));
39790
+ const unknownLabels = [];
39791
+ for (const stored of selected ?? []) {
39792
+ const id = normalizeAudioLabel(stored);
39793
+ if (known.has(id) || unknownLabels.includes(id)) continue;
39794
+ unknownLabels.push(id);
39795
+ choices.push({
39796
+ value: id,
39797
+ label: id,
39798
+ icon: UNKNOWN_LABEL_ICON,
39799
+ unknown: true
39800
+ });
39801
+ }
39802
+ return {
39803
+ choices,
39804
+ unknownLabels
39805
+ };
39806
+ }
39807
+ /**
39808
+ * Is this macro id selected? Compares NORMALIZED ids, so a rule authored with
39809
+ * the taxonomy spelling (`audio-dog`) reads as selected here.
39810
+ */
39811
+ function isAudioLabelSelected(labels, id) {
39812
+ const wanted = normalizeAudioLabel(id);
39813
+ return (labels ?? []).some((stored) => normalizeAudioLabel(stored) === wanted);
39814
+ }
39815
+ /**
39816
+ * Toggle one macro id. Removing drops EVERY stored spelling of it; adding
39817
+ * appends the bare macro id. Returns `undefined` when the last label goes — the
39818
+ * field is optional and absent means "any sound", never an empty array (which
39819
+ * `NcAudioConditionSchema` rejects outright).
39820
+ */
39821
+ function toggleAudioLabel(labels, id) {
39822
+ const wanted = normalizeAudioLabel(id);
39823
+ const current = labels ?? [];
39824
+ if (isAudioLabelSelected(current, wanted)) {
39825
+ const next = current.filter((stored) => normalizeAudioLabel(stored) !== wanted);
39826
+ return next.length > 0 ? next : void 0;
39827
+ }
39828
+ return [...current, wanted];
39829
+ }
39830
+ //#endregion
38725
39831
  //#region src/notification/condition-taxonomy.ts
38726
39832
  /** Condition ids that render the class picker (video macros/subs + audio). */
38727
39833
  var CLASS_CONDITION_IDS = new Set(["classes", "classesExclude"]);
@@ -39603,8 +40709,8 @@ function slideInsideFrame(rect, frameWidth, frameHeight) {
39603
40709
  //#endregion
39604
40710
  //#region src/pipeline/native-lease.ts
39605
40711
  /**
39606
- * THE native-frame **lease** knobs — TTL, RAM budget and demand window for the
39607
- * decode worker's native-resolution frame retention.
40712
+ * THE native-frame **lease** knobs — hold depth, RAM budget, demand window and
40713
+ * subject-tile budget for the decode worker's native-resolution retention.
39608
40714
  *
39609
40715
  * ## Why they live here and not in the addon that reads them
39610
40716
  *
@@ -39620,20 +40726,25 @@ function slideInsideFrame(rect, frameWidth, frameHeight) {
39620
40726
  * The lease is a per-decode-worker RAM window. Its purpose — the late
39621
40727
  * cross-process native crop landing on a full-resolution frame rather than the
39622
40728
  * ≤640 detection fallback — is a property of the PIPELINE, not of a node's
39623
- * hardware: a per-node TTL would mean the same camera produces different crop
40729
+ * hardware: a per-node window would mean the same camera produces different crop
39624
40730
  * quality depending on which node the balancer placed it on, and nobody could
39625
40731
  * tell that from the stored media. Node-level RAM pressure is already handled
39626
40732
  * by the per-session budget ceiling, which is itself one of these knobs.
39627
40733
  *
39628
40734
  * ## What each knob costs
39629
40735
  *
39630
- * A retained frame is a full NATIVE-resolution copy in system RAM. With the
40736
+ * A HELD frame is a full NATIVE-resolution copy in system RAM. With the
39631
40737
  * default pinned-RGB24 lease path (`CAMSTACK_SESSION_PINNED_RGB_CROP`, on):
39632
40738
  * 4K ≈ 24.9 MB/frame, 1080p ≈ 6.2 MB/frame. On the YUV420P path (flag off, and
39633
40739
  * for software-decoded sessions): 4K ≈ 12.4 MB, 1080p ≈ 3.1 MB. Worst-case
39634
- * resident RAM for ONE busy camera frameBytes × deliveredFps × ttlSeconds,
39635
- * clamped by the budget ceiling. See `docs/design/decode-path.md` "Lease
39636
- * admission" for what actually gets admitted.
40740
+ * resident RAM for ONE busy camera is now `frameBytes × holdFrames`, clamped by
40741
+ * the budget ceiling bounded by a COUNT because a held frame is waiting for
40742
+ * one specific event (its own detection result), not for a clock.
40743
+ *
40744
+ * A TILE is one subject at native resolution, JPEG-encoded: ~60-120 KB on 4K,
40745
+ * and nothing at all on a frame that detected nothing. That is the asymmetry
40746
+ * this whole shape exists for — see
40747
+ * `docs/design/2026-08-13-native-lease-two-tier-redesign.md`.
39637
40748
  */
39638
40749
  /**
39639
40750
  * Store identity of the lease knobs in `pipeline-orchestrator`'s GLOBAL
@@ -39641,10 +40752,11 @@ function slideInsideFrame(rect, frameWidth, frameHeight) {
39641
40752
  * the reader can walk every section instead of trusting the section id.
39642
40753
  */
39643
40754
  var NATIVE_LEASE_SECTION_ID = "native-lease";
39644
- var NATIVE_LEASE_TTL_KEY = "nativeLeaseTtlMs";
40755
+ var NATIVE_LEASE_HOLD_KEY = "nativeLeaseHoldFrames";
39645
40756
  var NATIVE_LEASE_BUDGET_KEY = "nativeLeaseBudgetMb";
39646
40757
  var NATIVE_LEASE_ACTIVITY_KEY = "nativeLeaseActivityMs";
39647
40758
  var NATIVE_LEASE_ADMISSION_KEY = "nativeLeaseAdmission";
40759
+ var NATIVE_LEASE_TILE_BUDGET_KEY = "nativeTileBudgetMb";
39648
40760
  /**
39649
40761
  * WHICH delivered frames the decode worker retains a native copy of.
39650
40762
  *
@@ -39671,25 +40783,32 @@ var NativeLeaseAdmissionSchema = zod.z.enum(["all", "inferred"]);
39671
40783
  */
39672
40784
  var NativeLeaseSettingsSchema = zod.z.object({
39673
40785
  /**
39674
- * How long a retained native frame is served before it counts as a miss.
40786
+ * How many delivered frames the worker HOLDS at once, waiting for each one's
40787
+ * detection result.
40788
+ *
40789
+ * This replaced a TTL on 2026-08-13, and the replacement is the whole point:
40790
+ * a time window was never related to the event the pixels were waiting for.
40791
+ * A held frame now lives from delivery until the runner has its `FrameResult`
40792
+ * — at which moment the runner cuts the subject tiles it actually wanted and
40793
+ * releases the frame. The bound exists only so a runner that stops answering
40794
+ * cannot pin RAM: above it the OLDEST held frame is dropped and counted.
39675
40795
  *
39676
- * Must cover the FULL late-crop horizon: detection inference + the
39677
- * cross-process inference-result hop to hub post-analysis + tracking + the
39678
- * tRPC crop round-trip back. Below ~500 ms the busiest cameras' subject crops
39679
- * outrun it and fall back to the ≤640 detection frame; above ~3 s the resident
39680
- * RAM per busy camera grows linearly with no measured hit-rate gain.
40796
+ * Sizing: the steady state is `inferenceLatency × deliveredFps`, measured at
40797
+ * 40-160 ms × ≤25 fps = 1-4 frames. The default leaves headroom for a hiccup
40798
+ * without ever approaching the old resident set (43 frames × 24.9 MB at 4K).
40799
+ * Raising it does not buy hit rate it buys tolerance for a slow runner, and
40800
+ * `holdOverflow` on the metrics line is what says you need it.
39681
40801
  */
39682
- ttlMs: zod.z.number().int().min(250).max(1e4),
40802
+ holdFrames: zod.z.number().int().min(1).max(64),
39683
40803
  /**
39684
40804
  * Hard per-decode-worker RAM ceiling for retained native frames, in MB.
39685
40805
  *
39686
- * Intended as a SAFETY ceiling with the TTL as the effective cap — but check
39687
- * which one is actually binding before reasoning from that. At the shipped
39688
- * 1024 MB and a 2 800 ms TTL, a 4K camera hits the CEILING first (~43 frames
39689
- * at ~24 MB each) and the TTL never gets to expire anything; `leaseMb` /
39690
- * `leaseFrames` on the metrics line say which. When the ceiling binds, a
39691
- * change that admits fewer frames buys retention WINDOW at constant RAM
39692
- * rather than giving RAM back — lower this knob if RAM is what you wanted.
40806
+ * Since 2026-08-13 this is a SAFETY ceiling and nothing else: `holdFrames`
40807
+ * is what decides how much is held, and the ceiling is the number above which
40808
+ * something is wrong. Before that it was the effective cap at 1024 MB with
40809
+ * a 2 800 ms TTL a 4K camera sat pinned at `leaseMb:1020, leaseFrames:43`
40810
+ * with the TTL expiring nothing, which is exactly the confusion the hold
40811
+ * removes. `leaseMb` / `leaseFrames` still say what is resident.
39693
40812
  * `0` DISABLES the lease entirely and falls the worker back to the tiny
39694
40813
  * leak-prone GPU surface ring (~85% crop miss; that is what the lease exists
39695
40814
  * to replace).
@@ -39715,25 +40834,47 @@ var NativeLeaseSettingsSchema = zod.z.object({
39715
40834
  * there is the signal that some caller names frames outside the inference set
39716
40835
  * and that this must go back to `all`.
39717
40836
  */
39718
- admission: NativeLeaseAdmissionSchema
40837
+ admission: NativeLeaseAdmissionSchema,
40838
+ /**
40839
+ * RAM ceiling per decode worker, in MB, for the SUBJECT TILES — the
40840
+ * compressed native crops the worker cuts at the moment a frame's detection
40841
+ * result arrives, and keeps long after the frame itself is freed.
40842
+ *
40843
+ * This is the knob that replaced the old retention window, and it buys about
40844
+ * three orders of magnitude more of it: a tile is one subject at native
40845
+ * resolution, JPEG-encoded (~60-120 KB on a 4K person), against ~24.9 MB for
40846
+ * the frame it was cut from. A frame on which nothing was detected costs
40847
+ * nothing at all, which is the real change — the old lease paid per FRAME and
40848
+ * was interrogated per SUBJECT.
40849
+ *
40850
+ * `0` DISABLES tiles, leaving only the hold window and the ≤640 RAM
40851
+ * fallback — i.e. the pre-2026-08-13 miss profile. Set it there only to
40852
+ * reproduce that.
40853
+ */
40854
+ tileBudgetMb: zod.z.number().int().min(0).max(1024)
39719
40855
  });
39720
40856
  /**
39721
- * The values in force when the operator has set nothing — byte-for-byte the
39722
- * constants the decode worker shipped with as env-var defaults, so making these
39723
- * settings changed no behaviour on the day it landed.
40857
+ * The values in force when the operator has set nothing.
40858
+ *
40859
+ * `budgetMb` stays at 1024 on the day the hold landed, deliberately: it stopped
40860
+ * being the retention window and became the OOM ceiling, and lowering a ceiling
40861
+ * in the same change that redefines it would make a regression and a retune
40862
+ * indistinguishable. Cut it once `tileHits` / `holdOverflow` have been read on
40863
+ * live traffic.
39724
40864
  */
39725
40865
  var DEFAULT_NATIVE_LEASE_SETTINGS = {
39726
- ttlMs: 1200,
40866
+ holdFrames: 8,
39727
40867
  budgetMb: 1024,
39728
40868
  activityMs: 15e3,
40869
+ tileBudgetMb: 64,
39729
40870
  admission: "inferred"
39730
40871
  };
39731
40872
  /** Slider bounds for the operator-facing knobs (orchestrator settings UI). */
39732
- var NATIVE_LEASE_TTL_FIELD = {
39733
- min: 250,
39734
- max: 1e4,
39735
- step: 50,
39736
- default: DEFAULT_NATIVE_LEASE_SETTINGS.ttlMs
40873
+ var NATIVE_LEASE_HOLD_FIELD = {
40874
+ min: 1,
40875
+ max: 64,
40876
+ step: 1,
40877
+ default: DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames
39737
40878
  };
39738
40879
  var NATIVE_LEASE_BUDGET_FIELD = {
39739
40880
  min: 0,
@@ -39747,6 +40888,12 @@ var NATIVE_LEASE_ACTIVITY_FIELD = {
39747
40888
  step: 1e3,
39748
40889
  default: DEFAULT_NATIVE_LEASE_SETTINGS.activityMs
39749
40890
  };
40891
+ var NATIVE_LEASE_TILE_BUDGET_FIELD = {
40892
+ min: 0,
40893
+ max: 1024,
40894
+ step: 16,
40895
+ default: DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb
40896
+ };
39750
40897
  /** Select options for the admission knob (orchestrator settings UI). */
39751
40898
  var NATIVE_LEASE_ADMISSION_FIELD = {
39752
40899
  options: [{
@@ -39766,7 +40913,7 @@ var NATIVE_LEASE_ADMISSION_FIELD = {
39766
40913
  * precedence being true and being a lie. `addon-settings.getGlobalSettings`
39767
40914
  * returns a HYDRATED payload, and `hydrateField` fills an unstored field with
39768
40915
  * the schema's own `default` (verified live on the hub: a cluster that has never
39769
- * opened the form still reports `nativeLeaseTtlMs = 1200`). A reader that took
40916
+ * opened the form still reports `nativeLeaseHoldFrames = 8`). A reader that took
39770
40917
  * that at face value would report all three knobs as "set" on every cluster on
39771
40918
  * the day this shipped, permanently retiring the `CAMSTACK_SESSION_NATIVE_LEASE_*`
39772
40919
  * emergency override that the precedence promises. There is no raw-store read on
@@ -39800,25 +40947,28 @@ function readAdmissionKnob(raw) {
39800
40947
  * {@link readKnob} for why the default counts as unset.
39801
40948
  */
39802
40949
  function readNativeLeaseOverride(config) {
39803
- const ttlMs = readKnob("ttlMs", config[NATIVE_LEASE_TTL_KEY]);
40950
+ const holdFrames = readKnob("holdFrames", config[NATIVE_LEASE_HOLD_KEY]);
39804
40951
  const budgetMb = readKnob("budgetMb", config[NATIVE_LEASE_BUDGET_KEY]);
39805
40952
  const activityMs = readKnob("activityMs", config[NATIVE_LEASE_ACTIVITY_KEY]);
39806
40953
  const admission = readAdmissionKnob(config[NATIVE_LEASE_ADMISSION_KEY]);
40954
+ const tileBudgetMb = readKnob("tileBudgetMb", config[NATIVE_LEASE_TILE_BUDGET_KEY]);
39807
40955
  return {
39808
- ...ttlMs === null ? {} : { ttlMs },
40956
+ ...holdFrames === null ? {} : { holdFrames },
39809
40957
  ...budgetMb === null ? {} : { budgetMb },
39810
40958
  ...activityMs === null ? {} : { activityMs },
39811
- ...admission === null ? {} : { admission }
40959
+ ...admission === null ? {} : { admission },
40960
+ ...tileBudgetMb === null ? {} : { tileBudgetMb }
39812
40961
  };
39813
40962
  }
39814
40963
  function isHydratedField(entry) {
39815
40964
  return typeof entry === "object" && entry !== null && "key" in entry;
39816
40965
  }
39817
40966
  var LEASE_KEYS = [
39818
- NATIVE_LEASE_TTL_KEY,
40967
+ NATIVE_LEASE_HOLD_KEY,
39819
40968
  NATIVE_LEASE_BUDGET_KEY,
39820
40969
  NATIVE_LEASE_ACTIVITY_KEY,
39821
- NATIVE_LEASE_ADMISSION_KEY
40970
+ NATIVE_LEASE_ADMISSION_KEY,
40971
+ NATIVE_LEASE_TILE_BUDGET_KEY
39822
40972
  ];
39823
40973
  /**
39824
40974
  * Extract the operator's lease overrides from an
@@ -41355,6 +42505,7 @@ exports.DEFAULT_EVENT_COLOR = DEFAULT_EVENT_COLOR;
41355
42505
  exports.DEFAULT_FEATURES = DEFAULT_FEATURES;
41356
42506
  exports.DEFAULT_NATIVE_LEASE_SETTINGS = DEFAULT_NATIVE_LEASE_SETTINGS;
41357
42507
  exports.DEFAULT_RETENTION = DEFAULT_RETENTION;
42508
+ exports.DEFAULT_RUNTIME_STATE_DURABILITY = require_sleep.DEFAULT_RUNTIME_STATE_DURABILITY;
41358
42509
  exports.DEFAULT_SCRUB_THUMBNAIL_PRESET = DEFAULT_SCRUB_THUMBNAIL_PRESET;
41359
42510
  exports.DEFAULT_TIMELAPSE_PREVIEW_TEXT = DEFAULT_TIMELAPSE_PREVIEW_TEXT;
41360
42511
  exports.DETAIL_CROP_PADDING_FIELD = DETAIL_CROP_PADDING_FIELD;
@@ -41597,11 +42748,22 @@ exports.NATIVE_LEASE_ADMISSION_FIELD = NATIVE_LEASE_ADMISSION_FIELD;
41597
42748
  exports.NATIVE_LEASE_ADMISSION_KEY = NATIVE_LEASE_ADMISSION_KEY;
41598
42749
  exports.NATIVE_LEASE_BUDGET_FIELD = NATIVE_LEASE_BUDGET_FIELD;
41599
42750
  exports.NATIVE_LEASE_BUDGET_KEY = NATIVE_LEASE_BUDGET_KEY;
42751
+ exports.NATIVE_LEASE_HOLD_FIELD = NATIVE_LEASE_HOLD_FIELD;
42752
+ exports.NATIVE_LEASE_HOLD_KEY = NATIVE_LEASE_HOLD_KEY;
41600
42753
  exports.NATIVE_LEASE_SECTION_ID = NATIVE_LEASE_SECTION_ID;
41601
- exports.NATIVE_LEASE_TTL_FIELD = NATIVE_LEASE_TTL_FIELD;
41602
- exports.NATIVE_LEASE_TTL_KEY = NATIVE_LEASE_TTL_KEY;
42754
+ exports.NATIVE_LEASE_TILE_BUDGET_FIELD = NATIVE_LEASE_TILE_BUDGET_FIELD;
42755
+ exports.NATIVE_LEASE_TILE_BUDGET_KEY = NATIVE_LEASE_TILE_BUDGET_KEY;
41603
42756
  exports.NC_ALARM_SYSTEM_EVENT_KINDS = NC_ALARM_SYSTEM_EVENT_KINDS;
41604
42757
  exports.NC_AUDIO_DBFS_FLOOR = NC_AUDIO_DBFS_FLOOR;
42758
+ exports.NC_AUDIO_DB_MAX = NC_AUDIO_DB_MAX;
42759
+ exports.NC_AUDIO_DB_MIN = NC_AUDIO_DB_MIN;
42760
+ exports.NC_AUDIO_DB_OFFERED = NC_AUDIO_DB_OFFERED;
42761
+ exports.NC_AUDIO_DB_STEP = NC_AUDIO_DB_STEP;
42762
+ exports.NC_AUDIO_DEFAULTS = NC_AUDIO_DEFAULTS;
42763
+ exports.NC_AUDIO_HIT_PERCENT_MAX = NC_AUDIO_HIT_PERCENT_MAX;
42764
+ exports.NC_AUDIO_HIT_PERCENT_MIN = NC_AUDIO_HIT_PERCENT_MIN;
42765
+ exports.NC_AUDIO_SAMPLING_MAX_SEC = NC_AUDIO_SAMPLING_MAX_SEC;
42766
+ exports.NC_AUDIO_SAMPLING_MIN_SEC = NC_AUDIO_SAMPLING_MIN_SEC;
41605
42767
  exports.NC_AUTHORABLE_SYSTEM_EVENT_KINDS = NC_AUTHORABLE_SYSTEM_EVENT_KINDS;
41606
42768
  exports.NC_BASE_CONDITION_KEYS = NC_BASE_CONDITION_KEYS;
41607
42769
  exports.NC_CONDITION_CATALOG = NC_CONDITION_CATALOG;
@@ -41609,6 +42771,7 @@ exports.NC_CONFIRM_DEFAULT_MAX_IMAGE_PX = NC_CONFIRM_DEFAULT_MAX_IMAGE_PX;
41609
42771
  exports.NC_CONFIRM_DEFAULT_TIMEOUT_MS = NC_CONFIRM_DEFAULT_TIMEOUT_MS;
41610
42772
  exports.NC_CONFIRM_MAX_TIMEOUT_MS = NC_CONFIRM_MAX_TIMEOUT_MS;
41611
42773
  exports.NC_CONFIRM_MIN_TIMEOUT_MS = NC_CONFIRM_MIN_TIMEOUT_MS;
42774
+ exports.NC_DEFAULT_SNOOZE_MINUTES = NC_DEFAULT_SNOOZE_MINUTES;
41612
42775
  exports.NC_HISTORY_LIMIT_DEFAULT = NC_HISTORY_LIMIT_DEFAULT;
41613
42776
  exports.NC_HISTORY_LIMIT_MAX = NC_HISTORY_LIMIT_MAX;
41614
42777
  exports.NC_MAX_PER_TRACK_IMMEDIATE = NC_MAX_PER_TRACK_IMMEDIATE;
@@ -41755,7 +42918,9 @@ exports.REACHABILITY_PROBE_TIMEOUT_MS = REACHABILITY_PROBE_TIMEOUT_MS;
41755
42918
  exports.RECOGNITION_TYPES = RECOGNITION_TYPES;
41756
42919
  exports.RECORDING_EXPORT_MAX_READ_BYTES = RECORDING_EXPORT_MAX_READ_BYTES;
41757
42920
  exports.RESERVED_BINDING_NAMES = RESERVED_BINDING_NAMES;
42921
+ exports.RESTORED_CAP_NAMES = RESTORED_CAP_NAMES;
41758
42922
  exports.RUNTIME_DEFAULTS = RUNTIME_DEFAULTS;
42923
+ exports.RUNTIME_STATE_POLICY = RUNTIME_STATE_POLICY;
41759
42924
  exports.RUNTIME_TO_FORMAT = RUNTIME_TO_FORMAT;
41760
42925
  exports.RawStateResultSchema = require_sleep.RawStateResultSchema;
41761
42926
  exports.ReadGopBytesResultSchema = ReadGopBytesResultSchema;
@@ -42027,7 +43192,10 @@ exports.assertTimelapseCadences = assertTimelapseCadences;
42027
43192
  exports.audioAnalysisCapability = audioAnalysisCapability;
42028
43193
  exports.audioAnalyzerCapability = audioAnalyzerCapability;
42029
43194
  exports.audioCodecCapability = audioCodecCapability;
43195
+ exports.audioIsFailClosed = audioIsFailClosed;
43196
+ exports.audioLabelChoices = audioLabelChoices;
42030
43197
  exports.audioMetricsCapability = audioMetricsCapability;
43198
+ exports.audioOrDefaults = audioOrDefaults;
42031
43199
  exports.audioPlanFromEncodeProfile = require_canonical_hash.audioPlanFromEncodeProfile;
42032
43200
  exports.authProviderCapability = authProviderCapability;
42033
43201
  exports.autoAssignProfiles = autoAssignProfiles;
@@ -42165,6 +43333,7 @@ exports.intercomCapability = intercomCapability;
42165
43333
  exports.invocationFromEncodeProfile = require_canonical_hash.invocationFromEncodeProfile;
42166
43334
  exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
42167
43335
  exports.isArrayOutputSchema = isArrayOutputSchema;
43336
+ exports.isAudioLabelSelected = isAudioLabelSelected;
42168
43337
  exports.isBaseConditionKey = isBaseConditionKey;
42169
43338
  exports.isCollectionArrayMethod = isCollectionArrayMethod;
42170
43339
  exports.isDeployableToAgent = isDeployableToAgent;
@@ -42174,6 +43343,7 @@ exports.isEvent = require_sleep.isEvent;
42174
43343
  exports.isIsolatedBuiltin = isIsolatedBuiltin;
42175
43344
  exports.isNode = isNode;
42176
43345
  exports.isObjectInput = isObjectInput;
43346
+ exports.isRestoredCap = isRestoredCap;
42177
43347
  exports.isSameAddonId = isSameAddonId;
42178
43348
  exports.isScheduleActive = isScheduleActive;
42179
43349
  exports.isSoftwareDecode = require_canonical_hash.isSoftwareDecode;
@@ -42222,6 +43392,7 @@ exports.networkQualityCapability = networkQualityCapability;
42222
43392
  exports.nodePin = require_sleep.nodePin;
42223
43393
  exports.nodesCapability = nodesCapability;
42224
43394
  exports.normalizeAddonInitResult = require_sleep.normalizeAddonInitResult;
43395
+ exports.normalizeAudioLabel = normalizeAudioLabel;
42225
43396
  exports.normalizeUnit = normalizeUnit;
42226
43397
  exports.notificationOutputCapability = notificationOutputCapability;
42227
43398
  exports.notificationRulesCapability = notificationRulesCapability;
@@ -42238,6 +43409,7 @@ exports.parseJsonObject = require_sleep.parseJsonObject;
42238
43409
  exports.parseJsonUnknown = require_sleep.parseJsonUnknown;
42239
43410
  exports.parseProfileBrokerId = require_sleep.parseProfileBrokerId;
42240
43411
  exports.parseStreamParamsFormPatch = parseStreamParamsFormPatch;
43412
+ exports.patchAudio = patchAudio;
42241
43413
  exports.petFeederCapability = petFeederCapability;
42242
43414
  exports.pickAccessoryControl = pickAccessoryControl;
42243
43415
  exports.pickDetailCropConvention = pickDetailCropConvention;
@@ -42290,6 +43462,7 @@ exports.resolveScrubThumbnailGeometry = resolveScrubThumbnailGeometry;
42290
43462
  exports.resolveVariantModelId = resolveVariantModelId;
42291
43463
  exports.runInferenceStep = runInferenceStep;
42292
43464
  exports.runtimeDevices = runtimeDevices;
43465
+ exports.runtimeStatePolicyFor = runtimeStatePolicyFor;
42293
43466
  exports.sceneMonitorCapability = sceneMonitorCapability;
42294
43467
  exports.scopeKey = require_sleep.scopeKey;
42295
43468
  exports.scopesAllowAddon = scopesAllowAddon;
@@ -42336,6 +43509,7 @@ exports.toExpressionValue = toExpressionValue;
42336
43509
  exports.toNodeId = require_sleep.toNodeId;
42337
43510
  exports.toStreamSourceEntry = toStreamSourceEntry;
42338
43511
  exports.toastCapability = toastCapability;
43512
+ exports.toggleAudioLabel = toggleAudioLabel;
42339
43513
  exports.tokenize = tokenize;
42340
43514
  exports.transcodeBody = transcodeBody;
42341
43515
  exports.tryConvertUnit = tryConvertUnit;