@camstack/addon-osd-manager 0.1.150 → 0.1.152

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 (18) hide show
  1. package/dist/{MotionZonesSettings-BsoHRAwv.mjs → MotionZonesSettings-D5c6b4QP.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-CzteSZYz.mjs → PrivacyMaskSettings-DMve7K-c.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-lNMMCYld.mjs → SceneMonitorEditor-e1z_C79o.mjs} +3 -3
  4. package/dist/_stub.js +143 -143
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-c-xC4wkm.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-C8rKpVdZ.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DQC5oc2I.mjs +26 -0
  7. package/dist/{hostInit-Dt8cFobm.mjs → hostInit-B8_o5hJj.mjs} +3 -3
  8. package/dist/index.js +580 -332
  9. package/dist/index.mjs +580 -332
  10. package/dist/{player-overlays-DXCWBCD_.mjs → player-overlays-BchlNvHO.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-CyswsC7o.mjs → responsive-DRZ6VoYV.mjs} +1 -1
  13. package/dist/{scene-monitor-copy-BVg1hi48.mjs → scene-monitor-copy-CmtMsrJb.mjs} +1 -1
  14. package/dist/{square-B7BGSM9i.mjs → square-BYLYL5VB.mjs} +1 -1
  15. package/dist/{trash-2-CuhyORxB.mjs → trash-2-BdEqSrHt.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-CbJBbGRZ.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-p2S4DkBF.mjs} +1 -1
  17. package/package.json +2 -2
  18. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B5zRUNv0.mjs +0 -26
package/dist/index.js CHANGED
@@ -11901,6 +11901,103 @@ var authProviderCapability = {
11901
11901
  mount: { kind: "skip" }
11902
11902
  };
11903
11903
  /**
11904
+ * How long one snapshot may take end to end: the pinned copy of the whole
11905
+ * database plus `PRAGMA integrity_check` over the copy, on the hub's slowest
11906
+ * disk. ~800 MB at a pessimistic 10 MB/s is under 3 minutes; the bound is
11907
+ * generous because a timeout fails the whole backup run.
11908
+ */
11909
+ var DATABASE_SNAPSHOT_TIMEOUT_MS = 30 * 6e4;
11910
+ /**
11911
+ * The databases an owner snapshots — declared by the OWNER, so a caller never
11912
+ * guesses which file is live from what sits beside it (a cleanly closed
11913
+ * database has no sidecars and is just as owned). Paths are absolute on the
11914
+ * owner's host.
11915
+ */
11916
+ var DatabaseSnapshotTargetsSchema = object({ databases: array(object({ livePath: string() })) });
11917
+ /** One b-tree table of the snapshot and the rows it holds. */
11918
+ var DatabaseSnapshotTableSchema = object({
11919
+ name: string(),
11920
+ rows: number().int().nonnegative()
11921
+ });
11922
+ /**
11923
+ * What the owner wrote. Every path is ABSOLUTE on the owner's host — the
11924
+ * caller pins the call to its own node, so it can read the file; a caller
11925
+ * that finds the path outside its own data root refuses it rather than
11926
+ * archiving something it cannot place.
11927
+ */
11928
+ var DatabaseSnapshotResultSchema = object({
11929
+ /** The live database the snapshot copies. Never to be copied as a file. */
11930
+ livePath: string(),
11931
+ /** The verified copy: `<livePath>` + {@link DATABASE_SNAPSHOT_SUFFIX}. */
11932
+ snapshotPath: string(),
11933
+ sizeBytes: number().int().nonnegative(),
11934
+ pageCount: number().int().nonnegative(),
11935
+ pageSize: number().int().positive(),
11936
+ /**
11937
+ * Every b-tree table with its row count. Read inside the SAME read
11938
+ * transaction the copy was taken from, and found EQUAL on the copy — so
11939
+ * this is the content of the snapshot, not an estimate of the source.
11940
+ */
11941
+ tables: array(DatabaseSnapshotTableSchema),
11942
+ /** Wall time of the pinned copy (row counts included). */
11943
+ copyMs: number().nonnegative(),
11944
+ /** Wall time of the checks on the copy (`integrity_check`, row counts). */
11945
+ verifyMs: number().nonnegative(),
11946
+ /** When the pinned read transaction began (ms epoch) — the snapshot's instant. */
11947
+ takenAt: number()
11948
+ });
11949
+ /**
11950
+ * A snapshot as an ARCHIVE records it (`ArchiveManifest.sqliteSnapshots`):
11951
+ * the same facts, with both paths relative to the archive root (the node's
11952
+ * data dir), because an archive outlives the host that made it.
11953
+ */
11954
+ var ArchiveSqliteSnapshotSchema = DatabaseSnapshotResultSchema.extend({
11955
+ /** Archive-relative path of the live database the archive does NOT carry. */
11956
+ livePath: string(),
11957
+ /** Archive-relative path of the verified copy it carries in its place. */
11958
+ snapshotPath: string()
11959
+ });
11960
+ /**
11961
+ * database-snapshot — the OWNER of a live SQLite database writes a consistent,
11962
+ * verified copy of it (D693).
11963
+ *
11964
+ * A live database cannot be copied as files: three files written by a live
11965
+ * process cannot be read at one instant from outside it, and a tar of the
11966
+ * `.db` / `-wal` / `-shm` trio was measured torn in 6/6 archives (D679). Nor may
11967
+ * another process open it (D679 rule 2). So the process that owns the
11968
+ * connection writes the copy — through its own connection, on a thread,
11969
+ * pinned to one read transaction — and checks it before handing it out.
11970
+ *
11971
+ * Hub-only and internal: `sqlite-settings` is the registrant, and its consumer
11972
+ * is `backup-orchestrator`, which calls it pinned to its own node so the path
11973
+ * it gets back is on its own host. `auth: 'admin'` explicitly — `internal`
11974
+ * does not gate the mount (see `data-store-provider`).
11975
+ */
11976
+ var databaseSnapshotCapability = {
11977
+ name: "database-snapshot",
11978
+ scope: "system",
11979
+ mode: "singleton",
11980
+ internal: true,
11981
+ methods: {
11982
+ /**
11983
+ * Which databases this owner snapshots. Answered from memory — no file is
11984
+ * touched — so a backup can ask it before deciding anything.
11985
+ */
11986
+ listDatabases: method(object({}), DatabaseSnapshotTargetsSchema, { auth: "admin" }),
11987
+ /**
11988
+ * Write `<db>.backup-snapshot` and return what it holds. Throws — and
11989
+ * leaves no file under the final name — when the copy fails
11990
+ * `integrity_check` or its row counts differ from the pinned source's.
11991
+ * A second call while one is running joins it.
11992
+ */
11993
+ writeSnapshot: method(object({}), DatabaseSnapshotResultSchema, {
11994
+ kind: "mutation",
11995
+ auth: "admin",
11996
+ timeoutMs: DATABASE_SNAPSHOT_TIMEOUT_MS
11997
+ })
11998
+ }
11999
+ };
12000
+ /**
11904
12001
  * Orchestrator-side destination metadata. The orchestrator computes
11905
12002
  * `id = <addonId>:<subId>` from its provider lookup so consumers
11906
12003
  * (admin UI, restore flow) see one canonical key.
@@ -12011,7 +12108,12 @@ var ArchiveManifestSchema = object({
12011
12108
  locations: array(string()),
12012
12109
  entries: array(ArchiveEntrySchema),
12013
12110
  totalBytes: number(),
12014
- totalFiles: number()
12111
+ totalFiles: number(),
12112
+ /**
12113
+ * Live SQLite databases this archive carries as their OWNER's verified
12114
+ * snapshot instead of as files (D693). Absent on archives made before.
12115
+ */
12116
+ sqliteSnapshots: array(ArchiveSqliteSnapshotSchema).optional()
12015
12117
  });
12016
12118
  var LocationStatSchema = object({
12017
12119
  name: string(),
@@ -13979,6 +14081,65 @@ var CompositionBlockStateSchema = object({
13979
14081
  reason: string().optional(),
13980
14082
  fields: array(CompositionFieldStateSchema)
13981
14083
  });
14084
+ var ComposerBlockSchema = object({
14085
+ id: string().min(1),
14086
+ name: string().min(1).max(120),
14087
+ enabled: boolean(),
14088
+ updatedAt: number(),
14089
+ composition: CompositionSchema
14090
+ });
14091
+ /**
14092
+ * A stored composition row that no longer PARSES — a schema a release
14093
+ * tightened, a row written by a newer build. Sent by id so the composer keeps
14094
+ * its device as a placeholder and reports the block `failed` naming the parse
14095
+ * error. Leaving it out would read, twice, exactly like a delete (D49).
14096
+ */
14097
+ var ComposerUnreadableBlockSchema = object({
14098
+ id: string().min(1),
14099
+ /** The row's stored name column; null when even that is unreadable (the device keeps the name it has). */
14100
+ name: string().nullable(),
14101
+ /** The parse error, in the operator's words. */
14102
+ error: string()
14103
+ });
14104
+ var composerCapability = {
14105
+ name: "composer",
14106
+ scope: "system",
14107
+ mode: "singleton",
14108
+ internal: true,
14109
+ methods: {
14110
+ /**
14111
+ * Not `apply`: tRPC reserves `apply`, `call` and `then` as router keys, and
14112
+ * `router({})` throws on one — every cap router is built at boot.
14113
+ */
14114
+ applyBlocks: method(object({
14115
+ /** The shared Blocks integration; null until `core-blocks` has reconciled it. */
14116
+ integrationId: string().nullable(),
14117
+ /** False when the `core_blocks` read failed: then nothing may be withdrawn (D49). */
14118
+ storeLoaded: boolean(),
14119
+ blocks: array(ComposerBlockSchema),
14120
+ /** Operator restarts: rebuild these runtimes and re-resolve their sources. Stores nothing (D62). */
14121
+ restartBlockIds: array(string()),
14122
+ /** Composition rows that exist but do not parse: held, never withdrawn, reported failed. */
14123
+ unreadableBlocks: array(ComposerUnreadableBlockSchema)
14124
+ }), object({
14125
+ blocks: array(object({
14126
+ blockId: string(),
14127
+ status: CompositionRunStatusSchema,
14128
+ reason: string().optional()
14129
+ })),
14130
+ /**
14131
+ * False when the composer declared and withdrew NOTHING (store not loaded,
14132
+ * integration unknown, its own rows unread or read empty once). An empty
14133
+ * apply the caller owes is paid off only by one the composer acted on (D49).
14134
+ */
14135
+ acted: boolean()
14136
+ }), {
14137
+ kind: "mutation",
14138
+ auth: "admin"
14139
+ }),
14140
+ getStates: method(object({ blockIds: array(string()) }), object({ states: array(CompositionBlockStateSchema) }), { auth: "admin" })
14141
+ }
14142
+ };
13982
14143
  var NUMBER = {
13983
14144
  kind: "fixed",
13984
14145
  result: ["number"]
@@ -14294,65 +14455,6 @@ var coreBlocksCapability = {
14294
14455
  })
14295
14456
  }
14296
14457
  };
14297
- var ComposerBlockSchema = object({
14298
- id: string().min(1),
14299
- name: string().min(1).max(120),
14300
- enabled: boolean(),
14301
- updatedAt: number(),
14302
- composition: CompositionSchema
14303
- });
14304
- /**
14305
- * A stored composition row that no longer PARSES — a schema a release
14306
- * tightened, a row written by a newer build. Sent by id so the composer keeps
14307
- * its device as a placeholder and reports the block `failed` naming the parse
14308
- * error. Leaving it out would read, twice, exactly like a delete (D49).
14309
- */
14310
- var ComposerUnreadableBlockSchema = object({
14311
- id: string().min(1),
14312
- /** The row's stored name column; null when even that is unreadable (the device keeps the name it has). */
14313
- name: string().nullable(),
14314
- /** The parse error, in the operator's words. */
14315
- error: string()
14316
- });
14317
- var composerCapability = {
14318
- name: "composer",
14319
- scope: "system",
14320
- mode: "singleton",
14321
- internal: true,
14322
- methods: {
14323
- /**
14324
- * Not `apply`: tRPC reserves `apply`, `call` and `then` as router keys, and
14325
- * `router({})` throws on one — every cap router is built at boot.
14326
- */
14327
- applyBlocks: method(object({
14328
- /** The shared Blocks integration; null until `core-blocks` has reconciled it. */
14329
- integrationId: string().nullable(),
14330
- /** False when the `core_blocks` read failed: then nothing may be withdrawn (D49). */
14331
- storeLoaded: boolean(),
14332
- blocks: array(ComposerBlockSchema),
14333
- /** Operator restarts: rebuild these runtimes and re-resolve their sources. Stores nothing (D62). */
14334
- restartBlockIds: array(string()),
14335
- /** Composition rows that exist but do not parse: held, never withdrawn, reported failed. */
14336
- unreadableBlocks: array(ComposerUnreadableBlockSchema)
14337
- }), object({
14338
- blocks: array(object({
14339
- blockId: string(),
14340
- status: CompositionRunStatusSchema,
14341
- reason: string().optional()
14342
- })),
14343
- /**
14344
- * False when the composer declared and withdrew NOTHING (store not loaded,
14345
- * integration unknown, its own rows unread or read empty once). An empty
14346
- * apply the caller owes is paid off only by one the composer acted on (D49).
14347
- */
14348
- acted: boolean()
14349
- }), {
14350
- kind: "mutation",
14351
- auth: "admin"
14352
- }),
14353
- getStates: method(object({ blockIds: array(string()) }), object({ states: array(CompositionBlockStateSchema) }), { auth: "admin" })
14354
- }
14355
- };
14356
14458
  /**
14357
14459
  * `custom-model-registry` — collection cap exposing operator-registered
14358
14460
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -17388,6 +17490,251 @@ var deviceManagerCapability = {
17388
17490
  }
17389
17491
  };
17390
17492
  /**
17493
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
17494
+ * name (same pattern as `snapshot`):
17495
+ *
17496
+ * - **Native** providers: registered per-device by device-driver
17497
+ * addons via `ctx.registerNativeCap` — either on a
17498
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
17499
+ * or directly on the camera (Reolink registers at camera level).
17500
+ * Emits an `onPressed` event every time the firmware pushes a
17501
+ * ring; status tracks the last press and a pressCount since start
17502
+ * (diagnostic).
17503
+ *
17504
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
17505
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
17506
+ * device (contact / switch / event-emitter …) into a doorbell for
17507
+ * a camera. `defaultActive: false` — the operator explicitly binds
17508
+ * it per camera in the device-bindings UI, then picks the source
17509
+ * device + trigger in the per-device settings.
17510
+ *
17511
+ * The DeviceEventPropagator re-emits `onPressed` on the camera parent
17512
+ * — subscribers listening at the camera level receive ring events
17513
+ * with `via[]` populated. No code on the parent needed.
17514
+ */
17515
+ var DoorbellStatusSchema = object({
17516
+ /** Ms epoch of the last press. null = never observed since this provider started. */
17517
+ lastPressedAt: number().nullable(),
17518
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
17519
+ pressCountSinceStart: number()
17520
+ });
17521
+ var DoorbellPressEventSchema = object({
17522
+ deviceId: number(),
17523
+ timestamp: number()
17524
+ });
17525
+ /**
17526
+ * The `doorbell-source` extension slot's editable value (D648): the camera's
17527
+ * virtual-doorbell trigger devices, exactly what the wrapper's
17528
+ * `doorbellSources` persists (as `{ deviceId }` records).
17529
+ */
17530
+ var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
17531
+ /**
17532
+ * The `doorbell-source` slot — which devices ring this camera's VIRTUAL
17533
+ * doorbell, owned by the `virtual-doorbell` wrapper over its
17534
+ * `doorbellSources` store (D648). A source rings when one of its
17535
+ * `SOURCE_CAPS` rises. Whether the wrapper is bound to the camera at all is a
17536
+ * different authority (`deviceManager.setWrapperActive`, D62) — the slot
17537
+ * reports it, it never writes it.
17538
+ */
17539
+ var DOORBELL_SOURCE_SLOT = {
17540
+ id: "doorbell-source",
17541
+ label: "Doorbell trigger devices",
17542
+ description: "Any of these becoming active rings this camera's virtual doorbell: a binary sensor when it goes active, a switch when it turns on.",
17543
+ shape: "association",
17544
+ hostDeviceTypes: [DeviceType.Camera],
17545
+ sourceCaps: SOURCE_CAPS,
17546
+ cardinality: "many",
17547
+ value: DoorbellSourceSlotValueSchema
17548
+ };
17549
+ var doorbellCapability = {
17550
+ name: "doorbell",
17551
+ scope: "device",
17552
+ deviceNative: true,
17553
+ mode: "singleton",
17554
+ kind: "wrapper",
17555
+ defaultActive: false,
17556
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
17557
+ extensionSlots: [DOORBELL_SOURCE_SLOT],
17558
+ methods: {},
17559
+ events: {
17560
+ /**
17561
+ * Fires once per physical press. Reolink delivers via Baichuan
17562
+ * push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
17563
+ * release/duration — it's a pulse.
17564
+ */
17565
+ onPressed: {
17566
+ data: DoorbellPressEventSchema,
17567
+ fromState: {
17568
+ field: "lastPressedAt",
17569
+ on: "advance"
17570
+ }
17571
+ } },
17572
+ status: {
17573
+ schema: DoorbellStatusSchema,
17574
+ kind: "push"
17575
+ },
17576
+ /**
17577
+ * Runtime-state slice — last press timestamp + lifetime press count.
17578
+ * Mirrored by the kernel and readable via
17579
+ * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
17580
+ * without subscribing.
17581
+ */
17582
+ runtimeState: DoorbellStatusSchema,
17583
+ /**
17584
+ * 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.
17585
+ *
17586
+ * See `RuntimeStateDurability`. Enforced by
17587
+ * `scripts/check-runtime-state-durability.ts`.
17588
+ */
17589
+ durability: "restored"
17590
+ };
17591
+ var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
17592
+ DeviceType.Camera, DeviceType.Camera;
17593
+ /** A slot's editable value, discriminated on `slot`. */
17594
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
17595
+ /** Every device type at least one slot is offered on — `device-extension`'s `deviceTypes`. */
17596
+ var EXTENSION_HOST_DEVICE_TYPES = [DeviceType.Camera];
17597
+ var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
17598
+ state: literal("unreachable"),
17599
+ reason: string()
17600
+ })]);
17601
+ /** One provider of slots — the D554 source-listing row. */
17602
+ var ExtensionSourceSchema = object({
17603
+ /** The routing prefix of every `slotRef` this provider serves. */
17604
+ source: string(),
17605
+ label: string(),
17606
+ /** Stamped by the dispatcher from the registry (D557). */
17607
+ addonId: string().optional(),
17608
+ availability: ExtensionSourceAvailabilitySchema
17609
+ });
17610
+ /**
17611
+ * How one picked source stands. `missing` — no such device (deleted, or an id
17612
+ * that never existed); `foreign` — a device the slot's owner cannot read from
17613
+ * (a source owned by another addon); `unavailable` — it exists but is not a
17614
+ * valid source now (a container, which is never a pick). A source in any of
17615
+ * these states is SHOWN, never silently dropped or re-pointed.
17616
+ */
17617
+ var ExtensionSourceHealthSchema = _enum([
17618
+ "ok",
17619
+ "missing",
17620
+ "foreign",
17621
+ "unavailable"
17622
+ ]);
17623
+ var ExtensionSlotSourceSchema = object({
17624
+ deviceId: number().int(),
17625
+ /** The device's name, or null when it could not be resolved (`missing`). */
17626
+ name: string().nullable(),
17627
+ health: ExtensionSourceHealthSchema,
17628
+ detail: string().optional()
17629
+ });
17630
+ /**
17631
+ * A DIFFERENT authority that gates whether the slot's sources do anything —
17632
+ * reported read-only, named, never written through this contract (D62). The
17633
+ * virtual doorbell's sources ring only while the wrapper is bound to the
17634
+ * camera, and that binding belongs to `deviceManager.setWrapperActive`.
17635
+ * `unknown` means the gate could not be read; it is not "inactive" (D315).
17636
+ */
17637
+ var ExtensionSlotActivationSchema = object({
17638
+ state: _enum([
17639
+ "active",
17640
+ "inactive",
17641
+ "unknown"
17642
+ ]),
17643
+ authority: string(),
17644
+ detail: string().optional()
17645
+ });
17646
+ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
17647
+ state: literal("ok"),
17648
+ /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
17649
+ slotRef: string(),
17650
+ slot: ExtensionSlotIdSchema,
17651
+ /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
17652
+ authority: string(),
17653
+ activation: ExtensionSlotActivationSchema.nullable(),
17654
+ value: ExtensionSlotValueSchema,
17655
+ sources: array(ExtensionSlotSourceSchema),
17656
+ /**
17657
+ * Devices the owner links to the host ON ITS OWN, by structure, and that
17658
+ * are therefore not in `value` (D655): a camera's device-tree children, and
17659
+ * the child a picked container stands for (D356 — a container is never a
17660
+ * pick, its children are). Read-only; the editor shows them and may offer
17661
+ * per-device options on them (the linked-devices tracked-event opt-in).
17662
+ * Absent when the owner links nothing beyond the picks, and from an owner
17663
+ * older than D655.
17664
+ */
17665
+ impliedSources: array(ExtensionSlotSourceSchema).optional()
17666
+ }), object({
17667
+ state: literal("unreachable"),
17668
+ addonId: string(),
17669
+ reason: string()
17670
+ })]);
17671
+ var ExtensionCandidatesSchema = object({ candidates: array(object({
17672
+ deviceId: number().int(),
17673
+ name: string(),
17674
+ type: string(),
17675
+ location: string().nullable(),
17676
+ /** The container a child belongs to — "Spotlight 2" alone does not say which. */
17677
+ parentName: string().nullable(),
17678
+ /** Pre-marked by the owner's suggestion rule. NEVER saved on its own (D356). */
17679
+ suggested: boolean(),
17680
+ /** Why it is suggested ("same location"), null when it is not. */
17681
+ reason: string().nullable()
17682
+ })) });
17683
+ /**
17684
+ * What a write did. `none` — applied in place. `host` / `parent` — the owner
17685
+ * reloaded the host (or, for an accessory child, its parent) because the
17686
+ * write changed what the host REGISTERS. A slot never adds a capability to its
17687
+ * host — that is a composition (D663) — so today's owners answer `none`.
17688
+ */
17689
+ var ExtensionSetResultSchema = object({
17690
+ applied: literal(true),
17691
+ reload: _enum([
17692
+ "none",
17693
+ "host",
17694
+ "parent"
17695
+ ])
17696
+ });
17697
+ var ExtensionSlotRefInputSchema = object({
17698
+ deviceId: number().int(),
17699
+ slotRef: string().min(1)
17700
+ });
17701
+ var ExtensionSetSlotInputSchema = ExtensionSlotRefInputSchema.extend({ value: ExtensionSlotValueSchema });
17702
+ var deviceExtensionCapability = {
17703
+ name: "device-extension",
17704
+ scope: "device",
17705
+ mode: "collection",
17706
+ kind: "wrapper",
17707
+ defaultActive: true,
17708
+ deviceTypes: EXTENSION_HOST_DEVICE_TYPES,
17709
+ deviceConfig: { ui: {
17710
+ kind: "widget",
17711
+ widgetId: "host/extensions-panel",
17712
+ tab: "general",
17713
+ label: "Extensions",
17714
+ order: 72
17715
+ } },
17716
+ methods: {
17717
+ /** One row per slot owner — the D554 source listing `slotRef` routes by. */
17718
+ listSources: method(object({ deviceId: number().int() }), array(ExtensionSourceSchema)),
17719
+ /**
17720
+ * Every slot this device is offered, read by each owner from its own
17721
+ * store. A provider that throws is ONE `unreachable` row (D554.4).
17722
+ */
17723
+ listSlots: method(object({ deviceId: number().int() }), array(ExtensionSlotRowSchema)),
17724
+ /** The devices the slot's editor may offer, suggestions pre-marked (D356). */
17725
+ listCandidates: method(ExtensionSlotRefInputSchema, ExtensionCandidatesSchema),
17726
+ /**
17727
+ * Write one slot through its owner's EXISTING write path. The owner
17728
+ * validates (a container is never a linked device, a camera is never its
17729
+ * own source) and refuses by name before persisting.
17730
+ */
17731
+ setSlot: method(ExtensionSetSlotInputSchema, ExtensionSetResultSchema, {
17732
+ kind: "mutation",
17733
+ auth: "admin"
17734
+ })
17735
+ }
17736
+ };
17737
+ /**
17391
17738
  * Per-field ownership (D663). A composition may CLAIM fields of a native
17392
17739
  * cap's runtime-state slice on an existing device: `replace` overlays the
17393
17740
  * claimed fields on the native slice, `add` builds a slice the device does
@@ -20875,17 +21222,25 @@ var NcScheduleSchema = object({
20875
21222
  */
20876
21223
  var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
20877
21224
  /**
21225
+ * How a summary window is SENT (D698): `rule` (absent) = ONE notification with
21226
+ * every section; `section` = one notification per section with something to
21227
+ * say (an unavailable section included), each titled by its section.
21228
+ */
21229
+ var NcStateSummarySplitSchema = _enum(["rule", "section"]);
21230
+ /**
20878
21231
  * `summary`: the rule sends NOTHING per event. When a window of `schedule`
20879
21232
  * CLOSES, it sends one notification with the current state of its kinds
20880
21233
  * (low batteries, consumables to replace, devices offline, updates
20881
- * available, ...), after its own filters. Nothing to report sends nothing.
21234
+ * available, ...), after its own filters — or, with `split: 'section'`
21235
+ * (D698), one notification per section. Nothing to report sends nothing.
20882
21236
  * The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
20883
21237
  * midnight crossing); the send time is each window's END. Refused on the
20884
21238
  * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
20885
21239
  */
20886
21240
  var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
20887
21241
  mode: literal("summary"),
20888
- schedule: NcScheduleSchema
21242
+ schedule: NcScheduleSchema,
21243
+ split: NcStateSummarySplitSchema.optional()
20889
21244
  })]);
20890
21245
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
20891
21246
  var NcPlateMatcherSchema = object({
@@ -21025,8 +21380,14 @@ var NcSensorTransitionSchema = _enum(["raised", "cleared"]);
21025
21380
  /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
21026
21381
  var NcZoneConditionSchema = object({
21027
21382
  ids: array(string().min(1)).min(1),
21028
- /** Quantifier over `ids` — at least one / every one visited. */
21029
- match: _enum(["any", "all"]).default("any")
21383
+ /**
21384
+ * LEGACY quantifier over `ids`. Absent = `any` (at least one visited), which
21385
+ * is what both editors mean and all they write since D690: "all of" was
21386
+ * removed from the editors because on an immediate rule it asked one
21387
+ * detection to stand in every zone at once. Still ACCEPTED, and `all` still
21388
+ * evaluated, so a stored rule keeps parsing and meaning what it meant.
21389
+ */
21390
+ match: _enum(["any", "all"]).optional()
21030
21391
  });
21031
21392
  /**
21032
21393
  * The P1 condition set — a flat AND of groups; absent group = pass;
@@ -21530,10 +21891,12 @@ var NcMediaPolicySchema = object({
21530
21891
  /**
21531
21892
  * Cooldown GRANULARITY over the subject's class — how much a fired
21532
21893
  * notification suppresses.
21533
- * - `shared` (default, and the absent value) — one window for the whole
21534
- * rule/scope: a cat silences the next dog for `cooldownSec`.
21535
- * - `per-class` — an independent window per detected class, so cat→dog fires
21536
- * at once and cat→cat still waits.
21894
+ * - `per-class` (default, and the absent value since 2026-10-01 — D36) — an
21895
+ * independent window per detected class: cat→dog fires at once, cat→cat
21896
+ * still waits.
21897
+ * - `shared` — one window for the whole rule/scope: a cat silences the next
21898
+ * dog for `cooldownSec`. Only written when an operator picks it; it is no
21899
+ * longer what an unset rule means.
21537
21900
  *
21538
21901
  * AUDIO subjects are ALWAYS per-class regardless of this setting: a scream
21539
21902
  * must not be swallowed by a bark's window (the precedent this generalizes —
@@ -21548,8 +21911,11 @@ var NcThrottleSchema = object({
21548
21911
  /**
21549
21912
  * Class granularity of the cooldown key. Optional rather than defaulted:
21550
21913
  * a Zod default does NOT run on the addon→addon cap path, so a persisted
21551
- * rule authored before this field simply carries none — and the engine
21552
- * reads absent as `shared`, the pre-existing behaviour.
21914
+ * rule authored before this field simply carries none. The engine used to
21915
+ * read absent as `shared` (D36); since 2026-10-01 it reads absent as
21916
+ * `per-class` — the operator's default — and only an explicit `shared`
21917
+ * still means `shared`. See `cooldownKey` / `keysPerClass` in the rule
21918
+ * engine, and D36's dated note.
21553
21919
  */
21554
21920
  granularity: NcThrottleGranularitySchema.optional()
21555
21921
  });
@@ -21873,11 +22239,23 @@ var NcStateSummaryPreviewSectionSchema = object({
21873
22239
  reason: string().optional()
21874
22240
  });
21875
22241
  /**
22242
+ * One notification a previewed summary would send (D698): the whole summary on
22243
+ * a `rule` split (no `section`), one section's on a `section` split.
22244
+ */
22245
+ var NcStateSummaryPreviewRowSchema = object({
22246
+ section: string().optional(),
22247
+ /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
22248
+ title: string(),
22249
+ text: string(),
22250
+ count: number().int().nonnegative()
22251
+ });
22252
+ /**
21876
22253
  * The summary a rule would send now. `empty` = nothing to report and every
21877
22254
  * section read: such a summary is NOT sent. `dropped` counts the open states
21878
22255
  * the rule's own filters (or a camera mute) left out, by reason.
21879
22256
  * `excludedGroups` names the kind groups selected that have no state to report
21880
- * (backups & exports).
22257
+ * (backups & exports). `split` and `rows` (D698) say how it would be sent —
22258
+ * optional, so an older hub's answer still parses.
21881
22259
  */
21882
22260
  var NcStateSummaryPreviewSchema = object({
21883
22261
  empty: boolean(),
@@ -21885,7 +22263,9 @@ var NcStateSummaryPreviewSchema = object({
21885
22263
  text: string(),
21886
22264
  sections: array(NcStateSummaryPreviewSectionSchema),
21887
22265
  dropped: record(string(), number().int().nonnegative()),
21888
- excludedGroups: array(string())
22266
+ excludedGroups: array(string()),
22267
+ split: NcStateSummarySplitSchema.optional(),
22268
+ rows: array(NcStateSummaryPreviewRowSchema).optional()
21889
22269
  });
21890
22270
  var NcTestResultSchema = object({
21891
22271
  recordId: string(),
@@ -22321,7 +22701,7 @@ var NC_CONDITION_CATALOG = [
22321
22701
  "package-event"
22322
22702
  ],
22323
22703
  phase: "P1",
22324
- description: "Admin zone ids; quantifier any/all over the visited set."
22704
+ description: "The zones the subject must be in (included) or never in (excluded)."
22325
22705
  },
22326
22706
  {
22327
22707
  id: "zonesExclude",
@@ -22344,29 +22724,24 @@ var NC_CONDITION_CATALOG = [
22344
22724
  options: [
22345
22725
  {
22346
22726
  value: "",
22347
- label: "Not set — entering",
22348
- hint: "Entries, and every detection that is not a crossing at all. Exits are excluded. This is what a rule without a crossing condition already does, and choosing it writes nothing to the rule."
22349
- },
22350
- {
22351
- value: "enter",
22352
- label: "Entered a zone",
22353
- hint: "The same matches as \"not set\", stated explicitly: entries and non-crossing detections pass, exits do not."
22727
+ label: "Entering",
22728
+ hint: "Entries and ordinary movement; never an exit."
22354
22729
  },
22355
22730
  {
22356
22731
  value: "exit",
22357
- label: "Left a zone",
22358
- hint: "ONLY an exit crossing. A detection that is not a crossing fails, so this rule cannot fire on ordinary movement — pair it with the zones the subject must have left. Also matches a tripwire crossed against its drawn direction."
22732
+ label: "Leaving",
22733
+ hint: "Only an exit from a zone."
22359
22734
  },
22360
22735
  {
22361
22736
  value: "any",
22362
- label: "Entered or left",
22363
- hint: "No direction filter: entries, exits and non-crossing detections alike. This is the only choice that adds exits to what the rule already matched."
22737
+ label: "Entering or leaving",
22738
+ hint: "Entries, exits and ordinary movement."
22364
22739
  }
22365
22740
  ],
22366
22741
  operator: "in",
22367
22742
  appliesTo: ["immediate"],
22368
22743
  phase: "P1",
22369
- description: "Direction of the zone crossing: enter (the default, and what every rule authored before this did) / exit / any. An exit rule matches only crossings."
22744
+ description: "Which way the subject crosses the zone."
22370
22745
  },
22371
22746
  {
22372
22747
  id: "labelEquals",
@@ -22503,7 +22878,7 @@ var NC_CONDITION_CATALOG = [
22503
22878
  "package-event"
22504
22879
  ],
22505
22880
  phase: "P3",
22506
- description: "Only fire while a scene is matched (the baseline is what we see) or diverged (it demonstrably is not) — “tell me about a person at the door, but only while the bin is still out”. A scene that cannot judge, or one this hub has not read yet, does NOT fire."
22881
+ description: "Only fire while a scene is matched or diverged."
22507
22882
  },
22508
22883
  {
22509
22884
  id: "sensorKinds",
@@ -25694,13 +26069,19 @@ var pipelineAnalyticsCapability = {
25694
26069
  *
25695
26070
  * Operator-driven from the DI Events page (batch delete). Routes through
25696
26071
  * the same widened track-deletion cascade (design §5.2).
26072
+ *
26073
+ * `sparedMediaIds` (D683 Task 14): media rows the cascade LEFT because a
26074
+ * live stationary entry vouches for them (its key frame, owned by a track
26075
+ * that moved and was kept). Released when the entry retires. Optional:
26076
+ * absent from a provider that predates it.
25697
26077
  */
25698
26078
  deleteTracks: method(object({
25699
26079
  deviceId: number(),
25700
26080
  trackIds: array(string()).min(1)
25701
26081
  }), object({
25702
26082
  deleted: number().int(),
25703
- failed: array(string()).readonly()
26083
+ failed: array(string()).readonly(),
26084
+ sparedMediaIds: array(number().int()).readonly().optional()
25704
26085
  }), {
25705
26086
  kind: "mutation",
25706
26087
  auth: "admin"
@@ -28798,6 +29179,25 @@ var SetCameraSensorsInputSchema = object({
28798
29179
  /** `null` removes the attachment: the camera goes back to `not-configured`. */
28799
29180
  config: CameraSensorsConfigSchema.nullable()
28800
29181
  });
29182
+ /**
29183
+ * Why an early occupancy look (D691) was not scheduled.
29184
+ *
29185
+ * - `unknown-camera` — no session was ever dispatched for it, so there is no
29186
+ * node and no config to attach a burst with.
29187
+ * - `out-of-range` — the ask is negative or further out than the orchestrator
29188
+ * will hold a timer for; the parked-object cadence is the answer there.
29189
+ * - `no-burst-producer` — this build has no burst producer at all.
29190
+ * - `already-looked` — this camera's ONE early look since its last session has
29191
+ * been spent. A look's own burst can re-arm a flickering flip, and a look
29192
+ * per arm would chain a burst every ~30 s forever (review H2); the next
29193
+ * real session re-arms the budget.
29194
+ */
29195
+ var OccupancyLookRefusalSchema = _enum([
29196
+ "unknown-camera",
29197
+ "out-of-range",
29198
+ "no-burst-producer",
29199
+ "already-looked"
29200
+ ]);
28801
29201
  var pipelineOrchestratorCapability = {
28802
29202
  name: "pipeline-orchestrator",
28803
29203
  scope: "system",
@@ -28805,6 +29205,32 @@ var pipelineOrchestratorCapability = {
28805
29205
  nodeIdMode: "data",
28806
29206
  exposesDeviceSettings: true,
28807
29207
  methods: {
29208
+ /**
29209
+ * D691 — an occupancy flip is pending on this camera and can commit
29210
+ * `afterMs` from now: dial ONE occupancy burst at the end of its sustain
29211
+ * window instead of leaving the commit to the 300 s parked-object cadence.
29212
+ * Called by the notification centre, the only component that knows a flip
29213
+ * is pending; the dial is the recheck's own (attach on the last session
29214
+ * node, `requestOccupancyBurst`, detach). One pending look per camera; a
29215
+ * session open at the due time is the look, and no burst is dialled.
29216
+ */
29217
+ requestOccupancyLook: method(object({
29218
+ deviceId: number().int().nonnegative(),
29219
+ /** How long from NOW until the pending flip's sustain window ends. RELATIVE,
29220
+ * so the caller's clock and the orchestrator's never have to agree. */
29221
+ afterMs: number().finite(),
29222
+ /** Why, for the log. */
29223
+ reason: string()
29224
+ }), object({
29225
+ scheduled: boolean(),
29226
+ /** When `scheduled`: how long until the burst is dialled. */
29227
+ dueInMs: number().optional(),
29228
+ /** When not `scheduled`: why. */
29229
+ refusedBecause: OccupancyLookRefusalSchema.optional()
29230
+ }), {
29231
+ kind: "mutation",
29232
+ auth: "admin"
29233
+ }),
28808
29234
  /** Internal storage-migration participant lease. While held, dispatch
28809
29235
  * requests are accepted as pending but no new runner attachment starts. */
28810
29236
  pauseForStorageMigration: method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
@@ -32177,251 +32603,6 @@ var videoclipsCapability = {
32177
32603
  }
32178
32604
  };
32179
32605
  /**
32180
- * Doorbell button cap. Two kinds of providers coexist behind this cap
32181
- * name (same pattern as `snapshot`):
32182
- *
32183
- * - **Native** providers: registered per-device by device-driver
32184
- * addons via `ctx.registerNativeCap` — either on a
32185
- * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
32186
- * or directly on the camera (Reolink registers at camera level).
32187
- * Emits an `onPressed` event every time the firmware pushes a
32188
- * ring; status tracks the last press and a pressCount since start
32189
- * (diagnostic).
32190
- *
32191
- * - **Wrapper** provider: the `virtual-doorbell` system builtin
32192
- * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
32193
- * device (contact / switch / event-emitter …) into a doorbell for
32194
- * a camera. `defaultActive: false` — the operator explicitly binds
32195
- * it per camera in the device-bindings UI, then picks the source
32196
- * device + trigger in the per-device settings.
32197
- *
32198
- * The DeviceEventPropagator re-emits `onPressed` on the camera parent
32199
- * — subscribers listening at the camera level receive ring events
32200
- * with `via[]` populated. No code on the parent needed.
32201
- */
32202
- var DoorbellStatusSchema = object({
32203
- /** Ms epoch of the last press. null = never observed since this provider started. */
32204
- lastPressedAt: number().nullable(),
32205
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
32206
- pressCountSinceStart: number()
32207
- });
32208
- var DoorbellPressEventSchema = object({
32209
- deviceId: number(),
32210
- timestamp: number()
32211
- });
32212
- /**
32213
- * The `doorbell-source` extension slot's editable value (D648): the camera's
32214
- * virtual-doorbell trigger devices, exactly what the wrapper's
32215
- * `doorbellSources` persists (as `{ deviceId }` records).
32216
- */
32217
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number().int().positive()) });
32218
- /**
32219
- * The `doorbell-source` slot — which devices ring this camera's VIRTUAL
32220
- * doorbell, owned by the `virtual-doorbell` wrapper over its
32221
- * `doorbellSources` store (D648). A source rings when one of its
32222
- * `SOURCE_CAPS` rises. Whether the wrapper is bound to the camera at all is a
32223
- * different authority (`deviceManager.setWrapperActive`, D62) — the slot
32224
- * reports it, it never writes it.
32225
- */
32226
- var DOORBELL_SOURCE_SLOT = {
32227
- id: "doorbell-source",
32228
- label: "Doorbell trigger devices",
32229
- description: "Any of these becoming active rings this camera's virtual doorbell: a binary sensor when it goes active, a switch when it turns on.",
32230
- shape: "association",
32231
- hostDeviceTypes: [DeviceType.Camera],
32232
- sourceCaps: SOURCE_CAPS,
32233
- cardinality: "many",
32234
- value: DoorbellSourceSlotValueSchema
32235
- };
32236
- var doorbellCapability = {
32237
- name: "doorbell",
32238
- scope: "device",
32239
- deviceNative: true,
32240
- mode: "singleton",
32241
- kind: "wrapper",
32242
- defaultActive: false,
32243
- deviceTypes: [DeviceType.Button, DeviceType.Camera],
32244
- extensionSlots: [DOORBELL_SOURCE_SLOT],
32245
- methods: {},
32246
- events: {
32247
- /**
32248
- * Fires once per physical press. Reolink delivers via Baichuan
32249
- * push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
32250
- * release/duration — it's a pulse.
32251
- */
32252
- onPressed: {
32253
- data: DoorbellPressEventSchema,
32254
- fromState: {
32255
- field: "lastPressedAt",
32256
- on: "advance"
32257
- }
32258
- } },
32259
- status: {
32260
- schema: DoorbellStatusSchema,
32261
- kind: "push"
32262
- },
32263
- /**
32264
- * Runtime-state slice — last press timestamp + lifetime press count.
32265
- * Mirrored by the kernel and readable via
32266
- * `device.state.doorbell.value`. UIs can show "last ring 5m ago"
32267
- * without subscribing.
32268
- */
32269
- runtimeState: DoorbellStatusSchema,
32270
- /**
32271
- * 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.
32272
- *
32273
- * See `RuntimeStateDurability`. Enforced by
32274
- * `scripts/check-runtime-state-durability.ts`.
32275
- */
32276
- durability: "restored"
32277
- };
32278
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
32279
- DeviceType.Camera, DeviceType.Camera;
32280
- /** A slot's editable value, discriminated on `slot`. */
32281
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
32282
- /** Every device type at least one slot is offered on — `device-extension`'s `deviceTypes`. */
32283
- var EXTENSION_HOST_DEVICE_TYPES = [DeviceType.Camera];
32284
- var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
32285
- state: literal("unreachable"),
32286
- reason: string()
32287
- })]);
32288
- /** One provider of slots — the D554 source-listing row. */
32289
- var ExtensionSourceSchema = object({
32290
- /** The routing prefix of every `slotRef` this provider serves. */
32291
- source: string(),
32292
- label: string(),
32293
- /** Stamped by the dispatcher from the registry (D557). */
32294
- addonId: string().optional(),
32295
- availability: ExtensionSourceAvailabilitySchema
32296
- });
32297
- /**
32298
- * How one picked source stands. `missing` — no such device (deleted, or an id
32299
- * that never existed); `foreign` — a device the slot's owner cannot read from
32300
- * (a source owned by another addon); `unavailable` — it exists but is not a
32301
- * valid source now (a container, which is never a pick). A source in any of
32302
- * these states is SHOWN, never silently dropped or re-pointed.
32303
- */
32304
- var ExtensionSourceHealthSchema = _enum([
32305
- "ok",
32306
- "missing",
32307
- "foreign",
32308
- "unavailable"
32309
- ]);
32310
- var ExtensionSlotSourceSchema = object({
32311
- deviceId: number().int(),
32312
- /** The device's name, or null when it could not be resolved (`missing`). */
32313
- name: string().nullable(),
32314
- health: ExtensionSourceHealthSchema,
32315
- detail: string().optional()
32316
- });
32317
- /**
32318
- * A DIFFERENT authority that gates whether the slot's sources do anything —
32319
- * reported read-only, named, never written through this contract (D62). The
32320
- * virtual doorbell's sources ring only while the wrapper is bound to the
32321
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
32322
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
32323
- */
32324
- var ExtensionSlotActivationSchema = object({
32325
- state: _enum([
32326
- "active",
32327
- "inactive",
32328
- "unknown"
32329
- ]),
32330
- authority: string(),
32331
- detail: string().optional()
32332
- });
32333
- var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
32334
- state: literal("ok"),
32335
- /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
32336
- slotRef: string(),
32337
- slot: ExtensionSlotIdSchema,
32338
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
32339
- authority: string(),
32340
- activation: ExtensionSlotActivationSchema.nullable(),
32341
- value: ExtensionSlotValueSchema,
32342
- sources: array(ExtensionSlotSourceSchema),
32343
- /**
32344
- * Devices the owner links to the host ON ITS OWN, by structure, and that
32345
- * are therefore not in `value` (D655): a camera's device-tree children, and
32346
- * the child a picked container stands for (D356 — a container is never a
32347
- * pick, its children are). Read-only; the editor shows them and may offer
32348
- * per-device options on them (the linked-devices tracked-event opt-in).
32349
- * Absent when the owner links nothing beyond the picks, and from an owner
32350
- * older than D655.
32351
- */
32352
- impliedSources: array(ExtensionSlotSourceSchema).optional()
32353
- }), object({
32354
- state: literal("unreachable"),
32355
- addonId: string(),
32356
- reason: string()
32357
- })]);
32358
- var ExtensionCandidatesSchema = object({ candidates: array(object({
32359
- deviceId: number().int(),
32360
- name: string(),
32361
- type: string(),
32362
- location: string().nullable(),
32363
- /** The container a child belongs to — "Spotlight 2" alone does not say which. */
32364
- parentName: string().nullable(),
32365
- /** Pre-marked by the owner's suggestion rule. NEVER saved on its own (D356). */
32366
- suggested: boolean(),
32367
- /** Why it is suggested ("same location"), null when it is not. */
32368
- reason: string().nullable()
32369
- })) });
32370
- /**
32371
- * What a write did. `none` — applied in place. `host` / `parent` — the owner
32372
- * reloaded the host (or, for an accessory child, its parent) because the
32373
- * write changed what the host REGISTERS. A slot never adds a capability to its
32374
- * host — that is a composition (D663) — so today's owners answer `none`.
32375
- */
32376
- var ExtensionSetResultSchema = object({
32377
- applied: literal(true),
32378
- reload: _enum([
32379
- "none",
32380
- "host",
32381
- "parent"
32382
- ])
32383
- });
32384
- var ExtensionSlotRefInputSchema = object({
32385
- deviceId: number().int(),
32386
- slotRef: string().min(1)
32387
- });
32388
- var ExtensionSetSlotInputSchema = ExtensionSlotRefInputSchema.extend({ value: ExtensionSlotValueSchema });
32389
- var deviceExtensionCapability = {
32390
- name: "device-extension",
32391
- scope: "device",
32392
- mode: "collection",
32393
- kind: "wrapper",
32394
- defaultActive: true,
32395
- deviceTypes: EXTENSION_HOST_DEVICE_TYPES,
32396
- deviceConfig: { ui: {
32397
- kind: "widget",
32398
- widgetId: "host/extensions-panel",
32399
- tab: "general",
32400
- label: "Extensions",
32401
- order: 72
32402
- } },
32403
- methods: {
32404
- /** One row per slot owner — the D554 source listing `slotRef` routes by. */
32405
- listSources: method(object({ deviceId: number().int() }), array(ExtensionSourceSchema)),
32406
- /**
32407
- * Every slot this device is offered, read by each owner from its own
32408
- * store. A provider that throws is ONE `unreachable` row (D554.4).
32409
- */
32410
- listSlots: method(object({ deviceId: number().int() }), array(ExtensionSlotRowSchema)),
32411
- /** The devices the slot's editor may offer, suggestions pre-marked (D356). */
32412
- listCandidates: method(ExtensionSlotRefInputSchema, ExtensionCandidatesSchema),
32413
- /**
32414
- * Write one slot through its owner's EXISTING write path. The owner
32415
- * validates (a container is never a linked device, a camera is never its
32416
- * own source) and refuses by name before persisting.
32417
- */
32418
- setSlot: method(ExtensionSetSlotInputSchema, ExtensionSetResultSchema, {
32419
- kind: "mutation",
32420
- auth: "admin"
32421
- })
32422
- }
32423
- };
32424
- /**
32425
32606
  * Optional client-side hints sent at session creation to help the provider
32426
32607
  * pick the best native source. All fields optional — a viewer that knows
32427
32608
  * nothing still gets a sane default. (Relocated from the retired `webrtc`
@@ -46487,6 +46668,7 @@ var ALL_CAPABILITY_DEFINITIONS = [
46487
46668
  coverCapability,
46488
46669
  customModelRegistryCapability,
46489
46670
  dataStoreProviderCapability,
46671
+ databaseSnapshotCapability,
46490
46672
  dayNightCapability,
46491
46673
  decoderCapability,
46492
46674
  detectionPipelineCapability,
@@ -47749,6 +47931,18 @@ Object.freeze({
47749
47931
  addonId: null,
47750
47932
  access: "view"
47751
47933
  },
47934
+ "databaseSnapshot.listDatabases": {
47935
+ capName: "database-snapshot",
47936
+ capScope: "system",
47937
+ addonId: null,
47938
+ access: "view"
47939
+ },
47940
+ "databaseSnapshot.writeSnapshot": {
47941
+ capName: "database-snapshot",
47942
+ capScope: "system",
47943
+ addonId: null,
47944
+ access: "create"
47945
+ },
47752
47946
  "dataStoreProvider.aggregate": {
47753
47947
  capName: "data-store-provider",
47754
47948
  capScope: "system",
@@ -51313,6 +51507,12 @@ Object.freeze({
51313
51507
  addonId: null,
51314
51508
  access: "delete"
51315
51509
  },
51510
+ "pipelineOrchestrator.requestOccupancyLook": {
51511
+ capName: "pipeline-orchestrator",
51512
+ capScope: "system",
51513
+ addonId: null,
51514
+ access: "create"
51515
+ },
51316
51516
  "pipelineOrchestrator.resetNodePipelineDefaults": {
51317
51517
  capName: "pipeline-orchestrator",
51318
51518
  capScope: "system",
@@ -55105,6 +55305,11 @@ Object.freeze({
55105
55305
  form: "single",
55106
55306
  optional: false
55107
55307
  }],
55308
+ "pipelineOrchestrator.requestOccupancyLook": [{
55309
+ name: "deviceId",
55310
+ form: "single",
55311
+ optional: false
55312
+ }],
55108
55313
  "pipelineOrchestrator.resolvePipeline": [{
55109
55314
  name: "deviceId",
55110
55315
  form: "single",
@@ -56346,6 +56551,18 @@ var STATE_SUMMARY_VARS = [
56346
56551
  systemEventKinds: [...SUMMARISABLE],
56347
56552
  systemEventDeliveries: ["summary"]
56348
56553
  },
56554
+ {
56555
+ name: "summarySection",
56556
+ label: "Summary: this section",
56557
+ description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
56558
+ example: "Batterie scariche",
56559
+ group: "system",
56560
+ families: ["rule"],
56561
+ fields: [...RULE_TEXT],
56562
+ kinds: ["system"],
56563
+ systemEventKinds: [...SUMMARISABLE],
56564
+ systemEventDeliveries: ["summary"]
56565
+ },
56349
56566
  ...STATE_SUMMARY_SECTION_VARS.map((v) => ({
56350
56567
  name: v.name,
56351
56568
  label: v.label,
@@ -56422,6 +56639,37 @@ var STATE_SUMMARY_VARS = [
56422
56639
  [...RULE_TEXT],
56423
56640
  ...STATE_SUMMARY_VARS
56424
56641
  ];
56642
+ new Set([
56643
+ "disarmed",
56644
+ "armed_home",
56645
+ "armed_away",
56646
+ "armed_night",
56647
+ "armed_vacation",
56648
+ "armed_custom_bypass",
56649
+ "arming",
56650
+ "disarming",
56651
+ "pending",
56652
+ "triggered",
56653
+ "open",
56654
+ "opening",
56655
+ "closing",
56656
+ "closed",
56657
+ "stopped",
56658
+ "locked",
56659
+ "unlocked",
56660
+ "locking",
56661
+ "unlocking",
56662
+ "jammed",
56663
+ "on",
56664
+ "off",
56665
+ "home",
56666
+ "not_home",
56667
+ "matched",
56668
+ "diverged",
56669
+ "unknown",
56670
+ "latched",
56671
+ "live"
56672
+ ]);
56425
56673
  /**
56426
56674
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
56427
56675
  *