@camstack/addon-import-alexa 0.2.13 → 0.2.15

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 (3) hide show
  1. package/dist/addon.js +723 -95
  2. package/dist/addon.mjs +723 -95
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -10997,7 +10997,14 @@ var cameraStreamsCapability = {
10997
10997
  low: string().optional()
10998
10998
  }),
10999
10999
  lastChangedAt: number()
11000
- })
11000
+ }),
11001
+ /**
11002
+ * 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.
11003
+ *
11004
+ * See `RuntimeStateDurability`. Enforced by
11005
+ * `scripts/check-runtime-state-durability.ts`.
11006
+ */
11007
+ durability: "session"
11001
11008
  };
11002
11009
  /** Where a block runs. The operator chooses — a block driving a device on an
11003
11010
  * agent is the reason placement is not fixed to the hub. */
@@ -11558,6 +11565,13 @@ var deviceDiscoveryCapability = {
11558
11565
  kind: "poll"
11559
11566
  },
11560
11567
  runtimeState: DeviceDiscoveryStatusSchema.extend({ lastFetchedAt: number().int().nonnegative() }),
11568
+ /**
11569
+ * Runtime-state durability: **session** — 5.7 KB of scan output on the largest device, fully re-derivable by re-scanning.
11570
+ *
11571
+ * See `RuntimeStateDurability`. Enforced by
11572
+ * `scripts/check-runtime-state-durability.ts`.
11573
+ */
11574
+ durability: "session",
11561
11575
  methods: {
11562
11576
  /**
11563
11577
  * Snapshot of the current `discovered` list. Returns the
@@ -13474,7 +13488,23 @@ var NotificationActionSchema = object({
13474
13488
  * else — see `notification-center/action-token.ts` for what that does and
13475
13489
  * does not buy.
13476
13490
  */
13477
- destructive: boolean().optional()
13491
+ destructive: boolean().optional(),
13492
+ /**
13493
+ * How the tap should REACH the url.
13494
+ *
13495
+ * `navigate` (absent, and every button authored before this field) opens it:
13496
+ * the phone leaves the notification and shows whatever the callback returns.
13497
+ * That is right for a button whose answer the operator wants to read.
13498
+ *
13499
+ * `background` fires it as a POST and stays put. It exists for the buttons
13500
+ * whose whole point is not to interrupt — "silence this for 30 minutes" is
13501
+ * an answer to the notification, and being thrown into a browser tab to
13502
+ * confirm it costs more attention than the notification did. A backend that
13503
+ * cannot do a background call renders it as an ordinary link (the adapters
13504
+ * fall back rather than dropping the button), so this is a preference, never
13505
+ * a requirement.
13506
+ */
13507
+ mode: _enum(["navigate", "background"]).optional()
13478
13508
  });
13479
13509
  /**
13480
13510
  * The canonical notification. `body` is the only hard field (Apprise model).
@@ -14326,7 +14356,17 @@ var alarmPanelCapability = {
14326
14356
  * full slice; renders an arm button per `availableModes` entry and
14327
14357
  * a PIN field iff `requiresCode === true`.
14328
14358
  */
14329
- runtimeState: AlarmPanelStatusSchema
14359
+ runtimeState: AlarmPanelStatusSchema,
14360
+ /**
14361
+ * Runtime-state durability: **restored** — armed state is the one thing a panel must not lose across a restart.
14362
+ *
14363
+ * See `RuntimeStateDurability`. Enforced by
14364
+ * `scripts/check-runtime-state-durability.ts`.
14365
+ */
14366
+ durability: "restored",
14367
+ /** Clock fields: written, but excluded from the compare that decides
14368
+ * whether persisting is worth a SQLite commit. */
14369
+ volatileStateFields: ["lastChangedAt"]
14330
14370
  };
14331
14371
  /**
14332
14372
  * Shared geometry vocabulary for on-frame shape caps — privacy-mask,
@@ -14503,6 +14543,9 @@ var NcSystemEventConditionSchema = object({
14503
14543
  nodeIds: array(string().min(1)).min(1).optional(),
14504
14544
  packageNames: array(string().min(1)).min(1).optional()
14505
14545
  });
14546
+ /** Hard ceiling on a window (24h). A snooze that could not expire would be an
14547
+ * outage the operator asked for once and forgot. */
14548
+ var NC_SNOOZE_MAX_MINUTES = 1440;
14506
14549
  /** Weekly schedule — OR of windows; absence on the rule = always active. */
14507
14550
  var NcScheduleSchema = object({
14508
14551
  windows: array(object({
@@ -14879,15 +14922,15 @@ var NcConditionsSchema = object({
14879
14922
  * (an `immediate` rule naming an `audio-*` class, one notification per
14880
14923
  * classified sample) stays exactly as it was for rules that already use it.
14881
14924
  *
14882
- * NOT in {@link NC_CONDITION_CATALOG} yet, and that is the sequencing rule
14883
- * rather than an oversight: the viewer mirrors the descriptor enums BY HAND
14884
- * (`camstack/src/data/notification-center.ts`, guarded by
14885
- * `scripts/check-viewer-condition-mirror.ts`) and its rule editor STRIPS the
14925
+ * In {@link NC_CONDITION_CATALOG} since P2, and the ORDER it got there is the
14926
+ * rule rather than an accident: the viewer mirrors the descriptor enums BY
14927
+ * HAND (`camstack/src/data/notification-center.ts`, guarded by
14928
+ * `scripts/check-viewer-condition-mirror.ts`) and its rule editor strips the
14886
14929
  * condition fields it does not know when a rule is saved from the phone.
14887
14930
  * Publishing an editor for a condition the app cannot round-trip is how an
14888
- * operator loses a rule's conditions by opening it — so the descriptor, the
14889
- * admin widget and the viewer mirror land together (P2 + P3), and only then
14890
- * does an audio rule become authorable.
14931
+ * operator loses a rule's conditions by opening it — so the viewer mirror
14932
+ * (P3, shipped) went FIRST, and the descriptor an editor renders from
14933
+ * follows here.
14891
14934
  */
14892
14935
  audio: NcAudioConditionSchema.optional()
14893
14936
  });
@@ -15123,6 +15166,30 @@ var NcRuleInputSchema = object({
15123
15166
  */
15124
15167
  snoozeAllowGlobal: boolean().optional(),
15125
15168
  /**
15169
+ * The snooze durations THIS rule's notification offers as buttons, in
15170
+ * minutes.
15171
+ *
15172
+ * Three states, and all three are distinct — which is exactly why this is
15173
+ * `.optional()` and never `.default()`. A Zod default does not run on the
15174
+ * addon cap path (three production failures in one day), so a schema default
15175
+ * would collapse the first two:
15176
+ *
15177
+ * | value | meaning |
15178
+ * | --- | --- |
15179
+ * | absent | the operator never said ⇒ {@link NC_DEFAULT_SNOOZE_MINUTES} |
15180
+ * | `[]` | **no snooze buttons on this rule** — the explicit override |
15181
+ * | a list | these choices, de-duplicated and sorted, at most four |
15182
+ *
15183
+ * `.max(4)` because the notifier's own action budget is small (ntfy allows
15184
+ * three buttons in total) and a rule that spent it all on snooze choices
15185
+ * would push its own tap-through actions off the notification.
15186
+ *
15187
+ * An empty list is NOT an alarm exemption: a rule the alarm is about, or
15188
+ * that arms the panel, is exempt automatically and cannot be silenced by a
15189
+ * window from anywhere (D133).
15190
+ */
15191
+ snoozeOptions: array(number().int().min(1).max(NC_SNOOZE_MAX_MINUTES)).max(4).optional(),
15192
+ /**
15126
15193
  * Devices this rule ACTUATES — arm the alarm, open a gate, turn on a light.
15127
15194
  *
15128
15195
  * This is what makes the rule set the alarm's trigger set without the alarm
@@ -15222,6 +15289,7 @@ var NcConditionDescriptorSchema = object({
15222
15289
  "device",
15223
15290
  "package",
15224
15291
  "occupancy",
15292
+ "audio",
15225
15293
  "system"
15226
15294
  ]),
15227
15295
  label: string(),
@@ -15240,6 +15308,7 @@ var NcConditionDescriptorSchema = object({
15240
15308
  "crossingSelect",
15241
15309
  "polygonDraw",
15242
15310
  "occupancy",
15311
+ "audio",
15243
15312
  "deviceState",
15244
15313
  "systemEvent"
15245
15314
  ]),
@@ -15391,7 +15460,20 @@ var NcSnoozeInputSchema = object({
15391
15460
  ruleId: string().optional(),
15392
15461
  /** Required when `scope: 'device'`. */
15393
15462
  deviceId: number().int().optional(),
15394
- durationMinutes: number().int().min(1).max(1440),
15463
+ /**
15464
+ * Narrow the window to these subject classes — "the cat, not the person".
15465
+ *
15466
+ * ORTHOGONAL to `scope`, deliberately, and absent means EVERY class: that is
15467
+ * what every window authored before this field meant, so no persisted row
15468
+ * changes meaning and no client has to learn anything to keep working.
15469
+ *
15470
+ * It is what makes the window's real key `(deviceId, classes[])` and lets it
15471
+ * cross rules (D133): the operator points at a camera and a kind of thing,
15472
+ * not at whichever of their four rules happened to produce the notification
15473
+ * they are dismissing.
15474
+ */
15475
+ classes: array(string().min(1)).min(1).optional(),
15476
+ durationMinutes: number().int().min(1).max(NC_SNOOZE_MAX_MINUTES),
15395
15477
  /**
15396
15478
  * Silence this for EVERY recipient, not just the caller. Permission is
15397
15479
  * checked server-side (the rule's `snoozeAllowGlobal`, or admin for the
@@ -15416,6 +15498,10 @@ var NcSnoozeSchema = object({
15416
15498
  scope: NcSnoozeScopeSchema,
15417
15499
  ruleId: string().optional(),
15418
15500
  deviceId: number().int().optional(),
15501
+ /** Subject classes this window covers. ABSENT = every class — see
15502
+ * {@link NcSnoozeInputSchema.shape.classes}. Lives in the JSON blob and has
15503
+ * no SQLite column: nothing queries a window by class. */
15504
+ classes: array(string().min(1)).min(1).optional(),
15419
15505
  startedAt: number(),
15420
15506
  /** Exclusive: at exactly this instant the snooze is over. Expiry is a
15421
15507
  * COMPARISON, not a job — no sweeper can leave the operator silenced. */
@@ -17516,7 +17602,14 @@ var zonesCapability = {
17516
17602
  * handle. Slice shape is `{ zones: Zone[] }` so future extensions
17517
17603
  * (e.g. zone groupings) can sit alongside the polygon list.
17518
17604
  */
17519
- runtimeState: object({ zones: array(ZoneSchema).readonly() })
17605
+ runtimeState: object({ zones: array(ZoneSchema).readonly() }),
17606
+ /**
17607
+ * 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.
17608
+ *
17609
+ * See `RuntimeStateDurability`. Enforced by
17610
+ * `scripts/check-runtime-state-durability.ts`.
17611
+ */
17612
+ durability: "restored"
17520
17613
  };
17521
17614
  /**
17522
17615
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
@@ -20315,7 +20408,17 @@ var airQualitySensorCapability = {
20315
20408
  schema: AirQualitySensorStatusSchema,
20316
20409
  kind: "push"
20317
20410
  },
20318
- runtimeState: AirQualitySensorStatusSchema
20411
+ runtimeState: AirQualitySensorStatusSchema,
20412
+ /**
20413
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
20414
+ *
20415
+ * See `RuntimeStateDurability`. Enforced by
20416
+ * `scripts/check-runtime-state-durability.ts`.
20417
+ */
20418
+ durability: "restored",
20419
+ /** Clock fields: written, but excluded from the compare that decides
20420
+ * whether persisting is worth a SQLite commit. */
20421
+ volatileStateFields: ["lastFetchedAt"]
20319
20422
  };
20320
20423
  /**
20321
20424
  * Ambient illuminance reading in lux. Drives Home Assistant `sensor`
@@ -20347,7 +20450,17 @@ var ambientLightSensorCapability = {
20347
20450
  schema: AmbientLightSensorStatusSchema,
20348
20451
  kind: "push"
20349
20452
  },
20350
- runtimeState: AmbientLightSensorStatusSchema
20453
+ runtimeState: AmbientLightSensorStatusSchema,
20454
+ /**
20455
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
20456
+ *
20457
+ * See `RuntimeStateDurability`. Enforced by
20458
+ * `scripts/check-runtime-state-durability.ts`.
20459
+ */
20460
+ durability: "restored",
20461
+ /** Clock fields: written, but excluded from the compare that decides
20462
+ * whether persisting is worth a SQLite commit. */
20463
+ volatileStateFields: ["lastFetchedAt"]
20351
20464
  };
20352
20465
  /**
20353
20466
  * Per-class audio metrics aggregated over a sliding window.
@@ -20465,7 +20578,14 @@ var audioMetricsCapability = {
20465
20578
  }), AudioMetricsHistorySchema)
20466
20579
  },
20467
20580
  /** Reactive runtime-state mirror — live `device.state.audioMetrics.value`. */
20468
- runtimeState: AudioMetricsSnapshotSchema
20581
+ runtimeState: AudioMetricsSnapshotSchema,
20582
+ /**
20583
+ * 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.
20584
+ *
20585
+ * See `RuntimeStateDurability`. Enforced by
20586
+ * `scripts/check-runtime-state-durability.ts`.
20587
+ */
20588
+ durability: "session"
20469
20589
  };
20470
20590
  /**
20471
20591
  * Automation-control cap. Models HA `automation.*` entities on
@@ -20527,7 +20647,14 @@ var automationControlCapability = {
20527
20647
  * reads `enabled` (toggle) + `isRunning` (spinner) + `lastError`
20528
20648
  * (badge) directly.
20529
20649
  */
20530
- runtimeState: AutomationControlStatusSchema
20650
+ runtimeState: AutomationControlStatusSchema,
20651
+ /**
20652
+ * 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.
20653
+ *
20654
+ * See `RuntimeStateDurability`. Enforced by
20655
+ * `scripts/check-runtime-state-durability.ts`.
20656
+ */
20657
+ durability: "session"
20531
20658
  };
20532
20659
  /**
20533
20660
  * Battery status snapshot. Emitted by providers whose device is
@@ -20633,7 +20760,17 @@ onStatusChanged: { data: object({
20633
20760
  * via `device.runtimeState.getCapState('battery')` regardless of
20634
20761
  * the underlying driver.
20635
20762
  */
20636
- runtimeState: BatteryStatusSchema
20763
+ runtimeState: BatteryStatusSchema,
20764
+ /**
20765
+ * 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.
20766
+ *
20767
+ * See `RuntimeStateDurability`. Enforced by
20768
+ * `scripts/check-runtime-state-durability.ts`.
20769
+ */
20770
+ durability: "restored",
20771
+ /** Clock fields: written, but excluded from the compare that decides
20772
+ * whether persisting is worth a SQLite commit. */
20773
+ volatileStateFields: ["lastUpdated"]
20637
20774
  };
20638
20775
  /**
20639
20776
  * Generic boolean sensor — last-resort fallback when no domain-
@@ -20662,7 +20799,17 @@ var binaryCapability = {
20662
20799
  schema: BinaryStatusSchema,
20663
20800
  kind: "push"
20664
20801
  },
20665
- runtimeState: BinaryStatusSchema
20802
+ runtimeState: BinaryStatusSchema,
20803
+ /**
20804
+ * Runtime-state durability: **restored** — transition-driven sensor state; the restored value gives the boot comparison.
20805
+ *
20806
+ * See `RuntimeStateDurability`. Enforced by
20807
+ * `scripts/check-runtime-state-durability.ts`.
20808
+ */
20809
+ durability: "restored",
20810
+ /** Clock fields: written, but excluded from the compare that decides
20811
+ * whether persisting is worth a SQLite commit. */
20812
+ volatileStateFields: ["lastChangedAt"]
20666
20813
  };
20667
20814
  /**
20668
20815
  * Dimmable-light brightness control. Co-exists with `switch` on the
@@ -20715,7 +20862,14 @@ onBrightnessChanged: { data: object({
20715
20862
  * by the kernel. Read via `device.state.brightness.value` so UI
20716
20863
  * sliders surface the current level without polling the provider.
20717
20864
  */
20718
- runtimeState: BrightnessStatusSchema
20865
+ runtimeState: BrightnessStatusSchema,
20866
+ /**
20867
+ * Runtime-state durability: **session** — live lamp state, re-published by the provider on connect.
20868
+ *
20869
+ * See `RuntimeStateDurability`. Enforced by
20870
+ * `scripts/check-runtime-state-durability.ts`.
20871
+ */
20872
+ durability: "session"
20719
20873
  };
20720
20874
  /**
20721
20875
  * button — device-scoped capability for HA `button.*` / `input_button.*`
@@ -20820,7 +20974,17 @@ var carbonMonoxideCapability = {
20820
20974
  schema: CarbonMonoxideStatusSchema,
20821
20975
  kind: "push"
20822
20976
  },
20823
- runtimeState: CarbonMonoxideStatusSchema
20977
+ runtimeState: CarbonMonoxideStatusSchema,
20978
+ /**
20979
+ * Runtime-state durability: **restored** — as `smoke`.
20980
+ *
20981
+ * See `RuntimeStateDurability`. Enforced by
20982
+ * `scripts/check-runtime-state-durability.ts`.
20983
+ */
20984
+ durability: "restored",
20985
+ /** Clock fields: written, but excluded from the compare that decides
20986
+ * whether persisting is worth a SQLite commit. */
20987
+ volatileStateFields: ["lastChangedAt"]
20824
20988
  };
20825
20989
  /**
20826
20990
  * HVAC / climate control cap. Models the full surface of a HA
@@ -20980,7 +21144,14 @@ var climateControlCapability = {
20980
21144
  * the full slice via `device.state.climate-control.value` and refresh
20981
21145
  * on every push without re-querying the provider.
20982
21146
  */
20983
- runtimeState: ClimateControlStatusSchema
21147
+ runtimeState: ClimateControlStatusSchema,
21148
+ /**
21149
+ * Runtime-state durability: **session** — as `brightness`; `currentTemp` moves continuously and is re-published on connect.
21150
+ *
21151
+ * See `RuntimeStateDurability`. Enforced by
21152
+ * `scripts/check-runtime-state-durability.ts`.
21153
+ */
21154
+ durability: "session"
20984
21155
  };
20985
21156
  /**
20986
21157
  * Color-light cap. Coexists with `switch` (on/off) and `brightness`
@@ -21090,7 +21261,14 @@ onColorChanged: { data: object({
21090
21261
  * kernel. Read via `device.state.color.value` so UI pickers surface
21091
21262
  * the current chromaticity without polling the provider.
21092
21263
  */
21093
- runtimeState: ColorStatusSchema
21264
+ runtimeState: ColorStatusSchema,
21265
+ /**
21266
+ * Runtime-state durability: **session** — as `brightness`.
21267
+ *
21268
+ * See `RuntimeStateDurability`. Enforced by
21269
+ * `scripts/check-runtime-state-durability.ts`.
21270
+ */
21271
+ durability: "session"
21094
21272
  };
21095
21273
  var ConnectionTestOutcomeSchema = discriminatedUnion("outcome", [
21096
21274
  object({
@@ -21148,7 +21326,17 @@ var connectivityCapability = {
21148
21326
  schema: ConnectivityStatusSchema,
21149
21327
  kind: "push"
21150
21328
  },
21151
- runtimeState: ConnectivityStatusSchema
21329
+ runtimeState: ConnectivityStatusSchema,
21330
+ /**
21331
+ * Runtime-state durability: **restored** — same shape and same argument as `device-status`, for links rather than devices.
21332
+ *
21333
+ * See `RuntimeStateDurability`. Enforced by
21334
+ * `scripts/check-runtime-state-durability.ts`.
21335
+ */
21336
+ durability: "restored",
21337
+ /** Clock fields: written, but excluded from the compare that decides
21338
+ * whether persisting is worth a SQLite commit. */
21339
+ volatileStateFields: ["lastChangedAt"]
21152
21340
  };
21153
21341
  /**
21154
21342
  * Generic device-consumables capability — surfaces a device's
@@ -21230,7 +21418,14 @@ reset: method(object({
21230
21418
  }
21231
21419
  }
21232
21420
  },
21233
- runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() })
21421
+ runtimeState: ConsumablesStatusSchema.extend({ lastFetchedAt: number() }),
21422
+ /**
21423
+ * Runtime-state durability: **session** — the authority is the appliance; the provider re-reads the whole item array on connect.
21424
+ *
21425
+ * See `RuntimeStateDurability`. Enforced by
21426
+ * `scripts/check-runtime-state-durability.ts`.
21427
+ */
21428
+ durability: "session"
21234
21429
  };
21235
21430
  /**
21236
21431
  * Door / window / opening / garage / valve contact sensor. Boolean
@@ -21261,7 +21456,17 @@ var contactCapability = {
21261
21456
  schema: ContactStatusSchema,
21262
21457
  kind: "push"
21263
21458
  },
21264
- runtimeState: ContactStatusSchema
21459
+ runtimeState: ContactStatusSchema,
21460
+ /**
21461
+ * Runtime-state durability: **restored** — a door left open across a restart must still read open.
21462
+ *
21463
+ * See `RuntimeStateDurability`. Enforced by
21464
+ * `scripts/check-runtime-state-durability.ts`.
21465
+ */
21466
+ durability: "restored",
21467
+ /** Clock fields: written, but excluded from the compare that decides
21468
+ * whether persisting is worth a SQLite commit. */
21469
+ volatileStateFields: ["lastChangedAt"]
21265
21470
  };
21266
21471
  /**
21267
21472
  * Status slice — flat object (the framework's `runtimeState` contract
@@ -21367,7 +21572,14 @@ var controlCapability = {
21367
21572
  * dropdown / text field / date picker) read the slice's discriminant
21368
21573
  * and value directly without polling the provider.
21369
21574
  */
21370
- runtimeState: ControlStatusSchema
21575
+ runtimeState: ControlStatusSchema,
21576
+ /**
21577
+ * Runtime-state durability: **session** — a generic control mirrors an external entity that re-publishes on connect; the options array is re-derived with it.
21578
+ *
21579
+ * See `RuntimeStateDurability`. Enforced by
21580
+ * `scripts/check-runtime-state-durability.ts`.
21581
+ */
21582
+ durability: "session"
21371
21583
  };
21372
21584
  var CoverStatusSchema = object({
21373
21585
  /** Lifecycle state of the cover. */
@@ -21428,7 +21640,17 @@ var coverCapability = {
21428
21640
  * Runtime-state slice — mirrored by the kernel. UI controls watch
21429
21641
  * the slice for live position changes during a move.
21430
21642
  */
21431
- runtimeState: CoverStatusSchema
21643
+ runtimeState: CoverStatusSchema,
21644
+ /**
21645
+ * Runtime-state durability: **restored** — position survives a restart on the device; the mirror should agree at boot rather than read blank.
21646
+ *
21647
+ * See `RuntimeStateDurability`. Enforced by
21648
+ * `scripts/check-runtime-state-durability.ts`.
21649
+ */
21650
+ durability: "restored",
21651
+ /** Clock fields: written, but excluded from the compare that decides
21652
+ * whether persisting is worth a SQLite commit. */
21653
+ volatileStateFields: ["lastChangedAt"]
21432
21654
  };
21433
21655
  /**
21434
21656
  * Vendor-neutral day/night (IR-cut) control — the per-camera config cap
@@ -21522,7 +21744,17 @@ var dayNightCapability = {
21522
21744
  schema: DayNightStatusSchema,
21523
21745
  kind: "poll"
21524
21746
  },
21525
- runtimeState: DayNightStatusSchema
21747
+ runtimeState: DayNightStatusSchema,
21748
+ /**
21749
+ * Runtime-state durability: **restored** — operator-set IR-cut behaviour; mutation-driven.
21750
+ *
21751
+ * See `RuntimeStateDurability`. Enforced by
21752
+ * `scripts/check-runtime-state-durability.ts`.
21753
+ */
21754
+ durability: "restored",
21755
+ /** Clock fields: written, but excluded from the compare that decides
21756
+ * whether persisting is worth a SQLite commit. */
21757
+ volatileStateFields: ["lastFetchedAt"]
21526
21758
  };
21527
21759
  /**
21528
21760
  * Generic device-level status snapshot. Auto-registered by `BaseDevice`
@@ -21569,7 +21801,17 @@ onStatusChanged: { data: object({
21569
21801
  schema: DeviceStatusSchema,
21570
21802
  kind: "push"
21571
21803
  },
21572
- runtimeState: DeviceStatusSchema
21804
+ runtimeState: DeviceStatusSchema,
21805
+ /**
21806
+ * 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.
21807
+ *
21808
+ * See `RuntimeStateDurability`. Enforced by
21809
+ * `scripts/check-runtime-state-durability.ts`.
21810
+ */
21811
+ durability: "restored",
21812
+ /** Clock fields: written, but excluded from the compare that decides
21813
+ * whether persisting is worth a SQLite commit. */
21814
+ volatileStateFields: ["lastChangedAt"]
21573
21815
  };
21574
21816
  /**
21575
21817
  * Doorbell button cap. Two kinds of providers coexist behind this cap
@@ -21631,7 +21873,14 @@ onPressed: { data: DoorbellPressEventSchema } },
21631
21873
  * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
21632
21874
  * without subscribing.
21633
21875
  */
21634
- runtimeState: DoorbellStatusSchema
21876
+ runtimeState: DoorbellStatusSchema,
21877
+ /**
21878
+ * 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.
21879
+ *
21880
+ * See `RuntimeStateDurability`. Enforced by
21881
+ * `scripts/check-runtime-state-durability.ts`.
21882
+ */
21883
+ durability: "restored"
21635
21884
  };
21636
21885
  /**
21637
21886
  * Enum-state sensor — a string value picked from a finite option set.
@@ -21671,7 +21920,17 @@ var enumSensorCapability = {
21671
21920
  schema: EnumSensorStatusSchema,
21672
21921
  kind: "push"
21673
21922
  },
21674
- runtimeState: EnumSensorStatusSchema
21923
+ runtimeState: EnumSensorStatusSchema,
21924
+ /**
21925
+ * Runtime-state durability: **restored** — as `numeric-sensor`; 80 devices.
21926
+ *
21927
+ * See `RuntimeStateDurability`. Enforced by
21928
+ * `scripts/check-runtime-state-durability.ts`.
21929
+ */
21930
+ durability: "restored",
21931
+ /** Clock fields: written, but excluded from the compare that decides
21932
+ * whether persisting is worth a SQLite commit. */
21933
+ volatileStateFields: ["lastFetchedAt"]
21675
21934
  };
21676
21935
  /**
21677
21936
  * Generic stateless event emitter. Installed on a `DeviceType.EventEmitter`
@@ -21707,7 +21966,14 @@ var eventEmitterCapability = {
21707
21966
  schema: EventEmitterStatusSchema,
21708
21967
  kind: "push"
21709
21968
  },
21710
- runtimeState: EventEmitterStatusSchema
21969
+ runtimeState: EventEmitterStatusSchema,
21970
+ /**
21971
+ * Runtime-state durability: **session** — `eventCountSinceStart` names its own scope.
21972
+ *
21973
+ * See `RuntimeStateDurability`. Enforced by
21974
+ * `scripts/check-runtime-state-durability.ts`.
21975
+ */
21976
+ durability: "session"
21711
21977
  };
21712
21978
  var EventItemSchema = object({
21713
21979
  id: string(),
@@ -21988,7 +22254,14 @@ var fanControlCapability = {
21988
22254
  * Runtime-state slice — mirrored by the kernel. UI fan speed
21989
22255
  * sliders read `percentage` for live updates.
21990
22256
  */
21991
- runtimeState: FanControlStatusSchema
22257
+ runtimeState: FanControlStatusSchema,
22258
+ /**
22259
+ * Runtime-state durability: **session** — as `brightness`.
22260
+ *
22261
+ * See `RuntimeStateDurability`. Enforced by
22262
+ * `scripts/check-runtime-state-durability.ts`.
22263
+ */
22264
+ durability: "session"
21992
22265
  };
21993
22266
  /**
21994
22267
  * Per-device feature/identity probe slice. Holds the runtime-resolved
@@ -22064,7 +22337,14 @@ onProbeChanged: { data: object({
22064
22337
  schema: FeatureProbeStatusSchema,
22065
22338
  kind: "push"
22066
22339
  },
22067
- runtimeState: FeatureProbeStatusSchema
22340
+ runtimeState: FeatureProbeStatusSchema,
22341
+ /**
22342
+ * 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.
22343
+ *
22344
+ * See `RuntimeStateDurability`. Enforced by
22345
+ * `scripts/check-runtime-state-durability.ts`.
22346
+ */
22347
+ durability: "session"
22068
22348
  };
22069
22349
  /**
22070
22350
  * Water leak / moisture sensor. Boolean "is liquid currently
@@ -22091,7 +22371,17 @@ var floodCapability = {
22091
22371
  schema: FloodStatusSchema,
22092
22372
  kind: "push"
22093
22373
  },
22094
- runtimeState: FloodStatusSchema
22374
+ runtimeState: FloodStatusSchema,
22375
+ /**
22376
+ * Runtime-state durability: **restored** — as `smoke`.
22377
+ *
22378
+ * See `RuntimeStateDurability`. Enforced by
22379
+ * `scripts/check-runtime-state-durability.ts`.
22380
+ */
22381
+ durability: "restored",
22382
+ /** Clock fields: written, but excluded from the compare that decides
22383
+ * whether persisting is worth a SQLite commit. */
22384
+ volatileStateFields: ["lastChangedAt"]
22095
22385
  };
22096
22386
  /**
22097
22387
  * Combustible-gas (LPG / methane / hydrogen) alarm sensor. Drives
@@ -22114,7 +22404,17 @@ var gasCapability = {
22114
22404
  schema: GasStatusSchema,
22115
22405
  kind: "push"
22116
22406
  },
22117
- runtimeState: GasStatusSchema
22407
+ runtimeState: GasStatusSchema,
22408
+ /**
22409
+ * Runtime-state durability: **restored** — as `smoke`.
22410
+ *
22411
+ * See `RuntimeStateDurability`. Enforced by
22412
+ * `scripts/check-runtime-state-durability.ts`.
22413
+ */
22414
+ durability: "restored",
22415
+ /** Clock fields: written, but excluded from the compare that decides
22416
+ * whether persisting is worth a SQLite commit. */
22417
+ volatileStateFields: ["lastChangedAt"]
22118
22418
  };
22119
22419
  /**
22120
22420
  * Humidifier / dehumidifier cap. Models HA `humidifier.*` entities —
@@ -22190,7 +22490,14 @@ var humidifierCapability = {
22190
22490
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
22191
22491
  * slice for live humidity / mode changes.
22192
22492
  */
22193
- runtimeState: HumidifierStatusSchema
22493
+ runtimeState: HumidifierStatusSchema,
22494
+ /**
22495
+ * Runtime-state durability: **session** — as `climate-control`.
22496
+ *
22497
+ * See `RuntimeStateDurability`. Enforced by
22498
+ * `scripts/check-runtime-state-durability.ts`.
22499
+ */
22500
+ durability: "session"
22194
22501
  };
22195
22502
  /**
22196
22503
  * Single-metric humidity reading. Drives Home Assistant `sensor`
@@ -22226,7 +22533,17 @@ var humiditySensorCapability = {
22226
22533
  schema: HumiditySensorStatusSchema,
22227
22534
  kind: "push"
22228
22535
  },
22229
- runtimeState: HumiditySensorStatusSchema
22536
+ runtimeState: HumiditySensorStatusSchema,
22537
+ /**
22538
+ * Runtime-state durability: **restored** — as `numeric-sensor` (67 of 75 writes were the clock alone).
22539
+ *
22540
+ * See `RuntimeStateDurability`. Enforced by
22541
+ * `scripts/check-runtime-state-durability.ts`.
22542
+ */
22543
+ durability: "restored",
22544
+ /** Clock fields: written, but excluded from the compare that decides
22545
+ * whether persisting is worth a SQLite commit. */
22546
+ volatileStateFields: ["lastFetchedAt"]
22230
22547
  };
22231
22548
  /**
22232
22549
  * Image display cap. Models a single still image exposed by an integration —
@@ -22264,7 +22581,14 @@ var imageCapability = {
22264
22581
  * Runtime-state slice — mirrored by the kernel. The UI reads `url`
22265
22582
  * directly and renders the still image.
22266
22583
  */
22267
- runtimeState: ImageStatusSchema
22584
+ runtimeState: ImageStatusSchema,
22585
+ /**
22586
+ * Runtime-state durability: **session** — a snapshot URL is a session-scoped handle; a restored one points at nothing.
22587
+ *
22588
+ * See `RuntimeStateDurability`. Enforced by
22589
+ * `scripts/check-runtime-state-durability.ts`.
22590
+ */
22591
+ durability: "session"
22268
22592
  };
22269
22593
  /**
22270
22594
  * Vendor-neutral image / picture-adjustment cap — the per-camera config
@@ -22413,7 +22737,17 @@ var imageSettingsCapability = {
22413
22737
  schema: ImageSettingsStatusSchema,
22414
22738
  kind: "poll"
22415
22739
  },
22416
- runtimeState: ImageSettingsStatusSchema
22740
+ runtimeState: ImageSettingsStatusSchema,
22741
+ /**
22742
+ * Runtime-state durability: **restored** — operator-set camera imaging; mutation-driven.
22743
+ *
22744
+ * See `RuntimeStateDurability`. Enforced by
22745
+ * `scripts/check-runtime-state-durability.ts`.
22746
+ */
22747
+ durability: "restored",
22748
+ /** Clock fields: written, but excluded from the compare that decides
22749
+ * whether persisting is worth a SQLite commit. */
22750
+ volatileStateFields: ["lastFetchedAt"]
22417
22751
  };
22418
22752
  /**
22419
22753
  * integrations — system-scoped singleton capability for integration
@@ -22753,7 +23087,14 @@ var lawnMowerControlCapability = {
22753
23087
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
22754
23088
  * slice for live activity + battery changes.
22755
23089
  */
22756
- runtimeState: LawnMowerControlStatusSchema
23090
+ runtimeState: LawnMowerControlStatusSchema,
23091
+ /**
23092
+ * Runtime-state durability: **session** — as `vacuum-control`.
23093
+ *
23094
+ * See `RuntimeStateDurability`. Enforced by
23095
+ * `scripts/check-runtime-state-durability.ts`.
23096
+ */
23097
+ durability: "session"
22757
23098
  };
22758
23099
  /**
22759
23100
  * local-network — hub-only singleton.
@@ -22959,7 +23300,17 @@ var lockControlCapability = {
22959
23300
  * read `state` and disable themselves during `locking`/`unlocking`
22960
23301
  * transitions.
22961
23302
  */
22962
- runtimeState: LockControlStatusSchema
23303
+ runtimeState: LockControlStatusSchema,
23304
+ /**
23305
+ * Runtime-state durability: **restored** — a lock left locked must still read locked.
23306
+ *
23307
+ * See `RuntimeStateDurability`. Enforced by
23308
+ * `scripts/check-runtime-state-durability.ts`.
23309
+ */
23310
+ durability: "restored",
23311
+ /** Clock fields: written, but excluded from the compare that decides
23312
+ * whether persisting is worth a SQLite commit. */
23313
+ volatileStateFields: ["lastChangedAt"]
22963
23314
  };
22964
23315
  /**
22965
23316
  * Media-player cap. Models HA `media_player.*` (Sonos, Chromecast,
@@ -23121,7 +23472,14 @@ var mediaPlayerCapability = {
23121
23472
  * full slice for live now-playing, volume, and progress updates
23122
23473
  * without polling.
23123
23474
  */
23124
- runtimeState: MediaPlayerStatusSchema
23475
+ runtimeState: MediaPlayerStatusSchema,
23476
+ /**
23477
+ * Runtime-state durability: **session** — a restored transport position describes a playback that stopped when the hub did.
23478
+ *
23479
+ * See `RuntimeStateDurability`. Enforced by
23480
+ * `scripts/check-runtime-state-durability.ts`.
23481
+ */
23482
+ durability: "session"
23125
23483
  };
23126
23484
  /**
23127
23485
  * mesh-network — collection cap for mesh-VPN providers.
@@ -23385,7 +23743,14 @@ onMotionChanged: { data: MotionOnMotionChangedDataSchema } },
23385
23743
  * `device.state.motion.value`. Reads never invoke the provider, so
23386
23744
  * UIs and other addons can poll the cached state safely.
23387
23745
  */
23388
- runtimeState: MotionStatusSchema
23746
+ runtimeState: MotionStatusSchema,
23747
+ /**
23748
+ * 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.
23749
+ *
23750
+ * See `RuntimeStateDurability`. Enforced by
23751
+ * `scripts/check-runtime-state-durability.ts`.
23752
+ */
23753
+ durability: "session"
23389
23754
  };
23390
23755
  /**
23391
23756
  * Motion-trigger toggle for accessory devices.
@@ -23450,7 +23815,14 @@ var motionTriggerCapability = {
23450
23815
  schema: MotionTriggerStatusSchema,
23451
23816
  kind: "command-driven"
23452
23817
  },
23453
- runtimeState: MotionTriggerRuntimeStateSchema
23818
+ runtimeState: MotionTriggerRuntimeStateSchema,
23819
+ /**
23820
+ * 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.
23821
+ *
23822
+ * See `RuntimeStateDurability`. Enforced by
23823
+ * `scripts/check-runtime-state-durability.ts`.
23824
+ */
23825
+ durability: "session"
23454
23826
  };
23455
23827
  /**
23456
23828
  * Motion-zones share the same MaskShape vocabulary as privacy-mask — the
@@ -23517,7 +23889,17 @@ var motionZonesCapability = {
23517
23889
  schema: MotionZoneStatusSchema,
23518
23890
  kind: "poll"
23519
23891
  },
23520
- runtimeState: MotionZoneStatusSchema
23892
+ runtimeState: MotionZoneStatusSchema,
23893
+ /**
23894
+ * 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.
23895
+ *
23896
+ * See `RuntimeStateDurability`. Enforced by
23897
+ * `scripts/check-runtime-state-durability.ts`.
23898
+ */
23899
+ durability: "restored",
23900
+ /** Clock fields: written, but excluded from the compare that decides
23901
+ * whether persisting is worth a SQLite commit. */
23902
+ volatileStateFields: ["lastFetchedAt"]
23521
23903
  };
23522
23904
  /**
23523
23905
  * On-camera AI object detection cap. Surfaces per-device the classes
@@ -23591,7 +23973,17 @@ var nativeObjectDetectionCapability = {
23591
23973
  schema: NativeObjectDetectionStatusSchema,
23592
23974
  kind: "push"
23593
23975
  },
23594
- runtimeState: NativeObjectDetectionRuntimeStateSchema
23976
+ runtimeState: NativeObjectDetectionRuntimeStateSchema,
23977
+ /**
23978
+ * 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.
23979
+ *
23980
+ * See `RuntimeStateDurability`. Enforced by
23981
+ * `scripts/check-runtime-state-durability.ts`.
23982
+ */
23983
+ durability: "restored",
23984
+ /** Clock fields: written, but excluded from the compare that decides
23985
+ * whether persisting is worth a SQLite commit. */
23986
+ volatileStateFields: ["lastFetchedAt"]
23595
23987
  };
23596
23988
  /**
23597
23989
  * network-quality — system-scoped singleton capability tracking RTT,
@@ -23925,7 +24317,14 @@ onSent: { data: object({
23925
24317
  * form reads `supports` to gate optional fields; history pane reads
23926
24318
  * `lastSentAt` / `lastError` / `queueDepth`.
23927
24319
  */
23928
- runtimeState: NotifierStatusSchema
24320
+ runtimeState: NotifierStatusSchema,
24321
+ /**
24322
+ * Runtime-state durability: **session** — live queue depth and last-send state; a restored queue depth describes a queue that no longer exists.
24323
+ *
24324
+ * See `RuntimeStateDurability`. Enforced by
24325
+ * `scripts/check-runtime-state-durability.ts`.
24326
+ */
24327
+ durability: "session"
23929
24328
  };
23930
24329
  /**
23931
24330
  * Generic numeric sensor — last-resort fallback when no typed numeric
@@ -23968,7 +24367,17 @@ var numericSensorCapability = {
23968
24367
  schema: NumericSensorStatusSchema,
23969
24368
  kind: "push"
23970
24369
  },
23971
- runtimeState: NumericSensorStatusSchema
24370
+ runtimeState: NumericSensorStatusSchema,
24371
+ /**
24372
+ * 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.
24373
+ *
24374
+ * See `RuntimeStateDurability`. Enforced by
24375
+ * `scripts/check-runtime-state-durability.ts`.
24376
+ */
24377
+ durability: "restored",
24378
+ /** Clock fields: written, but excluded from the compare that decides
24379
+ * whether persisting is worth a SQLite commit. */
24380
+ volatileStateFields: ["lastFetchedAt"]
23972
24381
  };
23973
24382
  /**
23974
24383
  * Generic on-screen-display (video overlay) cap. Each camera exposes
@@ -24353,7 +24762,14 @@ var petFeederCapability = {
24353
24762
  * the full slice via `device.state.petFeeder.value` and refresh on
24354
24763
  * every poll without re-querying the provider.
24355
24764
  */
24356
- runtimeState: PetFeederStatusSchema
24765
+ runtimeState: PetFeederStatusSchema,
24766
+ /**
24767
+ * Runtime-state durability: **session** — live appliance state re-published on connect.
24768
+ *
24769
+ * See `RuntimeStateDurability`. Enforced by
24770
+ * `scripts/check-runtime-state-durability.ts`.
24771
+ */
24772
+ durability: "session"
24357
24773
  };
24358
24774
  var VehicleSchema = object({
24359
24775
  id: string(),
@@ -24665,7 +25081,17 @@ var powerMeterCapability = {
24665
25081
  schema: PowerMeterStatusSchema,
24666
25082
  kind: "push"
24667
25083
  },
24668
- runtimeState: PowerMeterStatusSchema
25084
+ runtimeState: PowerMeterStatusSchema,
25085
+ /**
25086
+ * Runtime-state durability: **restored** — as `numeric-sensor`; `kwhTotal` is an accumulator whose restored value is the baseline.
25087
+ *
25088
+ * See `RuntimeStateDurability`. Enforced by
25089
+ * `scripts/check-runtime-state-durability.ts`.
25090
+ */
25091
+ durability: "restored",
25092
+ /** Clock fields: written, but excluded from the compare that decides
25093
+ * whether persisting is worth a SQLite commit. */
25094
+ volatileStateFields: ["lastFetchedAt"]
24669
25095
  };
24670
25096
  /**
24671
25097
  * Presence cap. Models HA `person.*` and `device_tracker.*` entities
@@ -24722,7 +25148,17 @@ var presenceCapability = {
24722
25148
  * the map pin is rendered (use `DeviceFeature.PresenceGps` for the
24723
25149
  * pre-fetch fast-path check).
24724
25150
  */
24725
- runtimeState: PresenceStatusSchema
25151
+ runtimeState: PresenceStatusSchema,
25152
+ /**
25153
+ * Runtime-state durability: **restored** — occupancy-relevant: the restored state is what an occupancy rule compares the first post-restart observation against.
25154
+ *
25155
+ * See `RuntimeStateDurability`. Enforced by
25156
+ * `scripts/check-runtime-state-durability.ts`.
25157
+ */
25158
+ durability: "restored",
25159
+ /** Clock fields: written, but excluded from the compare that decides
25160
+ * whether persisting is worth a SQLite commit. */
25161
+ volatileStateFields: ["lastChangedAt"]
24726
25162
  };
24727
25163
  /**
24728
25164
  * Atmospheric pressure reading in hectopascals. Drives Home Assistant
@@ -24758,7 +25194,17 @@ var pressureSensorCapability = {
24758
25194
  schema: PressureSensorStatusSchema,
24759
25195
  kind: "push"
24760
25196
  },
24761
- runtimeState: PressureSensorStatusSchema
25197
+ runtimeState: PressureSensorStatusSchema,
25198
+ /**
25199
+ * Runtime-state durability: **restored** — as `numeric-sensor`.
25200
+ *
25201
+ * See `RuntimeStateDurability`. Enforced by
25202
+ * `scripts/check-runtime-state-durability.ts`.
25203
+ */
25204
+ durability: "restored",
25205
+ /** Clock fields: written, but excluded from the compare that decides
25206
+ * whether persisting is worth a SQLite commit. */
25207
+ volatileStateFields: ["lastFetchedAt"]
24762
25208
  };
24763
25209
  /**
24764
25210
  * PRIVACY — what the camera deliberately does not capture. Two planes:
@@ -24888,7 +25334,17 @@ var privacyMaskCapability = {
24888
25334
  schema: PrivacyMaskStatusSchema,
24889
25335
  kind: "poll"
24890
25336
  },
24891
- runtimeState: PrivacyMaskStatusSchema
25337
+ runtimeState: PrivacyMaskStatusSchema,
25338
+ /**
25339
+ * Runtime-state durability: **restored** — operator-drawn regions, zero real churn — 22 writes in 25 minutes, every one of them the clock.
25340
+ *
25341
+ * See `RuntimeStateDurability`. Enforced by
25342
+ * `scripts/check-runtime-state-durability.ts`.
25343
+ */
25344
+ durability: "restored",
25345
+ /** Clock fields: written, but excluded from the compare that decides
25346
+ * whether persisting is worth a SQLite commit. */
25347
+ volatileStateFields: ["lastFetchedAt"]
24892
25348
  };
24893
25349
  var PtzPresetSchema = object({
24894
25350
  id: string(),
@@ -25054,7 +25510,14 @@ var ptzAutotrackCapability = {
25054
25510
  * fetch / cache / fallback logic out of the four cap methods —
25055
25511
  * they become trampolines over `runtimeState`.
25056
25512
  */
25057
- runtimeState: PtzAutotrackRuntimeStateSchema
25513
+ runtimeState: PtzAutotrackRuntimeStateSchema,
25514
+ /**
25515
+ * Runtime-state durability: **session** — mirrors the camera's own autotrack config, re-read on connect.
25516
+ *
25517
+ * See `RuntimeStateDurability`. Enforced by
25518
+ * `scripts/check-runtime-state-durability.ts`.
25519
+ */
25520
+ durability: "session"
25058
25521
  };
25059
25522
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
25060
25523
  kind: "mutation",
@@ -25401,13 +25864,24 @@ method(object({
25401
25864
  /** Playback-speed multiplier for the render (1 = realtime). */
25402
25865
  var ExportSpeedSchema = number().min(.25).max(32);
25403
25866
  /**
25404
- * One dense interval, in SECONDS FROM THE EXPORT'S OWN `fromMs`.
25867
+ * One dense interval, in WALL-CLOCK SECONDS FROM THE EXPORT'S OWN `fromMs`.
25868
+ *
25869
+ * **Wall clock, not ffmpeg's `t`** — and the recorder translates. A caller
25870
+ * derives these bounds from things that happened at a TIME (a track's
25871
+ * `firstSeen`), while `t` runs over the source playlist: the concatenation of
25872
+ * every segment present for the range, with each recording GAP removed. The
25873
+ * two agree only on a window that recorded without one interruption, and only
25874
+ * the render side knows the segments, so the translation lives there
25875
+ * (`export-dense-map.ts`, addon-pipeline).
25876
+ *
25877
+ * It was not always so. These seconds were fed to `between(t,…)` verbatim, and
25878
+ * on a 10 h window holding 29,393 s of footage every range landed late by the
25879
+ * gap accumulated before it — up to 6,607 s, well past EOF. Nothing matched,
25880
+ * the video was a uniform timelapse, and the log line reported the five ranges
25881
+ * that had been ASKED for (2026-08-13, export `57d14363`, camera 615).
25405
25882
  *
25406
- * Relative and not absolute epoch on purpose: the renderer's frame-select
25407
- * expression sees ffmpeg's `t`, which starts at 0 for the export's source
25408
- * playlist. Handing it absolute epochs would make every call site responsible
25409
- * for the same subtraction, and the one that forgot would emit a filter that
25410
- * selects nothing — silently, as a uniform timelapse.
25883
+ * Relative and not absolute epoch, because an absolute epoch would make every
25884
+ * call site responsible for the same subtraction.
25411
25885
  */
25412
25886
  var ExportDenseRangeSchema = object({
25413
25887
  fromSec: number().nonnegative(),
@@ -25693,7 +26167,14 @@ var sceneMonitorCapability = {
25693
26167
  schema: SceneMonitorStatusSchema,
25694
26168
  kind: "push"
25695
26169
  },
25696
- runtimeState: SceneMonitorStatusSchema
26170
+ runtimeState: SceneMonitorStatusSchema,
26171
+ /**
26172
+ * Runtime-state durability: **session** — re-derived from the current scene on the next evaluation.
26173
+ *
26174
+ * See `RuntimeStateDurability`. Enforced by
26175
+ * `scripts/check-runtime-state-durability.ts`.
26176
+ */
26177
+ durability: "session"
25697
26178
  };
25698
26179
  /**
25699
26180
  * Per-stage gating mode applied to the zones a rule references.
@@ -25837,7 +26318,14 @@ var scriptRunnerCapability = {
25837
26318
  * `isRunning` to render a spinner during execution and surfaces
25838
26319
  * `lastError` / `lastRunSuccess` in the recent-runs panel.
25839
26320
  */
25840
- runtimeState: ScriptRunnerStatusSchema
26321
+ runtimeState: ScriptRunnerStatusSchema,
26322
+ /**
26323
+ * Runtime-state durability: **session** — a restored `isRunning: true` describes a process that died with the previous hub.
26324
+ *
26325
+ * See `RuntimeStateDurability`. Enforced by
26326
+ * `scripts/check-runtime-state-durability.ts`.
26327
+ */
26328
+ durability: "session"
25841
26329
  };
25842
26330
  /**
25843
26331
  * Smoke alarm sensor — boolean "is smoke currently detected" with
@@ -25864,7 +26352,17 @@ var smokeCapability = {
25864
26352
  schema: SmokeStatusSchema,
25865
26353
  kind: "push"
25866
26354
  },
25867
- runtimeState: SmokeStatusSchema
26355
+ runtimeState: SmokeStatusSchema,
26356
+ /**
26357
+ * Runtime-state durability: **restored** — a safety sensor must not read "clear" merely because the hub restarted.
26358
+ *
26359
+ * See `RuntimeStateDurability`. Enforced by
26360
+ * `scripts/check-runtime-state-durability.ts`.
26361
+ */
26362
+ durability: "restored",
26363
+ /** Clock fields: written, but excluded from the compare that decides
26364
+ * whether persisting is worth a SQLite commit. */
26365
+ volatileStateFields: ["lastChangedAt"]
25868
26366
  };
25869
26367
  /**
25870
26368
  * One publishable camera stream as its OWNING PROVIDER describes it — the same
@@ -26037,7 +26535,17 @@ var streamParamsCapability = {
26037
26535
  schema: StreamParamsStatusSchema,
26038
26536
  kind: "poll"
26039
26537
  },
26040
- runtimeState: StreamParamsStatusSchema
26538
+ runtimeState: StreamParamsStatusSchema,
26539
+ /**
26540
+ * Runtime-state durability: **restored** — operator-set encoder profile; mutation-driven.
26541
+ *
26542
+ * See `RuntimeStateDurability`. Enforced by
26543
+ * `scripts/check-runtime-state-durability.ts`.
26544
+ */
26545
+ durability: "restored",
26546
+ /** Clock fields: written, but excluded from the compare that decides
26547
+ * whether persisting is worth a SQLite commit. */
26548
+ volatileStateFields: ["lastFetchedAt"]
26041
26549
  };
26042
26550
  /**
26043
26551
  * Generic on/off switch cap for accessory children (siren, floodlight,
@@ -26083,6 +26591,16 @@ var switchCapability = {
26083
26591
  * not need to re-query the provider after a setState mutation.
26084
26592
  */
26085
26593
  runtimeState: SwitchStatusSchema,
26594
+ /**
26595
+ * Runtime-state durability: **restored** — device state an operator reads as authoritative; 55 devices, transition-driven.
26596
+ *
26597
+ * See `RuntimeStateDurability`. Enforced by
26598
+ * `scripts/check-runtime-state-durability.ts`.
26599
+ */
26600
+ durability: "restored",
26601
+ /** Clock fields: written, but excluded from the compare that decides
26602
+ * whether persisting is worth a SQLite commit. */
26603
+ volatileStateFields: ["lastChangedAt"],
26086
26604
  settings: { bindings: [{
26087
26605
  kind: "scalar",
26088
26606
  statusPath: "on",
@@ -26162,7 +26680,17 @@ var tamperCapability = {
26162
26680
  schema: TamperStatusSchema,
26163
26681
  kind: "push"
26164
26682
  },
26165
- runtimeState: TamperStatusSchema
26683
+ runtimeState: TamperStatusSchema,
26684
+ /**
26685
+ * Runtime-state durability: **restored** — as `smoke`.
26686
+ *
26687
+ * See `RuntimeStateDurability`. Enforced by
26688
+ * `scripts/check-runtime-state-durability.ts`.
26689
+ */
26690
+ durability: "restored",
26691
+ /** Clock fields: written, but excluded from the compare that decides
26692
+ * whether persisting is worth a SQLite commit. */
26693
+ volatileStateFields: ["lastChangedAt"]
26166
26694
  };
26167
26695
  /**
26168
26696
  * Single-metric temperature reading. Drives Home Assistant `sensor`
@@ -26207,7 +26735,17 @@ var temperatureSensorCapability = {
26207
26735
  schema: TemperatureSensorStatusSchema,
26208
26736
  kind: "push"
26209
26737
  },
26210
- runtimeState: TemperatureSensorStatusSchema
26738
+ runtimeState: TemperatureSensorStatusSchema,
26739
+ /**
26740
+ * Runtime-state durability: **restored** — as `numeric-sensor` (69 of 125 writes were the clock alone).
26741
+ *
26742
+ * See `RuntimeStateDurability`. Enforced by
26743
+ * `scripts/check-runtime-state-durability.ts`.
26744
+ */
26745
+ durability: "restored",
26746
+ /** Clock fields: written, but excluded from the compare that decides
26747
+ * whether persisting is worth a SQLite commit. */
26748
+ volatileStateFields: ["lastFetchedAt"]
26211
26749
  };
26212
26750
  /**
26213
26751
  * toast — system-scoped singleton capability that streams toast
@@ -26276,7 +26814,14 @@ var updateCapability = {
26276
26814
  schema: UpdateStatusSchema,
26277
26815
  kind: "poll"
26278
26816
  },
26279
- runtimeState: UpdateStatusSchema
26817
+ runtimeState: UpdateStatusSchema,
26818
+ /**
26819
+ * Runtime-state durability: **session** — a restored `inProgress: true` describes an update that is no longer running; versions are re-probed at boot.
26820
+ *
26821
+ * See `RuntimeStateDurability`. Enforced by
26822
+ * `scripts/check-runtime-state-durability.ts`.
26823
+ */
26824
+ durability: "session"
26280
26825
  };
26281
26826
  var UserSummarySchema = object({
26282
26827
  id: string(),
@@ -26610,7 +27155,14 @@ var vacuumControlCapability = {
26610
27155
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
26611
27156
  * slice for live state + battery + fan-speed changes.
26612
27157
  */
26613
- runtimeState: VacuumControlStatusSchema
27158
+ runtimeState: VacuumControlStatusSchema,
27159
+ /**
27160
+ * Runtime-state durability: **session** — as `media-player` — a restored `state: cleaning` is a robot that is not cleaning.
27161
+ *
27162
+ * See `RuntimeStateDurability`. Enforced by
27163
+ * `scripts/check-runtime-state-durability.ts`.
27164
+ */
27165
+ durability: "session"
26614
27166
  };
26615
27167
  var ValveStatusSchema = object({
26616
27168
  /** Lifecycle state of the valve. */
@@ -26662,7 +27214,14 @@ var valveCapability = {
26662
27214
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
26663
27215
  * slice for live position changes during a move.
26664
27216
  */
26665
- runtimeState: ValveStatusSchema
27217
+ runtimeState: ValveStatusSchema,
27218
+ /**
27219
+ * Runtime-state durability: **session** — as `brightness`.
27220
+ *
27221
+ * See `RuntimeStateDurability`. Enforced by
27222
+ * `scripts/check-runtime-state-durability.ts`.
27223
+ */
27224
+ durability: "session"
26666
27225
  };
26667
27226
  /**
26668
27227
  * Vibration / shake / impact sensor. Drives Home Assistant
@@ -26684,7 +27243,17 @@ var vibrationCapability = {
26684
27243
  schema: VibrationStatusSchema,
26685
27244
  kind: "push"
26686
27245
  },
26687
- runtimeState: VibrationStatusSchema
27246
+ runtimeState: VibrationStatusSchema,
27247
+ /**
27248
+ * Runtime-state durability: **restored** — as `smoke`.
27249
+ *
27250
+ * See `RuntimeStateDurability`. Enforced by
27251
+ * `scripts/check-runtime-state-durability.ts`.
27252
+ */
27253
+ durability: "restored",
27254
+ /** Clock fields: written, but excluded from the compare that decides
27255
+ * whether persisting is worth a SQLite commit. */
27256
+ volatileStateFields: ["lastChangedAt"]
26688
27257
  };
26689
27258
  /**
26690
27259
  * Water heater / boiler cap. Models HA `water_heater.*` entities — a
@@ -26758,7 +27327,14 @@ var waterHeaterCapability = {
26758
27327
  * Runtime-state slice — mirrored by the kernel. UI controls watch the
26759
27328
  * slice for live temperature / mode / away changes.
26760
27329
  */
26761
- runtimeState: WaterHeaterStatusSchema
27330
+ runtimeState: WaterHeaterStatusSchema,
27331
+ /**
27332
+ * Runtime-state durability: **session** — as `climate-control`.
27333
+ *
27334
+ * See `RuntimeStateDurability`. Enforced by
27335
+ * `scripts/check-runtime-state-durability.ts`.
27336
+ */
27337
+ durability: "session"
26762
27338
  };
26763
27339
  /**
26764
27340
  * Weather provider cap. Models HA `weather.*` entities — a read-only
@@ -26817,7 +27393,14 @@ var weatherCapability = {
26817
27393
  * Runtime-state slice — mirrored by the kernel. The UI reads the
26818
27394
  * current conditions directly from the slice on each weather push.
26819
27395
  */
26820
- runtimeState: WeatherStatusSchema
27396
+ runtimeState: WeatherStatusSchema,
27397
+ /**
27398
+ * Runtime-state durability: **session** — a forecast is stale the moment the hub is down; the provider re-fetches on connect.
27399
+ *
27400
+ * See `RuntimeStateDurability`. Enforced by
27401
+ * `scripts/check-runtime-state-durability.ts`.
27402
+ */
27403
+ durability: "session"
26821
27404
  };
26822
27405
  /**
26823
27406
  * Per-zone occupancy aggregation produced by the analytics frame
@@ -26979,7 +27562,14 @@ var zoneAnalyticsCapability = {
26979
27562
  * automatically; the explicit `getCurrentSnapshot` cap method is
26980
27563
  * still useful for one-off polls without a subscription.
26981
27564
  */
26982
- runtimeState: CameraOccupancySnapshotSchema
27565
+ runtimeState: CameraOccupancySnapshotSchema,
27566
+ /**
27567
+ * Runtime-state durability: **session** — per-frame analytics; with `audio-metrics` it is ~90 % of the offered write rate. Re-derived on the next frame.
27568
+ *
27569
+ * See `RuntimeStateDurability`. Enforced by
27570
+ * `scripts/check-runtime-state-durability.ts`.
27571
+ */
27572
+ durability: "session"
26983
27573
  };
26984
27574
  /**
26985
27575
  * Stages a {@link ZoneRule} can apply to. Discriminator on the rules
@@ -27057,7 +27647,14 @@ var zoneRulesCapability = {
27057
27647
  motion: array(ZoneRuleSchema).readonly(),
27058
27648
  detection: array(ZoneRuleSchema).readonly(),
27059
27649
  package: array(ZoneRuleSchema).readonly()
27060
- })
27650
+ }),
27651
+ /**
27652
+ * Runtime-state durability: **restored** — operator intent, mutation-only, same argument as `zones`.
27653
+ *
27654
+ * See `RuntimeStateDurability`. Enforced by
27655
+ * `scripts/check-runtime-state-durability.ts`.
27656
+ */
27657
+ durability: "restored"
27061
27658
  };
27062
27659
  /**
27063
27660
  * Accessory device helpers — shared across drivers.
@@ -33744,6 +34341,7 @@ Object.freeze({
33744
34341
  "network-access": "ingress",
33745
34342
  "smtp-provider": "email"
33746
34343
  });
34344
+ new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
33747
34345
  new Set(["devices", "classes"]);
33748
34346
  /**
33749
34347
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
@@ -34021,25 +34619,32 @@ object({
34021
34619
  var NativeLeaseAdmissionSchema = _enum(["all", "inferred"]);
34022
34620
  object({
34023
34621
  /**
34024
- * How long a retained native frame is served before it counts as a miss.
34622
+ * How many delivered frames the worker HOLDS at once, waiting for each one's
34623
+ * detection result.
34025
34624
  *
34026
- * Must cover the FULL late-crop horizon: detection inference + the
34027
- * cross-process inference-result hop to hub post-analysis + tracking + the
34028
- * tRPC crop round-trip back. Below ~500 ms the busiest cameras' subject crops
34029
- * outrun it and fall back to the ≤640 detection frame; above ~3 s the resident
34030
- * RAM per busy camera grows linearly with no measured hit-rate gain.
34625
+ * This replaced a TTL on 2026-08-13, and the replacement is the whole point:
34626
+ * a time window was never related to the event the pixels were waiting for.
34627
+ * A held frame now lives from delivery until the runner has its `FrameResult`
34628
+ * at which moment the runner cuts the subject tiles it actually wanted and
34629
+ * releases the frame. The bound exists only so a runner that stops answering
34630
+ * cannot pin RAM: above it the OLDEST held frame is dropped and counted.
34631
+ *
34632
+ * Sizing: the steady state is `inferenceLatency × deliveredFps`, measured at
34633
+ * 40-160 ms × ≤25 fps = 1-4 frames. The default leaves headroom for a hiccup
34634
+ * without ever approaching the old resident set (43 frames × 24.9 MB at 4K).
34635
+ * Raising it does not buy hit rate — it buys tolerance for a slow runner, and
34636
+ * `holdOverflow` on the metrics line is what says you need it.
34031
34637
  */
34032
- ttlMs: number().int().min(250).max(1e4),
34638
+ holdFrames: number().int().min(1).max(64),
34033
34639
  /**
34034
34640
  * Hard per-decode-worker RAM ceiling for retained native frames, in MB.
34035
34641
  *
34036
- * Intended as a SAFETY ceiling with the TTL as the effective cap — but check
34037
- * which one is actually binding before reasoning from that. At the shipped
34038
- * 1024 MB and a 2 800 ms TTL, a 4K camera hits the CEILING first (~43 frames
34039
- * at ~24 MB each) and the TTL never gets to expire anything; `leaseMb` /
34040
- * `leaseFrames` on the metrics line say which. When the ceiling binds, a
34041
- * change that admits fewer frames buys retention WINDOW at constant RAM
34042
- * rather than giving RAM back — lower this knob if RAM is what you wanted.
34642
+ * Since 2026-08-13 this is a SAFETY ceiling and nothing else: `holdFrames`
34643
+ * is what decides how much is held, and the ceiling is the number above which
34644
+ * something is wrong. Before that it was the effective cap at 1024 MB with
34645
+ * a 2 800 ms TTL a 4K camera sat pinned at `leaseMb:1020, leaseFrames:43`
34646
+ * with the TTL expiring nothing, which is exactly the confusion the hold
34647
+ * removes. `leaseMb` / `leaseFrames` still say what is resident.
34043
34648
  * `0` DISABLES the lease entirely and falls the worker back to the tiny
34044
34649
  * leak-prone GPU surface ring (~85% crop miss; that is what the lease exists
34045
34650
  * to replace).
@@ -34065,22 +34670,45 @@ object({
34065
34670
  * there is the signal that some caller names frames outside the inference set
34066
34671
  * and that this must go back to `all`.
34067
34672
  */
34068
- admission: NativeLeaseAdmissionSchema
34673
+ admission: NativeLeaseAdmissionSchema,
34674
+ /**
34675
+ * RAM ceiling per decode worker, in MB, for the SUBJECT TILES — the
34676
+ * compressed native crops the worker cuts at the moment a frame's detection
34677
+ * result arrives, and keeps long after the frame itself is freed.
34678
+ *
34679
+ * This is the knob that replaced the old retention window, and it buys about
34680
+ * three orders of magnitude more of it: a tile is one subject at native
34681
+ * resolution, JPEG-encoded (~60-120 KB on a 4K person), against ~24.9 MB for
34682
+ * the frame it was cut from. A frame on which nothing was detected costs
34683
+ * nothing at all, which is the real change — the old lease paid per FRAME and
34684
+ * was interrogated per SUBJECT.
34685
+ *
34686
+ * `0` DISABLES tiles, leaving only the hold window and the ≤640 RAM
34687
+ * fallback — i.e. the pre-2026-08-13 miss profile. Set it there only to
34688
+ * reproduce that.
34689
+ */
34690
+ tileBudgetMb: number().int().min(0).max(1024)
34069
34691
  });
34070
34692
  /**
34071
- * The values in force when the operator has set nothing — byte-for-byte the
34072
- * constants the decode worker shipped with as env-var defaults, so making these
34073
- * settings changed no behaviour on the day it landed.
34693
+ * The values in force when the operator has set nothing.
34694
+ *
34695
+ * `budgetMb` stays at 1024 on the day the hold landed, deliberately: it stopped
34696
+ * being the retention window and became the OOM ceiling, and lowering a ceiling
34697
+ * in the same change that redefines it would make a regression and a retune
34698
+ * indistinguishable. Cut it once `tileHits` / `holdOverflow` have been read on
34699
+ * live traffic.
34074
34700
  */
34075
34701
  var DEFAULT_NATIVE_LEASE_SETTINGS = {
34076
- ttlMs: 1200,
34702
+ holdFrames: 8,
34077
34703
  budgetMb: 1024,
34078
34704
  activityMs: 15e3,
34705
+ tileBudgetMb: 64,
34079
34706
  admission: "inferred"
34080
34707
  };
34081
- DEFAULT_NATIVE_LEASE_SETTINGS.ttlMs;
34708
+ DEFAULT_NATIVE_LEASE_SETTINGS.holdFrames;
34082
34709
  DEFAULT_NATIVE_LEASE_SETTINGS.budgetMb;
34083
34710
  DEFAULT_NATIVE_LEASE_SETTINGS.activityMs;
34711
+ DEFAULT_NATIVE_LEASE_SETTINGS.tileBudgetMb;
34084
34712
  DEFAULT_NATIVE_LEASE_SETTINGS.admission;
34085
34713
  //#endregion
34086
34714
  //#region src/config.ts