@camstack/addon-osd-manager 0.1.6 → 0.1.8

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.
package/dist/index.mjs CHANGED
@@ -11389,7 +11389,14 @@ var cameraStreamsCapability = {
11389
11389
  low: string().optional()
11390
11390
  }),
11391
11391
  lastChangedAt: number()
11392
- })
11392
+ }),
11393
+ /**
11394
+ * 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.
11395
+ *
11396
+ * See `RuntimeStateDurability`. Enforced by
11397
+ * `scripts/check-runtime-state-durability.ts`.
11398
+ */
11399
+ durability: "session"
11393
11400
  };
11394
11401
  /** Where a block runs. The operator chooses — a block driving a device on an
11395
11402
  * agent is the reason placement is not fixed to the hub. */
@@ -12156,6 +12163,13 @@ var deviceDiscoveryCapability = {
12156
12163
  kind: "poll"
12157
12164
  },
12158
12165
  runtimeState: DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number().int().nonnegative() }),
12166
+ /**
12167
+ * Runtime-state durability: **session** — 5.7 KB of scan output on the largest device, fully re-derivable by re-scanning.
12168
+ *
12169
+ * See `RuntimeStateDurability`. Enforced by
12170
+ * `scripts/check-runtime-state-durability.ts`.
12171
+ */
12172
+ durability: "session",
12159
12173
  methods: {
12160
12174
  /**
12161
12175
  * Snapshot of the current `discovered` list. Returns the
@@ -14734,7 +14748,23 @@ var NotificationActionSchema = object({
14734
14748
  * else — see `notification-center/action-token.ts` for what that does and
14735
14749
  * does not buy.
14736
14750
  */
14737
- destructive: boolean().optional()
14751
+ destructive: boolean().optional(),
14752
+ /**
14753
+ * How the tap should REACH the url.
14754
+ *
14755
+ * `navigate` (absent, and every button authored before this field) opens it:
14756
+ * the phone leaves the notification and shows whatever the callback returns.
14757
+ * That is right for a button whose answer the operator wants to read.
14758
+ *
14759
+ * `background` fires it as a POST and stays put. It exists for the buttons
14760
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
14761
+ * an answer to the notification, and being thrown into a browser tab to
14762
+ * confirm it costs more attention than the notification did. A backend that
14763
+ * cannot do a background call renders it as an ordinary link (the adapters
14764
+ * fall back rather than dropping the button), so this is a preference, never
14765
+ * a requirement.
14766
+ */
14767
+ mode: _enum(["navigate", "background"]).optional()
14738
14768
  });
14739
14769
  /**
14740
14770
  * The canonical notification. `body` is the only hard field (Apprise model).
@@ -15600,7 +15630,17 @@ var alarmPanelCapability = {
15600
15630
  * full slice; renders an arm button per `availableModes` entry and
15601
15631
  * a PIN field iff `requiresCode === true`.
15602
15632
  */
15603
- runtimeState: AlarmPanelStatusSchema
15633
+ runtimeState: AlarmPanelStatusSchema,
15634
+ /**
15635
+ * Runtime-state durability: **restored** — armed state is the one thing a panel must not lose across a restart.
15636
+ *
15637
+ * See `RuntimeStateDurability`. Enforced by
15638
+ * `scripts/check-runtime-state-durability.ts`.
15639
+ */
15640
+ durability: "restored",
15641
+ /** Clock fields: written, but excluded from the compare that decides
15642
+ * whether persisting is worth a SQLite commit. */
15643
+ volatileStateFields: ["lastChangedAt"]
15604
15644
  };
15605
15645
  /**
15606
15646
  * Shared geometry vocabulary for on-frame shape caps — privacy-mask,
@@ -15777,6 +15817,9 @@ var NcSystemEventConditionSchema = object({
15777
15817
  nodeIds: array(string().min(1)).min(1).optional(),
15778
15818
  packageNames: array(string().min(1)).min(1).optional()
15779
15819
  });
15820
+ /** Hard ceiling on a window (24h). A snooze that could not expire would be an
15821
+ * outage the operator asked for once and forgot. */
15822
+ var NC_SNOOZE_MAX_MINUTES = 1440;
15780
15823
  /** Weekly schedule — OR of windows; absence on the rule = always active. */
15781
15824
  var NcScheduleSchema = object({
15782
15825
  windows: array(object({
@@ -16153,15 +16196,15 @@ var NcConditionsSchema = object({
16153
16196
  * (an `immediate` rule naming an `audio-*` class, one notification per
16154
16197
  * classified sample) stays exactly as it was for rules that already use it.
16155
16198
  *
16156
- * NOT in {@link NC_CONDITION_CATALOG} yet, and that is the sequencing rule
16157
- * rather than an oversight: the viewer mirrors the descriptor enums BY HAND
16158
- * (`camstack/src/data/notification-center.ts`, guarded by
16159
- * `scripts/check-viewer-condition-mirror.ts`) and its rule editor STRIPS the
16199
+ * In {@link NC_CONDITION_CATALOG} since P2, and the ORDER it got there is the
16200
+ * rule rather than an accident: the viewer mirrors the descriptor enums BY
16201
+ * HAND (`camstack/src/data/notification-center.ts`, guarded by
16202
+ * `scripts/check-viewer-condition-mirror.ts`) and its rule editor strips the
16160
16203
  * condition fields it does not know when a rule is saved from the phone.
16161
16204
  * Publishing an editor for a condition the app cannot round-trip is how an
16162
- * operator loses a rule's conditions by opening it — so the descriptor, the
16163
- * admin widget and the viewer mirror land together (P2 + P3), and only then
16164
- * does an audio rule become authorable.
16205
+ * operator loses a rule's conditions by opening it — so the viewer mirror
16206
+ * (P3, shipped) went FIRST, and the descriptor an editor renders from
16207
+ * follows here.
16165
16208
  */
16166
16209
  audio: NcAudioConditionSchema.optional()
16167
16210
  });
@@ -16397,6 +16440,30 @@ var NcRuleInputSchema = object({
16397
16440
  */
16398
16441
  snoozeAllowGlobal: boolean().optional(),
16399
16442
  /**
16443
+ * The snooze durations THIS rule's notification offers as buttons, in
16444
+ * minutes.
16445
+ *
16446
+ * Three states, and all three are distinct — which is exactly why this is
16447
+ * `.optional()` and never `.default()`. A Zod default does not run on the
16448
+ * addon cap path (three production failures in one day), so a schema default
16449
+ * would collapse the first two:
16450
+ *
16451
+ * | value | meaning |
16452
+ * | --- | --- |
16453
+ * | absent | the operator never said ⇒ {@link NC_DEFAULT_SNOOZE_MINUTES} |
16454
+ * | `[]` | **no snooze buttons on this rule** — the explicit override |
16455
+ * | a list | these choices, de-duplicated and sorted, at most four |
16456
+ *
16457
+ * `.max(4)` because the notifier's own action budget is small (ntfy allows
16458
+ * three buttons in total) and a rule that spent it all on snooze choices
16459
+ * would push its own tap-through actions off the notification.
16460
+ *
16461
+ * An empty list is NOT an alarm exemption: a rule the alarm is about, or
16462
+ * that arms the panel, is exempt automatically and cannot be silenced by a
16463
+ * window from anywhere (D133).
16464
+ */
16465
+ snoozeOptions: array(number().int().min(1).max(NC_SNOOZE_MAX_MINUTES)).max(4).optional(),
16466
+ /**
16400
16467
  * Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
16401
16468
  *
16402
16469
  * This is what makes the rule set the alarm's trigger set without the alarm
@@ -16496,6 +16563,7 @@ var NcConditionDescriptorSchema = object({
16496
16563
  "device",
16497
16564
  "package",
16498
16565
  "occupancy",
16566
+ "audio",
16499
16567
  "system"
16500
16568
  ]),
16501
16569
  label: string(),
@@ -16514,6 +16582,7 @@ var NcConditionDescriptorSchema = object({
16514
16582
  "crossingSelect",
16515
16583
  "polygonDraw",
16516
16584
  "occupancy",
16585
+ "audio",
16517
16586
  "deviceState",
16518
16587
  "systemEvent"
16519
16588
  ]),
@@ -16911,6 +16980,16 @@ var NC_CONDITION_CATALOG = [
16911
16980
  phase: "P1",
16912
16981
  description: "ZoneAnalytics occupancy edge (optionally zone/class-scoped): count crosses the threshold and holds for sustainSeconds. Fail-closed on a missing snapshot."
16913
16982
  },
16983
+ {
16984
+ id: "audio",
16985
+ group: "audio",
16986
+ label: "Sound",
16987
+ valueType: "audio",
16988
+ operator: "anyOf",
16989
+ appliesTo: ["immediate"],
16990
+ phase: "P2",
16991
+ 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)."
16992
+ },
16914
16993
  {
16915
16994
  id: "customZones",
16916
16995
  group: "zones",
@@ -17055,7 +17134,20 @@ var NcSnoozeInputSchema = object({
17055
17134
  ruleId: string().optional(),
17056
17135
  /** Required when `scope: 'device'`. */
17057
17136
  deviceId: number().int().optional(),
17058
- durationMinutes: number().int().min(1).max(1440),
17137
+ /**
17138
+ * Narrow the window to these subject classes — "the cat, not the person".
17139
+ *
17140
+ * ORTHOGONAL to `scope`, deliberately, and absent means EVERY class: that is
17141
+ * what every window authored before this field meant, so no persisted row
17142
+ * changes meaning and no client has to learn anything to keep working.
17143
+ *
17144
+ * It is what makes the window's real key `(deviceId, classes[])` and lets it
17145
+ * cross rules (D133): the operator points at a camera and a kind of thing,
17146
+ * not at whichever of their four rules happened to produce the notification
17147
+ * they are dismissing.
17148
+ */
17149
+ classes: array(string().min(1)).min(1).optional(),
17150
+ durationMinutes: number().int().min(1).max(NC_SNOOZE_MAX_MINUTES),
17059
17151
  /**
17060
17152
  * Silence this for EVERY recipient, not just the caller. Permission is
17061
17153
  * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
@@ -17080,6 +17172,10 @@ var NcSnoozeSchema = object({
17080
17172
  scope: NcSnoozeScopeSchema,
17081
17173
  ruleId: string().optional(),
17082
17174
  deviceId: number().int().optional(),
17175
+ /** Subject classes this window covers. ABSENT = every class — see
17176
+ * {@link NcSnoozeInputSchema.shape.classes}. Lives in the JSON blob and has
17177
+ * no SQLite column: nothing queries a window by class. */
17178
+ classes: array(string().min(1)).min(1).optional(),
17083
17179
  startedAt: number(),
17084
17180
  /** Exclusive: at exactly this instant the snooze is over. Expiry is a
17085
17181
  * COMPARISON, not a job — no sweeper can leave the operator silenced. */
@@ -19833,7 +19929,14 @@ var zonesCapability = {
19833
19929
  * handle. Slice shape is `{ zones: Zone[] }` so future extensions
19834
19930
  * (e.g. zone groupings) can sit alongside the polygon list.
19835
19931
  */
19836
- runtimeState: object({ zones: array(ZoneSchema).readonly() })
19932
+ runtimeState: object({ zones: array(ZoneSchema).readonly() }),
19933
+ /**
19934
+ * 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.
19935
+ *
19936
+ * See `RuntimeStateDurability`. Enforced by
19937
+ * `scripts/check-runtime-state-durability.ts`.
19938
+ */
19939
+ durability: "restored"
19837
19940
  };
19838
19941
  /**
19839
19942
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
@@ -23775,7 +23878,17 @@ var airQualitySensorCapability = {
23775
23878
  schema: AirQualitySensorStatusSchema,
23776
23879
  kind: "push"
23777
23880
  },
23778
- runtimeState: AirQualitySensorStatusSchema
23881
+ runtimeState: AirQualitySensorStatusSchema,
23882
+ /**
23883
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
23884
+ *
23885
+ * See `RuntimeStateDurability`. Enforced by
23886
+ * `scripts/check-runtime-state-durability.ts`.
23887
+ */
23888
+ durability: "restored",
23889
+ /** Clock fields: written, but excluded from the compare that decides
23890
+ * whether persisting is worth a SQLite commit. */
23891
+ volatileStateFields: ["lastFetchedAt"]
23779
23892
  };
23780
23893
  /**
23781
23894
  * Ambient illuminance reading in lux. Drives Home Assistant `sensor`
@@ -23807,7 +23920,17 @@ var ambientLightSensorCapability = {
23807
23920
  schema: AmbientLightSensorStatusSchema,
23808
23921
  kind: "push"
23809
23922
  },
23810
- runtimeState: AmbientLightSensorStatusSchema
23923
+ runtimeState: AmbientLightSensorStatusSchema,
23924
+ /**
23925
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
23926
+ *
23927
+ * See `RuntimeStateDurability`. Enforced by
23928
+ * `scripts/check-runtime-state-durability.ts`.
23929
+ */
23930
+ durability: "restored",
23931
+ /** Clock fields: written, but excluded from the compare that decides
23932
+ * whether persisting is worth a SQLite commit. */
23933
+ volatileStateFields: ["lastFetchedAt"]
23811
23934
  };
23812
23935
  /**
23813
23936
  * Per-class audio metrics aggregated over a sliding window.
@@ -23925,7 +24048,14 @@ var audioMetricsCapability = {
23925
24048
  }), AudioMetricsHistorySchema)
23926
24049
  },
23927
24050
  /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
23928
- runtimeState: AudioMetricsSnapshotSchema
24051
+ runtimeState: AudioMetricsSnapshotSchema,
24052
+ /**
24053
+ * 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.
24054
+ *
24055
+ * See `RuntimeStateDurability`. Enforced by
24056
+ * `scripts/check-runtime-state-durability.ts`.
24057
+ */
24058
+ durability: "session"
23929
24059
  };
23930
24060
  /**
23931
24061
  * Automation-control cap. Models HA `automation.*` entities on
@@ -23987,7 +24117,14 @@ var automationControlCapability = {
23987
24117
  * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
23988
24118
  * (badge) directly.
23989
24119
  */
23990
- runtimeState: AutomationControlStatusSchema
24120
+ runtimeState: AutomationControlStatusSchema,
24121
+ /**
24122
+ * 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.
24123
+ *
24124
+ * See `RuntimeStateDurability`. Enforced by
24125
+ * `scripts/check-runtime-state-durability.ts`.
24126
+ */
24127
+ durability: "session"
23991
24128
  };
23992
24129
  /**
23993
24130
  * Battery status snapshot. Emitted by providers whose device is
@@ -24093,7 +24230,17 @@ onStatusChanged: { data: object({
24093
24230
  * via `device.runtimeState.getCapState('battery')` regardless of
24094
24231
  * the underlying driver.
24095
24232
  */
24096
- runtimeState: BatteryStatusSchema
24233
+ runtimeState: BatteryStatusSchema,
24234
+ /**
24235
+ * 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.
24236
+ *
24237
+ * See `RuntimeStateDurability`. Enforced by
24238
+ * `scripts/check-runtime-state-durability.ts`.
24239
+ */
24240
+ durability: "restored",
24241
+ /** Clock fields: written, but excluded from the compare that decides
24242
+ * whether persisting is worth a SQLite commit. */
24243
+ volatileStateFields: ["lastUpdated"]
24097
24244
  };
24098
24245
  /**
24099
24246
  * Generic boolean sensor — last-resort fallback when no domain-
@@ -24122,7 +24269,17 @@ var binaryCapability = {
24122
24269
  schema: BinaryStatusSchema,
24123
24270
  kind: "push"
24124
24271
  },
24125
- runtimeState: BinaryStatusSchema
24272
+ runtimeState: BinaryStatusSchema,
24273
+ /**
24274
+ * Runtime-state durability: **restored** — transition-driven sensor state; the restored value gives the boot comparison.
24275
+ *
24276
+ * See `RuntimeStateDurability`. Enforced by
24277
+ * `scripts/check-runtime-state-durability.ts`.
24278
+ */
24279
+ durability: "restored",
24280
+ /** Clock fields: written, but excluded from the compare that decides
24281
+ * whether persisting is worth a SQLite commit. */
24282
+ volatileStateFields: ["lastChangedAt"]
24126
24283
  };
24127
24284
  /**
24128
24285
  * Dimmable-light brightness control. Co-exists with `switch` on the
@@ -24175,7 +24332,14 @@ onBrightnessChanged: { data: object({
24175
24332
  * by the kernel. Read via `device.state.brightness.value` so UI
24176
24333
  * sliders surface the current level without polling the provider.
24177
24334
  */
24178
- runtimeState: BrightnessStatusSchema
24335
+ runtimeState: BrightnessStatusSchema,
24336
+ /**
24337
+ * Runtime-state durability: **session** — live lamp state, re-published by the provider on connect.
24338
+ *
24339
+ * See `RuntimeStateDurability`. Enforced by
24340
+ * `scripts/check-runtime-state-durability.ts`.
24341
+ */
24342
+ durability: "session"
24179
24343
  };
24180
24344
  /**
24181
24345
  * button — device-scoped capability for HA `button.*` / `input_button.*`
@@ -24304,7 +24468,17 @@ var carbonMonoxideCapability = {
24304
24468
  schema: CarbonMonoxideStatusSchema,
24305
24469
  kind: "push"
24306
24470
  },
24307
- runtimeState: CarbonMonoxideStatusSchema
24471
+ runtimeState: CarbonMonoxideStatusSchema,
24472
+ /**
24473
+ * Runtime-state durability: **restored** — as `smoke`.
24474
+ *
24475
+ * See `RuntimeStateDurability`. Enforced by
24476
+ * `scripts/check-runtime-state-durability.ts`.
24477
+ */
24478
+ durability: "restored",
24479
+ /** Clock fields: written, but excluded from the compare that decides
24480
+ * whether persisting is worth a SQLite commit. */
24481
+ volatileStateFields: ["lastChangedAt"]
24308
24482
  };
24309
24483
  /**
24310
24484
  * HVAC / climate control cap. Models the full surface of a HA
@@ -24464,7 +24638,14 @@ var climateControlCapability = {
24464
24638
  * the full slice via `device.state.climate-control.value` and refresh
24465
24639
  * on every push without re-querying the provider.
24466
24640
  */
24467
- runtimeState: ClimateControlStatusSchema
24641
+ runtimeState: ClimateControlStatusSchema,
24642
+ /**
24643
+ * Runtime-state durability: **session** — as `brightness`; `currentTemp` moves continuously and is re-published on connect.
24644
+ *
24645
+ * See `RuntimeStateDurability`. Enforced by
24646
+ * `scripts/check-runtime-state-durability.ts`.
24647
+ */
24648
+ durability: "session"
24468
24649
  };
24469
24650
  /**
24470
24651
  * Color-light cap. Coexists with `switch` (on/off) and `brightness`
@@ -24574,7 +24755,14 @@ onColorChanged: { data: object({
24574
24755
  * kernel. Read via `device.state.color.value` so UI pickers surface
24575
24756
  * the current chromaticity without polling the provider.
24576
24757
  */
24577
- runtimeState: ColorStatusSchema
24758
+ runtimeState: ColorStatusSchema,
24759
+ /**
24760
+ * Runtime-state durability: **session** — as `brightness`.
24761
+ *
24762
+ * See `RuntimeStateDurability`. Enforced by
24763
+ * `scripts/check-runtime-state-durability.ts`.
24764
+ */
24765
+ durability: "session"
24578
24766
  };
24579
24767
  var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
24580
24768
  object({
@@ -24640,7 +24828,17 @@ var connectivityCapability = {
24640
24828
  schema: ConnectivityStatusSchema,
24641
24829
  kind: "push"
24642
24830
  },
24643
- runtimeState: ConnectivityStatusSchema
24831
+ runtimeState: ConnectivityStatusSchema,
24832
+ /**
24833
+ * Runtime-state durability: **restored** — same shape and same argument as `device-status`, for links rather than devices.
24834
+ *
24835
+ * See `RuntimeStateDurability`. Enforced by
24836
+ * `scripts/check-runtime-state-durability.ts`.
24837
+ */
24838
+ durability: "restored",
24839
+ /** Clock fields: written, but excluded from the compare that decides
24840
+ * whether persisting is worth a SQLite commit. */
24841
+ volatileStateFields: ["lastChangedAt"]
24644
24842
  };
24645
24843
  /**
24646
24844
  * Generic device-consumables capability — surfaces a device's
@@ -24722,7 +24920,14 @@ reset: method(object({
24722
24920
  }
24723
24921
  }
24724
24922
  },
24725
- runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() })
24923
+ runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() }),
24924
+ /**
24925
+ * Runtime-state durability: **session** — the authority is the appliance; the provider re-reads the whole item array on connect.
24926
+ *
24927
+ * See `RuntimeStateDurability`. Enforced by
24928
+ * `scripts/check-runtime-state-durability.ts`.
24929
+ */
24930
+ durability: "session"
24726
24931
  };
24727
24932
  /**
24728
24933
  * Door / window / opening / garage / valve contact sensor. Boolean
@@ -24753,7 +24958,17 @@ var contactCapability = {
24753
24958
  schema: ContactStatusSchema,
24754
24959
  kind: "push"
24755
24960
  },
24756
- runtimeState: ContactStatusSchema
24961
+ runtimeState: ContactStatusSchema,
24962
+ /**
24963
+ * Runtime-state durability: **restored** — a door left open across a restart must still read open.
24964
+ *
24965
+ * See `RuntimeStateDurability`. Enforced by
24966
+ * `scripts/check-runtime-state-durability.ts`.
24967
+ */
24968
+ durability: "restored",
24969
+ /** Clock fields: written, but excluded from the compare that decides
24970
+ * whether persisting is worth a SQLite commit. */
24971
+ volatileStateFields: ["lastChangedAt"]
24757
24972
  };
24758
24973
  /**
24759
24974
  * Status slice — flat object (the framework's `runtimeState` contract
@@ -24859,7 +25074,14 @@ var controlCapability = {
24859
25074
  * dropdown / text field / date picker) read the slice's discriminant
24860
25075
  * and value directly without polling the provider.
24861
25076
  */
24862
- runtimeState: ControlStatusSchema
25077
+ runtimeState: ControlStatusSchema,
25078
+ /**
25079
+ * Runtime-state durability: **session** — a generic control mirrors an external entity that re-publishes on connect; the options array is re-derived with it.
25080
+ *
25081
+ * See `RuntimeStateDurability`. Enforced by
25082
+ * `scripts/check-runtime-state-durability.ts`.
25083
+ */
25084
+ durability: "session"
24863
25085
  };
24864
25086
  var CoverStatusSchema = object({
24865
25087
  /** Lifecycle state of the cover. */
@@ -24920,7 +25142,17 @@ var coverCapability = {
24920
25142
  * Runtime-state slice — mirrored by the kernel. UI controls watch
24921
25143
  * the slice for live position changes during a move.
24922
25144
  */
24923
- runtimeState: CoverStatusSchema
25145
+ runtimeState: CoverStatusSchema,
25146
+ /**
25147
+ * Runtime-state durability: **restored** — position survives a restart on the device; the mirror should agree at boot rather than read blank.
25148
+ *
25149
+ * See `RuntimeStateDurability`. Enforced by
25150
+ * `scripts/check-runtime-state-durability.ts`.
25151
+ */
25152
+ durability: "restored",
25153
+ /** Clock fields: written, but excluded from the compare that decides
25154
+ * whether persisting is worth a SQLite commit. */
25155
+ volatileStateFields: ["lastChangedAt"]
24924
25156
  };
24925
25157
  /**
24926
25158
  * Vendor-neutral day/night (IR-cut) control — the per-camera config cap
@@ -25014,7 +25246,17 @@ var dayNightCapability = {
25014
25246
  schema: DayNightStatusSchema,
25015
25247
  kind: "poll"
25016
25248
  },
25017
- runtimeState: DayNightStatusSchema
25249
+ runtimeState: DayNightStatusSchema,
25250
+ /**
25251
+ * Runtime-state durability: **restored** — operator-set IR-cut behaviour; mutation-driven.
25252
+ *
25253
+ * See `RuntimeStateDurability`. Enforced by
25254
+ * `scripts/check-runtime-state-durability.ts`.
25255
+ */
25256
+ durability: "restored",
25257
+ /** Clock fields: written, but excluded from the compare that decides
25258
+ * whether persisting is worth a SQLite commit. */
25259
+ volatileStateFields: ["lastFetchedAt"]
25018
25260
  };
25019
25261
  /**
25020
25262
  * Generic device-level status snapshot. Auto-registered by `BaseDevice`
@@ -25061,7 +25303,17 @@ onStatusChanged: { data: object({
25061
25303
  schema: DeviceStatusSchema,
25062
25304
  kind: "push"
25063
25305
  },
25064
- runtimeState: DeviceStatusSchema
25306
+ runtimeState: DeviceStatusSchema,
25307
+ /**
25308
+ * 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.
25309
+ *
25310
+ * See `RuntimeStateDurability`. Enforced by
25311
+ * `scripts/check-runtime-state-durability.ts`.
25312
+ */
25313
+ durability: "restored",
25314
+ /** Clock fields: written, but excluded from the compare that decides
25315
+ * whether persisting is worth a SQLite commit. */
25316
+ volatileStateFields: ["lastChangedAt"]
25065
25317
  };
25066
25318
  /**
25067
25319
  * Doorbell button cap. Two kinds of providers coexist behind this cap
@@ -25123,7 +25375,14 @@ onPressed: { data: DoorbellPressEventSchema } },
25123
25375
  * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
25124
25376
  * without subscribing.
25125
25377
  */
25126
- runtimeState: DoorbellStatusSchema
25378
+ runtimeState: DoorbellStatusSchema,
25379
+ /**
25380
+ * 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.
25381
+ *
25382
+ * See `RuntimeStateDurability`. Enforced by
25383
+ * `scripts/check-runtime-state-durability.ts`.
25384
+ */
25385
+ durability: "restored"
25127
25386
  };
25128
25387
  /**
25129
25388
  * Enum-state sensor — a string value picked from a finite option set.
@@ -25163,7 +25422,17 @@ var enumSensorCapability = {
25163
25422
  schema: EnumSensorStatusSchema,
25164
25423
  kind: "push"
25165
25424
  },
25166
- runtimeState: EnumSensorStatusSchema
25425
+ runtimeState: EnumSensorStatusSchema,
25426
+ /**
25427
+ * Runtime-state durability: **restored** — as `numeric-sensor`; 80 devices.
25428
+ *
25429
+ * See `RuntimeStateDurability`. Enforced by
25430
+ * `scripts/check-runtime-state-durability.ts`.
25431
+ */
25432
+ durability: "restored",
25433
+ /** Clock fields: written, but excluded from the compare that decides
25434
+ * whether persisting is worth a SQLite commit. */
25435
+ volatileStateFields: ["lastFetchedAt"]
25167
25436
  };
25168
25437
  /**
25169
25438
  * Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
@@ -25199,7 +25468,14 @@ var eventEmitterCapability = {
25199
25468
  schema: EventEmitterStatusSchema,
25200
25469
  kind: "push"
25201
25470
  },
25202
- runtimeState: EventEmitterStatusSchema
25471
+ runtimeState: EventEmitterStatusSchema,
25472
+ /**
25473
+ * Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
25474
+ *
25475
+ * See `RuntimeStateDurability`. Enforced by
25476
+ * `scripts/check-runtime-state-durability.ts`.
25477
+ */
25478
+ durability: "session"
25203
25479
  };
25204
25480
  var EventItemSchema = object({
25205
25481
  id: string(),
@@ -25516,7 +25792,14 @@ var fanControlCapability = {
25516
25792
  * Runtime-state slice — mirrored by the kernel. UI fan speed
25517
25793
  * sliders read `percentage` for live updates.
25518
25794
  */
25519
- runtimeState: FanControlStatusSchema
25795
+ runtimeState: FanControlStatusSchema,
25796
+ /**
25797
+ * Runtime-state durability: **session** — as `brightness`.
25798
+ *
25799
+ * See `RuntimeStateDurability`. Enforced by
25800
+ * `scripts/check-runtime-state-durability.ts`.
25801
+ */
25802
+ durability: "session"
25520
25803
  };
25521
25804
  /**
25522
25805
  * Per-device feature/identity probe slice. Holds the runtime-resolved
@@ -25592,7 +25875,14 @@ onProbeChanged: { data: object({
25592
25875
  schema: FeatureProbeStatusSchema,
25593
25876
  kind: "push"
25594
25877
  },
25595
- runtimeState: FeatureProbeStatusSchema
25878
+ runtimeState: FeatureProbeStatusSchema,
25879
+ /**
25880
+ * 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.
25881
+ *
25882
+ * See `RuntimeStateDurability`. Enforced by
25883
+ * `scripts/check-runtime-state-durability.ts`.
25884
+ */
25885
+ durability: "session"
25596
25886
  };
25597
25887
  /**
25598
25888
  * Water leak / moisture sensor. Boolean "is liquid currently
@@ -25619,7 +25909,17 @@ var floodCapability = {
25619
25909
  schema: FloodStatusSchema,
25620
25910
  kind: "push"
25621
25911
  },
25622
- runtimeState: FloodStatusSchema
25912
+ runtimeState: FloodStatusSchema,
25913
+ /**
25914
+ * Runtime-state durability: **restored** — as `smoke`.
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: ["lastChangedAt"]
25623
25923
  };
25624
25924
  /**
25625
25925
  * Combustible-gas (LPG / methane / hydrogen) alarm sensor. Drives
@@ -25642,7 +25942,17 @@ var gasCapability = {
25642
25942
  schema: GasStatusSchema,
25643
25943
  kind: "push"
25644
25944
  },
25645
- runtimeState: GasStatusSchema
25945
+ runtimeState: GasStatusSchema,
25946
+ /**
25947
+ * Runtime-state durability: **restored** — as `smoke`.
25948
+ *
25949
+ * See `RuntimeStateDurability`. Enforced by
25950
+ * `scripts/check-runtime-state-durability.ts`.
25951
+ */
25952
+ durability: "restored",
25953
+ /** Clock fields: written, but excluded from the compare that decides
25954
+ * whether persisting is worth a SQLite commit. */
25955
+ volatileStateFields: ["lastChangedAt"]
25646
25956
  };
25647
25957
  /**
25648
25958
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
@@ -25718,7 +26028,14 @@ var humidifierCapability = {
25718
26028
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
25719
26029
  * slice for live humidity / mode changes.
25720
26030
  */
25721
- runtimeState: HumidifierStatusSchema
26031
+ runtimeState: HumidifierStatusSchema,
26032
+ /**
26033
+ * Runtime-state durability: **session** — as `climate-control`.
26034
+ *
26035
+ * See `RuntimeStateDurability`. Enforced by
26036
+ * `scripts/check-runtime-state-durability.ts`.
26037
+ */
26038
+ durability: "session"
25722
26039
  };
25723
26040
  /**
25724
26041
  * Single-metric humidity reading. Drives Home Assistant `sensor`
@@ -25754,7 +26071,17 @@ var humiditySensorCapability = {
25754
26071
  schema: HumiditySensorStatusSchema,
25755
26072
  kind: "push"
25756
26073
  },
25757
- runtimeState: HumiditySensorStatusSchema
26074
+ runtimeState: HumiditySensorStatusSchema,
26075
+ /**
26076
+ * Runtime-state durability: **restored** — as `numeric-sensor` (67 of 75 writes were the clock alone).
26077
+ *
26078
+ * See `RuntimeStateDurability`. Enforced by
26079
+ * `scripts/check-runtime-state-durability.ts`.
26080
+ */
26081
+ durability: "restored",
26082
+ /** Clock fields: written, but excluded from the compare that decides
26083
+ * whether persisting is worth a SQLite commit. */
26084
+ volatileStateFields: ["lastFetchedAt"]
25758
26085
  };
25759
26086
  /**
25760
26087
  * Image display cap. Models a single still image exposed by an integration —
@@ -25792,7 +26119,14 @@ var imageCapability = {
25792
26119
  * Runtime-state slice — mirrored by the kernel. The UI reads `url`
25793
26120
  * directly and renders the still image.
25794
26121
  */
25795
- runtimeState: ImageStatusSchema
26122
+ runtimeState: ImageStatusSchema,
26123
+ /**
26124
+ * Runtime-state durability: **session** — a snapshot URL is a session-scoped handle; a restored one points at nothing.
26125
+ *
26126
+ * See `RuntimeStateDurability`. Enforced by
26127
+ * `scripts/check-runtime-state-durability.ts`.
26128
+ */
26129
+ durability: "session"
25796
26130
  };
25797
26131
  /**
25798
26132
  * Vendor-neutral image / picture-adjustment cap — the per-camera config
@@ -25941,7 +26275,17 @@ var imageSettingsCapability = {
25941
26275
  schema: ImageSettingsStatusSchema,
25942
26276
  kind: "poll"
25943
26277
  },
25944
- runtimeState: ImageSettingsStatusSchema
26278
+ runtimeState: ImageSettingsStatusSchema,
26279
+ /**
26280
+ * Runtime-state durability: **restored** — operator-set camera imaging; mutation-driven.
26281
+ *
26282
+ * See `RuntimeStateDurability`. Enforced by
26283
+ * `scripts/check-runtime-state-durability.ts`.
26284
+ */
26285
+ durability: "restored",
26286
+ /** Clock fields: written, but excluded from the compare that decides
26287
+ * whether persisting is worth a SQLite commit. */
26288
+ volatileStateFields: ["lastFetchedAt"]
25945
26289
  };
25946
26290
  /**
25947
26291
  * integrations — system-scoped singleton capability for integration
@@ -26350,7 +26694,14 @@ var lawnMowerControlCapability = {
26350
26694
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
26351
26695
  * slice for live activity + battery changes.
26352
26696
  */
26353
- runtimeState: LawnMowerControlStatusSchema
26697
+ runtimeState: LawnMowerControlStatusSchema,
26698
+ /**
26699
+ * Runtime-state durability: **session** — as `vacuum-control`.
26700
+ *
26701
+ * See `RuntimeStateDurability`. Enforced by
26702
+ * `scripts/check-runtime-state-durability.ts`.
26703
+ */
26704
+ durability: "session"
26354
26705
  };
26355
26706
  /**
26356
26707
  * local-network — hub-only singleton.
@@ -26626,7 +26977,17 @@ var lockControlCapability = {
26626
26977
  * read `state` and disable themselves during `locking`/`unlocking`
26627
26978
  * transitions.
26628
26979
  */
26629
- runtimeState: LockControlStatusSchema
26980
+ runtimeState: LockControlStatusSchema,
26981
+ /**
26982
+ * Runtime-state durability: **restored** — a lock left locked must still read locked.
26983
+ *
26984
+ * See `RuntimeStateDurability`. Enforced by
26985
+ * `scripts/check-runtime-state-durability.ts`.
26986
+ */
26987
+ durability: "restored",
26988
+ /** Clock fields: written, but excluded from the compare that decides
26989
+ * whether persisting is worth a SQLite commit. */
26990
+ volatileStateFields: ["lastChangedAt"]
26630
26991
  };
26631
26992
  /**
26632
26993
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
@@ -26788,7 +27149,14 @@ var mediaPlayerCapability = {
26788
27149
  * full slice for live now-playing, volume, and progress updates
26789
27150
  * without polling.
26790
27151
  */
26791
- runtimeState: MediaPlayerStatusSchema
27152
+ runtimeState: MediaPlayerStatusSchema,
27153
+ /**
27154
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
27155
+ *
27156
+ * See `RuntimeStateDurability`. Enforced by
27157
+ * `scripts/check-runtime-state-durability.ts`.
27158
+ */
27159
+ durability: "session"
26792
27160
  };
26793
27161
  /**
26794
27162
  * mesh-network — collection cap for mesh-VPN providers.
@@ -27113,7 +27481,14 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
27113
27481
  * `device.state.motion.value`. Reads never invoke the provider, so
27114
27482
  * UIs and other addons can poll the cached state safely.
27115
27483
  */
27116
- runtimeState: MotionStatusSchema
27484
+ runtimeState: MotionStatusSchema,
27485
+ /**
27486
+ * 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.
27487
+ *
27488
+ * See `RuntimeStateDurability`. Enforced by
27489
+ * `scripts/check-runtime-state-durability.ts`.
27490
+ */
27491
+ durability: "session"
27117
27492
  };
27118
27493
  /**
27119
27494
  * Motion-trigger toggle for accessory devices.
@@ -27178,7 +27553,14 @@ var motionTriggerCapability = {
27178
27553
  schema: MotionTriggerStatusSchema,
27179
27554
  kind: "command-driven"
27180
27555
  },
27181
- runtimeState: MotionTriggerRuntimeStateSchema
27556
+ runtimeState: MotionTriggerRuntimeStateSchema,
27557
+ /**
27558
+ * 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.
27559
+ *
27560
+ * See `RuntimeStateDurability`. Enforced by
27561
+ * `scripts/check-runtime-state-durability.ts`.
27562
+ */
27563
+ durability: "session"
27182
27564
  };
27183
27565
  /**
27184
27566
  * Motion-zones share the same MaskShape vocabulary as privacy-mask — the
@@ -27245,7 +27627,17 @@ var motionZonesCapability = {
27245
27627
  schema: MotionZoneStatusSchema,
27246
27628
  kind: "poll"
27247
27629
  },
27248
- runtimeState: MotionZoneStatusSchema
27630
+ runtimeState: MotionZoneStatusSchema,
27631
+ /**
27632
+ * 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.
27633
+ *
27634
+ * See `RuntimeStateDurability`. Enforced by
27635
+ * `scripts/check-runtime-state-durability.ts`.
27636
+ */
27637
+ durability: "restored",
27638
+ /** Clock fields: written, but excluded from the compare that decides
27639
+ * whether persisting is worth a SQLite commit. */
27640
+ volatileStateFields: ["lastFetchedAt"]
27249
27641
  };
27250
27642
  /**
27251
27643
  * On-camera AI object detection cap. Surfaces per-device the classes
@@ -27319,7 +27711,17 @@ var nativeObjectDetectionCapability = {
27319
27711
  schema: NativeObjectDetectionStatusSchema,
27320
27712
  kind: "push"
27321
27713
  },
27322
- runtimeState: NativeObjectDetectionRuntimeStateSchema
27714
+ runtimeState: NativeObjectDetectionRuntimeStateSchema,
27715
+ /**
27716
+ * 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.
27717
+ *
27718
+ * See `RuntimeStateDurability`. Enforced by
27719
+ * `scripts/check-runtime-state-durability.ts`.
27720
+ */
27721
+ durability: "restored",
27722
+ /** Clock fields: written, but excluded from the compare that decides
27723
+ * whether persisting is worth a SQLite commit. */
27724
+ volatileStateFields: ["lastFetchedAt"]
27323
27725
  };
27324
27726
  /**
27325
27727
  * network-quality — system-scoped singleton capability tracking RTT,
@@ -27706,7 +28108,14 @@ onSent: { data: object({
27706
28108
  * form reads `supports` to gate optional fields; history pane reads
27707
28109
  * `lastSentAt` / `lastError` / `queueDepth`.
27708
28110
  */
27709
- runtimeState: NotifierStatusSchema
28111
+ runtimeState: NotifierStatusSchema,
28112
+ /**
28113
+ * Runtime-state durability: **session** — live queue depth and last-send state; a restored queue depth describes a queue that no longer exists.
28114
+ *
28115
+ * See `RuntimeStateDurability`. Enforced by
28116
+ * `scripts/check-runtime-state-durability.ts`.
28117
+ */
28118
+ durability: "session"
27710
28119
  };
27711
28120
  /**
27712
28121
  * Generic numeric sensor — last-resort fallback when no typed numeric
@@ -27749,7 +28158,17 @@ var numericSensorCapability = {
27749
28158
  schema: NumericSensorStatusSchema,
27750
28159
  kind: "push"
27751
28160
  },
27752
- runtimeState: NumericSensorStatusSchema
28161
+ runtimeState: NumericSensorStatusSchema,
28162
+ /**
28163
+ * 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.
28164
+ *
28165
+ * See `RuntimeStateDurability`. Enforced by
28166
+ * `scripts/check-runtime-state-durability.ts`.
28167
+ */
28168
+ durability: "restored",
28169
+ /** Clock fields: written, but excluded from the compare that decides
28170
+ * whether persisting is worth a SQLite commit. */
28171
+ volatileStateFields: ["lastFetchedAt"]
27753
28172
  };
27754
28173
  /**
27755
28174
  * Generic on-screen-display (video overlay) cap. Each camera exposes
@@ -28227,7 +28646,14 @@ var petFeederCapability = {
28227
28646
  * the full slice via `device.state.petFeeder.value` and refresh on
28228
28647
  * every poll without re-querying the provider.
28229
28648
  */
28230
- runtimeState: PetFeederStatusSchema
28649
+ runtimeState: PetFeederStatusSchema,
28650
+ /**
28651
+ * Runtime-state durability: **session** — live appliance state re-published on connect.
28652
+ *
28653
+ * See `RuntimeStateDurability`. Enforced by
28654
+ * `scripts/check-runtime-state-durability.ts`.
28655
+ */
28656
+ durability: "session"
28231
28657
  };
28232
28658
  var VehicleSchema = object({
28233
28659
  id: string(),
@@ -28583,7 +29009,17 @@ var powerMeterCapability = {
28583
29009
  schema: PowerMeterStatusSchema,
28584
29010
  kind: "push"
28585
29011
  },
28586
- runtimeState: PowerMeterStatusSchema
29012
+ runtimeState: PowerMeterStatusSchema,
29013
+ /**
29014
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
29015
+ *
29016
+ * See `RuntimeStateDurability`. Enforced by
29017
+ * `scripts/check-runtime-state-durability.ts`.
29018
+ */
29019
+ durability: "restored",
29020
+ /** Clock fields: written, but excluded from the compare that decides
29021
+ * whether persisting is worth a SQLite commit. */
29022
+ volatileStateFields: ["lastFetchedAt"]
28587
29023
  };
28588
29024
  /**
28589
29025
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -28640,7 +29076,17 @@ var presenceCapability = {
28640
29076
  * the map pin is rendered (use `DeviceFeature.PresenceGps` for the
28641
29077
  * pre-fetch fast-path check).
28642
29078
  */
28643
- runtimeState: PresenceStatusSchema
29079
+ runtimeState: PresenceStatusSchema,
29080
+ /**
29081
+ * Runtime-state durability: **restored** — occupancy-relevant: the restored state is what an occupancy rule compares the first post-restart observation against.
29082
+ *
29083
+ * See `RuntimeStateDurability`. Enforced by
29084
+ * `scripts/check-runtime-state-durability.ts`.
29085
+ */
29086
+ durability: "restored",
29087
+ /** Clock fields: written, but excluded from the compare that decides
29088
+ * whether persisting is worth a SQLite commit. */
29089
+ volatileStateFields: ["lastChangedAt"]
28644
29090
  };
28645
29091
  /**
28646
29092
  * Atmospheric pressure reading in hectopascals. Drives Home Assistant
@@ -28676,7 +29122,17 @@ var pressureSensorCapability = {
28676
29122
  schema: PressureSensorStatusSchema,
28677
29123
  kind: "push"
28678
29124
  },
28679
- runtimeState: PressureSensorStatusSchema
29125
+ runtimeState: PressureSensorStatusSchema,
29126
+ /**
29127
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
29128
+ *
29129
+ * See `RuntimeStateDurability`. Enforced by
29130
+ * `scripts/check-runtime-state-durability.ts`.
29131
+ */
29132
+ durability: "restored",
29133
+ /** Clock fields: written, but excluded from the compare that decides
29134
+ * whether persisting is worth a SQLite commit. */
29135
+ volatileStateFields: ["lastFetchedAt"]
28680
29136
  };
28681
29137
  /**
28682
29138
  * PRIVACY — what the camera deliberately does not capture. Two planes:
@@ -28806,7 +29262,17 @@ var privacyMaskCapability = {
28806
29262
  schema: PrivacyMaskStatusSchema,
28807
29263
  kind: "poll"
28808
29264
  },
28809
- runtimeState: PrivacyMaskStatusSchema
29265
+ runtimeState: PrivacyMaskStatusSchema,
29266
+ /**
29267
+ * Runtime-state durability: **restored** — operator-drawn regions, zero real churn — 22 writes in 25 minutes, every one of them the clock.
29268
+ *
29269
+ * See `RuntimeStateDurability`. Enforced by
29270
+ * `scripts/check-runtime-state-durability.ts`.
29271
+ */
29272
+ durability: "restored",
29273
+ /** Clock fields: written, but excluded from the compare that decides
29274
+ * whether persisting is worth a SQLite commit. */
29275
+ volatileStateFields: ["lastFetchedAt"]
28810
29276
  };
28811
29277
  var PtzPresetSchema = object({
28812
29278
  id: string(),
@@ -29014,7 +29480,14 @@ var ptzAutotrackCapability = {
29014
29480
  * fetch / cache / fallback logic out of the four cap methods —
29015
29481
  * they become trampolines over `runtimeState`.
29016
29482
  */
29017
- runtimeState: PtzAutotrackRuntimeStateSchema
29483
+ runtimeState: PtzAutotrackRuntimeStateSchema,
29484
+ /**
29485
+ * Runtime-state durability: **session** — mirrors the camera's own autotrack config, re-read on connect.
29486
+ *
29487
+ * See `RuntimeStateDurability`. Enforced by
29488
+ * `scripts/check-runtime-state-durability.ts`.
29489
+ */
29490
+ durability: "session"
29018
29491
  };
29019
29492
  /**
29020
29493
  * reboot — device-scoped capability for "soft" device reboots (firmware
@@ -29484,13 +29957,24 @@ var recordingCapability = {
29484
29957
  /** Playback-speed multiplier for the render (1 = realtime). */
29485
29958
  var ExportSpeedSchema = number().min(.25).max(32);
29486
29959
  /**
29487
- * One dense interval, in SECONDS FROM THE EXPORT'S OWN `fromMs`.
29960
+ * One dense interval, in WALL-CLOCK SECONDS FROM THE EXPORT'S OWN `fromMs`.
29488
29961
  *
29489
- * Relative and not absolute epoch on purpose: the renderer's frame-select
29490
- * expression sees ffmpeg's `t`, which starts at 0 for the export's source
29491
- * playlist. Handing it absolute epochs would make every call site responsible
29492
- * for the same subtraction, and the one that forgot would emit a filter that
29493
- * selects nothing silently, as a uniform timelapse.
29962
+ * **Wall clock, not ffmpeg's `t`** and the recorder translates. A caller
29963
+ * derives these bounds from things that happened at a TIME (a track's
29964
+ * `firstSeen`), while `t` runs over the source playlist: the concatenation of
29965
+ * every segment present for the range, with each recording GAP removed. The
29966
+ * two agree only on a window that recorded without one interruption, and only
29967
+ * the render side knows the segments, so the translation lives there
29968
+ * (`export-dense-map.ts`, addon-pipeline).
29969
+ *
29970
+ * It was not always so. These seconds were fed to `between(t,…)` verbatim, and
29971
+ * on a 10 h window holding 29,393 s of footage every range landed late by the
29972
+ * gap accumulated before it — up to 6,607 s, well past EOF. Nothing matched,
29973
+ * the video was a uniform timelapse, and the log line reported the five ranges
29974
+ * that had been ASKED for (2026-08-13, export `57d14363`, camera 615).
29975
+ *
29976
+ * Relative and not absolute epoch, because an absolute epoch would make every
29977
+ * call site responsible for the same subtraction.
29494
29978
  */
29495
29979
  var ExportDenseRangeSchema = object({
29496
29980
  fromSec: number().nonnegative(),
@@ -29811,7 +30295,14 @@ var sceneMonitorCapability = {
29811
30295
  schema: SceneMonitorStatusSchema,
29812
30296
  kind: "push"
29813
30297
  },
29814
- runtimeState: SceneMonitorStatusSchema
30298
+ runtimeState: SceneMonitorStatusSchema,
30299
+ /**
30300
+ * Runtime-state durability: **session** — re-derived from the current scene on the next evaluation.
30301
+ *
30302
+ * See `RuntimeStateDurability`. Enforced by
30303
+ * `scripts/check-runtime-state-durability.ts`.
30304
+ */
30305
+ durability: "session"
29815
30306
  };
29816
30307
  /**
29817
30308
  * Per-stage gating mode applied to the zones a rule references.
@@ -29955,7 +30446,14 @@ var scriptRunnerCapability = {
29955
30446
  * `isRunning` to render a spinner during execution and surfaces
29956
30447
  * `lastError` / `lastRunSuccess` in the recent-runs panel.
29957
30448
  */
29958
- runtimeState: ScriptRunnerStatusSchema
30449
+ runtimeState: ScriptRunnerStatusSchema,
30450
+ /**
30451
+ * Runtime-state durability: **session** — a restored `isRunning: true` describes a process that died with the previous hub.
30452
+ *
30453
+ * See `RuntimeStateDurability`. Enforced by
30454
+ * `scripts/check-runtime-state-durability.ts`.
30455
+ */
30456
+ durability: "session"
29959
30457
  };
29960
30458
  /**
29961
30459
  * Smoke alarm sensor — boolean "is smoke currently detected" with
@@ -29982,7 +30480,17 @@ var smokeCapability = {
29982
30480
  schema: SmokeStatusSchema,
29983
30481
  kind: "push"
29984
30482
  },
29985
- runtimeState: SmokeStatusSchema
30483
+ runtimeState: SmokeStatusSchema,
30484
+ /**
30485
+ * Runtime-state durability: **restored** — a safety sensor must not read "clear" merely because the hub restarted.
30486
+ *
30487
+ * See `RuntimeStateDurability`. Enforced by
30488
+ * `scripts/check-runtime-state-durability.ts`.
30489
+ */
30490
+ durability: "restored",
30491
+ /** Clock fields: written, but excluded from the compare that decides
30492
+ * whether persisting is worth a SQLite commit. */
30493
+ volatileStateFields: ["lastChangedAt"]
29986
30494
  };
29987
30495
  /**
29988
30496
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -30168,7 +30676,17 @@ var streamParamsCapability = {
30168
30676
  schema: StreamParamsStatusSchema,
30169
30677
  kind: "poll"
30170
30678
  },
30171
- runtimeState: StreamParamsStatusSchema
30679
+ runtimeState: StreamParamsStatusSchema,
30680
+ /**
30681
+ * Runtime-state durability: **restored** — operator-set encoder profile; mutation-driven.
30682
+ *
30683
+ * See `RuntimeStateDurability`. Enforced by
30684
+ * `scripts/check-runtime-state-durability.ts`.
30685
+ */
30686
+ durability: "restored",
30687
+ /** Clock fields: written, but excluded from the compare that decides
30688
+ * whether persisting is worth a SQLite commit. */
30689
+ volatileStateFields: ["lastFetchedAt"]
30172
30690
  };
30173
30691
  /**
30174
30692
  * Generic on/off switch cap for accessory children (siren, floodlight,
@@ -30214,6 +30732,16 @@ var switchCapability = {
30214
30732
  * not need to re-query the provider after a setState mutation.
30215
30733
  */
30216
30734
  runtimeState: SwitchStatusSchema,
30735
+ /**
30736
+ * Runtime-state durability: **restored** — device state an operator reads as authoritative; 55 devices, transition-driven.
30737
+ *
30738
+ * See `RuntimeStateDurability`. Enforced by
30739
+ * `scripts/check-runtime-state-durability.ts`.
30740
+ */
30741
+ durability: "restored",
30742
+ /** Clock fields: written, but excluded from the compare that decides
30743
+ * whether persisting is worth a SQLite commit. */
30744
+ volatileStateFields: ["lastChangedAt"],
30217
30745
  settings: { bindings: [{
30218
30746
  kind: "scalar",
30219
30747
  statusPath: "on",
@@ -30308,7 +30836,17 @@ var tamperCapability = {
30308
30836
  schema: TamperStatusSchema,
30309
30837
  kind: "push"
30310
30838
  },
30311
- runtimeState: TamperStatusSchema
30839
+ runtimeState: TamperStatusSchema,
30840
+ /**
30841
+ * Runtime-state durability: **restored** — as `smoke`.
30842
+ *
30843
+ * See `RuntimeStateDurability`. Enforced by
30844
+ * `scripts/check-runtime-state-durability.ts`.
30845
+ */
30846
+ durability: "restored",
30847
+ /** Clock fields: written, but excluded from the compare that decides
30848
+ * whether persisting is worth a SQLite commit. */
30849
+ volatileStateFields: ["lastChangedAt"]
30312
30850
  };
30313
30851
  /**
30314
30852
  * Single-metric temperature reading. Drives Home Assistant `sensor`
@@ -30353,7 +30891,17 @@ var temperatureSensorCapability = {
30353
30891
  schema: TemperatureSensorStatusSchema,
30354
30892
  kind: "push"
30355
30893
  },
30356
- runtimeState: TemperatureSensorStatusSchema
30894
+ runtimeState: TemperatureSensorStatusSchema,
30895
+ /**
30896
+ * Runtime-state durability: **restored** — as `numeric-sensor` (69 of 125 writes were the clock alone).
30897
+ *
30898
+ * See `RuntimeStateDurability`. Enforced by
30899
+ * `scripts/check-runtime-state-durability.ts`.
30900
+ */
30901
+ durability: "restored",
30902
+ /** Clock fields: written, but excluded from the compare that decides
30903
+ * whether persisting is worth a SQLite commit. */
30904
+ volatileStateFields: ["lastFetchedAt"]
30357
30905
  };
30358
30906
  /**
30359
30907
  * toast — system-scoped singleton capability that streams toast
@@ -30429,7 +30977,14 @@ var updateCapability = {
30429
30977
  schema: UpdateStatusSchema,
30430
30978
  kind: "poll"
30431
30979
  },
30432
- runtimeState: UpdateStatusSchema
30980
+ runtimeState: UpdateStatusSchema,
30981
+ /**
30982
+ * Runtime-state durability: **session** — a restored `inProgress: true` describes an update that is no longer running; versions are re-probed at boot.
30983
+ *
30984
+ * See `RuntimeStateDurability`. Enforced by
30985
+ * `scripts/check-runtime-state-durability.ts`.
30986
+ */
30987
+ durability: "session"
30433
30988
  };
30434
30989
  var UserSummarySchema = object({
30435
30990
  id: string(),
@@ -30805,7 +31360,14 @@ var vacuumControlCapability = {
30805
31360
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30806
31361
  * slice for live state + battery + fan-speed changes.
30807
31362
  */
30808
- runtimeState: VacuumControlStatusSchema
31363
+ runtimeState: VacuumControlStatusSchema,
31364
+ /**
31365
+ * Runtime-state durability: **session** — as `media-player` — a restored `state: cleaning` is a robot that is not cleaning.
31366
+ *
31367
+ * See `RuntimeStateDurability`. Enforced by
31368
+ * `scripts/check-runtime-state-durability.ts`.
31369
+ */
31370
+ durability: "session"
30809
31371
  };
30810
31372
  var ValveStatusSchema = object({
30811
31373
  /** Lifecycle state of the valve. */
@@ -30857,7 +31419,14 @@ var valveCapability = {
30857
31419
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30858
31420
  * slice for live position changes during a move.
30859
31421
  */
30860
- runtimeState: ValveStatusSchema
31422
+ runtimeState: ValveStatusSchema,
31423
+ /**
31424
+ * Runtime-state durability: **session** — as `brightness`.
31425
+ *
31426
+ * See `RuntimeStateDurability`. Enforced by
31427
+ * `scripts/check-runtime-state-durability.ts`.
31428
+ */
31429
+ durability: "session"
30861
31430
  };
30862
31431
  /**
30863
31432
  * Vibration / shake / impact sensor. Drives Home Assistant
@@ -30879,7 +31448,17 @@ var vibrationCapability = {
30879
31448
  schema: VibrationStatusSchema,
30880
31449
  kind: "push"
30881
31450
  },
30882
- runtimeState: VibrationStatusSchema
31451
+ runtimeState: VibrationStatusSchema,
31452
+ /**
31453
+ * Runtime-state durability: **restored** — as `smoke`.
31454
+ *
31455
+ * See `RuntimeStateDurability`. Enforced by
31456
+ * `scripts/check-runtime-state-durability.ts`.
31457
+ */
31458
+ durability: "restored",
31459
+ /** Clock fields: written, but excluded from the compare that decides
31460
+ * whether persisting is worth a SQLite commit. */
31461
+ volatileStateFields: ["lastChangedAt"]
30883
31462
  };
30884
31463
  /**
30885
31464
  * Water heater / boiler cap. Models HA `water_heater.*` entities — a
@@ -30953,7 +31532,14 @@ var waterHeaterCapability = {
30953
31532
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
30954
31533
  * slice for live temperature / mode / away changes.
30955
31534
  */
30956
- runtimeState: WaterHeaterStatusSchema
31535
+ runtimeState: WaterHeaterStatusSchema,
31536
+ /**
31537
+ * Runtime-state durability: **session** — as `climate-control`.
31538
+ *
31539
+ * See `RuntimeStateDurability`. Enforced by
31540
+ * `scripts/check-runtime-state-durability.ts`.
31541
+ */
31542
+ durability: "session"
30957
31543
  };
30958
31544
  /**
30959
31545
  * Weather provider cap. Models HA `weather.*` entities — a read-only
@@ -31012,7 +31598,14 @@ var weatherCapability = {
31012
31598
  * Runtime-state slice — mirrored by the kernel. The UI reads the
31013
31599
  * current conditions directly from the slice on each weather push.
31014
31600
  */
31015
- runtimeState: WeatherStatusSchema
31601
+ runtimeState: WeatherStatusSchema,
31602
+ /**
31603
+ * Runtime-state durability: **session** — a forecast is stale the moment the hub is down; the provider re-fetches on connect.
31604
+ *
31605
+ * See `RuntimeStateDurability`. Enforced by
31606
+ * `scripts/check-runtime-state-durability.ts`.
31607
+ */
31608
+ durability: "session"
31016
31609
  };
31017
31610
  /**
31018
31611
  * Per-zone occupancy aggregation produced by the analytics frame
@@ -31174,7 +31767,14 @@ var zoneAnalyticsCapability = {
31174
31767
  * automatically; the explicit `getCurrentSnapshot` cap method is
31175
31768
  * still useful for one-off polls without a subscription.
31176
31769
  */
31177
- runtimeState: CameraOccupancySnapshotSchema
31770
+ runtimeState: CameraOccupancySnapshotSchema,
31771
+ /**
31772
+ * Runtime-state durability: **session** — per-frame analytics; with `audio-metrics` it is ~90 % of the offered write rate. Re-derived on the next frame.
31773
+ *
31774
+ * See `RuntimeStateDurability`. Enforced by
31775
+ * `scripts/check-runtime-state-durability.ts`.
31776
+ */
31777
+ durability: "session"
31178
31778
  };
31179
31779
  /**
31180
31780
  * Stages a {@link ZoneRule} can apply to. Discriminator on the rules
@@ -31252,7 +31852,14 @@ var zoneRulesCapability = {
31252
31852
  motion: array(ZoneRuleSchema).readonly(),
31253
31853
  detection: array(ZoneRuleSchema).readonly(),
31254
31854
  package: array(ZoneRuleSchema).readonly()
31255
- })
31855
+ }),
31856
+ /**
31857
+ * Runtime-state durability: **restored** — operator intent, mutation-only, same argument as `zones`.
31858
+ *
31859
+ * See `RuntimeStateDurability`. Enforced by
31860
+ * `scripts/check-runtime-state-durability.ts`.
31861
+ */
31862
+ durability: "restored"
31256
31863
  };
31257
31864
  /**
31258
31865
  * Most specific first. Extending this list is how a new device kind becomes
@@ -37119,6 +37726,7 @@ Object.freeze({
37119
37726
  "network-access": "ingress",
37120
37727
  "smtp-provider": "email"
37121
37728
  });
37729
+ new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37122
37730
  new Set(["devices", "classes"]);
37123
37731
  var WEEKDAY_TO_DAY = {
37124
37732
  Sun: 0,
@@ -37456,25 +38064,32 @@ object({
37456
38064
  var NativeLeaseAdmissionSchema = _enum(["all", "inferred"]);
37457
38065
  object({
37458
38066
  /**
37459
- * How long a retained native frame is served before it counts as a miss.
38067
+ * How many delivered frames the worker HOLDS at once, waiting for each one's
38068
+ * detection result.
37460
38069
  *
37461
- * Must cover the FULL late-crop horizon: detection inference + the
37462
- * cross-process inference-result hop to hub post-analysis + tracking + the
37463
- * tRPC crop round-trip back. Below ~500 ms the busiest cameras' subject crops
37464
- * outrun it and fall back to the ≤640 detection frame; above ~3 s the resident
37465
- * RAM per busy camera grows linearly with no measured hit-rate gain.
38070
+ * This replaced a TTL on 2026-08-13, and the replacement is the whole point:
38071
+ * a time window was never related to the event the pixels were waiting for.
38072
+ * A held frame now lives from delivery until the runner has its `FrameResult`
38073
+ * at which moment the runner cuts the subject tiles it actually wanted and
38074
+ * releases the frame. The bound exists only so a runner that stops answering
38075
+ * cannot pin RAM: above it the OLDEST held frame is dropped and counted.
38076
+ *
38077
+ * Sizing: the steady state is `inferenceLatency × deliveredFps`, measured at
38078
+ * 40-160 ms × ≤25 fps = 1-4 frames. The default leaves headroom for a hiccup
38079
+ * without ever approaching the old resident set (43 frames × 24.9 MB at 4K).
38080
+ * Raising it does not buy hit rate — it buys tolerance for a slow runner, and
38081
+ * `holdOverflow` on the metrics line is what says you need it.
37466
38082
  */
37467
- ttlMs: number().int().min(250).max(1e4),
38083
+ holdFrames: number().int().min(1).max(64),
37468
38084
  /**
37469
38085
  * Hard per-decode-worker RAM ceiling for retained native frames, in MB.
37470
38086
  *
37471
- * Intended as a SAFETY ceiling with the TTL as the effective cap — but check
37472
- * which one is actually binding before reasoning from that. At the shipped
37473
- * 1024 MB and a 2 800 ms TTL, a 4K camera hits the CEILING first (~43 frames
37474
- * at ~24 MB each) and the TTL never gets to expire anything; `leaseMb` /
37475
- * `leaseFrames` on the metrics line say which. When the ceiling binds, a
37476
- * change that admits fewer frames buys retention WINDOW at constant RAM
37477
- * rather than giving RAM back — lower this knob if RAM is what you wanted.
38087
+ * Since 2026-08-13 this is a SAFETY ceiling and nothing else: `holdFrames`
38088
+ * is what decides how much is held, and the ceiling is the number above which
38089
+ * something is wrong. Before that it was the effective cap at 1024 MB with
38090
+ * a 2 800 ms TTL a 4K camera sat pinned at `leaseMb:1020, leaseFrames:43`
38091
+ * with the TTL expiring nothing, which is exactly the confusion the hold
38092
+ * removes. `leaseMb` / `leaseFrames` still say what is resident.
37478
38093
  * `0` DISABLES the lease entirely and falls the worker back to the tiny
37479
38094
  * leak-prone GPU surface ring (~85% crop miss; that is what the lease exists
37480
38095
  * to replace).
@@ -37500,22 +38115,45 @@ object({
37500
38115
  * there is the signal that some caller names frames outside the inference set
37501
38116
  * and that this must go back to `all`.
37502
38117
  */
37503
- admission: NativeLeaseAdmissionSchema
38118
+ admission: NativeLeaseAdmissionSchema,
38119
+ /**
38120
+ * RAM ceiling per decode worker, in MB, for the SUBJECT TILES — the
38121
+ * compressed native crops the worker cuts at the moment a frame's detection
38122
+ * result arrives, and keeps long after the frame itself is freed.
38123
+ *
38124
+ * This is the knob that replaced the old retention window, and it buys about
38125
+ * three orders of magnitude more of it: a tile is one subject at native
38126
+ * resolution, JPEG-encoded (~60-120 KB on a 4K person), against ~24.9 MB for
38127
+ * the frame it was cut from. A frame on which nothing was detected costs
38128
+ * nothing at all, which is the real change — the old lease paid per FRAME and
38129
+ * was interrogated per SUBJECT.
38130
+ *
38131
+ * `0` DISABLES tiles, leaving only the hold window and the ≤640 RAM
38132
+ * fallback — i.e. the pre-2026-08-13 miss profile. Set it there only to
38133
+ * reproduce that.
38134
+ */
38135
+ tileBudgetMb: number().int().min(0).max(1024)
37504
38136
  });
37505
38137
  /**
37506
- * The values in force when the operator has set nothing — byte-for-byte the
37507
- * constants the decode worker shipped with as env-var defaults, so making these
37508
- * settings changed no behaviour on the day it landed.
38138
+ * The values in force when the operator has set nothing.
38139
+ *
38140
+ * `budgetMb` stays at 1024 on the day the hold landed, deliberately: it stopped
38141
+ * being the retention window and became the OOM ceiling, and lowering a ceiling
38142
+ * in the same change that redefines it would make a regression and a retune
38143
+ * indistinguishable. Cut it once `tileHits` / `holdOverflow` have been read on
38144
+ * live traffic.
37509
38145
  */
37510
38146
  var DEFAULT_NATIVE_LEASE_SETTINGS = {
37511
- ttlMs: 1200,
38147
+ holdFrames: 8,
37512
38148
  budgetMb: 1024,
37513
38149
  activityMs: 15e3,
38150
+ tileBudgetMb: 64,
37514
38151
  admission: "inferred"
37515
38152
  };
37516
- DEFAULT_NATIVE_LEASE_SETTINGS.ttlMs;
38153
+ DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
37517
38154
  DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
37518
38155
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
38156
+ DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
37519
38157
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
37520
38158
  //#endregion
37521
38159
  //#region src/bindings-store.ts