@camstack/addon-provider-dreo 0.2.99 → 0.2.101

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 +292 -58
  2. package/dist/addon.mjs +292 -58
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -35,7 +35,7 @@ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["modu
35
35
  //#endregion
36
36
  let crypto$1 = require("crypto");
37
37
  let events = require("events");
38
- //#region ../types/dist/event-category-DAXzJeTX.mjs
38
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
39
39
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
40
40
  EventCategory["SystemBoot"] = "system.boot";
41
41
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -596,7 +596,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
596
596
  * a package zone — a newly-appeared stationary object of a package class
597
597
  * that cleared the class / zone / dwell / size gates. Payload:
598
598
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
599
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
599
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
600
600
  * Telemetry (D8): the durable record is the `package-events` store row;
601
601
  * this bus topic drives notifier rules + live UI. See
602
602
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5382,7 +5382,7 @@ var ZodIssueCode = {
5382
5382
  var ZodFirstPartyTypeKind;
5383
5383
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5384
5384
  //#endregion
5385
- //#region ../types/dist/sleep-BnujYGPe.mjs
5385
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5386
5386
  /**
5387
5387
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5388
5388
  * window to float samples (D455).
@@ -7349,13 +7349,30 @@ new Set([
7349
7349
  "scene",
7350
7350
  "motion",
7351
7351
  "object",
7352
- "audio"
7352
+ "audio",
7353
+ "mosaic"
7353
7354
  ]);
7354
- new Set([
7355
+ /**
7356
+ * The owner types that are event tables, in the order the event-media resolver
7357
+ * should consider them. Exported so a consumer asking "is this key an event?"
7358
+ * does not re-spell the list and drift from it.
7359
+ */
7360
+ var EVENT_OWNER_TYPES = [
7355
7361
  "motion",
7356
7362
  "object",
7357
7363
  "audio"
7358
- ]);
7364
+ ];
7365
+ /**
7366
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7367
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7368
+ * `(table, id)`).
7369
+ *
7370
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7371
+ * table would otherwise be accepted by the codec and refused at the door, with
7372
+ * nothing failing until a caller asked.
7373
+ */
7374
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7375
+ new Set(EVENT_OWNER_TYPES);
7359
7376
  var EncodeProfileSchema = object({
7360
7377
  video: object({
7361
7378
  codec: _enum([
@@ -7394,7 +7411,23 @@ var EncodeProfileSchema = object({
7394
7411
  "zerolatency",
7395
7412
  "film",
7396
7413
  "animation"
7397
- ]).optional()
7414
+ ]).optional(),
7415
+ /**
7416
+ * ONE slice per access unit.
7417
+ *
7418
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7419
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7420
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7421
+ * frame-end per frame: the first pictures render and the video then freezes
7422
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7423
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7424
+ *
7425
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7426
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7427
+ * consumers meaning the same thing spelled differently would stop sharing one
7428
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7429
+ */
7430
+ singleSlicePerFrame: boolean().optional()
7398
7431
  }),
7399
7432
  audio: union([literal("passthrough"), object({
7400
7433
  codec: _enum([
@@ -7444,7 +7477,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7444
7477
  audio: "passthrough"
7445
7478
  };
7446
7479
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7447
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7480
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7448
7481
  /**
7449
7482
  * Deep wiring healthcheck — snapshot of active reachability probes across
7450
7483
  * every declared capability + widget of every installed plugin, on every
@@ -12554,6 +12587,16 @@ method(object({
12554
12587
  filter: QueryFilterSchema.optional(),
12555
12588
  columns: array(string()).readonly().optional()
12556
12589
  }), array(SettingsRecordSchema).readonly()), method(object({
12590
+ namespace: string().optional(),
12591
+ collection: string(),
12592
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12593
+ * index on the collection. Never empty. */
12594
+ conflict: array(string()).readonly(),
12595
+ record: BulkRecordSchema
12596
+ }), object({
12597
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12598
+ * row's own id on an update. */
12599
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12557
12600
  namespace: string().optional(),
12558
12601
  collection: string(),
12559
12602
  record: object({
@@ -12688,6 +12731,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12688
12731
  id: union([string(), number()]) }), {
12689
12732
  kind: "mutation",
12690
12733
  auth: "admin"
12734
+ }), method(object({
12735
+ namespace: string().optional(),
12736
+ collection: string(),
12737
+ conflict: array(string()).readonly(),
12738
+ record: BulkRecordSchema
12739
+ }), object({ id: union([string(), number()]) }), {
12740
+ kind: "mutation",
12741
+ auth: "admin"
12691
12742
  }), method(object({
12692
12743
  namespace: string().optional(),
12693
12744
  collection: string(),
@@ -19187,8 +19238,8 @@ var TrackPositionSchema = object({
19187
19238
  var TrackSnapshotSchema = object({
19188
19239
  timestamp: number(),
19189
19240
  position: TrackPositionSchema,
19190
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19191
- mediaKey: string()
19241
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19242
+ mediaId: number().int()
19192
19243
  });
19193
19244
  /**
19194
19245
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19792,14 +19843,14 @@ var ObjectEventSchema = object({
19792
19843
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19793
19844
  frameWidth: number().optional(),
19794
19845
  frameHeight: number().optional(),
19795
- /** MediaStore key for the crop attached to this event (if any). */
19796
- mediaKey: string().optional(),
19797
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19846
+ /** Media row id of the crop attached to this event (if any) — D482. */
19847
+ mediaId: number().int().optional(),
19848
+ /** Design B: media row id of the track's native-resolution key frame (the
19798
19849
  * best-detection full frame). Resolve via the event-media data-plane
19799
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19850
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19800
19851
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19801
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19802
- keyFrameMediaKey: string().optional(),
19852
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19853
+ keyFrameMediaId: number().int().optional(),
19803
19854
  /** Populated by B5 (recording playback URL for this event). */
19804
19855
  mediaUrl: string().optional(),
19805
19856
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19849,7 +19900,7 @@ var MediaFileKindEnum = _enum([
19849
19900
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19850
19901
  *
19851
19902
  * `url` points at the `event-media` data plane
19852
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19903
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19853
19904
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19854
19905
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19855
19906
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19857,10 +19908,13 @@ var MediaFileKindEnum = _enum([
19857
19908
  * `data-plane-access.ts` for the rule and the one gap it does not close
19858
19909
  * (per-device scoping).
19859
19910
  *
19860
- * The URL is built from the row's **stored** key, which is not always its
19861
- * published `kind`: a track's face/plate crop is stored as `crop` under
19911
+ * The URL is built from the row's own id, which since D482 says nothing about
19912
+ * the row's owner or kind — and that is the point. The published `kind` is not
19913
+ * the stored one (a track's face/plate crop is stored as `crop` under
19862
19914
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19863
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19915
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19916
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19917
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19864
19918
  *
19865
19919
  * ## `base64` is TRANSITIONAL and is going away
19866
19920
  *
@@ -19873,7 +19927,19 @@ var MediaFileKindEnum = _enum([
19873
19927
  * `analytics-query-facade.ts`; nothing else reads it.
19874
19928
  */
19875
19929
  var MediaFileSchema = object({
19876
- key: string(),
19930
+ /**
19931
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19932
+ *
19933
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19934
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19935
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19936
+ * different rows that spell the same number, and a track whose
19937
+ * `bestEventId` was an object event was served a motion event's snapshot —
19938
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19939
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19940
+ * at all.
19941
+ */
19942
+ mediaId: number().int(),
19877
19943
  kind: MediaFileKindEnum,
19878
19944
  sizeBytes: number(),
19879
19945
  timestamp: number()
@@ -19981,7 +20047,7 @@ var RetrainTrackSchema = object({
19981
20047
  });
19982
20048
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19983
20049
  var RetrainFrameCandidateSchema = object({
19984
- mediaKey: string(),
20050
+ mediaId: number().int(),
19985
20051
  kind: MediaFileKindEnum,
19986
20052
  timestamp: number(),
19987
20053
  sizeBytes: number().int(),
@@ -19995,7 +20061,7 @@ var RetrainFrameSchema = object({
19995
20061
  deviceId: number(),
19996
20062
  trackId: string(),
19997
20063
  /** Provenance only. It may already point at nothing — that is expected. */
19998
- sourceMediaKey: string(),
20064
+ sourceMediaId: number().int(),
19999
20065
  sourceKind: MediaFileKindEnum,
20000
20066
  sizeBytes: number().int(),
20001
20067
  width: number().int(),
@@ -20011,7 +20077,7 @@ var RetrainCopyRefusalSchema = _enum([
20011
20077
  var RetrainFrameSelectionSchema = object({
20012
20078
  copied: array(RetrainFrameSchema).readonly(),
20013
20079
  refused: array(object({
20014
- sourceMediaKey: string(),
20080
+ sourceMediaId: number().int(),
20015
20081
  reason: RetrainCopyRefusalSchema
20016
20082
  })).readonly()
20017
20083
  });
@@ -20019,7 +20085,7 @@ var RetrainFrameListSchema = object({
20019
20085
  candidates: array(RetrainFrameCandidateSchema).readonly(),
20020
20086
  copies: array(RetrainFrameSchema).readonly(),
20021
20087
  /** What the page pre-selects — the native key frame when one survives. */
20022
- autoPickMediaKey: string().optional()
20088
+ autoPickMediaId: number().int().optional()
20023
20089
  });
20024
20090
  /** What the operator asked the assist to look for. */
20025
20091
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20114,9 +20180,11 @@ var RecentTracksQueryInput = object({
20114
20180
  });
20115
20181
  /**
20116
20182
  * A Summary (D359): the per-camera session envelope that references tracks.
20117
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20118
- * served by the event-media plane by key), null until the first frame with a
20119
- * member visible has been materialised.
20183
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20184
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20185
+ * first frame with a member visible has been materialised (D482 — it was the
20186
+ * row's composite key, which embedded the owner and could name another table's
20187
+ * row once event ids became per-table rowids).
20120
20188
  */
20121
20189
  var SummarySchema = object({
20122
20190
  id: string(),
@@ -20125,19 +20193,115 @@ var SummarySchema = object({
20125
20193
  lastActiveAt: number(),
20126
20194
  sealedAt: number().nullable(),
20127
20195
  memberCount: number().int().nonnegative(),
20128
- groupShotKey: string().nullable()
20196
+ groupShotMediaId: number().int().nullable()
20129
20197
  });
20130
20198
  var SummariesQueryInput = object({
20131
20199
  deviceIds: array(number()).min(1),
20132
20200
  since: number().optional(),
20133
20201
  until: number().optional(),
20134
- limit: number().int().min(1).max(200).default(50)
20202
+ limit: number().int().min(1).max(200).default(50),
20203
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20204
+ * meaning of "dog" on every summary surface. */
20205
+ classes: array(string().min(1).max(60)).max(40).optional()
20135
20206
  });
20136
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20137
20207
  var SummaryDetailSchema = object({
20138
20208
  summary: SummarySchema,
20139
- /** Member track ids, in absorption order. */
20140
- trackIds: array(string()).readonly()
20209
+ /** Members in absorption order, oldest join first. */
20210
+ members: array(object({
20211
+ trackId: string(),
20212
+ /**
20213
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20214
+ * and the track no longer exists to fill it.
20215
+ *
20216
+ * The join, not the latest opinion: class voting can re-decide a track's
20217
+ * class afterwards, and a group's composition is the history of who came
20218
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20219
+ * dog" — rather than being handed a second, denormalised tally that would
20220
+ * drift from the members it claims to describe.
20221
+ */
20222
+ className: string().nullable(),
20223
+ /** `primary` is the one member that carries the group's identity. */
20224
+ role: _enum([
20225
+ "primary",
20226
+ "sibling",
20227
+ "child"
20228
+ ]).nullable(),
20229
+ joinedAt: number(),
20230
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20231
+ * at read time. Absent when nothing resolved, never an empty string. */
20232
+ label: string().optional(),
20233
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20234
+ * recognised nobody, and absent for a member whose track has been pruned —
20235
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20236
+ subLabel: string().optional()
20237
+ })).readonly()
20238
+ });
20239
+ /**
20240
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20241
+ *
20242
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20243
+ * we extend when we notice we need more". Enough to draw the card ("two people
20244
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20245
+ * are on {@link getSummary}: shipping every member of every group so a client
20246
+ * can count them turns a page of two hundred groups into a thousand records to
20247
+ * draw two hundred badges.
20248
+ */
20249
+ var SummaryListItemSchema = object({
20250
+ summary: SummarySchema,
20251
+ /** Class counts, most numerous first; `className: null` is a member whose
20252
+ * class never resolved, counted so the counts add up to `memberCount`. */
20253
+ classes: array(object({
20254
+ className: string().nullable(),
20255
+ count: number().int().positive()
20256
+ })).readonly()
20257
+ });
20258
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20259
+ /**
20260
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20261
+ *
20262
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20263
+ * summaries feed that could only answer "the last N for these cameras"
20264
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20265
+ * else. Two feeds with different ordering contracts would be two ideas of what
20266
+ * "newest" means, on one screen.
20267
+ */
20268
+ var RecentSummariesQueryInput = object({
20269
+ /** Devices to merge. An empty array yields an empty page. */
20270
+ deviceIds: array(number()),
20271
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20272
+ since: number().optional(),
20273
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20274
+ until: number().optional(),
20275
+ limit: number().int().min(1).max(500).default(100),
20276
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20277
+ cursor: string().optional(),
20278
+ /**
20279
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20280
+ * deliberately the same name: the events filter tree emits one list of ticked
20281
+ * taxonomy nodes, and a summaries feed that called it something else would
20282
+ * make the operator's one filter component produce two shapes.
20283
+ *
20284
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20285
+ * me the arrivals with a dog" has to return the group of four people who had
20286
+ * one with them; a filter on the group's own fields could only ever answer
20287
+ * about its primary.
20288
+ *
20289
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20290
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20291
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20292
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20293
+ * that read only the class would answer "animal" and never "dog".
20294
+ *
20295
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20296
+ */
20297
+ classes: array(string().min(1).max(60)).max(40).optional()
20298
+ });
20299
+ var RecentSummariesPageSchema = object({
20300
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20301
+ * {@link listSummaries} — one list shape, so a card renders the same
20302
+ * whichever feed drew it. */
20303
+ summaries: array(SummaryListItemSchema).readonly(),
20304
+ nextCursor: string().nullable()
20141
20305
  });
20142
20306
  var RecentTracksPageSchema = object({
20143
20307
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20551,7 +20715,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20551
20715
  classes: array(string()).optional(),
20552
20716
  /** See {@link ExcludeSourcesDoc}. */
20553
20717
  excludeSources: array(TrackSourceSchema).optional()
20554
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20718
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20555
20719
  kind: "mutation",
20556
20720
  auth: "admin"
20557
20721
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -20679,7 +20843,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20679
20843
  }), method(object({
20680
20844
  deviceId: number(),
20681
20845
  trackId: string(),
20682
- mediaKeys: array(string()).min(1)
20846
+ mediaIds: array(number().int()).min(1)
20683
20847
  }), RetrainFrameSelectionSchema, {
20684
20848
  kind: "mutation",
20685
20849
  auth: "admin"
@@ -20747,7 +20911,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20747
20911
  kind: "query",
20748
20912
  auth: "admin"
20749
20913
  }), method(object({
20750
- eventId: string(),
20914
+ /** Which of the three event tables owns the row. */
20915
+ ownerType: EventOwnerTypeSchema,
20916
+ /** The event's rowid in that table (D474: an event id is a number). */
20917
+ eventId: number().int(),
20751
20918
  kind: MediaFileKindEnum.optional(),
20752
20919
  deviceId: number()
20753
20920
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20758,7 +20925,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20758
20925
  trackId: string(),
20759
20926
  deviceId: number()
20760
20927
  }), array(MediaFileInfoSchema).readonly()), method(object({
20761
- eventId: string(),
20928
+ ownerType: EventOwnerTypeSchema,
20929
+ eventId: number().int(),
20762
20930
  deviceId: number()
20763
20931
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20764
20932
  kind: "mutation",
@@ -25959,8 +26127,8 @@ var IdentitySchema = object({
25959
26127
  id: string(),
25960
26128
  name: string(),
25961
26129
  sampleCount: number().int().nonnegative(),
25962
- coverMediaKey: string().optional(),
25963
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
26130
+ coverMediaId: number().int().optional(),
26131
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25964
26132
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25965
26133
  coverBase64: string().optional()
25966
26134
  });
@@ -25986,8 +26154,8 @@ var IdentitySampleAuditRowSchema = object({
25986
26154
  enrolledAt: number().int(),
25987
26155
  deviceId: number().int().optional(),
25988
26156
  source: _enum(["detected", "photo"]),
25989
- /** The enrolled crop's media key, so the panel can show the evidence. */
25990
- mediaKey: string().optional(),
26157
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
26158
+ mediaId: number().int().optional(),
25991
26159
  /**
25992
26160
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25993
26161
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -26056,15 +26224,15 @@ var FaceInfoSchema = object({
26056
26224
  */
26057
26225
  cropUrl: string().optional(),
26058
26226
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
26059
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26227
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
26060
26228
  * legacy rows written before design B. */
26061
26229
  faceBbox: BoundingBoxSchema.optional(),
26062
26230
  /** Design B: MediaStore key of the track's native-resolution key frame.
26063
26231
  * Fetch the native JPEG via the event-media data-plane
26064
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26232
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26065
26233
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26066
26234
  * back to the inline `base64` face crop. */
26067
- keyFrameMediaKey: string().optional(),
26235
+ keyFrameMediaId: number().int().optional(),
26068
26236
  /** Winning identity-match cosine (0..1) for this face's track, when an
26069
26237
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26070
26238
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26133,11 +26301,14 @@ var FaceClusterSchema = object({
26133
26301
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26134
26302
  * dialog already rendering its key FRAME from the `event-media` plane.
26135
26303
  *
26136
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26137
- * media key directly, so this needed no new plane and no new access decision.
26304
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26305
+ * resolves a media row by its own id, so this needed no new plane and no new
26306
+ * access decision.
26138
26307
  */
26139
26308
  var MediaFileLiteSchema$1 = object({
26140
- key: string(),
26309
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26310
+ * (D482). It was the row's composite key, which embedded the owner. */
26311
+ mediaId: number().int(),
26141
26312
  kind: string(),
26142
26313
  url: string(),
26143
26314
  sizeBytes: number(),
@@ -26154,7 +26325,7 @@ method(object({
26154
26325
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26155
26326
  * four times and the viewer holds at `staleTime: 30_000`.
26156
26327
  *
26157
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26328
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26158
26329
  * the `event-media` plane serves that key `immutable` with an ETag.
26159
26330
  *
26160
26331
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29517,8 +29688,8 @@ var VehicleSchema = object({
29517
29688
  id: string(),
29518
29689
  name: string(),
29519
29690
  sampleCount: number().int().nonnegative(),
29520
- coverMediaKey: string().optional(),
29521
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29691
+ coverMediaId: number().int().optional(),
29692
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29522
29693
  coverBase64: string().optional()
29523
29694
  });
29524
29695
  var VehicleSampleInfoSchema = object({
@@ -29551,7 +29722,7 @@ var PlateInfoSchema = object({
29551
29722
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29552
29723
  plateBbox: BoundingBoxSchema.optional(),
29553
29724
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29554
- keyFrameMediaKey: string().optional(),
29725
+ keyFrameMediaId: number().int().optional(),
29555
29726
  base64: string().optional(),
29556
29727
  /**
29557
29728
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29572,11 +29743,14 @@ var PlateInfoSchema = object({
29572
29743
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29573
29744
  * dialog already rendering its key FRAME from the `event-media` plane.
29574
29745
  *
29575
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29576
- * media key directly, so this needed no new plane and no new access decision.
29746
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29747
+ * resolves a media row by its own id, so this needed no new plane and no new
29748
+ * access decision.
29577
29749
  */
29578
29750
  var MediaFileLiteSchema = object({
29579
- key: string(),
29751
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29752
+ * (D482). It was the row's composite key, which embedded the owner. */
29753
+ mediaId: number().int(),
29580
29754
  kind: string(),
29581
29755
  url: string(),
29582
29756
  sizeBytes: number(),
@@ -29635,7 +29809,7 @@ method(object({
29635
29809
  }), method(object({
29636
29810
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29637
29811
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29638
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29812
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29639
29813
  * INPUT field: stripped by the hub router until the train ships, which
29640
29814
  * resolves to `false` and is exactly the intended default. */
29641
29815
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31204,7 +31378,14 @@ var SceneReferenceSchema = object({
31204
31378
  modelId: string(),
31205
31379
  condition: SceneConditionSchema,
31206
31380
  capturedAt: number(),
31207
- thumbnailMediaId: string().optional(),
31381
+ /**
31382
+ * The reference thumbnail's media ROW id (D482).
31383
+ *
31384
+ * The name already said `mediaId` while the value was the row's composite
31385
+ * key — a leftover that read as done and was not. It is the row's own id
31386
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31387
+ */
31388
+ thumbnailMediaId: number().int().optional(),
31208
31389
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31209
31390
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31210
31391
  * normalized rect frame a different piece of world, and the scene would
@@ -32295,7 +32476,29 @@ var LoggingSettingsPatchSchema = object({
32295
32476
  * disarmed the channels it did not mention would make the Levels page and
32296
32477
  * the Diagnostics page fight over the same value.
32297
32478
  */
32298
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
32479
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
32480
+ /**
32481
+ * How many days the analytics OPS LOG is kept — the audit trail of what
32482
+ * retention actually deleted.
32483
+ *
32484
+ * It lives HERE, on the logging document, and not on the analytics addon's
32485
+ * settings form, on the operator's instruction: every other log-ish window in
32486
+ * this system is configured through this one document, and a retention audit
32487
+ * configured somewhere else is a second place to look when the question is
32488
+ * "what happened to my data".
32489
+ *
32490
+ * That placement has a cost worth stating: this document ships in the
32491
+ * framework closure, so this knob needs a published `@camstack/server` while
32492
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
32493
+ *
32494
+ * `retention-model.ts` argued this one must NOT follow a device window,
32495
+ * because the audit trail is the history of the SWEEP and not of the camera.
32496
+ * That argument stands and is untouched — what changes is that 30 days stops
32497
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
32498
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
32499
+ * state in which the next 9.5 GiB leak cannot be explained.
32500
+ */
32501
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
32299
32502
  });
32300
32503
  /**
32301
32504
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -32368,6 +32571,14 @@ var LoggingSettingsStateSchema = object({
32368
32571
  channels: array(LogChannelDescriptorSchema).readonly(),
32369
32572
  /** The channels ARMED right now, each with its deadline. */
32370
32573
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
32574
+ /**
32575
+ * The ops-log window in days, as it is in force. `0` = kept forever.
32576
+ *
32577
+ * Reported rather than left to the caller's memory of what it wrote: this is
32578
+ * the value the analytics sweep will actually use, and an operator asking how
32579
+ * long their audit trail lives should not have to infer it from a default.
32580
+ */
32581
+ opsLogRetentionDays: number().int().min(0),
32371
32582
  persisted: boolean()
32372
32583
  });
32373
32584
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -33188,7 +33399,7 @@ var StationaryObjectSchema = object({
33188
33399
  /** Enrichment label carried from the source track (identity / plate). */
33189
33400
  label: string().optional(),
33190
33401
  /** Native-resolution key-frame media key for the parked object's best image. */
33191
- keyFrameMediaKey: string().optional()
33402
+ keyFrameMediaId: number().int().optional()
33192
33403
  });
33193
33404
  var CameraOccupancySnapshotSchema = object({
33194
33405
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35926,6 +36137,12 @@ Object.freeze({
35926
36137
  addonId: null,
35927
36138
  access: "create"
35928
36139
  },
36140
+ "dataStoreProvider.upsert": {
36141
+ capName: "data-store-provider",
36142
+ capScope: "system",
36143
+ addonId: null,
36144
+ access: "create"
36145
+ },
35929
36146
  "dayNight.getOptions": {
35930
36147
  capName: "day-night",
35931
36148
  capScope: "device",
@@ -38362,6 +38579,12 @@ Object.freeze({
38362
38579
  addonId: null,
38363
38580
  access: "view"
38364
38581
  },
38582
+ "pipelineAnalytics.listRecentSummaries": {
38583
+ capName: "pipeline-analytics",
38584
+ capScope: "device",
38585
+ addonId: null,
38586
+ access: "view"
38587
+ },
38365
38588
  "pipelineAnalytics.listRecentTracks": {
38366
38589
  capName: "pipeline-analytics",
38367
38590
  capScope: "device",
@@ -39790,6 +40013,12 @@ Object.freeze({
39790
40013
  addonId: null,
39791
40014
  access: "create"
39792
40015
  },
40016
+ "settingsStore.upsert": {
40017
+ capName: "settings-store",
40018
+ capScope: "system",
40019
+ addonId: null,
40020
+ access: "create"
40021
+ },
39793
40022
  "smtpProvider.getStatus": {
39794
40023
  capName: "smtp-provider",
39795
40024
  capScope: "system",
@@ -42183,6 +42412,11 @@ Object.freeze({
42183
42412
  form: "single",
42184
42413
  optional: true
42185
42414
  }],
42415
+ "pipelineAnalytics.listRecentSummaries": [{
42416
+ name: "deviceIds",
42417
+ form: "array",
42418
+ optional: false
42419
+ }],
42186
42420
  "pipelineAnalytics.listRecentTracks": [{
42187
42421
  name: "deviceIds",
42188
42422
  form: "array",
package/dist/addon.mjs CHANGED
@@ -36,7 +36,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
36
36
  var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
37
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
38
38
  //#endregion
39
- //#region ../types/dist/event-category-DAXzJeTX.mjs
39
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
40
40
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
41
41
  EventCategory["SystemBoot"] = "system.boot";
42
42
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -597,7 +597,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
597
597
  * a package zone — a newly-appeared stationary object of a package class
598
598
  * that cleared the class / zone / dwell / size gates. Payload:
599
599
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
600
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
600
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
601
601
  * Telemetry (D8): the durable record is the `package-events` store row;
602
602
  * this bus topic drives notifier rules + live UI. See
603
603
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5383,7 +5383,7 @@ var ZodIssueCode = {
5383
5383
  var ZodFirstPartyTypeKind;
5384
5384
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5385
5385
  //#endregion
5386
- //#region ../types/dist/sleep-BnujYGPe.mjs
5386
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5387
5387
  /**
5388
5388
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5389
5389
  * window to float samples (D455).
@@ -7350,13 +7350,30 @@ new Set([
7350
7350
  "scene",
7351
7351
  "motion",
7352
7352
  "object",
7353
- "audio"
7353
+ "audio",
7354
+ "mosaic"
7354
7355
  ]);
7355
- new Set([
7356
+ /**
7357
+ * The owner types that are event tables, in the order the event-media resolver
7358
+ * should consider them. Exported so a consumer asking "is this key an event?"
7359
+ * does not re-spell the list and drift from it.
7360
+ */
7361
+ var EVENT_OWNER_TYPES = [
7356
7362
  "motion",
7357
7363
  "object",
7358
7364
  "audio"
7359
- ]);
7365
+ ];
7366
+ /**
7367
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7368
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7369
+ * `(table, id)`).
7370
+ *
7371
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7372
+ * table would otherwise be accepted by the codec and refused at the door, with
7373
+ * nothing failing until a caller asked.
7374
+ */
7375
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7376
+ new Set(EVENT_OWNER_TYPES);
7360
7377
  var EncodeProfileSchema = object({
7361
7378
  video: object({
7362
7379
  codec: _enum([
@@ -7395,7 +7412,23 @@ var EncodeProfileSchema = object({
7395
7412
  "zerolatency",
7396
7413
  "film",
7397
7414
  "animation"
7398
- ]).optional()
7415
+ ]).optional(),
7416
+ /**
7417
+ * ONE slice per access unit.
7418
+ *
7419
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7420
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7421
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7422
+ * frame-end per frame: the first pictures render and the video then freezes
7423
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7424
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7425
+ *
7426
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7427
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7428
+ * consumers meaning the same thing spelled differently would stop sharing one
7429
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7430
+ */
7431
+ singleSlicePerFrame: boolean().optional()
7399
7432
  }),
7400
7433
  audio: union([literal("passthrough"), object({
7401
7434
  codec: _enum([
@@ -7445,7 +7478,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7445
7478
  audio: "passthrough"
7446
7479
  };
7447
7480
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7448
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7481
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7449
7482
  /**
7450
7483
  * Deep wiring healthcheck — snapshot of active reachability probes across
7451
7484
  * every declared capability + widget of every installed plugin, on every
@@ -12555,6 +12588,16 @@ method(object({
12555
12588
  filter: QueryFilterSchema.optional(),
12556
12589
  columns: array(string()).readonly().optional()
12557
12590
  }), array(SettingsRecordSchema).readonly()), method(object({
12591
+ namespace: string().optional(),
12592
+ collection: string(),
12593
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12594
+ * index on the collection. Never empty. */
12595
+ conflict: array(string()).readonly(),
12596
+ record: BulkRecordSchema
12597
+ }), object({
12598
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12599
+ * row's own id on an update. */
12600
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12558
12601
  namespace: string().optional(),
12559
12602
  collection: string(),
12560
12603
  record: object({
@@ -12689,6 +12732,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12689
12732
  id: union([string(), number()]) }), {
12690
12733
  kind: "mutation",
12691
12734
  auth: "admin"
12735
+ }), method(object({
12736
+ namespace: string().optional(),
12737
+ collection: string(),
12738
+ conflict: array(string()).readonly(),
12739
+ record: BulkRecordSchema
12740
+ }), object({ id: union([string(), number()]) }), {
12741
+ kind: "mutation",
12742
+ auth: "admin"
12692
12743
  }), method(object({
12693
12744
  namespace: string().optional(),
12694
12745
  collection: string(),
@@ -19188,8 +19239,8 @@ var TrackPositionSchema = object({
19188
19239
  var TrackSnapshotSchema = object({
19189
19240
  timestamp: number(),
19190
19241
  position: TrackPositionSchema,
19191
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19192
- mediaKey: string()
19242
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19243
+ mediaId: number().int()
19193
19244
  });
19194
19245
  /**
19195
19246
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19793,14 +19844,14 @@ var ObjectEventSchema = object({
19793
19844
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19794
19845
  frameWidth: number().optional(),
19795
19846
  frameHeight: number().optional(),
19796
- /** MediaStore key for the crop attached to this event (if any). */
19797
- mediaKey: string().optional(),
19798
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19847
+ /** Media row id of the crop attached to this event (if any) — D482. */
19848
+ mediaId: number().int().optional(),
19849
+ /** Design B: media row id of the track's native-resolution key frame (the
19799
19850
  * best-detection full frame). Resolve via the event-media data-plane
19800
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19851
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19801
19852
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19802
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19803
- keyFrameMediaKey: string().optional(),
19853
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19854
+ keyFrameMediaId: number().int().optional(),
19804
19855
  /** Populated by B5 (recording playback URL for this event). */
19805
19856
  mediaUrl: string().optional(),
19806
19857
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19850,7 +19901,7 @@ var MediaFileKindEnum = _enum([
19850
19901
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19851
19902
  *
19852
19903
  * `url` points at the `event-media` data plane
19853
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19904
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19854
19905
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19855
19906
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19856
19907
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19858,10 +19909,13 @@ var MediaFileKindEnum = _enum([
19858
19909
  * `data-plane-access.ts` for the rule and the one gap it does not close
19859
19910
  * (per-device scoping).
19860
19911
  *
19861
- * The URL is built from the row's **stored** key, which is not always its
19862
- * published `kind`: a track's face/plate crop is stored as `crop` under
19912
+ * The URL is built from the row's own id, which since D482 says nothing about
19913
+ * the row's owner or kind — and that is the point. The published `kind` is not
19914
+ * the stored one (a track's face/plate crop is stored as `crop` under
19863
19915
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19864
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19916
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19917
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19918
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19865
19919
  *
19866
19920
  * ## `base64` is TRANSITIONAL and is going away
19867
19921
  *
@@ -19874,7 +19928,19 @@ var MediaFileKindEnum = _enum([
19874
19928
  * `analytics-query-facade.ts`; nothing else reads it.
19875
19929
  */
19876
19930
  var MediaFileSchema = object({
19877
- key: string(),
19931
+ /**
19932
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19933
+ *
19934
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19935
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19936
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19937
+ * different rows that spell the same number, and a track whose
19938
+ * `bestEventId` was an object event was served a motion event's snapshot —
19939
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19940
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19941
+ * at all.
19942
+ */
19943
+ mediaId: number().int(),
19878
19944
  kind: MediaFileKindEnum,
19879
19945
  sizeBytes: number(),
19880
19946
  timestamp: number()
@@ -19982,7 +20048,7 @@ var RetrainTrackSchema = object({
19982
20048
  });
19983
20049
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19984
20050
  var RetrainFrameCandidateSchema = object({
19985
- mediaKey: string(),
20051
+ mediaId: number().int(),
19986
20052
  kind: MediaFileKindEnum,
19987
20053
  timestamp: number(),
19988
20054
  sizeBytes: number().int(),
@@ -19996,7 +20062,7 @@ var RetrainFrameSchema = object({
19996
20062
  deviceId: number(),
19997
20063
  trackId: string(),
19998
20064
  /** Provenance only. It may already point at nothing — that is expected. */
19999
- sourceMediaKey: string(),
20065
+ sourceMediaId: number().int(),
20000
20066
  sourceKind: MediaFileKindEnum,
20001
20067
  sizeBytes: number().int(),
20002
20068
  width: number().int(),
@@ -20012,7 +20078,7 @@ var RetrainCopyRefusalSchema = _enum([
20012
20078
  var RetrainFrameSelectionSchema = object({
20013
20079
  copied: array(RetrainFrameSchema).readonly(),
20014
20080
  refused: array(object({
20015
- sourceMediaKey: string(),
20081
+ sourceMediaId: number().int(),
20016
20082
  reason: RetrainCopyRefusalSchema
20017
20083
  })).readonly()
20018
20084
  });
@@ -20020,7 +20086,7 @@ var RetrainFrameListSchema = object({
20020
20086
  candidates: array(RetrainFrameCandidateSchema).readonly(),
20021
20087
  copies: array(RetrainFrameSchema).readonly(),
20022
20088
  /** What the page pre-selects — the native key frame when one survives. */
20023
- autoPickMediaKey: string().optional()
20089
+ autoPickMediaId: number().int().optional()
20024
20090
  });
20025
20091
  /** What the operator asked the assist to look for. */
20026
20092
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20115,9 +20181,11 @@ var RecentTracksQueryInput = object({
20115
20181
  });
20116
20182
  /**
20117
20183
  * A Summary (D359): the per-camera session envelope that references tracks.
20118
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20119
- * served by the event-media plane by key), null until the first frame with a
20120
- * member visible has been materialised.
20184
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20185
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20186
+ * first frame with a member visible has been materialised (D482 — it was the
20187
+ * row's composite key, which embedded the owner and could name another table's
20188
+ * row once event ids became per-table rowids).
20121
20189
  */
20122
20190
  var SummarySchema = object({
20123
20191
  id: string(),
@@ -20126,19 +20194,115 @@ var SummarySchema = object({
20126
20194
  lastActiveAt: number(),
20127
20195
  sealedAt: number().nullable(),
20128
20196
  memberCount: number().int().nonnegative(),
20129
- groupShotKey: string().nullable()
20197
+ groupShotMediaId: number().int().nullable()
20130
20198
  });
20131
20199
  var SummariesQueryInput = object({
20132
20200
  deviceIds: array(number()).min(1),
20133
20201
  since: number().optional(),
20134
20202
  until: number().optional(),
20135
- limit: number().int().min(1).max(200).default(50)
20203
+ limit: number().int().min(1).max(200).default(50),
20204
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20205
+ * meaning of "dog" on every summary surface. */
20206
+ classes: array(string().min(1).max(60)).max(40).optional()
20136
20207
  });
20137
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20138
20208
  var SummaryDetailSchema = object({
20139
20209
  summary: SummarySchema,
20140
- /** Member track ids, in absorption order. */
20141
- trackIds: array(string()).readonly()
20210
+ /** Members in absorption order, oldest join first. */
20211
+ members: array(object({
20212
+ trackId: string(),
20213
+ /**
20214
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20215
+ * and the track no longer exists to fill it.
20216
+ *
20217
+ * The join, not the latest opinion: class voting can re-decide a track's
20218
+ * class afterwards, and a group's composition is the history of who came
20219
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20220
+ * dog" — rather than being handed a second, denormalised tally that would
20221
+ * drift from the members it claims to describe.
20222
+ */
20223
+ className: string().nullable(),
20224
+ /** `primary` is the one member that carries the group's identity. */
20225
+ role: _enum([
20226
+ "primary",
20227
+ "sibling",
20228
+ "child"
20229
+ ]).nullable(),
20230
+ joinedAt: number(),
20231
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20232
+ * at read time. Absent when nothing resolved, never an empty string. */
20233
+ label: string().optional(),
20234
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20235
+ * recognised nobody, and absent for a member whose track has been pruned —
20236
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20237
+ subLabel: string().optional()
20238
+ })).readonly()
20239
+ });
20240
+ /**
20241
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20242
+ *
20243
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20244
+ * we extend when we notice we need more". Enough to draw the card ("two people
20245
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20246
+ * are on {@link getSummary}: shipping every member of every group so a client
20247
+ * can count them turns a page of two hundred groups into a thousand records to
20248
+ * draw two hundred badges.
20249
+ */
20250
+ var SummaryListItemSchema = object({
20251
+ summary: SummarySchema,
20252
+ /** Class counts, most numerous first; `className: null` is a member whose
20253
+ * class never resolved, counted so the counts add up to `memberCount`. */
20254
+ classes: array(object({
20255
+ className: string().nullable(),
20256
+ count: number().int().positive()
20257
+ })).readonly()
20258
+ });
20259
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20260
+ /**
20261
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20262
+ *
20263
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20264
+ * summaries feed that could only answer "the last N for these cameras"
20265
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20266
+ * else. Two feeds with different ordering contracts would be two ideas of what
20267
+ * "newest" means, on one screen.
20268
+ */
20269
+ var RecentSummariesQueryInput = object({
20270
+ /** Devices to merge. An empty array yields an empty page. */
20271
+ deviceIds: array(number()),
20272
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20273
+ since: number().optional(),
20274
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20275
+ until: number().optional(),
20276
+ limit: number().int().min(1).max(500).default(100),
20277
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20278
+ cursor: string().optional(),
20279
+ /**
20280
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20281
+ * deliberately the same name: the events filter tree emits one list of ticked
20282
+ * taxonomy nodes, and a summaries feed that called it something else would
20283
+ * make the operator's one filter component produce two shapes.
20284
+ *
20285
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20286
+ * me the arrivals with a dog" has to return the group of four people who had
20287
+ * one with them; a filter on the group's own fields could only ever answer
20288
+ * about its primary.
20289
+ *
20290
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20291
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20292
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20293
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20294
+ * that read only the class would answer "animal" and never "dog".
20295
+ *
20296
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20297
+ */
20298
+ classes: array(string().min(1).max(60)).max(40).optional()
20299
+ });
20300
+ var RecentSummariesPageSchema = object({
20301
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20302
+ * {@link listSummaries} — one list shape, so a card renders the same
20303
+ * whichever feed drew it. */
20304
+ summaries: array(SummaryListItemSchema).readonly(),
20305
+ nextCursor: string().nullable()
20142
20306
  });
20143
20307
  var RecentTracksPageSchema = object({
20144
20308
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20552,7 +20716,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20552
20716
  classes: array(string()).optional(),
20553
20717
  /** See {@link ExcludeSourcesDoc}. */
20554
20718
  excludeSources: array(TrackSourceSchema).optional()
20555
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20719
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20556
20720
  kind: "mutation",
20557
20721
  auth: "admin"
20558
20722
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -20680,7 +20844,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20680
20844
  }), method(object({
20681
20845
  deviceId: number(),
20682
20846
  trackId: string(),
20683
- mediaKeys: array(string()).min(1)
20847
+ mediaIds: array(number().int()).min(1)
20684
20848
  }), RetrainFrameSelectionSchema, {
20685
20849
  kind: "mutation",
20686
20850
  auth: "admin"
@@ -20748,7 +20912,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20748
20912
  kind: "query",
20749
20913
  auth: "admin"
20750
20914
  }), method(object({
20751
- eventId: string(),
20915
+ /** Which of the three event tables owns the row. */
20916
+ ownerType: EventOwnerTypeSchema,
20917
+ /** The event's rowid in that table (D474: an event id is a number). */
20918
+ eventId: number().int(),
20752
20919
  kind: MediaFileKindEnum.optional(),
20753
20920
  deviceId: number()
20754
20921
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20759,7 +20926,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20759
20926
  trackId: string(),
20760
20927
  deviceId: number()
20761
20928
  }), array(MediaFileInfoSchema).readonly()), method(object({
20762
- eventId: string(),
20929
+ ownerType: EventOwnerTypeSchema,
20930
+ eventId: number().int(),
20763
20931
  deviceId: number()
20764
20932
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20765
20933
  kind: "mutation",
@@ -25960,8 +26128,8 @@ var IdentitySchema = object({
25960
26128
  id: string(),
25961
26129
  name: string(),
25962
26130
  sampleCount: number().int().nonnegative(),
25963
- coverMediaKey: string().optional(),
25964
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
26131
+ coverMediaId: number().int().optional(),
26132
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25965
26133
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25966
26134
  coverBase64: string().optional()
25967
26135
  });
@@ -25987,8 +26155,8 @@ var IdentitySampleAuditRowSchema = object({
25987
26155
  enrolledAt: number().int(),
25988
26156
  deviceId: number().int().optional(),
25989
26157
  source: _enum(["detected", "photo"]),
25990
- /** The enrolled crop's media key, so the panel can show the evidence. */
25991
- mediaKey: string().optional(),
26158
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
26159
+ mediaId: number().int().optional(),
25992
26160
  /**
25993
26161
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25994
26162
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -26057,15 +26225,15 @@ var FaceInfoSchema = object({
26057
26225
  */
26058
26226
  cropUrl: string().optional(),
26059
26227
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
26060
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
26228
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
26061
26229
  * legacy rows written before design B. */
26062
26230
  faceBbox: BoundingBoxSchema.optional(),
26063
26231
  /** Design B: MediaStore key of the track's native-resolution key frame.
26064
26232
  * Fetch the native JPEG via the event-media data-plane
26065
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
26233
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
26066
26234
  * track produced no key frame (e.g. native/onboard source) — the UI falls
26067
26235
  * back to the inline `base64` face crop. */
26068
- keyFrameMediaKey: string().optional(),
26236
+ keyFrameMediaId: number().int().optional(),
26069
26237
  /** Winning identity-match cosine (0..1) for this face's track, when an
26070
26238
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
26071
26239
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -26134,11 +26302,14 @@ var FaceClusterSchema = object({
26134
26302
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
26135
26303
  * dialog already rendering its key FRAME from the `event-media` plane.
26136
26304
  *
26137
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
26138
- * media key directly, so this needed no new plane and no new access decision.
26305
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
26306
+ * resolves a media row by its own id, so this needed no new plane and no new
26307
+ * access decision.
26139
26308
  */
26140
26309
  var MediaFileLiteSchema$1 = object({
26141
- key: string(),
26310
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
26311
+ * (D482). It was the row's composite key, which embedded the owner. */
26312
+ mediaId: number().int(),
26142
26313
  kind: string(),
26143
26314
  url: string(),
26144
26315
  sizeBytes: number(),
@@ -26155,7 +26326,7 @@ method(object({
26155
26326
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
26156
26327
  * four times and the viewer holds at `staleTime: 30_000`.
26157
26328
  *
26158
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
26329
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
26159
26330
  * the `event-media` plane serves that key `immutable` with an ETag.
26160
26331
  *
26161
26332
  * **This is an INPUT field, so it does not reach the addon until the
@@ -29518,8 +29689,8 @@ var VehicleSchema = object({
29518
29689
  id: string(),
29519
29690
  name: string(),
29520
29691
  sampleCount: number().int().nonnegative(),
29521
- coverMediaKey: string().optional(),
29522
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
29692
+ coverMediaId: number().int().optional(),
29693
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
29523
29694
  coverBase64: string().optional()
29524
29695
  });
29525
29696
  var VehicleSampleInfoSchema = object({
@@ -29552,7 +29723,7 @@ var PlateInfoSchema = object({
29552
29723
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
29553
29724
  plateBbox: BoundingBoxSchema.optional(),
29554
29725
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
29555
- keyFrameMediaKey: string().optional(),
29726
+ keyFrameMediaId: number().int().optional(),
29556
29727
  base64: string().optional(),
29557
29728
  /**
29558
29729
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -29573,11 +29744,14 @@ var PlateInfoSchema = object({
29573
29744
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
29574
29745
  * dialog already rendering its key FRAME from the `event-media` plane.
29575
29746
  *
29576
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
29577
- * media key directly, so this needed no new plane and no new access decision.
29747
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
29748
+ * resolves a media row by its own id, so this needed no new plane and no new
29749
+ * access decision.
29578
29750
  */
29579
29751
  var MediaFileLiteSchema = object({
29580
- key: string(),
29752
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
29753
+ * (D482). It was the row's composite key, which embedded the owner. */
29754
+ mediaId: number().int(),
29581
29755
  kind: string(),
29582
29756
  url: string(),
29583
29757
  sizeBytes: number(),
@@ -29636,7 +29810,7 @@ method(object({
29636
29810
  }), method(object({
29637
29811
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
29638
29812
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
29639
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
29813
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
29640
29814
  * INPUT field: stripped by the hub router until the train ships, which
29641
29815
  * resolves to `false` and is exactly the intended default. */
29642
29816
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -31205,7 +31379,14 @@ var SceneReferenceSchema = object({
31205
31379
  modelId: string(),
31206
31380
  condition: SceneConditionSchema,
31207
31381
  capturedAt: number(),
31208
- thumbnailMediaId: string().optional(),
31382
+ /**
31383
+ * The reference thumbnail's media ROW id (D482).
31384
+ *
31385
+ * The name already said `mediaId` while the value was the row's composite
31386
+ * key — a leftover that read as done and was not. It is the row's own id
31387
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
31388
+ */
31389
+ thumbnailMediaId: number().int().optional(),
31209
31390
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
31210
31391
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
31211
31392
  * normalized rect frame a different piece of world, and the scene would
@@ -32296,7 +32477,29 @@ var LoggingSettingsPatchSchema = object({
32296
32477
  * disarmed the channels it did not mention would make the Levels page and
32297
32478
  * the Diagnostics page fight over the same value.
32298
32479
  */
32299
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
32480
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
32481
+ /**
32482
+ * How many days the analytics OPS LOG is kept — the audit trail of what
32483
+ * retention actually deleted.
32484
+ *
32485
+ * It lives HERE, on the logging document, and not on the analytics addon's
32486
+ * settings form, on the operator's instruction: every other log-ish window in
32487
+ * this system is configured through this one document, and a retention audit
32488
+ * configured somewhere else is a second place to look when the question is
32489
+ * "what happened to my data".
32490
+ *
32491
+ * That placement has a cost worth stating: this document ships in the
32492
+ * framework closure, so this knob needs a published `@camstack/server` while
32493
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
32494
+ *
32495
+ * `retention-model.ts` argued this one must NOT follow a device window,
32496
+ * because the audit trail is the history of the SWEEP and not of the camera.
32497
+ * That argument stands and is untouched — what changes is that 30 days stops
32498
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
32499
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
32500
+ * state in which the next 9.5 GiB leak cannot be explained.
32501
+ */
32502
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
32300
32503
  });
32301
32504
  /**
32302
32505
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -32369,6 +32572,14 @@ var LoggingSettingsStateSchema = object({
32369
32572
  channels: array(LogChannelDescriptorSchema).readonly(),
32370
32573
  /** The channels ARMED right now, each with its deadline. */
32371
32574
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
32575
+ /**
32576
+ * The ops-log window in days, as it is in force. `0` = kept forever.
32577
+ *
32578
+ * Reported rather than left to the caller's memory of what it wrote: this is
32579
+ * the value the analytics sweep will actually use, and an operator asking how
32580
+ * long their audit trail lives should not have to infer it from a default.
32581
+ */
32582
+ opsLogRetentionDays: number().int().min(0),
32372
32583
  persisted: boolean()
32373
32584
  });
32374
32585
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
@@ -33189,7 +33400,7 @@ var StationaryObjectSchema = object({
33189
33400
  /** Enrichment label carried from the source track (identity / plate). */
33190
33401
  label: string().optional(),
33191
33402
  /** Native-resolution key-frame media key for the parked object's best image. */
33192
- keyFrameMediaKey: string().optional()
33403
+ keyFrameMediaId: number().int().optional()
33193
33404
  });
33194
33405
  var CameraOccupancySnapshotSchema = object({
33195
33406
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -35927,6 +36138,12 @@ Object.freeze({
35927
36138
  addonId: null,
35928
36139
  access: "create"
35929
36140
  },
36141
+ "dataStoreProvider.upsert": {
36142
+ capName: "data-store-provider",
36143
+ capScope: "system",
36144
+ addonId: null,
36145
+ access: "create"
36146
+ },
35930
36147
  "dayNight.getOptions": {
35931
36148
  capName: "day-night",
35932
36149
  capScope: "device",
@@ -38363,6 +38580,12 @@ Object.freeze({
38363
38580
  addonId: null,
38364
38581
  access: "view"
38365
38582
  },
38583
+ "pipelineAnalytics.listRecentSummaries": {
38584
+ capName: "pipeline-analytics",
38585
+ capScope: "device",
38586
+ addonId: null,
38587
+ access: "view"
38588
+ },
38366
38589
  "pipelineAnalytics.listRecentTracks": {
38367
38590
  capName: "pipeline-analytics",
38368
38591
  capScope: "device",
@@ -39791,6 +40014,12 @@ Object.freeze({
39791
40014
  addonId: null,
39792
40015
  access: "create"
39793
40016
  },
40017
+ "settingsStore.upsert": {
40018
+ capName: "settings-store",
40019
+ capScope: "system",
40020
+ addonId: null,
40021
+ access: "create"
40022
+ },
39794
40023
  "smtpProvider.getStatus": {
39795
40024
  capName: "smtp-provider",
39796
40025
  capScope: "system",
@@ -42184,6 +42413,11 @@ Object.freeze({
42184
42413
  form: "single",
42185
42414
  optional: true
42186
42415
  }],
42416
+ "pipelineAnalytics.listRecentSummaries": [{
42417
+ name: "deviceIds",
42418
+ form: "array",
42419
+ optional: false
42420
+ }],
42187
42421
  "pipelineAnalytics.listRecentTracks": [{
42188
42422
  name: "deviceIds",
42189
42423
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-dreo",
3
- "version": "0.2.99",
3
+ "version": "0.2.101",
4
4
  "description": "Dreo smart-device (fan / air-circulator / purifier / heater / humidifier) device-provider addon for CamStack — wraps the @apocaliss92/nodedreo Dreo cloud client (REST + WebSocket)",
5
5
  "keywords": [
6
6
  "camstack",