@camstack/addon-ai 0.4.127 → 0.4.128

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 +690 -390
  2. package/dist/addon.mjs +690 -390
  3. package/package.json +2 -2
package/dist/addon.js CHANGED
@@ -5535,7 +5535,7 @@ function number(params) {
5535
5535
  return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
5536
5536
  }
5537
5537
  //#endregion
5538
- //#region ../types/dist/sleep-DlrgY4Qb.mjs
5538
+ //#region ../types/dist/sleep-gU6hqv4_.mjs
5539
5539
  /**
5540
5540
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5541
5541
  * window to float samples (D455).
@@ -7623,7 +7623,59 @@ var DeviceRole = /* @__PURE__ */ function(DeviceRole) {
7623
7623
  return DeviceRole;
7624
7624
  }({});
7625
7625
  //#endregion
7626
- //#region ../types/dist/composition-BK-GTFYb.mjs
7626
+ //#region ../types/dist/composition-Dq42XWp0.mjs
7627
+ /**
7628
+ * The ffmpeg build every CamStack node downloads, per platform and arch.
7629
+ *
7630
+ * ## Why a download, on every node, including the container
7631
+ *
7632
+ * The version of ffmpeg used to be CODE: baked into the image, changeable only
7633
+ * by rebuilding and rolling it. Now it is DATA — this file states the release,
7634
+ * and a node that boots with a different one downloads it. Changing ffmpeg is a
7635
+ * framework bump, not an image rebuild.
7636
+ *
7637
+ * It also settles the licence question by removing it. FFmpeg's licence is
7638
+ * chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
7639
+ * (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
7640
+ * makes the result undistributable. These are GPL v3 builds, and we do not
7641
+ * distribute them: the user's own node fetches the archive from the project
7642
+ * that publishes it, so we convey no GPL work and inherit none of the
7643
+ * obligations that come with conveying one. That is the whole reason the image
7644
+ * no longer installs ffmpeg at all — not a technical preference.
7645
+ *
7646
+ * ## Why jellyfin-ffmpeg
7647
+ *
7648
+ * One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
7649
+ * from one release, with the widest hardware surface of anything maintained:
7650
+ * VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
7651
+ * MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
7652
+ * so the binary is not linked against a driver stack it may not find.
7653
+ *
7654
+ * **What it still needs from the host, measured 2026-09-19.** On the Unraid
7655
+ * HOST, which has no libva, `-vaapi_device` aborts the process outright —
7656
+ * `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
7657
+ * "Assertion in generated code"' failed`. Inside our container, which installs
7658
+ * libva plus the iHD driver, the same command encodes: verified end to end with
7659
+ * `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
7660
+ * KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
7661
+ *
7662
+ * ## Why each artifact carries a checksum and a claim
7663
+ *
7664
+ * The download is now the only source, so `sha256` is verified before anything
7665
+ * is extracted — jellyfin publishes no checksums on its release assets, so
7666
+ * these are ours, taken at the version bump. And `expectedHwaccels` is what the
7667
+ * build is supposed to be able to do: a binary that probes short of its own
7668
+ * claim is a source that changed under us, which is precisely what went
7669
+ * unnoticed for months when a static build turned out to carry no VAAPI at all
7670
+ * (D536).
7671
+ */
7672
+ /** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
7673
+ var FFMPEG_RELEASE = "8.1.2-5";
7674
+ var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
7675
+ function portable(target) {
7676
+ return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
7677
+ }
7678
+ portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7627
7679
  /**
7628
7680
  * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7629
7681
  * valutatore puro del suo FRONTE.
@@ -8427,7 +8479,6 @@ var CompositionSchema = object({
8427
8479
  });
8428
8480
  new Set([
8429
8481
  "track",
8430
- "summary",
8431
8482
  "face",
8432
8483
  "identity",
8433
8484
  "plate",
@@ -9096,6 +9147,82 @@ var ArchivedDebugNoteQueryInputSchema = object({
9096
9147
  /** Max rows returned, newest-first. */
9097
9148
  limit: number$1().int().min(1).max(1e3).optional()
9098
9149
  });
9150
+ var TrackRelationKindSchema = _enum(["same-subject", "different-subjects"]);
9151
+ var TrackSideSchema = _enum(["before", "after"]);
9152
+ var TrackPurityValueSchema = _enum(["mixed", "not-a-subject"]);
9153
+ var TrackRelationSchema = object({
9154
+ id: string(),
9155
+ deviceId: number$1().int(),
9156
+ leftTrackId: string(),
9157
+ rightTrackId: string(),
9158
+ leftSide: TrackSideSchema.nullable(),
9159
+ rightSide: TrackSideSchema.nullable(),
9160
+ relation: TrackRelationKindSchema,
9161
+ presence: literal("continuous").nullable(),
9162
+ leftStartedAt: number$1().int().nullable(),
9163
+ rightStartedAt: number$1().int().nullable(),
9164
+ createdAt: number$1().int(),
9165
+ updatedAt: number$1().int(),
9166
+ author: string().nullable()
9167
+ });
9168
+ var TrackPuritySchema = object({
9169
+ id: string(),
9170
+ deviceId: number$1().int(),
9171
+ sourceTrackId: string(),
9172
+ purity: TrackPurityValueSchema,
9173
+ switchAtMs: number$1().int().nullable(),
9174
+ startedAt: number$1().int().nullable(),
9175
+ createdAt: number$1().int(),
9176
+ updatedAt: number$1().int(),
9177
+ author: string().nullable()
9178
+ });
9179
+ var SetTrackRelationInputSchema = object({
9180
+ deviceId: number$1().int(),
9181
+ trackId: string().min(1),
9182
+ otherTrackId: string().min(1),
9183
+ relation: TrackRelationKindSchema,
9184
+ presence: literal("continuous").optional(),
9185
+ trackSide: TrackSideSchema.optional(),
9186
+ otherSide: TrackSideSchema.optional()
9187
+ });
9188
+ var ClearTrackRelationInputSchema = object({
9189
+ deviceId: number$1().int(),
9190
+ trackId: string().min(1),
9191
+ otherTrackId: string().min(1)
9192
+ });
9193
+ var ClearTrackRelationResultSchema = object({ cleared: boolean() });
9194
+ var SetTrackPurityInputSchema = object({
9195
+ deviceId: number$1().int(),
9196
+ trackId: string().min(1),
9197
+ purity: _enum([
9198
+ "pure",
9199
+ "mixed",
9200
+ "not-a-subject"
9201
+ ]),
9202
+ switchAtMs: number$1().int().optional()
9203
+ });
9204
+ /** `null` when `pure` cleared the row. */
9205
+ var SetTrackPurityResultSchema = object({ purity: TrackPuritySchema.nullable() });
9206
+ var ListTrackRelationsInputSchema = object({
9207
+ deviceId: number$1().int(),
9208
+ trackId: string().optional(),
9209
+ since: number$1().int().optional(),
9210
+ until: number$1().int().optional(),
9211
+ limit: number$1().int().min(1).max(5e3).optional()
9212
+ });
9213
+ var ListTrackRelationsResultSchema = object({
9214
+ relations: array(TrackRelationSchema).readonly(),
9215
+ purities: array(TrackPuritySchema).readonly(),
9216
+ truncated: boolean()
9217
+ });
9218
+ _enum([
9219
+ "unknown-track",
9220
+ "cross-device",
9221
+ "self-relation",
9222
+ "contradiction",
9223
+ "switch-outside-track",
9224
+ "switch-needs-mixed"
9225
+ ]);
9099
9226
  /**
9100
9227
  * Ops-log — the durable, append-only operations audit shared by the
9101
9228
  * recordings and events management surfaces.
@@ -11649,6 +11776,68 @@ method(object({
11649
11776
  password: string()
11650
11777
  }), AuthResultSchema.nullable(), { kind: "mutation" }), method(object({ state: string() }), string()), method(record(string(), string()), AuthResultSchema, { kind: "mutation" }), method(object({ token: string() }), AuthResultSchema.nullable());
11651
11778
  /**
11779
+ * How long one snapshot may take end to end: the pinned copy of the whole
11780
+ * database plus `PRAGMA integrity_check` over the copy, on the hub's slowest
11781
+ * disk. ~800 MB at a pessimistic 10 MB/s is under 3 minutes; the bound is
11782
+ * generous because a timeout fails the whole backup run.
11783
+ */
11784
+ var DATABASE_SNAPSHOT_TIMEOUT_MS = 30 * 6e4;
11785
+ /**
11786
+ * The databases an owner snapshots — declared by the OWNER, so a caller never
11787
+ * guesses which file is live from what sits beside it (a cleanly closed
11788
+ * database has no sidecars and is just as owned). Paths are absolute on the
11789
+ * owner's host.
11790
+ */
11791
+ var DatabaseSnapshotTargetsSchema = object({ databases: array(object({ livePath: string() })) });
11792
+ /** One b-tree table of the snapshot and the rows it holds. */
11793
+ var DatabaseSnapshotTableSchema = object({
11794
+ name: string(),
11795
+ rows: number$1().int().nonnegative()
11796
+ });
11797
+ /**
11798
+ * What the owner wrote. Every path is ABSOLUTE on the owner's host — the
11799
+ * caller pins the call to its own node, so it can read the file; a caller
11800
+ * that finds the path outside its own data root refuses it rather than
11801
+ * archiving something it cannot place.
11802
+ */
11803
+ var DatabaseSnapshotResultSchema = object({
11804
+ /** The live database the snapshot copies. Never to be copied as a file. */
11805
+ livePath: string(),
11806
+ /** The verified copy: `<livePath>` + {@link DATABASE_SNAPSHOT_SUFFIX}. */
11807
+ snapshotPath: string(),
11808
+ sizeBytes: number$1().int().nonnegative(),
11809
+ pageCount: number$1().int().nonnegative(),
11810
+ pageSize: number$1().int().positive(),
11811
+ /**
11812
+ * Every b-tree table with its row count. Read inside the SAME read
11813
+ * transaction the copy was taken from, and found EQUAL on the copy — so
11814
+ * this is the content of the snapshot, not an estimate of the source.
11815
+ */
11816
+ tables: array(DatabaseSnapshotTableSchema),
11817
+ /** Wall time of the pinned copy (row counts included). */
11818
+ copyMs: number$1().nonnegative(),
11819
+ /** Wall time of the checks on the copy (`integrity_check`, row counts). */
11820
+ verifyMs: number$1().nonnegative(),
11821
+ /** When the pinned read transaction began (ms epoch) — the snapshot's instant. */
11822
+ takenAt: number$1()
11823
+ });
11824
+ /**
11825
+ * A snapshot as an ARCHIVE records it (`ArchiveManifest.sqliteSnapshots`):
11826
+ * the same facts, with both paths relative to the archive root (the node's
11827
+ * data dir), because an archive outlives the host that made it.
11828
+ */
11829
+ var ArchiveSqliteSnapshotSchema = DatabaseSnapshotResultSchema.extend({
11830
+ /** Archive-relative path of the live database the archive does NOT carry. */
11831
+ livePath: string(),
11832
+ /** Archive-relative path of the verified copy it carries in its place. */
11833
+ snapshotPath: string()
11834
+ });
11835
+ method(object({}), DatabaseSnapshotTargetsSchema, { auth: "admin" }), method(object({}), DatabaseSnapshotResultSchema, {
11836
+ kind: "mutation",
11837
+ auth: "admin",
11838
+ timeoutMs: DATABASE_SNAPSHOT_TIMEOUT_MS
11839
+ });
11840
+ /**
11652
11841
  * Orchestrator-side destination metadata. The orchestrator computes
11653
11842
  * `id = <addonId>:<subId>` from its provider lookup so consumers
11654
11843
  * (admin UI, restore flow) see one canonical key.
@@ -11759,7 +11948,12 @@ var ArchiveManifestSchema = object({
11759
11948
  locations: array(string()),
11760
11949
  entries: array(ArchiveEntrySchema),
11761
11950
  totalBytes: number$1(),
11762
- totalFiles: number$1()
11951
+ totalFiles: number$1(),
11952
+ /**
11953
+ * Live SQLite databases this archive carries as their OWNER's verified
11954
+ * snapshot instead of as files (D693). Absent on archives made before.
11955
+ */
11956
+ sqliteSnapshots: array(ArchiveSqliteSnapshotSchema).optional()
11763
11957
  });
11764
11958
  var LocationStatSchema = object({
11765
11959
  name: string(),
@@ -13183,6 +13377,116 @@ var CompositionBlockStateSchema = object({
13183
13377
  reason: string().optional(),
13184
13378
  fields: array(CompositionFieldStateSchema)
13185
13379
  });
13380
+ var ComposerBlockSchema = object({
13381
+ id: string().min(1),
13382
+ name: string().min(1).max(120),
13383
+ enabled: boolean(),
13384
+ updatedAt: number$1(),
13385
+ composition: CompositionSchema
13386
+ });
13387
+ /**
13388
+ * A stored composition row that no longer PARSES — a schema a release
13389
+ * tightened, a row written by a newer build. Sent by id so the composer keeps
13390
+ * its device as a placeholder and reports the block `failed` naming the parse
13391
+ * error. Leaving it out would read, twice, exactly like a delete (D49).
13392
+ */
13393
+ var ComposerUnreadableBlockSchema = object({
13394
+ id: string().min(1),
13395
+ /** The row's stored name column; null when even that is unreadable (the device keeps the name it has). */
13396
+ name: string().nullable(),
13397
+ /** The parse error, in the operator's words. */
13398
+ error: string()
13399
+ });
13400
+ method(object({
13401
+ /** The shared Blocks integration; null until `core-blocks` has reconciled it. */
13402
+ integrationId: string().nullable(),
13403
+ /** False when the `core_blocks` read failed: then nothing may be withdrawn (D49). */
13404
+ storeLoaded: boolean(),
13405
+ blocks: array(ComposerBlockSchema),
13406
+ /** Operator restarts: rebuild these runtimes and re-resolve their sources. Stores nothing (D62). */
13407
+ restartBlockIds: array(string()),
13408
+ /** Composition rows that exist but do not parse: held, never withdrawn, reported failed. */
13409
+ unreadableBlocks: array(ComposerUnreadableBlockSchema)
13410
+ }), object({
13411
+ blocks: array(object({
13412
+ blockId: string(),
13413
+ status: CompositionRunStatusSchema,
13414
+ reason: string().optional()
13415
+ })),
13416
+ /**
13417
+ * False when the composer declared and withdrew NOTHING (store not loaded,
13418
+ * integration unknown, its own rows unread or read empty once). An empty
13419
+ * apply the caller owes is paid off only by one the composer acted on (D49).
13420
+ */
13421
+ acted: boolean()
13422
+ }), {
13423
+ kind: "mutation",
13424
+ auth: "admin"
13425
+ }), method(object({ blockIds: array(string()) }), object({ states: array(CompositionBlockStateSchema) }), { auth: "admin" });
13426
+ var NUMBER = {
13427
+ kind: "fixed",
13428
+ result: ["number"]
13429
+ };
13430
+ var BOOLEAN = {
13431
+ kind: "fixed",
13432
+ result: ["boolean"]
13433
+ };
13434
+ Object.freeze({
13435
+ min: NUMBER,
13436
+ max: NUMBER,
13437
+ abs: NUMBER,
13438
+ floor: NUMBER,
13439
+ ceil: NUMBER,
13440
+ sqrt: NUMBER,
13441
+ round: NUMBER,
13442
+ pow: NUMBER,
13443
+ clamp: NUMBER,
13444
+ avg: NUMBER,
13445
+ sum: NUMBER,
13446
+ age: NUMBER,
13447
+ convert: NUMBER,
13448
+ coalesce: { kind: "coalesce" },
13449
+ any: BOOLEAN,
13450
+ all: BOOLEAN,
13451
+ within: BOOLEAN,
13452
+ latest: {
13453
+ kind: "fixed",
13454
+ result: ["number", "null"]
13455
+ },
13456
+ number: {
13457
+ kind: "fixed",
13458
+ result: ["number", "null"]
13459
+ },
13460
+ rose: {
13461
+ kind: "fixed",
13462
+ result: ["number", "null"]
13463
+ },
13464
+ count: NUMBER
13465
+ });
13466
+ `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
13467
+ /**
13468
+ * What a composition may BUILD on a device type (G2, D676): the caps a new
13469
+ * device of that type, or an existing one that lacks them, can be given. The
13470
+ * blockers are the validator's own refusal codes, and a spec compares this
13471
+ * listing with `planComposition` for every capability — the editor can never
13472
+ * offer a feature the save would refuse for the same reason.
13473
+ */
13474
+ var ComposableFieldSchema = object({
13475
+ path: string(),
13476
+ kind: CompositionFieldKindSchema,
13477
+ nullable: boolean(),
13478
+ required: boolean(),
13479
+ enumValues: array(string()).optional()
13480
+ });
13481
+ var ComposableCapabilitiesSchema = object({ caps: array(object({
13482
+ cap: string(),
13483
+ fields: array(ComposableFieldSchema),
13484
+ /** Present for an item-array cap (`consumables.items`). */
13485
+ itemArray: object({
13486
+ path: string(),
13487
+ keyField: string()
13488
+ }).optional()
13489
+ })) });
13186
13490
  /** Where a block runs. The operator chooses — a block driving a device on an
13187
13491
  * agent is the reason placement is not fixed to the hub. */
13188
13492
  var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
@@ -13347,7 +13651,7 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
13347
13651
  }), method(object({ blockId: string() }), CompositionGetReadSchema, { auth: "admin" }), method(object({}), CompositionListReadSchema, { auth: "admin" }), method(object({
13348
13652
  addonId: string().min(1),
13349
13653
  stableId: string().min(1)
13350
- }), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({
13654
+ }), CompositionCustomizationReadSchema, { auth: "admin" }), method(object({ deviceType: _enum(DeviceType) }), ComposableCapabilitiesSchema, { auth: "admin" }), method(object({
13351
13655
  composition: CompositionSchema,
13352
13656
  blockId: string().optional()
13353
13657
  }), CompositionValidationSchema, {
@@ -13355,52 +13659,6 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
13355
13659
  auth: "admin",
13356
13660
  access: "view"
13357
13661
  });
13358
- var ComposerBlockSchema = object({
13359
- id: string().min(1),
13360
- name: string().min(1).max(120),
13361
- enabled: boolean(),
13362
- updatedAt: number$1(),
13363
- composition: CompositionSchema
13364
- });
13365
- /**
13366
- * A stored composition row that no longer PARSES — a schema a release
13367
- * tightened, a row written by a newer build. Sent by id so the composer keeps
13368
- * its device as a placeholder and reports the block `failed` naming the parse
13369
- * error. Leaving it out would read, twice, exactly like a delete (D49).
13370
- */
13371
- var ComposerUnreadableBlockSchema = object({
13372
- id: string().min(1),
13373
- /** The row's stored name column; null when even that is unreadable (the device keeps the name it has). */
13374
- name: string().nullable(),
13375
- /** The parse error, in the operator's words. */
13376
- error: string()
13377
- });
13378
- method(object({
13379
- /** The shared Blocks integration; null until `core-blocks` has reconciled it. */
13380
- integrationId: string().nullable(),
13381
- /** False when the `core_blocks` read failed: then nothing may be withdrawn (D49). */
13382
- storeLoaded: boolean(),
13383
- blocks: array(ComposerBlockSchema),
13384
- /** Operator restarts: rebuild these runtimes and re-resolve their sources. Stores nothing (D62). */
13385
- restartBlockIds: array(string()),
13386
- /** Composition rows that exist but do not parse: held, never withdrawn, reported failed. */
13387
- unreadableBlocks: array(ComposerUnreadableBlockSchema)
13388
- }), object({
13389
- blocks: array(object({
13390
- blockId: string(),
13391
- status: CompositionRunStatusSchema,
13392
- reason: string().optional()
13393
- })),
13394
- /**
13395
- * False when the composer declared and withdrew NOTHING (store not loaded,
13396
- * integration unknown, its own rows unread or read empty once). An empty
13397
- * apply the caller owes is paid off only by one the composer acted on (D49).
13398
- */
13399
- acted: boolean()
13400
- }), {
13401
- kind: "mutation",
13402
- auth: "admin"
13403
- }), method(object({ blockIds: array(string()) }), object({ states: array(CompositionBlockStateSchema) }), { auth: "admin" });
13404
13662
  /**
13405
13663
  * `custom-model-registry` — collection cap exposing operator-registered
13406
13664
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -15249,6 +15507,138 @@ method(object({
15249
15507
  deviceIds: array(number$1()).readonly(),
15250
15508
  caps: array(string()).readonly().optional()
15251
15509
  }), record(string(), record(string(), unknown().nullable())));
15510
+ object({
15511
+ /** Ms epoch of the last press. null = never observed since this provider started. */
15512
+ lastPressedAt: number$1().nullable(),
15513
+ /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
15514
+ pressCountSinceStart: number$1()
15515
+ });
15516
+ object({
15517
+ deviceId: number$1(),
15518
+ timestamp: number$1()
15519
+ });
15520
+ /**
15521
+ * The `doorbell-source` extension slot's editable value (D648): the camera's
15522
+ * virtual-doorbell trigger devices, exactly what the wrapper's
15523
+ * `doorbellSources` persists (as `{ deviceId }` records).
15524
+ */
15525
+ var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number$1().int().positive()) });
15526
+ DeviceType.Camera;
15527
+ DeviceType.Button, DeviceType.Camera;
15528
+ var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
15529
+ DeviceType.Camera, DeviceType.Camera;
15530
+ /** A slot's editable value, discriminated on `slot`. */
15531
+ var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
15532
+ DeviceType.Camera;
15533
+ var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
15534
+ state: literal("unreachable"),
15535
+ reason: string()
15536
+ })]);
15537
+ /** One provider of slots — the D554 source-listing row. */
15538
+ var ExtensionSourceSchema = object({
15539
+ /** The routing prefix of every `slotRef` this provider serves. */
15540
+ source: string(),
15541
+ label: string(),
15542
+ /** Stamped by the dispatcher from the registry (D557). */
15543
+ addonId: string().optional(),
15544
+ availability: ExtensionSourceAvailabilitySchema
15545
+ });
15546
+ /**
15547
+ * How one picked source stands. `missing` — no such device (deleted, or an id
15548
+ * that never existed); `foreign` — a device the slot's owner cannot read from
15549
+ * (a source owned by another addon); `unavailable` — it exists but is not a
15550
+ * valid source now (a container, which is never a pick). A source in any of
15551
+ * these states is SHOWN, never silently dropped or re-pointed.
15552
+ */
15553
+ var ExtensionSourceHealthSchema = _enum([
15554
+ "ok",
15555
+ "missing",
15556
+ "foreign",
15557
+ "unavailable"
15558
+ ]);
15559
+ var ExtensionSlotSourceSchema = object({
15560
+ deviceId: number$1().int(),
15561
+ /** The device's name, or null when it could not be resolved (`missing`). */
15562
+ name: string().nullable(),
15563
+ health: ExtensionSourceHealthSchema,
15564
+ detail: string().optional()
15565
+ });
15566
+ /**
15567
+ * A DIFFERENT authority that gates whether the slot's sources do anything —
15568
+ * reported read-only, named, never written through this contract (D62). The
15569
+ * virtual doorbell's sources ring only while the wrapper is bound to the
15570
+ * camera, and that binding belongs to `deviceManager.setWrapperActive`.
15571
+ * `unknown` means the gate could not be read; it is not "inactive" (D315).
15572
+ */
15573
+ var ExtensionSlotActivationSchema = object({
15574
+ state: _enum([
15575
+ "active",
15576
+ "inactive",
15577
+ "unknown"
15578
+ ]),
15579
+ authority: string(),
15580
+ detail: string().optional()
15581
+ });
15582
+ var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
15583
+ state: literal("ok"),
15584
+ /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
15585
+ slotRef: string(),
15586
+ slot: ExtensionSlotIdSchema,
15587
+ /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
15588
+ authority: string(),
15589
+ activation: ExtensionSlotActivationSchema.nullable(),
15590
+ value: ExtensionSlotValueSchema,
15591
+ sources: array(ExtensionSlotSourceSchema),
15592
+ /**
15593
+ * Devices the owner links to the host ON ITS OWN, by structure, and that
15594
+ * are therefore not in `value` (D655): a camera's device-tree children, and
15595
+ * the child a picked container stands for (D356 — a container is never a
15596
+ * pick, its children are). Read-only; the editor shows them and may offer
15597
+ * per-device options on them (the linked-devices tracked-event opt-in).
15598
+ * Absent when the owner links nothing beyond the picks, and from an owner
15599
+ * older than D655.
15600
+ */
15601
+ impliedSources: array(ExtensionSlotSourceSchema).optional()
15602
+ }), object({
15603
+ state: literal("unreachable"),
15604
+ addonId: string(),
15605
+ reason: string()
15606
+ })]);
15607
+ var ExtensionCandidatesSchema = object({ candidates: array(object({
15608
+ deviceId: number$1().int(),
15609
+ name: string(),
15610
+ type: string(),
15611
+ location: string().nullable(),
15612
+ /** The container a child belongs to — "Spotlight 2" alone does not say which. */
15613
+ parentName: string().nullable(),
15614
+ /** Pre-marked by the owner's suggestion rule. NEVER saved on its own (D356). */
15615
+ suggested: boolean(),
15616
+ /** Why it is suggested ("same location"), null when it is not. */
15617
+ reason: string().nullable()
15618
+ })) });
15619
+ /**
15620
+ * What a write did. `none` — applied in place. `host` / `parent` — the owner
15621
+ * reloaded the host (or, for an accessory child, its parent) because the
15622
+ * write changed what the host REGISTERS. A slot never adds a capability to its
15623
+ * host — that is a composition (D663) — so today's owners answer `none`.
15624
+ */
15625
+ var ExtensionSetResultSchema = object({
15626
+ applied: literal(true),
15627
+ reload: _enum([
15628
+ "none",
15629
+ "host",
15630
+ "parent"
15631
+ ])
15632
+ });
15633
+ var ExtensionSlotRefInputSchema = object({
15634
+ deviceId: number$1().int(),
15635
+ slotRef: string().min(1)
15636
+ });
15637
+ var ExtensionSetSlotInputSchema = ExtensionSlotRefInputSchema.extend({ value: ExtensionSlotValueSchema });
15638
+ method(object({ deviceId: number$1().int() }), array(ExtensionSourceSchema)), method(object({ deviceId: number$1().int() }), array(ExtensionSlotRowSchema)), method(ExtensionSlotRefInputSchema, ExtensionCandidatesSchema), method(ExtensionSetSlotInputSchema, ExtensionSetResultSchema, {
15639
+ kind: "mutation",
15640
+ auth: "admin"
15641
+ });
15252
15642
  /**
15253
15643
  * Per-field ownership (D663). A composition may CLAIM fields of a native
15254
15644
  * cap's runtime-state slice on an existing device: `replace` overlays the
@@ -18329,17 +18719,25 @@ var NcScheduleSchema = object({
18329
18719
  */
18330
18720
  var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
18331
18721
  /**
18722
+ * How a summary window is SENT (D698): `rule` (absent) = ONE notification with
18723
+ * every section; `section` = one notification per section with something to
18724
+ * say (an unavailable section included), each titled by its section.
18725
+ */
18726
+ var NcStateSummarySplitSchema = _enum(["rule", "section"]);
18727
+ /**
18332
18728
  * `summary`: the rule sends NOTHING per event. When a window of `schedule`
18333
18729
  * CLOSES, it sends one notification with the current state of its kinds
18334
18730
  * (low batteries, consumables to replace, devices offline, updates
18335
- * available, ...), after its own filters. Nothing to report sends nothing.
18731
+ * available, ...), after its own filters — or, with `split: 'section'`
18732
+ * (D698), one notification per section. Nothing to report sends nothing.
18336
18733
  * The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
18337
18734
  * midnight crossing); the send time is each window's END. Refused on the
18338
18735
  * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
18339
18736
  */
18340
18737
  var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
18341
18738
  mode: literal("summary"),
18342
- schedule: NcScheduleSchema
18739
+ schedule: NcScheduleSchema,
18740
+ split: NcStateSummarySplitSchema.optional()
18343
18741
  })]);
18344
18742
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
18345
18743
  var NcPlateMatcherSchema = object({
@@ -18479,8 +18877,14 @@ var NcSensorTransitionSchema = _enum(["raised", "cleared"]);
18479
18877
  /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
18480
18878
  var NcZoneConditionSchema = object({
18481
18879
  ids: array(string().min(1)).min(1),
18482
- /** Quantifier over `ids` — at least one / every one visited. */
18483
- match: _enum(["any", "all"]).default("any")
18880
+ /**
18881
+ * LEGACY quantifier over `ids`. Absent = `any` (at least one visited), which
18882
+ * is what both editors mean and all they write since D690: "all of" was
18883
+ * removed from the editors because on an immediate rule it asked one
18884
+ * detection to stand in every zone at once. Still ACCEPTED, and `all` still
18885
+ * evaluated, so a stored rule keeps parsing and meaning what it meant.
18886
+ */
18887
+ match: _enum(["any", "all"]).optional()
18484
18888
  });
18485
18889
  /**
18486
18890
  * The P1 condition set — a flat AND of groups; absent group = pass;
@@ -18984,10 +19388,12 @@ var NcMediaPolicySchema = object({
18984
19388
  /**
18985
19389
  * Cooldown GRANULARITY over the subject's class — how much a fired
18986
19390
  * notification suppresses.
18987
- * - `shared` (default, and the absent value) — one window for the whole
18988
- * rule/scope: a cat silences the next dog for `cooldownSec`.
18989
- * - `per-class` — an independent window per detected class, so cat→dog fires
18990
- * at once and cat→cat still waits.
19391
+ * - `per-class` (default, and the absent value since 2026-10-01 — D36) — an
19392
+ * independent window per detected class: cat→dog fires at once, cat→cat
19393
+ * still waits.
19394
+ * - `shared` — one window for the whole rule/scope: a cat silences the next
19395
+ * dog for `cooldownSec`. Only written when an operator picks it; it is no
19396
+ * longer what an unset rule means.
18991
19397
  *
18992
19398
  * AUDIO subjects are ALWAYS per-class regardless of this setting: a scream
18993
19399
  * must not be swallowed by a bark's window (the precedent this generalizes —
@@ -19002,8 +19408,11 @@ var NcThrottleSchema = object({
19002
19408
  /**
19003
19409
  * Class granularity of the cooldown key. Optional rather than defaulted:
19004
19410
  * a Zod default does NOT run on the addon→addon cap path, so a persisted
19005
- * rule authored before this field simply carries none — and the engine
19006
- * reads absent as `shared`, the pre-existing behaviour.
19411
+ * rule authored before this field simply carries none. The engine used to
19412
+ * read absent as `shared` (D36); since 2026-10-01 it reads absent as
19413
+ * `per-class` — the operator's default — and only an explicit `shared`
19414
+ * still means `shared`. See `cooldownKey` / `keysPerClass` in the rule
19415
+ * engine, and D36's dated note.
19007
19416
  */
19008
19417
  granularity: NcThrottleGranularitySchema.optional()
19009
19418
  });
@@ -19327,11 +19736,23 @@ var NcStateSummaryPreviewSectionSchema = object({
19327
19736
  reason: string().optional()
19328
19737
  });
19329
19738
  /**
19739
+ * One notification a previewed summary would send (D698): the whole summary on
19740
+ * a `rule` split (no `section`), one section's on a `section` split.
19741
+ */
19742
+ var NcStateSummaryPreviewRowSchema = object({
19743
+ section: string().optional(),
19744
+ /** The section's name on a per-section row; every reported section's, joined, on a combined one. */
19745
+ title: string(),
19746
+ text: string(),
19747
+ count: number$1().int().nonnegative()
19748
+ });
19749
+ /**
19330
19750
  * The summary a rule would send now. `empty` = nothing to report and every
19331
19751
  * section read: such a summary is NOT sent. `dropped` counts the open states
19332
19752
  * the rule's own filters (or a camera mute) left out, by reason.
19333
19753
  * `excludedGroups` names the kind groups selected that have no state to report
19334
- * (backups & exports).
19754
+ * (backups & exports). `split` and `rows` (D698) say how it would be sent —
19755
+ * optional, so an older hub's answer still parses.
19335
19756
  */
19336
19757
  var NcStateSummaryPreviewSchema = object({
19337
19758
  empty: boolean(),
@@ -19339,7 +19760,9 @@ var NcStateSummaryPreviewSchema = object({
19339
19760
  text: string(),
19340
19761
  sections: array(NcStateSummaryPreviewSectionSchema),
19341
19762
  dropped: record(string(), number$1().int().nonnegative()),
19342
- excludedGroups: array(string())
19763
+ excludedGroups: array(string()),
19764
+ split: NcStateSummarySplitSchema.optional(),
19765
+ rows: array(NcStateSummaryPreviewRowSchema).optional()
19343
19766
  });
19344
19767
  var NcTestResultSchema = object({
19345
19768
  recordId: string(),
@@ -20355,7 +20778,7 @@ function cameraSensorRefKey(ref) {
20355
20778
  }
20356
20779
  /** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
20357
20780
  var CameraSensorsModeSchema = _enum(["in-addition"]);
20358
- object({
20781
+ var CameraSensorsConfigSchema = object({
20359
20782
  mode: CameraSensorsModeSchema,
20360
20783
  sensors: array(CameraSensorRefSchema).min(1).max(8)
20361
20784
  }).superRefine((value, ctx) => {
@@ -21345,6 +21768,21 @@ var ObjectEventSchema = object({
21345
21768
  keyFrameMediaId: number$1().int().optional(),
21346
21769
  /** Populated by B5 (recording playback URL for this event). */
21347
21770
  mediaUrl: string().optional(),
21771
+ /**
21772
+ * When the change this row records HAPPENED, epoch ms, when that is earlier
21773
+ * than {@link timestamp} and the producer could date it (D684).
21774
+ *
21775
+ * Set on a package PICK-UP row only: its `timestamp` is when the departure
21776
+ * was CONCLUDED — five minutes of unseen observed time after the parcel went
21777
+ * — and a notification's footage has to be cut around the pick-up, not the
21778
+ * empty doormat after it (D682). Stored so every path that rebuilds a
21779
+ * notification subject from the row (the boot reconcile, a dry-run) anchors
21780
+ * on the same instant the live hook did.
21781
+ *
21782
+ * `timestamp` keeps its meaning. Absent on every other row and on every
21783
+ * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
21784
+ */
21785
+ observedAt: number$1().optional(),
21348
21786
  /** The parent track's key-event importance [0,1], propagated to every object
21349
21787
  * event of the track (so an event row can be sorted by importance without a
21350
21788
  * track join). Absent on legacy rows / before the track was scored. */
@@ -21680,131 +22118,6 @@ var RecentTracksQueryInput = object({
21680
22118
  /** See {@link ExcludeSourcesDoc}. */
21681
22119
  excludeSources: array(TrackSourceSchema).optional()
21682
22120
  });
21683
- /**
21684
- * A Summary (D359): the per-camera session envelope that references tracks.
21685
- * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
21686
- * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
21687
- * first frame with a member visible has been materialised (D482 — it was the
21688
- * row's composite key, which embedded the owner and could name another table's
21689
- * row once event ids became per-table rowids).
21690
- */
21691
- var SummarySchema = object({
21692
- id: string(),
21693
- deviceId: number$1(),
21694
- openedAt: number$1(),
21695
- lastActiveAt: number$1(),
21696
- sealedAt: number$1().nullable(),
21697
- memberCount: number$1().int().nonnegative(),
21698
- groupShotMediaId: number$1().int().nullable()
21699
- });
21700
- var SummariesQueryInput = object({
21701
- deviceIds: array(number$1()).min(1),
21702
- since: number$1().optional(),
21703
- until: number$1().optional(),
21704
- limit: number$1().int().min(1).max(200).default(50),
21705
- /** The same member-wise filter {@link listRecentSummaries} takes — one
21706
- * meaning of "dog" on every summary surface. */
21707
- classes: array(string().min(1).max(60)).max(40).optional()
21708
- });
21709
- var SummaryDetailSchema = object({
21710
- summary: SummarySchema,
21711
- /** Members in absorption order, oldest join first. */
21712
- members: array(object({
21713
- trackId: string(),
21714
- /**
21715
- * The track's class at the time it JOINED, or `null` when it was unresolved
21716
- * and the track no longer exists to fill it.
21717
- *
21718
- * The join, not the latest opinion: class voting can re-decide a track's
21719
- * class afterwards, and a group's composition is the history of who came
21720
- * together. A consumer counts these to badge a thumbnail — "two people and a
21721
- * dog" — rather than being handed a second, denormalised tally that would
21722
- * drift from the members it claims to describe.
21723
- */
21724
- className: string().nullable(),
21725
- /** `primary` is the one member that carries the group's identity. */
21726
- role: _enum([
21727
- "primary",
21728
- "sibling",
21729
- "child"
21730
- ]).nullable(),
21731
- joinedAt: number$1(),
21732
- /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
21733
- * at read time. Absent when nothing resolved, never an empty string. */
21734
- label: string().optional(),
21735
- /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
21736
- * recognised nobody, and absent for a member whose track has been pruned —
21737
- * a thumbnail that names the wrong person is worse than one naming nobody. */
21738
- subLabel: string().optional()
21739
- })).readonly()
21740
- });
21741
- /**
21742
- * What a LIST of groups carries: the envelope plus what it is MADE OF.
21743
- *
21744
- * Coarse on purpose — the operator's rule is "only the rough data on the list,
21745
- * we extend when we notice we need more". Enough to draw the card ("two people
21746
- * and a dog") and no more. The MEMBERS, their names and the per-member links
21747
- * are on {@link getSummary}: shipping every member of every group so a client
21748
- * can count them turns a page of two hundred groups into a thousand records to
21749
- * draw two hundred badges.
21750
- */
21751
- var SummaryListItemSchema = object({
21752
- summary: SummarySchema,
21753
- /** Class counts, most numerous first; `className: null` is a member whose
21754
- * class never resolved, counted so the counts add up to `memberCount`. */
21755
- classes: array(object({
21756
- className: string().nullable(),
21757
- count: number$1().int().positive()
21758
- })).readonly()
21759
- });
21760
- var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
21761
- /**
21762
- * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
21763
- *
21764
- * The reel on the cameras page and the Events gallery both ride a cursor, so a
21765
- * summaries feed that could only answer "the last N for these cameras"
21766
- * (`listSummaries`) would make groups available in the timeline and nowhere
21767
- * else. Two feeds with different ordering contracts would be two ideas of what
21768
- * "newest" means, on one screen.
21769
- */
21770
- var RecentSummariesQueryInput = object({
21771
- /** Devices to merge. An empty array yields an empty page. */
21772
- deviceIds: array(number$1()),
21773
- /** Window lower bound on `lastActiveAt` (inclusive). */
21774
- since: number$1().optional(),
21775
- /** Window upper bound on `lastActiveAt` (inclusive). */
21776
- until: number$1().optional(),
21777
- limit: number$1().int().min(1).max(500).default(100),
21778
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
21779
- cursor: string().optional(),
21780
- /**
21781
- * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
21782
- * deliberately the same name: the events filter tree emits one list of ticked
21783
- * taxonomy nodes, and a summaries feed that called it something else would
21784
- * make the operator's one filter component produce two shapes.
21785
- *
21786
- * A question about the MEMBERS: a group matches when ANY of them does. "Show
21787
- * me the arrivals with a dog" has to return the group of four people who had
21788
- * one with them; a filter on the group's own fields could only ever answer
21789
- * about its primary.
21790
- *
21791
- * Matched against all THREE tiers of a member — the class (`animal`), the
21792
- * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
21793
- * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
21794
- * an event kind in the tree and a tier-1 label on an `animal` track; a filter
21795
- * that read only the class would answer "animal" and never "dog".
21796
- *
21797
- * ABSENT or EMPTY means no filter — absent is not a selection of none.
21798
- */
21799
- classes: array(string().min(1).max(60)).max(40).optional()
21800
- });
21801
- var RecentSummariesPageSchema = object({
21802
- /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
21803
- * {@link listSummaries} — one list shape, so a card renders the same
21804
- * whichever feed drew it. */
21805
- summaries: array(SummaryListItemSchema).readonly(),
21806
- nextCursor: string().nullable()
21807
- });
21808
22121
  var RecentTracksPageSchema = object({
21809
22122
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
21810
22123
  tracks: array(TrackSchema).readonly(),
@@ -22316,7 +22629,7 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
22316
22629
  classes: array(string()).optional(),
22317
22630
  /** See {@link ExcludeSourcesDoc}. */
22318
22631
  excludeSources: array(TrackSourceSchema).optional()
22319
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
22632
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number$1() }), _void(), {
22320
22633
  kind: "mutation",
22321
22634
  auth: "admin"
22322
22635
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number$1() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -22370,7 +22683,8 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
22370
22683
  trackIds: array(string()).min(1)
22371
22684
  }), object({
22372
22685
  deleted: number$1().int(),
22373
- failed: array(string()).readonly()
22686
+ failed: array(string()).readonly(),
22687
+ sparedMediaIds: array(number$1().int()).readonly().optional()
22374
22688
  }), {
22375
22689
  kind: "mutation",
22376
22690
  auth: "admin"
@@ -22379,7 +22693,7 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
22379
22693
  deviceId: number$1(),
22380
22694
  trackId: string(),
22381
22695
  flags: TrackFlagsPatchSchema
22382
- }), TrackFlagsSchema, { kind: "mutation" }), method(ArchivedDebugNoteQueryInputSchema, array(ArchivedDebugNoteSchema).readonly(), { kind: "query" }), method(BirthDecisionQueryInputSchema, array(BirthDecisionRecordSchema).readonly(), { kind: "query" }), method(object({}), EventStoreFootprintSchema, {
22696
+ }), TrackFlagsSchema, { kind: "mutation" }), method(ArchivedDebugNoteQueryInputSchema, array(ArchivedDebugNoteSchema).readonly(), { kind: "query" }), method(SetTrackRelationInputSchema, TrackRelationSchema, { kind: "mutation" }), method(ClearTrackRelationInputSchema, ClearTrackRelationResultSchema, { kind: "mutation" }), method(SetTrackPurityInputSchema, SetTrackPurityResultSchema, { kind: "mutation" }), method(ListTrackRelationsInputSchema, ListTrackRelationsResultSchema, { kind: "query" }), method(BirthDecisionQueryInputSchema, array(BirthDecisionRecordSchema).readonly(), { kind: "query" }), method(object({}), EventStoreFootprintSchema, {
22383
22697
  kind: "query",
22384
22698
  auth: "admin"
22385
22699
  }), method(object({ deviceId: number$1().int().optional() }), EventMediaKindBreakdownSchema, {
@@ -24537,7 +24851,64 @@ var NodeInferenceDevicesSchema = object({
24537
24851
  reachable: boolean(),
24538
24852
  devices: array(NodeInferenceDeviceSchema).readonly()
24539
24853
  });
24540
- method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
24854
+ /**
24855
+ * Pipeline Orchestrator capability — global load balancer + camera dispatcher.
24856
+ *
24857
+ * Lives on the hub. Tracks the camera-to-agent assignment map, listens to
24858
+ * device lifecycle events, queries each runner's capacity + hardware
24859
+ * profile, and dispatches `attachCamera` / `detachCamera` cap calls to the
24860
+ * appropriate runner instances. Persists assignments via the multi-level
24861
+ * settings store so they survive hub restarts.
24862
+ *
24863
+ * Strictly the control plane: never sees a frame, never calls inference,
24864
+ * never drains queues. The runner does that work locally on each agent.
24865
+ */
24866
+ /**
24867
+ * The input of `setCameraSensors` (G5, D676): the ONE typed write path of a
24868
+ * camera's attached sensors (D672). `config: null` removes the attachment —
24869
+ * the camera reads `not-configured` again, never `unreadable`.
24870
+ */
24871
+ var SetCameraSensorsInputSchema = object({
24872
+ deviceId: number$1().int().nonnegative(),
24873
+ /** `null` removes the attachment: the camera goes back to `not-configured`. */
24874
+ config: CameraSensorsConfigSchema.nullable()
24875
+ });
24876
+ /**
24877
+ * Why an early occupancy look (D691) was not scheduled.
24878
+ *
24879
+ * - `unknown-camera` — no session was ever dispatched for it, so there is no
24880
+ * node and no config to attach a burst with.
24881
+ * - `out-of-range` — the ask is negative or further out than the orchestrator
24882
+ * will hold a timer for; the parked-object cadence is the answer there.
24883
+ * - `no-burst-producer` — this build has no burst producer at all.
24884
+ * - `already-looked` — this camera's ONE early look since its last session has
24885
+ * been spent. A look's own burst can re-arm a flickering flip, and a look
24886
+ * per arm would chain a burst every ~30 s forever (review H2); the next
24887
+ * real session re-arms the budget.
24888
+ */
24889
+ var OccupancyLookRefusalSchema = _enum([
24890
+ "unknown-camera",
24891
+ "out-of-range",
24892
+ "no-burst-producer",
24893
+ "already-looked"
24894
+ ]);
24895
+ method(object({
24896
+ deviceId: number$1().int().nonnegative(),
24897
+ /** How long from NOW until the pending flip's sustain window ends. RELATIVE,
24898
+ * so the caller's clock and the orchestrator's never have to agree. */
24899
+ afterMs: number$1().finite(),
24900
+ /** Why, for the log. */
24901
+ reason: string()
24902
+ }), object({
24903
+ scheduled: boolean(),
24904
+ /** When `scheduled`: how long until the burst is dialled. */
24905
+ dueInMs: number$1().optional(),
24906
+ /** When not `scheduled`: why. */
24907
+ refusedBecause: OccupancyLookRefusalSchema.optional()
24908
+ }), {
24909
+ kind: "mutation",
24910
+ auth: "admin"
24911
+ }), method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
24541
24912
  kind: "mutation",
24542
24913
  auth: "admin"
24543
24914
  }), method(StorageMigrationLeaseInputSchema, object({ resumed: literal(true) }), {
@@ -24691,7 +25062,10 @@ method(StorageMigrationLeaseInputSchema, object({ paused: literal(true) }), {
24691
25062
  }), CameraSwitchGroupSchema, {
24692
25063
  kind: "mutation",
24693
25064
  auth: "admin"
24694
- }), method(object({ deviceId: number$1() }), CameraStatusSchema), method(object({ deviceIds: array(number$1()).optional() }), array(CameraStatusSchema).readonly()), method(_void(), DiskReconcileJobSchema, {
25065
+ }), method(object({ deviceId: number$1() }), CameraStatusSchema), method(object({ deviceIds: array(number$1()).optional() }), array(CameraStatusSchema).readonly()), method(SetCameraSensorsInputSchema, object({ success: literal(true) }), {
25066
+ kind: "mutation",
25067
+ auth: "admin"
25068
+ }), method(_void(), DiskReconcileJobSchema, {
24695
25069
  kind: "mutation",
24696
25070
  auth: "admin"
24697
25071
  }), method(_void(), DiskReconcileJobSchema, { auth: "admin" }), method(_void(), array(PipelineTemplateSchema).readonly()), method(object({
@@ -26867,138 +27241,6 @@ DeviceType.Camera, method(object({
26867
27241
  kind: "query",
26868
27242
  auth: "protected"
26869
27243
  });
26870
- object({
26871
- /** Ms epoch of the last press. null = never observed since this provider started. */
26872
- lastPressedAt: number$1().nullable(),
26873
- /** Counter since provider start. Resets on reboot. Useful for metrics/debug. */
26874
- pressCountSinceStart: number$1()
26875
- });
26876
- object({
26877
- deviceId: number$1(),
26878
- timestamp: number$1()
26879
- });
26880
- /**
26881
- * The `doorbell-source` extension slot's editable value (D648): the camera's
26882
- * virtual-doorbell trigger devices, exactly what the wrapper's
26883
- * `doorbellSources` persists (as `{ deviceId }` records).
26884
- */
26885
- var DoorbellSourceSlotValueSchema = object({ deviceIds: array(number$1().int().positive()) });
26886
- DeviceType.Camera;
26887
- DeviceType.Button, DeviceType.Camera;
26888
- var ExtensionSlotIdSchema = _enum(["doorbell-source", "linked-devices"]);
26889
- DeviceType.Camera, DeviceType.Camera;
26890
- /** A slot's editable value, discriminated on `slot`. */
26891
- var ExtensionSlotValueSchema = discriminatedUnion("slot", [DoorbellSourceSlotValueSchema.extend({ slot: literal("doorbell-source") }), LinkedDevicesSlotValueSchema.extend({ slot: literal("linked-devices") })]);
26892
- DeviceType.Camera;
26893
- var ExtensionSourceAvailabilitySchema = discriminatedUnion("state", [object({ state: literal("ok") }), object({
26894
- state: literal("unreachable"),
26895
- reason: string()
26896
- })]);
26897
- /** One provider of slots — the D554 source-listing row. */
26898
- var ExtensionSourceSchema = object({
26899
- /** The routing prefix of every `slotRef` this provider serves. */
26900
- source: string(),
26901
- label: string(),
26902
- /** Stamped by the dispatcher from the registry (D557). */
26903
- addonId: string().optional(),
26904
- availability: ExtensionSourceAvailabilitySchema
26905
- });
26906
- /**
26907
- * How one picked source stands. `missing` — no such device (deleted, or an id
26908
- * that never existed); `foreign` — a device the slot's owner cannot read from
26909
- * (a source owned by another addon); `unavailable` — it exists but is not a
26910
- * valid source now (a container, which is never a pick). A source in any of
26911
- * these states is SHOWN, never silently dropped or re-pointed.
26912
- */
26913
- var ExtensionSourceHealthSchema = _enum([
26914
- "ok",
26915
- "missing",
26916
- "foreign",
26917
- "unavailable"
26918
- ]);
26919
- var ExtensionSlotSourceSchema = object({
26920
- deviceId: number$1().int(),
26921
- /** The device's name, or null when it could not be resolved (`missing`). */
26922
- name: string().nullable(),
26923
- health: ExtensionSourceHealthSchema,
26924
- detail: string().optional()
26925
- });
26926
- /**
26927
- * A DIFFERENT authority that gates whether the slot's sources do anything —
26928
- * reported read-only, named, never written through this contract (D62). The
26929
- * virtual doorbell's sources ring only while the wrapper is bound to the
26930
- * camera, and that binding belongs to `deviceManager.setWrapperActive`.
26931
- * `unknown` means the gate could not be read; it is not "inactive" (D315).
26932
- */
26933
- var ExtensionSlotActivationSchema = object({
26934
- state: _enum([
26935
- "active",
26936
- "inactive",
26937
- "unknown"
26938
- ]),
26939
- authority: string(),
26940
- detail: string().optional()
26941
- });
26942
- var ExtensionSlotRowSchema = discriminatedUnion("state", [object({
26943
- state: literal("ok"),
26944
- /** `<source>:<slotId>` — the routing key for `listCandidates` / `setSlot`. */
26945
- slotRef: string(),
26946
- slot: ExtensionSlotIdSchema,
26947
- /** Names the store written (D62), e.g. `virtual-doorbell:doorbellSources`. */
26948
- authority: string(),
26949
- activation: ExtensionSlotActivationSchema.nullable(),
26950
- value: ExtensionSlotValueSchema,
26951
- sources: array(ExtensionSlotSourceSchema),
26952
- /**
26953
- * Devices the owner links to the host ON ITS OWN, by structure, and that
26954
- * are therefore not in `value` (D655): a camera's device-tree children, and
26955
- * the child a picked container stands for (D356 — a container is never a
26956
- * pick, its children are). Read-only; the editor shows them and may offer
26957
- * per-device options on them (the linked-devices tracked-event opt-in).
26958
- * Absent when the owner links nothing beyond the picks, and from an owner
26959
- * older than D655.
26960
- */
26961
- impliedSources: array(ExtensionSlotSourceSchema).optional()
26962
- }), object({
26963
- state: literal("unreachable"),
26964
- addonId: string(),
26965
- reason: string()
26966
- })]);
26967
- var ExtensionCandidatesSchema = object({ candidates: array(object({
26968
- deviceId: number$1().int(),
26969
- name: string(),
26970
- type: string(),
26971
- location: string().nullable(),
26972
- /** The container a child belongs to — "Spotlight 2" alone does not say which. */
26973
- parentName: string().nullable(),
26974
- /** Pre-marked by the owner's suggestion rule. NEVER saved on its own (D356). */
26975
- suggested: boolean(),
26976
- /** Why it is suggested ("same location"), null when it is not. */
26977
- reason: string().nullable()
26978
- })) });
26979
- /**
26980
- * What a write did. `none` — applied in place. `host` / `parent` — the owner
26981
- * reloaded the host (or, for an accessory child, its parent) because the
26982
- * write changed what the host REGISTERS. A slot never adds a capability to its
26983
- * host — that is a composition (D663) — so today's owners answer `none`.
26984
- */
26985
- var ExtensionSetResultSchema = object({
26986
- applied: literal(true),
26987
- reload: _enum([
26988
- "none",
26989
- "host",
26990
- "parent"
26991
- ])
26992
- });
26993
- var ExtensionSlotRefInputSchema = object({
26994
- deviceId: number$1().int(),
26995
- slotRef: string().min(1)
26996
- });
26997
- var ExtensionSetSlotInputSchema = ExtensionSlotRefInputSchema.extend({ value: ExtensionSlotValueSchema });
26998
- method(object({ deviceId: number$1().int() }), array(ExtensionSourceSchema)), method(object({ deviceId: number$1().int() }), array(ExtensionSlotRowSchema)), method(ExtensionSlotRefInputSchema, ExtensionCandidatesSchema), method(ExtensionSetSlotInputSchema, ExtensionSetResultSchema, {
26999
- kind: "mutation",
27000
- auth: "admin"
27001
- });
27002
27244
  /**
27003
27245
  * Optional client-side hints sent at session creation to help the provider
27004
27246
  * pick the best native source. All fields optional — a viewer that knows
@@ -35592,47 +35834,6 @@ DeviceType.Camera, method(object({
35592
35834
  detection: array(ZoneRuleSchema).readonly(),
35593
35835
  package: array(ZoneRuleSchema).readonly()
35594
35836
  });
35595
- var NUMBER = {
35596
- kind: "fixed",
35597
- result: ["number"]
35598
- };
35599
- var BOOLEAN = {
35600
- kind: "fixed",
35601
- result: ["boolean"]
35602
- };
35603
- Object.freeze({
35604
- min: NUMBER,
35605
- max: NUMBER,
35606
- abs: NUMBER,
35607
- floor: NUMBER,
35608
- ceil: NUMBER,
35609
- sqrt: NUMBER,
35610
- round: NUMBER,
35611
- pow: NUMBER,
35612
- clamp: NUMBER,
35613
- avg: NUMBER,
35614
- sum: NUMBER,
35615
- age: NUMBER,
35616
- convert: NUMBER,
35617
- coalesce: { kind: "coalesce" },
35618
- any: BOOLEAN,
35619
- all: BOOLEAN,
35620
- within: BOOLEAN,
35621
- latest: {
35622
- kind: "fixed",
35623
- result: ["number", "null"]
35624
- },
35625
- number: {
35626
- kind: "fixed",
35627
- result: ["number", "null"]
35628
- },
35629
- rose: {
35630
- kind: "fixed",
35631
- result: ["number", "null"]
35632
- },
35633
- count: NUMBER
35634
- });
35635
- `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
35636
35837
  /**
35637
35838
  * Accessory device helpers — shared across drivers.
35638
35839
  *
@@ -38055,6 +38256,12 @@ Object.freeze({
38055
38256
  addonId: null,
38056
38257
  access: "view"
38057
38258
  },
38259
+ "coreBlocks.listComposableCaps": {
38260
+ capName: "core-blocks",
38261
+ capScope: "system",
38262
+ addonId: null,
38263
+ access: "view"
38264
+ },
38058
38265
  "coreBlocks.listCompositions": {
38059
38266
  capName: "core-blocks",
38060
38267
  capScope: "system",
@@ -38133,6 +38340,18 @@ Object.freeze({
38133
38340
  addonId: null,
38134
38341
  access: "view"
38135
38342
  },
38343
+ "databaseSnapshot.listDatabases": {
38344
+ capName: "database-snapshot",
38345
+ capScope: "system",
38346
+ addonId: null,
38347
+ access: "view"
38348
+ },
38349
+ "databaseSnapshot.writeSnapshot": {
38350
+ capName: "database-snapshot",
38351
+ capScope: "system",
38352
+ addonId: null,
38353
+ access: "create"
38354
+ },
38136
38355
  "dataStoreProvider.aggregate": {
38137
38356
  capName: "data-store-provider",
38138
38357
  capScope: "system",
@@ -40869,6 +41088,12 @@ Object.freeze({
40869
41088
  addonId: null,
40870
41089
  access: "create"
40871
41090
  },
41091
+ "pipelineAnalytics.clearTrackRelation": {
41092
+ capName: "pipeline-analytics",
41093
+ capScope: "device",
41094
+ addonId: null,
41095
+ access: "delete"
41096
+ },
40872
41097
  "pipelineAnalytics.clearTracks": {
40873
41098
  capName: "pipeline-analytics",
40874
41099
  capScope: "device",
@@ -41025,12 +41250,6 @@ Object.freeze({
41025
41250
  addonId: null,
41026
41251
  access: "view"
41027
41252
  },
41028
- "pipelineAnalytics.getSummary": {
41029
- capName: "pipeline-analytics",
41030
- capScope: "device",
41031
- addonId: null,
41032
- access: "view"
41033
- },
41034
41253
  "pipelineAnalytics.getTrack": {
41035
41254
  capName: "pipeline-analytics",
41036
41255
  capScope: "device",
@@ -41091,12 +41310,6 @@ Object.freeze({
41091
41310
  addonId: null,
41092
41311
  access: "view"
41093
41312
  },
41094
- "pipelineAnalytics.listRecentSummaries": {
41095
- capName: "pipeline-analytics",
41096
- capScope: "device",
41097
- addonId: null,
41098
- access: "view"
41099
- },
41100
41313
  "pipelineAnalytics.listRecentTracks": {
41101
41314
  capName: "pipeline-analytics",
41102
41315
  capScope: "device",
@@ -41127,13 +41340,13 @@ Object.freeze({
41127
41340
  addonId: null,
41128
41341
  access: "view"
41129
41342
  },
41130
- "pipelineAnalytics.listSummaries": {
41343
+ "pipelineAnalytics.listTrackMedia": {
41131
41344
  capName: "pipeline-analytics",
41132
41345
  capScope: "device",
41133
41346
  addonId: null,
41134
41347
  access: "view"
41135
41348
  },
41136
- "pipelineAnalytics.listTrackMedia": {
41349
+ "pipelineAnalytics.listTrackRelations": {
41137
41350
  capName: "pipeline-analytics",
41138
41351
  capScope: "device",
41139
41352
  addonId: null,
@@ -41253,6 +41466,18 @@ Object.freeze({
41253
41466
  addonId: null,
41254
41467
  access: "create"
41255
41468
  },
41469
+ "pipelineAnalytics.setTrackPurity": {
41470
+ capName: "pipeline-analytics",
41471
+ capScope: "device",
41472
+ addonId: null,
41473
+ access: "create"
41474
+ },
41475
+ "pipelineAnalytics.setTrackRelation": {
41476
+ capName: "pipeline-analytics",
41477
+ capScope: "device",
41478
+ addonId: null,
41479
+ access: "create"
41480
+ },
41256
41481
  "pipelineAnalytics.startStorageMigrationMove": {
41257
41482
  capName: "pipeline-analytics",
41258
41483
  capScope: "device",
@@ -41691,6 +41916,12 @@ Object.freeze({
41691
41916
  addonId: null,
41692
41917
  access: "delete"
41693
41918
  },
41919
+ "pipelineOrchestrator.requestOccupancyLook": {
41920
+ capName: "pipeline-orchestrator",
41921
+ capScope: "system",
41922
+ addonId: null,
41923
+ access: "create"
41924
+ },
41694
41925
  "pipelineOrchestrator.resetNodePipelineDefaults": {
41695
41926
  capName: "pipeline-orchestrator",
41696
41927
  capScope: "system",
@@ -41757,6 +41988,12 @@ Object.freeze({
41757
41988
  addonId: null,
41758
41989
  access: "create"
41759
41990
  },
41991
+ "pipelineOrchestrator.setCameraSensors": {
41992
+ capName: "pipeline-orchestrator",
41993
+ capScope: "system",
41994
+ addonId: null,
41995
+ access: "create"
41996
+ },
41760
41997
  "pipelineOrchestrator.setCameraStepOverride": {
41761
41998
  capName: "pipeline-orchestrator",
41762
41999
  capScope: "system",
@@ -45167,6 +45404,11 @@ Object.freeze({
45167
45404
  form: "single",
45168
45405
  optional: false
45169
45406
  }],
45407
+ "pipelineAnalytics.clearTrackRelation": [{
45408
+ name: "deviceId",
45409
+ form: "single",
45410
+ optional: false
45411
+ }],
45170
45412
  "pipelineAnalytics.clearTracks": [{
45171
45413
  name: "deviceId",
45172
45414
  form: "single",
@@ -45302,11 +45544,6 @@ Object.freeze({
45302
45544
  form: "single",
45303
45545
  optional: true
45304
45546
  }],
45305
- "pipelineAnalytics.listRecentSummaries": [{
45306
- name: "deviceIds",
45307
- form: "array",
45308
- optional: false
45309
- }],
45310
45547
  "pipelineAnalytics.listRecentTracks": [{
45311
45548
  name: "deviceIds",
45312
45549
  form: "array",
@@ -45317,12 +45554,12 @@ Object.freeze({
45317
45554
  form: "array",
45318
45555
  optional: true
45319
45556
  }],
45320
- "pipelineAnalytics.listSummaries": [{
45321
- name: "deviceIds",
45322
- form: "array",
45557
+ "pipelineAnalytics.listTrackMedia": [{
45558
+ name: "deviceId",
45559
+ form: "single",
45323
45560
  optional: false
45324
45561
  }],
45325
- "pipelineAnalytics.listTrackMedia": [{
45562
+ "pipelineAnalytics.listTrackRelations": [{
45326
45563
  name: "deviceId",
45327
45564
  form: "single",
45328
45565
  optional: false
@@ -45397,6 +45634,16 @@ Object.freeze({
45397
45634
  form: "single",
45398
45635
  optional: false
45399
45636
  }],
45637
+ "pipelineAnalytics.setTrackPurity": [{
45638
+ name: "deviceId",
45639
+ form: "single",
45640
+ optional: false
45641
+ }],
45642
+ "pipelineAnalytics.setTrackRelation": [{
45643
+ name: "deviceId",
45644
+ form: "single",
45645
+ optional: false
45646
+ }],
45400
45647
  "pipelineAnalytics.wipeAllAnalytics": [{
45401
45648
  name: "deviceId",
45402
45649
  form: "single",
@@ -45467,6 +45714,11 @@ Object.freeze({
45467
45714
  form: "single",
45468
45715
  optional: false
45469
45716
  }],
45717
+ "pipelineOrchestrator.requestOccupancyLook": [{
45718
+ name: "deviceId",
45719
+ form: "single",
45720
+ optional: false
45721
+ }],
45470
45722
  "pipelineOrchestrator.resolvePipeline": [{
45471
45723
  name: "deviceId",
45472
45724
  form: "single",
@@ -45477,6 +45729,11 @@ Object.freeze({
45477
45729
  form: "single",
45478
45730
  optional: false
45479
45731
  }],
45732
+ "pipelineOrchestrator.setCameraSensors": [{
45733
+ name: "deviceId",
45734
+ form: "single",
45735
+ optional: false
45736
+ }],
45480
45737
  "pipelineOrchestrator.setCameraStepOverride": [{
45481
45738
  name: "deviceId",
45482
45739
  form: "single",
@@ -46623,6 +46880,18 @@ var STATE_SUMMARY_VARS = [
46623
46880
  systemEventKinds: [...SUMMARISABLE],
46624
46881
  systemEventDeliveries: ["summary"]
46625
46882
  },
46883
+ {
46884
+ name: "summarySection",
46885
+ label: "Summary: this section",
46886
+ description: "The section a per-section summary row is about (D698); on a summary sent as one message, every section it reports, comma-separated.",
46887
+ example: "Batterie scariche",
46888
+ group: "system",
46889
+ families: ["rule"],
46890
+ fields: [...RULE_TEXT],
46891
+ kinds: ["system"],
46892
+ systemEventKinds: [...SUMMARISABLE],
46893
+ systemEventDeliveries: ["summary"]
46894
+ },
46626
46895
  ...STATE_SUMMARY_SECTION_VARS.map((v) => ({
46627
46896
  name: v.name,
46628
46897
  label: v.label,
@@ -46699,6 +46968,37 @@ var STATE_SUMMARY_VARS = [
46699
46968
  [...RULE_TEXT],
46700
46969
  ...STATE_SUMMARY_VARS
46701
46970
  ];
46971
+ new Set([
46972
+ "disarmed",
46973
+ "armed_home",
46974
+ "armed_away",
46975
+ "armed_night",
46976
+ "armed_vacation",
46977
+ "armed_custom_bypass",
46978
+ "arming",
46979
+ "disarming",
46980
+ "pending",
46981
+ "triggered",
46982
+ "open",
46983
+ "opening",
46984
+ "closing",
46985
+ "closed",
46986
+ "stopped",
46987
+ "locked",
46988
+ "unlocked",
46989
+ "locking",
46990
+ "unlocking",
46991
+ "jammed",
46992
+ "on",
46993
+ "off",
46994
+ "home",
46995
+ "not_home",
46996
+ "matched",
46997
+ "diverged",
46998
+ "unknown",
46999
+ "latched",
47000
+ "live"
47001
+ ]);
46702
47002
  /**
46703
47003
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
46704
47004
  *