@camstack/addon-provider-petkit 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
@@ -1101,7 +1101,7 @@ var Nodepetkit = class {
1101
1101
  }
1102
1102
  };
1103
1103
  //#endregion
1104
- //#region ../types/dist/event-category-DAXzJeTX.mjs
1104
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
1105
1105
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
1106
1106
  EventCategory["SystemBoot"] = "system.boot";
1107
1107
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1662,7 +1662,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1662
1662
  * a package zone — a newly-appeared stationary object of a package class
1663
1663
  * that cleared the class / zone / dwell / size gates. Payload:
1664
1664
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
1665
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
1665
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
1666
1666
  * Telemetry (D8): the durable record is the `package-events` store row;
1667
1667
  * this bus topic drives notifier rules + live UI. See
1668
1668
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -6448,7 +6448,7 @@ var ZodIssueCode = {
6448
6448
  var ZodFirstPartyTypeKind;
6449
6449
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6450
6450
  //#endregion
6451
- //#region ../types/dist/sleep-BnujYGPe.mjs
6451
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
6452
6452
  /**
6453
6453
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6454
6454
  * window to float samples (D455).
@@ -8415,13 +8415,30 @@ new Set([
8415
8415
  "scene",
8416
8416
  "motion",
8417
8417
  "object",
8418
- "audio"
8418
+ "audio",
8419
+ "mosaic"
8419
8420
  ]);
8420
- new Set([
8421
+ /**
8422
+ * The owner types that are event tables, in the order the event-media resolver
8423
+ * should consider them. Exported so a consumer asking "is this key an event?"
8424
+ * does not re-spell the list and drift from it.
8425
+ */
8426
+ var EVENT_OWNER_TYPES = [
8421
8427
  "motion",
8422
8428
  "object",
8423
8429
  "audio"
8424
- ]);
8430
+ ];
8431
+ /**
8432
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
8433
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
8434
+ * `(table, id)`).
8435
+ *
8436
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
8437
+ * table would otherwise be accepted by the codec and refused at the door, with
8438
+ * nothing failing until a caller asked.
8439
+ */
8440
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
8441
+ new Set(EVENT_OWNER_TYPES);
8425
8442
  var EncodeProfileSchema = object({
8426
8443
  video: object({
8427
8444
  codec: _enum([
@@ -8460,7 +8477,23 @@ var EncodeProfileSchema = object({
8460
8477
  "zerolatency",
8461
8478
  "film",
8462
8479
  "animation"
8463
- ]).optional()
8480
+ ]).optional(),
8481
+ /**
8482
+ * ONE slice per access unit.
8483
+ *
8484
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
8485
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
8486
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
8487
+ * frame-end per frame: the first pictures render and the video then freezes
8488
+ * for good while the audio, on its own plane, plays on. Measured on this hub
8489
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
8490
+ *
8491
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
8492
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
8493
+ * consumers meaning the same thing spelled differently would stop sharing one
8494
+ * child. Absent means "whatever the encoder does" — today's behaviour.
8495
+ */
8496
+ singleSlicePerFrame: boolean().optional()
8464
8497
  }),
8465
8498
  audio: union([literal("passthrough"), object({
8466
8499
  codec: _enum([
@@ -8510,7 +8543,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
8510
8543
  audio: "passthrough"
8511
8544
  };
8512
8545
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
8513
- ({ ...BASE_LIVE_EGRESS_PROFILE });
8546
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
8514
8547
  /**
8515
8548
  * Deep wiring healthcheck — snapshot of active reachability probes across
8516
8549
  * every declared capability + widget of every installed plugin, on every
@@ -13620,6 +13653,16 @@ method(object({
13620
13653
  filter: QueryFilterSchema.optional(),
13621
13654
  columns: array(string()).readonly().optional()
13622
13655
  }), array(SettingsRecordSchema).readonly()), method(object({
13656
+ namespace: string().optional(),
13657
+ collection: string(),
13658
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
13659
+ * index on the collection. Never empty. */
13660
+ conflict: array(string()).readonly(),
13661
+ record: BulkRecordSchema
13662
+ }), object({
13663
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
13664
+ * row's own id on an update. */
13665
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
13623
13666
  namespace: string().optional(),
13624
13667
  collection: string(),
13625
13668
  record: object({
@@ -13754,6 +13797,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13754
13797
  id: union([string(), number()]) }), {
13755
13798
  kind: "mutation",
13756
13799
  auth: "admin"
13800
+ }), method(object({
13801
+ namespace: string().optional(),
13802
+ collection: string(),
13803
+ conflict: array(string()).readonly(),
13804
+ record: BulkRecordSchema
13805
+ }), object({ id: union([string(), number()]) }), {
13806
+ kind: "mutation",
13807
+ auth: "admin"
13757
13808
  }), method(object({
13758
13809
  namespace: string().optional(),
13759
13810
  collection: string(),
@@ -20253,8 +20304,8 @@ var TrackPositionSchema = object({
20253
20304
  var TrackSnapshotSchema = object({
20254
20305
  timestamp: number(),
20255
20306
  position: TrackPositionSchema,
20256
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
20257
- mediaKey: string()
20307
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
20308
+ mediaId: number().int()
20258
20309
  });
20259
20310
  /**
20260
20311
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -20858,14 +20909,14 @@ var ObjectEventSchema = object({
20858
20909
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
20859
20910
  frameWidth: number().optional(),
20860
20911
  frameHeight: number().optional(),
20861
- /** MediaStore key for the crop attached to this event (if any). */
20862
- mediaKey: string().optional(),
20863
- /** Design B: MediaStore key of the track's native-resolution key frame (the
20912
+ /** Media row id of the crop attached to this event (if any) — D482. */
20913
+ mediaId: number().int().optional(),
20914
+ /** Design B: media row id of the track's native-resolution key frame (the
20864
20915
  * best-detection full frame). Resolve via the event-media data-plane
20865
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
20916
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
20866
20917
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
20867
- * sources — consumers fall back to `mediaKey` (the tight crop). */
20868
- keyFrameMediaKey: string().optional(),
20918
+ * sources — consumers fall back to `mediaId` (the tight crop). */
20919
+ keyFrameMediaId: number().int().optional(),
20869
20920
  /** Populated by B5 (recording playback URL for this event). */
20870
20921
  mediaUrl: string().optional(),
20871
20922
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -20915,7 +20966,7 @@ var MediaFileKindEnum = _enum([
20915
20966
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
20916
20967
  *
20917
20968
  * `url` points at the `event-media` data plane
20918
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
20969
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
20919
20970
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
20920
20971
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
20921
20972
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -20923,10 +20974,13 @@ var MediaFileKindEnum = _enum([
20923
20974
  * `data-plane-access.ts` for the rule and the one gap it does not close
20924
20975
  * (per-device scoping).
20925
20976
  *
20926
- * The URL is built from the row's **stored** key, which is not always its
20927
- * published `kind`: a track's face/plate crop is stored as `crop` under
20977
+ * The URL is built from the row's own id, which since D482 says nothing about
20978
+ * the row's owner or kind — and that is the point. The published `kind` is not
20979
+ * the stored one (a track's face/plate crop is stored as `crop` under
20928
20980
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
20929
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
20981
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
20982
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
20983
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
20930
20984
  *
20931
20985
  * ## `base64` is TRANSITIONAL and is going away
20932
20986
  *
@@ -20939,7 +20993,19 @@ var MediaFileKindEnum = _enum([
20939
20993
  * `analytics-query-facade.ts`; nothing else reads it.
20940
20994
  */
20941
20995
  var MediaFileSchema = object({
20942
- key: string(),
20996
+ /**
20997
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
20998
+ *
20999
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
21000
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
21001
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
21002
+ * different rows that spell the same number, and a track whose
21003
+ * `bestEventId` was an object event was served a motion event's snapshot —
21004
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
21005
+ * surrogate id cannot name the wrong row's owner, because it names no owner
21006
+ * at all.
21007
+ */
21008
+ mediaId: number().int(),
20943
21009
  kind: MediaFileKindEnum,
20944
21010
  sizeBytes: number(),
20945
21011
  timestamp: number()
@@ -21047,7 +21113,7 @@ var RetrainTrackSchema = object({
21047
21113
  });
21048
21114
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
21049
21115
  var RetrainFrameCandidateSchema = object({
21050
- mediaKey: string(),
21116
+ mediaId: number().int(),
21051
21117
  kind: MediaFileKindEnum,
21052
21118
  timestamp: number(),
21053
21119
  sizeBytes: number().int(),
@@ -21061,7 +21127,7 @@ var RetrainFrameSchema = object({
21061
21127
  deviceId: number(),
21062
21128
  trackId: string(),
21063
21129
  /** Provenance only. It may already point at nothing — that is expected. */
21064
- sourceMediaKey: string(),
21130
+ sourceMediaId: number().int(),
21065
21131
  sourceKind: MediaFileKindEnum,
21066
21132
  sizeBytes: number().int(),
21067
21133
  width: number().int(),
@@ -21077,7 +21143,7 @@ var RetrainCopyRefusalSchema = _enum([
21077
21143
  var RetrainFrameSelectionSchema = object({
21078
21144
  copied: array(RetrainFrameSchema).readonly(),
21079
21145
  refused: array(object({
21080
- sourceMediaKey: string(),
21146
+ sourceMediaId: number().int(),
21081
21147
  reason: RetrainCopyRefusalSchema
21082
21148
  })).readonly()
21083
21149
  });
@@ -21085,7 +21151,7 @@ var RetrainFrameListSchema = object({
21085
21151
  candidates: array(RetrainFrameCandidateSchema).readonly(),
21086
21152
  copies: array(RetrainFrameSchema).readonly(),
21087
21153
  /** What the page pre-selects — the native key frame when one survives. */
21088
- autoPickMediaKey: string().optional()
21154
+ autoPickMediaId: number().int().optional()
21089
21155
  });
21090
21156
  /** What the operator asked the assist to look for. */
21091
21157
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -21180,9 +21246,11 @@ var RecentTracksQueryInput = object({
21180
21246
  });
21181
21247
  /**
21182
21248
  * A Summary (D359): the per-camera session envelope that references tracks.
21183
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
21184
- * served by the event-media plane by key), null until the first frame with a
21185
- * member visible has been materialised.
21249
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
21250
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
21251
+ * first frame with a member visible has been materialised (D482 — it was the
21252
+ * row's composite key, which embedded the owner and could name another table's
21253
+ * row once event ids became per-table rowids).
21186
21254
  */
21187
21255
  var SummarySchema = object({
21188
21256
  id: string(),
@@ -21191,19 +21259,115 @@ var SummarySchema = object({
21191
21259
  lastActiveAt: number(),
21192
21260
  sealedAt: number().nullable(),
21193
21261
  memberCount: number().int().nonnegative(),
21194
- groupShotKey: string().nullable()
21262
+ groupShotMediaId: number().int().nullable()
21195
21263
  });
21196
21264
  var SummariesQueryInput = object({
21197
21265
  deviceIds: array(number()).min(1),
21198
21266
  since: number().optional(),
21199
21267
  until: number().optional(),
21200
- limit: number().int().min(1).max(200).default(50)
21268
+ limit: number().int().min(1).max(200).default(50),
21269
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
21270
+ * meaning of "dog" on every summary surface. */
21271
+ classes: array(string().min(1).max(60)).max(40).optional()
21201
21272
  });
21202
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
21203
21273
  var SummaryDetailSchema = object({
21204
21274
  summary: SummarySchema,
21205
- /** Member track ids, in absorption order. */
21206
- trackIds: array(string()).readonly()
21275
+ /** Members in absorption order, oldest join first. */
21276
+ members: array(object({
21277
+ trackId: string(),
21278
+ /**
21279
+ * The track's class at the time it JOINED, or `null` when it was unresolved
21280
+ * and the track no longer exists to fill it.
21281
+ *
21282
+ * The join, not the latest opinion: class voting can re-decide a track's
21283
+ * class afterwards, and a group's composition is the history of who came
21284
+ * together. A consumer counts these to badge a thumbnail — "two people and a
21285
+ * dog" — rather than being handed a second, denormalised tally that would
21286
+ * drift from the members it claims to describe.
21287
+ */
21288
+ className: string().nullable(),
21289
+ /** `primary` is the one member that carries the group's identity. */
21290
+ role: _enum([
21291
+ "primary",
21292
+ "sibling",
21293
+ "child"
21294
+ ]).nullable(),
21295
+ joinedAt: number(),
21296
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
21297
+ * at read time. Absent when nothing resolved, never an empty string. */
21298
+ label: string().optional(),
21299
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
21300
+ * recognised nobody, and absent for a member whose track has been pruned —
21301
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
21302
+ subLabel: string().optional()
21303
+ })).readonly()
21304
+ });
21305
+ /**
21306
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
21307
+ *
21308
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
21309
+ * we extend when we notice we need more". Enough to draw the card ("two people
21310
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
21311
+ * are on {@link getSummary}: shipping every member of every group so a client
21312
+ * can count them turns a page of two hundred groups into a thousand records to
21313
+ * draw two hundred badges.
21314
+ */
21315
+ var SummaryListItemSchema = object({
21316
+ summary: SummarySchema,
21317
+ /** Class counts, most numerous first; `className: null` is a member whose
21318
+ * class never resolved, counted so the counts add up to `memberCount`. */
21319
+ classes: array(object({
21320
+ className: string().nullable(),
21321
+ count: number().int().positive()
21322
+ })).readonly()
21323
+ });
21324
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
21325
+ /**
21326
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
21327
+ *
21328
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
21329
+ * summaries feed that could only answer "the last N for these cameras"
21330
+ * (`listSummaries`) would make groups available in the timeline and nowhere
21331
+ * else. Two feeds with different ordering contracts would be two ideas of what
21332
+ * "newest" means, on one screen.
21333
+ */
21334
+ var RecentSummariesQueryInput = object({
21335
+ /** Devices to merge. An empty array yields an empty page. */
21336
+ deviceIds: array(number()),
21337
+ /** Window lower bound on `lastActiveAt` (inclusive). */
21338
+ since: number().optional(),
21339
+ /** Window upper bound on `lastActiveAt` (inclusive). */
21340
+ until: number().optional(),
21341
+ limit: number().int().min(1).max(500).default(100),
21342
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
21343
+ cursor: string().optional(),
21344
+ /**
21345
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
21346
+ * deliberately the same name: the events filter tree emits one list of ticked
21347
+ * taxonomy nodes, and a summaries feed that called it something else would
21348
+ * make the operator's one filter component produce two shapes.
21349
+ *
21350
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
21351
+ * me the arrivals with a dog" has to return the group of four people who had
21352
+ * one with them; a filter on the group's own fields could only ever answer
21353
+ * about its primary.
21354
+ *
21355
+ * Matched against all THREE tiers of a member — the class (`animal`), the
21356
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
21357
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
21358
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
21359
+ * that read only the class would answer "animal" and never "dog".
21360
+ *
21361
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
21362
+ */
21363
+ classes: array(string().min(1).max(60)).max(40).optional()
21364
+ });
21365
+ var RecentSummariesPageSchema = object({
21366
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
21367
+ * {@link listSummaries} — one list shape, so a card renders the same
21368
+ * whichever feed drew it. */
21369
+ summaries: array(SummaryListItemSchema).readonly(),
21370
+ nextCursor: string().nullable()
21207
21371
  });
21208
21372
  var RecentTracksPageSchema = object({
21209
21373
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -21617,7 +21781,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21617
21781
  classes: array(string()).optional(),
21618
21782
  /** See {@link ExcludeSourcesDoc}. */
21619
21783
  excludeSources: array(TrackSourceSchema).optional()
21620
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
21784
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
21621
21785
  kind: "mutation",
21622
21786
  auth: "admin"
21623
21787
  }), 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({
@@ -21745,7 +21909,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21745
21909
  }), method(object({
21746
21910
  deviceId: number(),
21747
21911
  trackId: string(),
21748
- mediaKeys: array(string()).min(1)
21912
+ mediaIds: array(number().int()).min(1)
21749
21913
  }), RetrainFrameSelectionSchema, {
21750
21914
  kind: "mutation",
21751
21915
  auth: "admin"
@@ -21813,7 +21977,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21813
21977
  kind: "query",
21814
21978
  auth: "admin"
21815
21979
  }), method(object({
21816
- eventId: string(),
21980
+ /** Which of the three event tables owns the row. */
21981
+ ownerType: EventOwnerTypeSchema,
21982
+ /** The event's rowid in that table (D474: an event id is a number). */
21983
+ eventId: number().int(),
21817
21984
  kind: MediaFileKindEnum.optional(),
21818
21985
  deviceId: number()
21819
21986
  }), array(MediaFileSchema).readonly()), method(object({
@@ -21824,7 +21991,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21824
21991
  trackId: string(),
21825
21992
  deviceId: number()
21826
21993
  }), array(MediaFileInfoSchema).readonly()), method(object({
21827
- eventId: string(),
21994
+ ownerType: EventOwnerTypeSchema,
21995
+ eventId: number().int(),
21828
21996
  deviceId: number()
21829
21997
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21830
21998
  kind: "mutation",
@@ -27025,8 +27193,8 @@ var IdentitySchema = object({
27025
27193
  id: string(),
27026
27194
  name: string(),
27027
27195
  sampleCount: number().int().nonnegative(),
27028
- coverMediaKey: string().optional(),
27029
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
27196
+ coverMediaId: number().int().optional(),
27197
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
27030
27198
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
27031
27199
  coverBase64: string().optional()
27032
27200
  });
@@ -27052,8 +27220,8 @@ var IdentitySampleAuditRowSchema = object({
27052
27220
  enrolledAt: number().int(),
27053
27221
  deviceId: number().int().optional(),
27054
27222
  source: _enum(["detected", "photo"]),
27055
- /** The enrolled crop's media key, so the panel can show the evidence. */
27056
- mediaKey: string().optional(),
27223
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
27224
+ mediaId: number().int().optional(),
27057
27225
  /**
27058
27226
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
27059
27227
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -27122,15 +27290,15 @@ var FaceInfoSchema = object({
27122
27290
  */
27123
27291
  cropUrl: string().optional(),
27124
27292
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
27125
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
27293
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
27126
27294
  * legacy rows written before design B. */
27127
27295
  faceBbox: BoundingBoxSchema.optional(),
27128
27296
  /** Design B: MediaStore key of the track's native-resolution key frame.
27129
27297
  * Fetch the native JPEG via the event-media data-plane
27130
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
27298
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
27131
27299
  * track produced no key frame (e.g. native/onboard source) — the UI falls
27132
27300
  * back to the inline `base64` face crop. */
27133
- keyFrameMediaKey: string().optional(),
27301
+ keyFrameMediaId: number().int().optional(),
27134
27302
  /** Winning identity-match cosine (0..1) for this face's track, when an
27135
27303
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
27136
27304
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -27199,11 +27367,14 @@ var FaceClusterSchema = object({
27199
27367
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27200
27368
  * dialog already rendering its key FRAME from the `event-media` plane.
27201
27369
  *
27202
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27203
- * media key directly, so this needed no new plane and no new access decision.
27370
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
27371
+ * resolves a media row by its own id, so this needed no new plane and no new
27372
+ * access decision.
27204
27373
  */
27205
27374
  var MediaFileLiteSchema$1 = object({
27206
- key: string(),
27375
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
27376
+ * (D482). It was the row's composite key, which embedded the owner. */
27377
+ mediaId: number().int(),
27207
27378
  kind: string(),
27208
27379
  url: string(),
27209
27380
  sizeBytes: number(),
@@ -27220,7 +27391,7 @@ method(object({
27220
27391
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
27221
27392
  * four times and the viewer holds at `staleTime: 30_000`.
27222
27393
  *
27223
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
27394
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
27224
27395
  * the `event-media` plane serves that key `immutable` with an ETag.
27225
27396
  *
27226
27397
  * **This is an INPUT field, so it does not reach the addon until the
@@ -30583,8 +30754,8 @@ var VehicleSchema = object({
30583
30754
  id: string(),
30584
30755
  name: string(),
30585
30756
  sampleCount: number().int().nonnegative(),
30586
- coverMediaKey: string().optional(),
30587
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
30757
+ coverMediaId: number().int().optional(),
30758
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
30588
30759
  coverBase64: string().optional()
30589
30760
  });
30590
30761
  var VehicleSampleInfoSchema = object({
@@ -30617,7 +30788,7 @@ var PlateInfoSchema = object({
30617
30788
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
30618
30789
  plateBbox: BoundingBoxSchema.optional(),
30619
30790
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
30620
- keyFrameMediaKey: string().optional(),
30791
+ keyFrameMediaId: number().int().optional(),
30621
30792
  base64: string().optional(),
30622
30793
  /**
30623
30794
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -30638,11 +30809,14 @@ var PlateInfoSchema = object({
30638
30809
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
30639
30810
  * dialog already rendering its key FRAME from the `event-media` plane.
30640
30811
  *
30641
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
30642
- * media key directly, so this needed no new plane and no new access decision.
30812
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
30813
+ * resolves a media row by its own id, so this needed no new plane and no new
30814
+ * access decision.
30643
30815
  */
30644
30816
  var MediaFileLiteSchema = object({
30645
- key: string(),
30817
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
30818
+ * (D482). It was the row's composite key, which embedded the owner. */
30819
+ mediaId: number().int(),
30646
30820
  kind: string(),
30647
30821
  url: string(),
30648
30822
  sizeBytes: number(),
@@ -30701,7 +30875,7 @@ method(object({
30701
30875
  }), method(object({
30702
30876
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
30703
30877
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
30704
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
30878
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
30705
30879
  * INPUT field: stripped by the hub router until the train ships, which
30706
30880
  * resolves to `false` and is exactly the intended default. */
30707
30881
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -32270,7 +32444,14 @@ var SceneReferenceSchema = object({
32270
32444
  modelId: string(),
32271
32445
  condition: SceneConditionSchema,
32272
32446
  capturedAt: number(),
32273
- thumbnailMediaId: string().optional(),
32447
+ /**
32448
+ * The reference thumbnail's media ROW id (D482).
32449
+ *
32450
+ * The name already said `mediaId` while the value was the row's composite
32451
+ * key — a leftover that read as done and was not. It is the row's own id
32452
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
32453
+ */
32454
+ thumbnailMediaId: number().int().optional(),
32274
32455
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
32275
32456
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
32276
32457
  * normalized rect frame a different piece of world, and the scene would
@@ -33361,7 +33542,29 @@ var LoggingSettingsPatchSchema = object({
33361
33542
  * disarmed the channels it did not mention would make the Levels page and
33362
33543
  * the Diagnostics page fight over the same value.
33363
33544
  */
33364
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
33545
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
33546
+ /**
33547
+ * How many days the analytics OPS LOG is kept — the audit trail of what
33548
+ * retention actually deleted.
33549
+ *
33550
+ * It lives HERE, on the logging document, and not on the analytics addon's
33551
+ * settings form, on the operator's instruction: every other log-ish window in
33552
+ * this system is configured through this one document, and a retention audit
33553
+ * configured somewhere else is a second place to look when the question is
33554
+ * "what happened to my data".
33555
+ *
33556
+ * That placement has a cost worth stating: this document ships in the
33557
+ * framework closure, so this knob needs a published `@camstack/server` while
33558
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
33559
+ *
33560
+ * `retention-model.ts` argued this one must NOT follow a device window,
33561
+ * because the audit trail is the history of the SWEEP and not of the camera.
33562
+ * That argument stands and is untouched — what changes is that 30 days stops
33563
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
33564
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
33565
+ * state in which the next 9.5 GiB leak cannot be explained.
33566
+ */
33567
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
33365
33568
  });
33366
33569
  /**
33367
33570
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -33434,6 +33637,14 @@ var LoggingSettingsStateSchema = object({
33434
33637
  channels: array(LogChannelDescriptorSchema).readonly(),
33435
33638
  /** The channels ARMED right now, each with its deadline. */
33436
33639
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
33640
+ /**
33641
+ * The ops-log window in days, as it is in force. `0` = kept forever.
33642
+ *
33643
+ * Reported rather than left to the caller's memory of what it wrote: this is
33644
+ * the value the analytics sweep will actually use, and an operator asking how
33645
+ * long their audit trail lives should not have to infer it from a default.
33646
+ */
33647
+ opsLogRetentionDays: number().int().min(0),
33437
33648
  persisted: boolean()
33438
33649
  });
33439
33650
  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(), {
@@ -34254,7 +34465,7 @@ var StationaryObjectSchema = object({
34254
34465
  /** Enrichment label carried from the source track (identity / plate). */
34255
34466
  label: string().optional(),
34256
34467
  /** Native-resolution key-frame media key for the parked object's best image. */
34257
- keyFrameMediaKey: string().optional()
34468
+ keyFrameMediaId: number().int().optional()
34258
34469
  });
34259
34470
  var CameraOccupancySnapshotSchema = object({
34260
34471
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -36992,6 +37203,12 @@ Object.freeze({
36992
37203
  addonId: null,
36993
37204
  access: "create"
36994
37205
  },
37206
+ "dataStoreProvider.upsert": {
37207
+ capName: "data-store-provider",
37208
+ capScope: "system",
37209
+ addonId: null,
37210
+ access: "create"
37211
+ },
36995
37212
  "dayNight.getOptions": {
36996
37213
  capName: "day-night",
36997
37214
  capScope: "device",
@@ -39428,6 +39645,12 @@ Object.freeze({
39428
39645
  addonId: null,
39429
39646
  access: "view"
39430
39647
  },
39648
+ "pipelineAnalytics.listRecentSummaries": {
39649
+ capName: "pipeline-analytics",
39650
+ capScope: "device",
39651
+ addonId: null,
39652
+ access: "view"
39653
+ },
39431
39654
  "pipelineAnalytics.listRecentTracks": {
39432
39655
  capName: "pipeline-analytics",
39433
39656
  capScope: "device",
@@ -40856,6 +41079,12 @@ Object.freeze({
40856
41079
  addonId: null,
40857
41080
  access: "create"
40858
41081
  },
41082
+ "settingsStore.upsert": {
41083
+ capName: "settings-store",
41084
+ capScope: "system",
41085
+ addonId: null,
41086
+ access: "create"
41087
+ },
40859
41088
  "smtpProvider.getStatus": {
40860
41089
  capName: "smtp-provider",
40861
41090
  capScope: "system",
@@ -43249,6 +43478,11 @@ Object.freeze({
43249
43478
  form: "single",
43250
43479
  optional: true
43251
43480
  }],
43481
+ "pipelineAnalytics.listRecentSummaries": [{
43482
+ name: "deviceIds",
43483
+ form: "array",
43484
+ optional: false
43485
+ }],
43252
43486
  "pipelineAnalytics.listRecentTracks": [{
43253
43487
  name: "deviceIds",
43254
43488
  form: "array",
package/dist/addon.mjs CHANGED
@@ -1100,7 +1100,7 @@ var Nodepetkit = class {
1100
1100
  }
1101
1101
  };
1102
1102
  //#endregion
1103
- //#region ../types/dist/event-category-DAXzJeTX.mjs
1103
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
1104
1104
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
1105
1105
  EventCategory["SystemBoot"] = "system.boot";
1106
1106
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -1661,7 +1661,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
1661
1661
  * a package zone — a newly-appeared stationary object of a package class
1662
1662
  * that cleared the class / zone / dwell / size gates. Payload:
1663
1663
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
1664
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
1664
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
1665
1665
  * Telemetry (D8): the durable record is the `package-events` store row;
1666
1666
  * this bus topic drives notifier rules + live UI. See
1667
1667
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -6447,7 +6447,7 @@ var ZodIssueCode = {
6447
6447
  var ZodFirstPartyTypeKind;
6448
6448
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
6449
6449
  //#endregion
6450
- //#region ../types/dist/sleep-BnujYGPe.mjs
6450
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
6451
6451
  /**
6452
6452
  * The audio chunk plane's byte format, and the ONE expansion from a coded
6453
6453
  * window to float samples (D455).
@@ -8414,13 +8414,30 @@ new Set([
8414
8414
  "scene",
8415
8415
  "motion",
8416
8416
  "object",
8417
- "audio"
8417
+ "audio",
8418
+ "mosaic"
8418
8419
  ]);
8419
- new Set([
8420
+ /**
8421
+ * The owner types that are event tables, in the order the event-media resolver
8422
+ * should consider them. Exported so a consumer asking "is this key an event?"
8423
+ * does not re-spell the list and drift from it.
8424
+ */
8425
+ var EVENT_OWNER_TYPES = [
8420
8426
  "motion",
8421
8427
  "object",
8422
8428
  "audio"
8423
- ]);
8429
+ ];
8430
+ /**
8431
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
8432
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
8433
+ * `(table, id)`).
8434
+ *
8435
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
8436
+ * table would otherwise be accepted by the codec and refused at the door, with
8437
+ * nothing failing until a caller asked.
8438
+ */
8439
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
8440
+ new Set(EVENT_OWNER_TYPES);
8424
8441
  var EncodeProfileSchema = object({
8425
8442
  video: object({
8426
8443
  codec: _enum([
@@ -8459,7 +8476,23 @@ var EncodeProfileSchema = object({
8459
8476
  "zerolatency",
8460
8477
  "film",
8461
8478
  "animation"
8462
- ]).optional()
8479
+ ]).optional(),
8480
+ /**
8481
+ * ONE slice per access unit.
8482
+ *
8483
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
8484
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
8485
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
8486
+ * frame-end per frame: the first pictures render and the video then freezes
8487
+ * for good while the audio, on its own plane, plays on. Measured on this hub
8488
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
8489
+ *
8490
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
8491
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
8492
+ * consumers meaning the same thing spelled differently would stop sharing one
8493
+ * child. Absent means "whatever the encoder does" — today's behaviour.
8494
+ */
8495
+ singleSlicePerFrame: boolean().optional()
8463
8496
  }),
8464
8497
  audio: union([literal("passthrough"), object({
8465
8498
  codec: _enum([
@@ -8509,7 +8542,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
8509
8542
  audio: "passthrough"
8510
8543
  };
8511
8544
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
8512
- ({ ...BASE_LIVE_EGRESS_PROFILE });
8545
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
8513
8546
  /**
8514
8547
  * Deep wiring healthcheck — snapshot of active reachability probes across
8515
8548
  * every declared capability + widget of every installed plugin, on every
@@ -13619,6 +13652,16 @@ method(object({
13619
13652
  filter: QueryFilterSchema.optional(),
13620
13653
  columns: array(string()).readonly().optional()
13621
13654
  }), array(SettingsRecordSchema).readonly()), method(object({
13655
+ namespace: string().optional(),
13656
+ collection: string(),
13657
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
13658
+ * index on the collection. Never empty. */
13659
+ conflict: array(string()).readonly(),
13660
+ record: BulkRecordSchema
13661
+ }), object({
13662
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
13663
+ * row's own id on an update. */
13664
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
13622
13665
  namespace: string().optional(),
13623
13666
  collection: string(),
13624
13667
  record: object({
@@ -13753,6 +13796,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13753
13796
  id: union([string(), number()]) }), {
13754
13797
  kind: "mutation",
13755
13798
  auth: "admin"
13799
+ }), method(object({
13800
+ namespace: string().optional(),
13801
+ collection: string(),
13802
+ conflict: array(string()).readonly(),
13803
+ record: BulkRecordSchema
13804
+ }), object({ id: union([string(), number()]) }), {
13805
+ kind: "mutation",
13806
+ auth: "admin"
13756
13807
  }), method(object({
13757
13808
  namespace: string().optional(),
13758
13809
  collection: string(),
@@ -20252,8 +20303,8 @@ var TrackPositionSchema = object({
20252
20303
  var TrackSnapshotSchema = object({
20253
20304
  timestamp: number(),
20254
20305
  position: TrackPositionSchema,
20255
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
20256
- mediaKey: string()
20306
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
20307
+ mediaId: number().int()
20257
20308
  });
20258
20309
  /**
20259
20310
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -20857,14 +20908,14 @@ var ObjectEventSchema = object({
20857
20908
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
20858
20909
  frameWidth: number().optional(),
20859
20910
  frameHeight: number().optional(),
20860
- /** MediaStore key for the crop attached to this event (if any). */
20861
- mediaKey: string().optional(),
20862
- /** Design B: MediaStore key of the track's native-resolution key frame (the
20911
+ /** Media row id of the crop attached to this event (if any) — D482. */
20912
+ mediaId: number().int().optional(),
20913
+ /** Design B: media row id of the track's native-resolution key frame (the
20863
20914
  * best-detection full frame). Resolve via the event-media data-plane
20864
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
20915
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
20865
20916
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
20866
- * sources — consumers fall back to `mediaKey` (the tight crop). */
20867
- keyFrameMediaKey: string().optional(),
20917
+ * sources — consumers fall back to `mediaId` (the tight crop). */
20918
+ keyFrameMediaId: number().int().optional(),
20868
20919
  /** Populated by B5 (recording playback URL for this event). */
20869
20920
  mediaUrl: string().optional(),
20870
20921
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -20914,7 +20965,7 @@ var MediaFileKindEnum = _enum([
20914
20965
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
20915
20966
  *
20916
20967
  * `url` points at the `event-media` data plane
20917
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
20968
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
20918
20969
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
20919
20970
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
20920
20971
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -20922,10 +20973,13 @@ var MediaFileKindEnum = _enum([
20922
20973
  * `data-plane-access.ts` for the rule and the one gap it does not close
20923
20974
  * (per-device scoping).
20924
20975
  *
20925
- * The URL is built from the row's **stored** key, which is not always its
20926
- * published `kind`: a track's face/plate crop is stored as `crop` under
20976
+ * The URL is built from the row's own id, which since D482 says nothing about
20977
+ * the row's owner or kind — and that is the point. The published `kind` is not
20978
+ * the stored one (a track's face/plate crop is stored as `crop` under
20927
20979
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
20928
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
20980
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
20981
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
20982
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
20929
20983
  *
20930
20984
  * ## `base64` is TRANSITIONAL and is going away
20931
20985
  *
@@ -20938,7 +20992,19 @@ var MediaFileKindEnum = _enum([
20938
20992
  * `analytics-query-facade.ts`; nothing else reads it.
20939
20993
  */
20940
20994
  var MediaFileSchema = object({
20941
- key: string(),
20995
+ /**
20996
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
20997
+ *
20998
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
20999
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
21000
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
21001
+ * different rows that spell the same number, and a track whose
21002
+ * `bestEventId` was an object event was served a motion event's snapshot —
21003
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
21004
+ * surrogate id cannot name the wrong row's owner, because it names no owner
21005
+ * at all.
21006
+ */
21007
+ mediaId: number().int(),
20942
21008
  kind: MediaFileKindEnum,
20943
21009
  sizeBytes: number(),
20944
21010
  timestamp: number()
@@ -21046,7 +21112,7 @@ var RetrainTrackSchema = object({
21046
21112
  });
21047
21113
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
21048
21114
  var RetrainFrameCandidateSchema = object({
21049
- mediaKey: string(),
21115
+ mediaId: number().int(),
21050
21116
  kind: MediaFileKindEnum,
21051
21117
  timestamp: number(),
21052
21118
  sizeBytes: number().int(),
@@ -21060,7 +21126,7 @@ var RetrainFrameSchema = object({
21060
21126
  deviceId: number(),
21061
21127
  trackId: string(),
21062
21128
  /** Provenance only. It may already point at nothing — that is expected. */
21063
- sourceMediaKey: string(),
21129
+ sourceMediaId: number().int(),
21064
21130
  sourceKind: MediaFileKindEnum,
21065
21131
  sizeBytes: number().int(),
21066
21132
  width: number().int(),
@@ -21076,7 +21142,7 @@ var RetrainCopyRefusalSchema = _enum([
21076
21142
  var RetrainFrameSelectionSchema = object({
21077
21143
  copied: array(RetrainFrameSchema).readonly(),
21078
21144
  refused: array(object({
21079
- sourceMediaKey: string(),
21145
+ sourceMediaId: number().int(),
21080
21146
  reason: RetrainCopyRefusalSchema
21081
21147
  })).readonly()
21082
21148
  });
@@ -21084,7 +21150,7 @@ var RetrainFrameListSchema = object({
21084
21150
  candidates: array(RetrainFrameCandidateSchema).readonly(),
21085
21151
  copies: array(RetrainFrameSchema).readonly(),
21086
21152
  /** What the page pre-selects — the native key frame when one survives. */
21087
- autoPickMediaKey: string().optional()
21153
+ autoPickMediaId: number().int().optional()
21088
21154
  });
21089
21155
  /** What the operator asked the assist to look for. */
21090
21156
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -21179,9 +21245,11 @@ var RecentTracksQueryInput = object({
21179
21245
  });
21180
21246
  /**
21181
21247
  * A Summary (D359): the per-camera session envelope that references tracks.
21182
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
21183
- * served by the event-media plane by key), null until the first frame with a
21184
- * member visible has been materialised.
21248
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
21249
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
21250
+ * first frame with a member visible has been materialised (D482 — it was the
21251
+ * row's composite key, which embedded the owner and could name another table's
21252
+ * row once event ids became per-table rowids).
21185
21253
  */
21186
21254
  var SummarySchema = object({
21187
21255
  id: string(),
@@ -21190,19 +21258,115 @@ var SummarySchema = object({
21190
21258
  lastActiveAt: number(),
21191
21259
  sealedAt: number().nullable(),
21192
21260
  memberCount: number().int().nonnegative(),
21193
- groupShotKey: string().nullable()
21261
+ groupShotMediaId: number().int().nullable()
21194
21262
  });
21195
21263
  var SummariesQueryInput = object({
21196
21264
  deviceIds: array(number()).min(1),
21197
21265
  since: number().optional(),
21198
21266
  until: number().optional(),
21199
- limit: number().int().min(1).max(200).default(50)
21267
+ limit: number().int().min(1).max(200).default(50),
21268
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
21269
+ * meaning of "dog" on every summary surface. */
21270
+ classes: array(string().min(1).max(60)).max(40).optional()
21200
21271
  });
21201
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
21202
21272
  var SummaryDetailSchema = object({
21203
21273
  summary: SummarySchema,
21204
- /** Member track ids, in absorption order. */
21205
- trackIds: array(string()).readonly()
21274
+ /** Members in absorption order, oldest join first. */
21275
+ members: array(object({
21276
+ trackId: string(),
21277
+ /**
21278
+ * The track's class at the time it JOINED, or `null` when it was unresolved
21279
+ * and the track no longer exists to fill it.
21280
+ *
21281
+ * The join, not the latest opinion: class voting can re-decide a track's
21282
+ * class afterwards, and a group's composition is the history of who came
21283
+ * together. A consumer counts these to badge a thumbnail — "two people and a
21284
+ * dog" — rather than being handed a second, denormalised tally that would
21285
+ * drift from the members it claims to describe.
21286
+ */
21287
+ className: string().nullable(),
21288
+ /** `primary` is the one member that carries the group's identity. */
21289
+ role: _enum([
21290
+ "primary",
21291
+ "sibling",
21292
+ "child"
21293
+ ]).nullable(),
21294
+ joinedAt: number(),
21295
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
21296
+ * at read time. Absent when nothing resolved, never an empty string. */
21297
+ label: string().optional(),
21298
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
21299
+ * recognised nobody, and absent for a member whose track has been pruned —
21300
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
21301
+ subLabel: string().optional()
21302
+ })).readonly()
21303
+ });
21304
+ /**
21305
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
21306
+ *
21307
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
21308
+ * we extend when we notice we need more". Enough to draw the card ("two people
21309
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
21310
+ * are on {@link getSummary}: shipping every member of every group so a client
21311
+ * can count them turns a page of two hundred groups into a thousand records to
21312
+ * draw two hundred badges.
21313
+ */
21314
+ var SummaryListItemSchema = object({
21315
+ summary: SummarySchema,
21316
+ /** Class counts, most numerous first; `className: null` is a member whose
21317
+ * class never resolved, counted so the counts add up to `memberCount`. */
21318
+ classes: array(object({
21319
+ className: string().nullable(),
21320
+ count: number().int().positive()
21321
+ })).readonly()
21322
+ });
21323
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
21324
+ /**
21325
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
21326
+ *
21327
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
21328
+ * summaries feed that could only answer "the last N for these cameras"
21329
+ * (`listSummaries`) would make groups available in the timeline and nowhere
21330
+ * else. Two feeds with different ordering contracts would be two ideas of what
21331
+ * "newest" means, on one screen.
21332
+ */
21333
+ var RecentSummariesQueryInput = object({
21334
+ /** Devices to merge. An empty array yields an empty page. */
21335
+ deviceIds: array(number()),
21336
+ /** Window lower bound on `lastActiveAt` (inclusive). */
21337
+ since: number().optional(),
21338
+ /** Window upper bound on `lastActiveAt` (inclusive). */
21339
+ until: number().optional(),
21340
+ limit: number().int().min(1).max(500).default(100),
21341
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
21342
+ cursor: string().optional(),
21343
+ /**
21344
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
21345
+ * deliberately the same name: the events filter tree emits one list of ticked
21346
+ * taxonomy nodes, and a summaries feed that called it something else would
21347
+ * make the operator's one filter component produce two shapes.
21348
+ *
21349
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
21350
+ * me the arrivals with a dog" has to return the group of four people who had
21351
+ * one with them; a filter on the group's own fields could only ever answer
21352
+ * about its primary.
21353
+ *
21354
+ * Matched against all THREE tiers of a member — the class (`animal`), the
21355
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
21356
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
21357
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
21358
+ * that read only the class would answer "animal" and never "dog".
21359
+ *
21360
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
21361
+ */
21362
+ classes: array(string().min(1).max(60)).max(40).optional()
21363
+ });
21364
+ var RecentSummariesPageSchema = object({
21365
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
21366
+ * {@link listSummaries} — one list shape, so a card renders the same
21367
+ * whichever feed drew it. */
21368
+ summaries: array(SummaryListItemSchema).readonly(),
21369
+ nextCursor: string().nullable()
21206
21370
  });
21207
21371
  var RecentTracksPageSchema = object({
21208
21372
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -21616,7 +21780,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21616
21780
  classes: array(string()).optional(),
21617
21781
  /** See {@link ExcludeSourcesDoc}. */
21618
21782
  excludeSources: array(TrackSourceSchema).optional()
21619
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
21783
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
21620
21784
  kind: "mutation",
21621
21785
  auth: "admin"
21622
21786
  }), 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({
@@ -21744,7 +21908,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21744
21908
  }), method(object({
21745
21909
  deviceId: number(),
21746
21910
  trackId: string(),
21747
- mediaKeys: array(string()).min(1)
21911
+ mediaIds: array(number().int()).min(1)
21748
21912
  }), RetrainFrameSelectionSchema, {
21749
21913
  kind: "mutation",
21750
21914
  auth: "admin"
@@ -21812,7 +21976,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21812
21976
  kind: "query",
21813
21977
  auth: "admin"
21814
21978
  }), method(object({
21815
- eventId: string(),
21979
+ /** Which of the three event tables owns the row. */
21980
+ ownerType: EventOwnerTypeSchema,
21981
+ /** The event's rowid in that table (D474: an event id is a number). */
21982
+ eventId: number().int(),
21816
21983
  kind: MediaFileKindEnum.optional(),
21817
21984
  deviceId: number()
21818
21985
  }), array(MediaFileSchema).readonly()), method(object({
@@ -21823,7 +21990,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21823
21990
  trackId: string(),
21824
21991
  deviceId: number()
21825
21992
  }), array(MediaFileInfoSchema).readonly()), method(object({
21826
- eventId: string(),
21993
+ ownerType: EventOwnerTypeSchema,
21994
+ eventId: number().int(),
21827
21995
  deviceId: number()
21828
21996
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21829
21997
  kind: "mutation",
@@ -27024,8 +27192,8 @@ var IdentitySchema = object({
27024
27192
  id: string(),
27025
27193
  name: string(),
27026
27194
  sampleCount: number().int().nonnegative(),
27027
- coverMediaKey: string().optional(),
27028
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
27195
+ coverMediaId: number().int().optional(),
27196
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
27029
27197
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
27030
27198
  coverBase64: string().optional()
27031
27199
  });
@@ -27051,8 +27219,8 @@ var IdentitySampleAuditRowSchema = object({
27051
27219
  enrolledAt: number().int(),
27052
27220
  deviceId: number().int().optional(),
27053
27221
  source: _enum(["detected", "photo"]),
27054
- /** The enrolled crop's media key, so the panel can show the evidence. */
27055
- mediaKey: string().optional(),
27222
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
27223
+ mediaId: number().int().optional(),
27056
27224
  /**
27057
27225
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
27058
27226
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -27121,15 +27289,15 @@ var FaceInfoSchema = object({
27121
27289
  */
27122
27290
  cropUrl: string().optional(),
27123
27291
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
27124
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
27292
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
27125
27293
  * legacy rows written before design B. */
27126
27294
  faceBbox: BoundingBoxSchema.optional(),
27127
27295
  /** Design B: MediaStore key of the track's native-resolution key frame.
27128
27296
  * Fetch the native JPEG via the event-media data-plane
27129
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
27297
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
27130
27298
  * track produced no key frame (e.g. native/onboard source) — the UI falls
27131
27299
  * back to the inline `base64` face crop. */
27132
- keyFrameMediaKey: string().optional(),
27300
+ keyFrameMediaId: number().int().optional(),
27133
27301
  /** Winning identity-match cosine (0..1) for this face's track, when an
27134
27302
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
27135
27303
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -27198,11 +27366,14 @@ var FaceClusterSchema = object({
27198
27366
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27199
27367
  * dialog already rendering its key FRAME from the `event-media` plane.
27200
27368
  *
27201
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27202
- * media key directly, so this needed no new plane and no new access decision.
27369
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
27370
+ * resolves a media row by its own id, so this needed no new plane and no new
27371
+ * access decision.
27203
27372
  */
27204
27373
  var MediaFileLiteSchema$1 = object({
27205
- key: string(),
27374
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
27375
+ * (D482). It was the row's composite key, which embedded the owner. */
27376
+ mediaId: number().int(),
27206
27377
  kind: string(),
27207
27378
  url: string(),
27208
27379
  sizeBytes: number(),
@@ -27219,7 +27390,7 @@ method(object({
27219
27390
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
27220
27391
  * four times and the viewer holds at `staleTime: 30_000`.
27221
27392
  *
27222
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
27393
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
27223
27394
  * the `event-media` plane serves that key `immutable` with an ETag.
27224
27395
  *
27225
27396
  * **This is an INPUT field, so it does not reach the addon until the
@@ -30582,8 +30753,8 @@ var VehicleSchema = object({
30582
30753
  id: string(),
30583
30754
  name: string(),
30584
30755
  sampleCount: number().int().nonnegative(),
30585
- coverMediaKey: string().optional(),
30586
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
30756
+ coverMediaId: number().int().optional(),
30757
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
30587
30758
  coverBase64: string().optional()
30588
30759
  });
30589
30760
  var VehicleSampleInfoSchema = object({
@@ -30616,7 +30787,7 @@ var PlateInfoSchema = object({
30616
30787
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
30617
30788
  plateBbox: BoundingBoxSchema.optional(),
30618
30789
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
30619
- keyFrameMediaKey: string().optional(),
30790
+ keyFrameMediaId: number().int().optional(),
30620
30791
  base64: string().optional(),
30621
30792
  /**
30622
30793
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -30637,11 +30808,14 @@ var PlateInfoSchema = object({
30637
30808
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
30638
30809
  * dialog already rendering its key FRAME from the `event-media` plane.
30639
30810
  *
30640
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
30641
- * media key directly, so this needed no new plane and no new access decision.
30811
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
30812
+ * resolves a media row by its own id, so this needed no new plane and no new
30813
+ * access decision.
30642
30814
  */
30643
30815
  var MediaFileLiteSchema = object({
30644
- key: string(),
30816
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
30817
+ * (D482). It was the row's composite key, which embedded the owner. */
30818
+ mediaId: number().int(),
30645
30819
  kind: string(),
30646
30820
  url: string(),
30647
30821
  sizeBytes: number(),
@@ -30700,7 +30874,7 @@ method(object({
30700
30874
  }), method(object({
30701
30875
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
30702
30876
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
30703
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
30877
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
30704
30878
  * INPUT field: stripped by the hub router until the train ships, which
30705
30879
  * resolves to `false` and is exactly the intended default. */
30706
30880
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -32269,7 +32443,14 @@ var SceneReferenceSchema = object({
32269
32443
  modelId: string(),
32270
32444
  condition: SceneConditionSchema,
32271
32445
  capturedAt: number(),
32272
- thumbnailMediaId: string().optional(),
32446
+ /**
32447
+ * The reference thumbnail's media ROW id (D482).
32448
+ *
32449
+ * The name already said `mediaId` while the value was the row's composite
32450
+ * key — a leftover that read as done and was not. It is the row's own id
32451
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
32452
+ */
32453
+ thumbnailMediaId: number().int().optional(),
32273
32454
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
32274
32455
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
32275
32456
  * normalized rect frame a different piece of world, and the scene would
@@ -33360,7 +33541,29 @@ var LoggingSettingsPatchSchema = object({
33360
33541
  * disarmed the channels it did not mention would make the Levels page and
33361
33542
  * the Diagnostics page fight over the same value.
33362
33543
  */
33363
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
33544
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
33545
+ /**
33546
+ * How many days the analytics OPS LOG is kept — the audit trail of what
33547
+ * retention actually deleted.
33548
+ *
33549
+ * It lives HERE, on the logging document, and not on the analytics addon's
33550
+ * settings form, on the operator's instruction: every other log-ish window in
33551
+ * this system is configured through this one document, and a retention audit
33552
+ * configured somewhere else is a second place to look when the question is
33553
+ * "what happened to my data".
33554
+ *
33555
+ * That placement has a cost worth stating: this document ships in the
33556
+ * framework closure, so this knob needs a published `@camstack/server` while
33557
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
33558
+ *
33559
+ * `retention-model.ts` argued this one must NOT follow a device window,
33560
+ * because the audit trail is the history of the SWEEP and not of the camera.
33561
+ * That argument stands and is untouched — what changes is that 30 days stops
33562
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
33563
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
33564
+ * state in which the next 9.5 GiB leak cannot be explained.
33565
+ */
33566
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
33364
33567
  });
33365
33568
  /**
33366
33569
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -33433,6 +33636,14 @@ var LoggingSettingsStateSchema = object({
33433
33636
  channels: array(LogChannelDescriptorSchema).readonly(),
33434
33637
  /** The channels ARMED right now, each with its deadline. */
33435
33638
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
33639
+ /**
33640
+ * The ops-log window in days, as it is in force. `0` = kept forever.
33641
+ *
33642
+ * Reported rather than left to the caller's memory of what it wrote: this is
33643
+ * the value the analytics sweep will actually use, and an operator asking how
33644
+ * long their audit trail lives should not have to infer it from a default.
33645
+ */
33646
+ opsLogRetentionDays: number().int().min(0),
33436
33647
  persisted: boolean()
33437
33648
  });
33438
33649
  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(), {
@@ -34253,7 +34464,7 @@ var StationaryObjectSchema = object({
34253
34464
  /** Enrichment label carried from the source track (identity / plate). */
34254
34465
  label: string().optional(),
34255
34466
  /** Native-resolution key-frame media key for the parked object's best image. */
34256
- keyFrameMediaKey: string().optional()
34467
+ keyFrameMediaId: number().int().optional()
34257
34468
  });
34258
34469
  var CameraOccupancySnapshotSchema = object({
34259
34470
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -36991,6 +37202,12 @@ Object.freeze({
36991
37202
  addonId: null,
36992
37203
  access: "create"
36993
37204
  },
37205
+ "dataStoreProvider.upsert": {
37206
+ capName: "data-store-provider",
37207
+ capScope: "system",
37208
+ addonId: null,
37209
+ access: "create"
37210
+ },
36994
37211
  "dayNight.getOptions": {
36995
37212
  capName: "day-night",
36996
37213
  capScope: "device",
@@ -39427,6 +39644,12 @@ Object.freeze({
39427
39644
  addonId: null,
39428
39645
  access: "view"
39429
39646
  },
39647
+ "pipelineAnalytics.listRecentSummaries": {
39648
+ capName: "pipeline-analytics",
39649
+ capScope: "device",
39650
+ addonId: null,
39651
+ access: "view"
39652
+ },
39430
39653
  "pipelineAnalytics.listRecentTracks": {
39431
39654
  capName: "pipeline-analytics",
39432
39655
  capScope: "device",
@@ -40855,6 +41078,12 @@ Object.freeze({
40855
41078
  addonId: null,
40856
41079
  access: "create"
40857
41080
  },
41081
+ "settingsStore.upsert": {
41082
+ capName: "settings-store",
41083
+ capScope: "system",
41084
+ addonId: null,
41085
+ access: "create"
41086
+ },
40858
41087
  "smtpProvider.getStatus": {
40859
41088
  capName: "smtp-provider",
40860
41089
  capScope: "system",
@@ -43248,6 +43477,11 @@ Object.freeze({
43248
43477
  form: "single",
43249
43478
  optional: true
43250
43479
  }],
43480
+ "pipelineAnalytics.listRecentSummaries": [{
43481
+ name: "deviceIds",
43482
+ form: "array",
43483
+ optional: false
43484
+ }],
43251
43485
  "pipelineAnalytics.listRecentTracks": [{
43252
43486
  name: "deviceIds",
43253
43487
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-petkit",
3
- "version": "0.2.99",
3
+ "version": "0.2.101",
4
4
  "description": "PetKit smart-feeder device-provider addon for CamStack — wraps the @apocaliss92/nodepetkit PetKit cloud client",
5
5
  "keywords": [
6
6
  "camstack",