@camstack/addon-export-hap 1.2.110 → 1.2.112

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.
@@ -84,7 +84,7 @@ function carryForward(base, existing, keys) {
84
84
  return out;
85
85
  }
86
86
  //#endregion
87
- //#region ../types/dist/event-category-DAXzJeTX.mjs
87
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
88
88
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
89
89
  EventCategory["SystemBoot"] = "system.boot";
90
90
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -645,7 +645,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
645
645
  * a package zone — a newly-appeared stationary object of a package class
646
646
  * that cleared the class / zone / dwell / size gates. Payload:
647
647
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
648
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
648
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
649
649
  * Telemetry (D8): the durable record is the `package-events` store row;
650
650
  * this bus topic drives notifier rules + live UI. See
651
651
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5431,7 +5431,7 @@ var ZodIssueCode = {
5431
5431
  var ZodFirstPartyTypeKind;
5432
5432
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5433
5433
  //#endregion
5434
- //#region ../types/dist/sleep-BnujYGPe.mjs
5434
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5435
5435
  /**
5436
5436
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5437
5437
  * window to float samples (D455).
@@ -7951,13 +7951,30 @@ new Set([
7951
7951
  "scene",
7952
7952
  "motion",
7953
7953
  "object",
7954
- "audio"
7954
+ "audio",
7955
+ "mosaic"
7955
7956
  ]);
7956
- new Set([
7957
+ /**
7958
+ * The owner types that are event tables, in the order the event-media resolver
7959
+ * should consider them. Exported so a consumer asking "is this key an event?"
7960
+ * does not re-spell the list and drift from it.
7961
+ */
7962
+ var EVENT_OWNER_TYPES = [
7957
7963
  "motion",
7958
7964
  "object",
7959
7965
  "audio"
7960
- ]);
7966
+ ];
7967
+ /**
7968
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7969
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7970
+ * `(table, id)`).
7971
+ *
7972
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7973
+ * table would otherwise be accepted by the codec and refused at the door, with
7974
+ * nothing failing until a caller asked.
7975
+ */
7976
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7977
+ new Set(EVENT_OWNER_TYPES);
7961
7978
  var EncodeProfileSchema = object({
7962
7979
  video: object({
7963
7980
  codec: _enum([
@@ -13136,6 +13153,16 @@ method(object({
13136
13153
  filter: QueryFilterSchema.optional(),
13137
13154
  columns: array(string()).readonly().optional()
13138
13155
  }), array(SettingsRecordSchema).readonly()), method(object({
13156
+ namespace: string().optional(),
13157
+ collection: string(),
13158
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
13159
+ * index on the collection. Never empty. */
13160
+ conflict: array(string()).readonly(),
13161
+ record: BulkRecordSchema
13162
+ }), object({
13163
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
13164
+ * row's own id on an update. */
13165
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
13139
13166
  namespace: string().optional(),
13140
13167
  collection: string(),
13141
13168
  record: object({
@@ -13270,6 +13297,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13270
13297
  id: union([string(), number()]) }), {
13271
13298
  kind: "mutation",
13272
13299
  auth: "admin"
13300
+ }), method(object({
13301
+ namespace: string().optional(),
13302
+ collection: string(),
13303
+ conflict: array(string()).readonly(),
13304
+ record: BulkRecordSchema
13305
+ }), object({ id: union([string(), number()]) }), {
13306
+ kind: "mutation",
13307
+ auth: "admin"
13273
13308
  }), method(object({
13274
13309
  namespace: string().optional(),
13275
13310
  collection: string(),
@@ -19567,8 +19602,8 @@ var TrackPositionSchema = object({
19567
19602
  var TrackSnapshotSchema = object({
19568
19603
  timestamp: number(),
19569
19604
  position: TrackPositionSchema,
19570
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19571
- mediaKey: string()
19605
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19606
+ mediaId: number().int()
19572
19607
  });
19573
19608
  /**
19574
19609
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -20172,14 +20207,14 @@ var ObjectEventSchema = object({
20172
20207
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
20173
20208
  frameWidth: number().optional(),
20174
20209
  frameHeight: number().optional(),
20175
- /** MediaStore key for the crop attached to this event (if any). */
20176
- mediaKey: string().optional(),
20177
- /** Design B: MediaStore key of the track's native-resolution key frame (the
20210
+ /** Media row id of the crop attached to this event (if any) — D482. */
20211
+ mediaId: number().int().optional(),
20212
+ /** Design B: media row id of the track's native-resolution key frame (the
20178
20213
  * best-detection full frame). Resolve via the event-media data-plane
20179
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
20214
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
20180
20215
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
20181
- * sources — consumers fall back to `mediaKey` (the tight crop). */
20182
- keyFrameMediaKey: string().optional(),
20216
+ * sources — consumers fall back to `mediaId` (the tight crop). */
20217
+ keyFrameMediaId: number().int().optional(),
20183
20218
  /** Populated by B5 (recording playback URL for this event). */
20184
20219
  mediaUrl: string().optional(),
20185
20220
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -20229,7 +20264,7 @@ var MediaFileKindEnum = _enum([
20229
20264
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
20230
20265
  *
20231
20266
  * `url` points at the `event-media` data plane
20232
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
20267
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
20233
20268
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
20234
20269
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
20235
20270
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -20237,10 +20272,13 @@ var MediaFileKindEnum = _enum([
20237
20272
  * `data-plane-access.ts` for the rule and the one gap it does not close
20238
20273
  * (per-device scoping).
20239
20274
  *
20240
- * The URL is built from the row's **stored** key, which is not always its
20241
- * published `kind`: a track's face/plate crop is stored as `crop` under
20275
+ * The URL is built from the row's own id, which since D482 says nothing about
20276
+ * the row's owner or kind — and that is the point. The published `kind` is not
20277
+ * the stored one (a track's face/plate crop is stored as `crop` under
20242
20278
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
20243
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
20279
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
20280
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
20281
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
20244
20282
  *
20245
20283
  * ## `base64` is TRANSITIONAL and is going away
20246
20284
  *
@@ -20253,7 +20291,19 @@ var MediaFileKindEnum = _enum([
20253
20291
  * `analytics-query-facade.ts`; nothing else reads it.
20254
20292
  */
20255
20293
  var MediaFileSchema = object({
20256
- key: string(),
20294
+ /**
20295
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
20296
+ *
20297
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
20298
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
20299
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
20300
+ * different rows that spell the same number, and a track whose
20301
+ * `bestEventId` was an object event was served a motion event's snapshot —
20302
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
20303
+ * surrogate id cannot name the wrong row's owner, because it names no owner
20304
+ * at all.
20305
+ */
20306
+ mediaId: number().int(),
20257
20307
  kind: MediaFileKindEnum,
20258
20308
  sizeBytes: number(),
20259
20309
  timestamp: number()
@@ -20361,7 +20411,7 @@ var RetrainTrackSchema = object({
20361
20411
  });
20362
20412
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
20363
20413
  var RetrainFrameCandidateSchema = object({
20364
- mediaKey: string(),
20414
+ mediaId: number().int(),
20365
20415
  kind: MediaFileKindEnum,
20366
20416
  timestamp: number(),
20367
20417
  sizeBytes: number().int(),
@@ -20375,7 +20425,7 @@ var RetrainFrameSchema = object({
20375
20425
  deviceId: number(),
20376
20426
  trackId: string(),
20377
20427
  /** Provenance only. It may already point at nothing — that is expected. */
20378
- sourceMediaKey: string(),
20428
+ sourceMediaId: number().int(),
20379
20429
  sourceKind: MediaFileKindEnum,
20380
20430
  sizeBytes: number().int(),
20381
20431
  width: number().int(),
@@ -20391,7 +20441,7 @@ var RetrainCopyRefusalSchema = _enum([
20391
20441
  var RetrainFrameSelectionSchema = object({
20392
20442
  copied: array(RetrainFrameSchema).readonly(),
20393
20443
  refused: array(object({
20394
- sourceMediaKey: string(),
20444
+ sourceMediaId: number().int(),
20395
20445
  reason: RetrainCopyRefusalSchema
20396
20446
  })).readonly()
20397
20447
  });
@@ -20399,7 +20449,7 @@ var RetrainFrameListSchema = object({
20399
20449
  candidates: array(RetrainFrameCandidateSchema).readonly(),
20400
20450
  copies: array(RetrainFrameSchema).readonly(),
20401
20451
  /** What the page pre-selects — the native key frame when one survives. */
20402
- autoPickMediaKey: string().optional()
20452
+ autoPickMediaId: number().int().optional()
20403
20453
  });
20404
20454
  /** What the operator asked the assist to look for. */
20405
20455
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20494,9 +20544,11 @@ var RecentTracksQueryInput = object({
20494
20544
  });
20495
20545
  /**
20496
20546
  * A Summary (D359): the per-camera session envelope that references tracks.
20497
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20498
- * served by the event-media plane by key), null until the first frame with a
20499
- * member visible has been materialised.
20547
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20548
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20549
+ * first frame with a member visible has been materialised (D482 — it was the
20550
+ * row's composite key, which embedded the owner and could name another table's
20551
+ * row once event ids became per-table rowids).
20500
20552
  */
20501
20553
  var SummarySchema = object({
20502
20554
  id: string(),
@@ -20505,19 +20557,115 @@ var SummarySchema = object({
20505
20557
  lastActiveAt: number(),
20506
20558
  sealedAt: number().nullable(),
20507
20559
  memberCount: number().int().nonnegative(),
20508
- groupShotKey: string().nullable()
20560
+ groupShotMediaId: number().int().nullable()
20509
20561
  });
20510
20562
  var SummariesQueryInput = object({
20511
20563
  deviceIds: array(number()).min(1),
20512
20564
  since: number().optional(),
20513
20565
  until: number().optional(),
20514
- limit: number().int().min(1).max(200).default(50)
20566
+ limit: number().int().min(1).max(200).default(50),
20567
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20568
+ * meaning of "dog" on every summary surface. */
20569
+ classes: array(string().min(1).max(60)).max(40).optional()
20515
20570
  });
20516
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20517
20571
  var SummaryDetailSchema = object({
20518
20572
  summary: SummarySchema,
20519
- /** Member track ids, in absorption order. */
20520
- trackIds: array(string()).readonly()
20573
+ /** Members in absorption order, oldest join first. */
20574
+ members: array(object({
20575
+ trackId: string(),
20576
+ /**
20577
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20578
+ * and the track no longer exists to fill it.
20579
+ *
20580
+ * The join, not the latest opinion: class voting can re-decide a track's
20581
+ * class afterwards, and a group's composition is the history of who came
20582
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20583
+ * dog" — rather than being handed a second, denormalised tally that would
20584
+ * drift from the members it claims to describe.
20585
+ */
20586
+ className: string().nullable(),
20587
+ /** `primary` is the one member that carries the group's identity. */
20588
+ role: _enum([
20589
+ "primary",
20590
+ "sibling",
20591
+ "child"
20592
+ ]).nullable(),
20593
+ joinedAt: number(),
20594
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20595
+ * at read time. Absent when nothing resolved, never an empty string. */
20596
+ label: string().optional(),
20597
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20598
+ * recognised nobody, and absent for a member whose track has been pruned —
20599
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20600
+ subLabel: string().optional()
20601
+ })).readonly()
20602
+ });
20603
+ /**
20604
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20605
+ *
20606
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20607
+ * we extend when we notice we need more". Enough to draw the card ("two people
20608
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20609
+ * are on {@link getSummary}: shipping every member of every group so a client
20610
+ * can count them turns a page of two hundred groups into a thousand records to
20611
+ * draw two hundred badges.
20612
+ */
20613
+ var SummaryListItemSchema = object({
20614
+ summary: SummarySchema,
20615
+ /** Class counts, most numerous first; `className: null` is a member whose
20616
+ * class never resolved, counted so the counts add up to `memberCount`. */
20617
+ classes: array(object({
20618
+ className: string().nullable(),
20619
+ count: number().int().positive()
20620
+ })).readonly()
20621
+ });
20622
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20623
+ /**
20624
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20625
+ *
20626
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20627
+ * summaries feed that could only answer "the last N for these cameras"
20628
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20629
+ * else. Two feeds with different ordering contracts would be two ideas of what
20630
+ * "newest" means, on one screen.
20631
+ */
20632
+ var RecentSummariesQueryInput = object({
20633
+ /** Devices to merge. An empty array yields an empty page. */
20634
+ deviceIds: array(number()),
20635
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20636
+ since: number().optional(),
20637
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20638
+ until: number().optional(),
20639
+ limit: number().int().min(1).max(500).default(100),
20640
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20641
+ cursor: string().optional(),
20642
+ /**
20643
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20644
+ * deliberately the same name: the events filter tree emits one list of ticked
20645
+ * taxonomy nodes, and a summaries feed that called it something else would
20646
+ * make the operator's one filter component produce two shapes.
20647
+ *
20648
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20649
+ * me the arrivals with a dog" has to return the group of four people who had
20650
+ * one with them; a filter on the group's own fields could only ever answer
20651
+ * about its primary.
20652
+ *
20653
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20654
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20655
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20656
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20657
+ * that read only the class would answer "animal" and never "dog".
20658
+ *
20659
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20660
+ */
20661
+ classes: array(string().min(1).max(60)).max(40).optional()
20662
+ });
20663
+ var RecentSummariesPageSchema = object({
20664
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20665
+ * {@link listSummaries} — one list shape, so a card renders the same
20666
+ * whichever feed drew it. */
20667
+ summaries: array(SummaryListItemSchema).readonly(),
20668
+ nextCursor: string().nullable()
20521
20669
  });
20522
20670
  var RecentTracksPageSchema = object({
20523
20671
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20931,7 +21079,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20931
21079
  classes: array(string()).optional(),
20932
21080
  /** See {@link ExcludeSourcesDoc}. */
20933
21081
  excludeSources: array(TrackSourceSchema).optional()
20934
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
21082
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20935
21083
  kind: "mutation",
20936
21084
  auth: "admin"
20937
21085
  }), 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({
@@ -21059,7 +21207,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21059
21207
  }), method(object({
21060
21208
  deviceId: number(),
21061
21209
  trackId: string(),
21062
- mediaKeys: array(string()).min(1)
21210
+ mediaIds: array(number().int()).min(1)
21063
21211
  }), RetrainFrameSelectionSchema, {
21064
21212
  kind: "mutation",
21065
21213
  auth: "admin"
@@ -21127,7 +21275,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21127
21275
  kind: "query",
21128
21276
  auth: "admin"
21129
21277
  }), method(object({
21130
- eventId: string(),
21278
+ /** Which of the three event tables owns the row. */
21279
+ ownerType: EventOwnerTypeSchema,
21280
+ /** The event's rowid in that table (D474: an event id is a number). */
21281
+ eventId: number().int(),
21131
21282
  kind: MediaFileKindEnum.optional(),
21132
21283
  deviceId: number()
21133
21284
  }), array(MediaFileSchema).readonly()), method(object({
@@ -21138,7 +21289,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21138
21289
  trackId: string(),
21139
21290
  deviceId: number()
21140
21291
  }), array(MediaFileInfoSchema).readonly()), method(object({
21141
- eventId: string(),
21292
+ ownerType: EventOwnerTypeSchema,
21293
+ eventId: number().int(),
21142
21294
  deviceId: number()
21143
21295
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21144
21296
  kind: "mutation",
@@ -25621,8 +25773,8 @@ var IdentitySchema = object({
25621
25773
  id: string(),
25622
25774
  name: string(),
25623
25775
  sampleCount: number().int().nonnegative(),
25624
- coverMediaKey: string().optional(),
25625
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25776
+ coverMediaId: number().int().optional(),
25777
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25626
25778
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25627
25779
  coverBase64: string().optional()
25628
25780
  });
@@ -25648,8 +25800,8 @@ var IdentitySampleAuditRowSchema = object({
25648
25800
  enrolledAt: number().int(),
25649
25801
  deviceId: number().int().optional(),
25650
25802
  source: _enum(["detected", "photo"]),
25651
- /** The enrolled crop's media key, so the panel can show the evidence. */
25652
- mediaKey: string().optional(),
25803
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25804
+ mediaId: number().int().optional(),
25653
25805
  /**
25654
25806
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25655
25807
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25718,15 +25870,15 @@ var FaceInfoSchema = object({
25718
25870
  */
25719
25871
  cropUrl: string().optional(),
25720
25872
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25721
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
25873
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25722
25874
  * legacy rows written before design B. */
25723
25875
  faceBbox: BoundingBoxSchema.optional(),
25724
25876
  /** Design B: MediaStore key of the track's native-resolution key frame.
25725
25877
  * Fetch the native JPEG via the event-media data-plane
25726
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
25878
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
25727
25879
  * track produced no key frame (e.g. native/onboard source) — the UI falls
25728
25880
  * back to the inline `base64` face crop. */
25729
- keyFrameMediaKey: string().optional(),
25881
+ keyFrameMediaId: number().int().optional(),
25730
25882
  /** Winning identity-match cosine (0..1) for this face's track, when an
25731
25883
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
25732
25884
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -25795,11 +25947,14 @@ var FaceClusterSchema = object({
25795
25947
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25796
25948
  * dialog already rendering its key FRAME from the `event-media` plane.
25797
25949
  *
25798
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25799
- * media key directly, so this needed no new plane and no new access decision.
25950
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
25951
+ * resolves a media row by its own id, so this needed no new plane and no new
25952
+ * access decision.
25800
25953
  */
25801
25954
  var MediaFileLiteSchema$1 = object({
25802
- key: string(),
25955
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
25956
+ * (D482). It was the row's composite key, which embedded the owner. */
25957
+ mediaId: number().int(),
25803
25958
  kind: string(),
25804
25959
  url: string(),
25805
25960
  sizeBytes: number(),
@@ -25816,7 +25971,7 @@ method(object({
25816
25971
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
25817
25972
  * four times and the viewer holds at `staleTime: 30_000`.
25818
25973
  *
25819
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
25974
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
25820
25975
  * the `event-media` plane serves that key `immutable` with an ETag.
25821
25976
  *
25822
25977
  * **This is an INPUT field, so it does not reach the addon until the
@@ -28300,8 +28455,8 @@ var VehicleSchema = object({
28300
28455
  id: string(),
28301
28456
  name: string(),
28302
28457
  sampleCount: number().int().nonnegative(),
28303
- coverMediaKey: string().optional(),
28304
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
28458
+ coverMediaId: number().int().optional(),
28459
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
28305
28460
  coverBase64: string().optional()
28306
28461
  });
28307
28462
  var VehicleSampleInfoSchema = object({
@@ -28334,7 +28489,7 @@ var PlateInfoSchema = object({
28334
28489
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
28335
28490
  plateBbox: BoundingBoxSchema.optional(),
28336
28491
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
28337
- keyFrameMediaKey: string().optional(),
28492
+ keyFrameMediaId: number().int().optional(),
28338
28493
  base64: string().optional(),
28339
28494
  /**
28340
28495
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -28355,11 +28510,14 @@ var PlateInfoSchema = object({
28355
28510
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
28356
28511
  * dialog already rendering its key FRAME from the `event-media` plane.
28357
28512
  *
28358
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
28359
- * media key directly, so this needed no new plane and no new access decision.
28513
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
28514
+ * resolves a media row by its own id, so this needed no new plane and no new
28515
+ * access decision.
28360
28516
  */
28361
28517
  var MediaFileLiteSchema = object({
28362
- key: string(),
28518
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
28519
+ * (D482). It was the row's composite key, which embedded the owner. */
28520
+ mediaId: number().int(),
28363
28521
  kind: string(),
28364
28522
  url: string(),
28365
28523
  sizeBytes: number(),
@@ -28418,7 +28576,7 @@ method(object({
28418
28576
  }), method(object({
28419
28577
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
28420
28578
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
28421
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
28579
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
28422
28580
  * INPUT field: stripped by the hub router until the train ships, which
28423
28581
  * resolves to `false` and is exactly the intended default. */
28424
28582
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -29742,7 +29900,14 @@ var SceneReferenceSchema = object({
29742
29900
  modelId: string(),
29743
29901
  condition: SceneConditionSchema,
29744
29902
  capturedAt: number(),
29745
- thumbnailMediaId: string().optional(),
29903
+ /**
29904
+ * The reference thumbnail's media ROW id (D482).
29905
+ *
29906
+ * The name already said `mediaId` while the value was the row's composite
29907
+ * key — a leftover that read as done and was not. It is the row's own id
29908
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
29909
+ */
29910
+ thumbnailMediaId: number().int().optional(),
29746
29911
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
29747
29912
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
29748
29913
  * normalized rect frame a different piece of world, and the scene would
@@ -30565,7 +30730,29 @@ var LoggingSettingsPatchSchema = object({
30565
30730
  * disarmed the channels it did not mention would make the Levels page and
30566
30731
  * the Diagnostics page fight over the same value.
30567
30732
  */
30568
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
30733
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
30734
+ /**
30735
+ * How many days the analytics OPS LOG is kept — the audit trail of what
30736
+ * retention actually deleted.
30737
+ *
30738
+ * It lives HERE, on the logging document, and not on the analytics addon's
30739
+ * settings form, on the operator's instruction: every other log-ish window in
30740
+ * this system is configured through this one document, and a retention audit
30741
+ * configured somewhere else is a second place to look when the question is
30742
+ * "what happened to my data".
30743
+ *
30744
+ * That placement has a cost worth stating: this document ships in the
30745
+ * framework closure, so this knob needs a published `@camstack/server` while
30746
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
30747
+ *
30748
+ * `retention-model.ts` argued this one must NOT follow a device window,
30749
+ * because the audit trail is the history of the SWEEP and not of the camera.
30750
+ * That argument stands and is untouched — what changes is that 30 days stops
30751
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
30752
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
30753
+ * state in which the next 9.5 GiB leak cannot be explained.
30754
+ */
30755
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
30569
30756
  });
30570
30757
  /**
30571
30758
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -30638,6 +30825,14 @@ var LoggingSettingsStateSchema = object({
30638
30825
  channels: array(LogChannelDescriptorSchema).readonly(),
30639
30826
  /** The channels ARMED right now, each with its deadline. */
30640
30827
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
30828
+ /**
30829
+ * The ops-log window in days, as it is in force. `0` = kept forever.
30830
+ *
30831
+ * Reported rather than left to the caller's memory of what it wrote: this is
30832
+ * the value the analytics sweep will actually use, and an operator asking how
30833
+ * long their audit trail lives should not have to infer it from a default.
30834
+ */
30835
+ opsLogRetentionDays: number().int().min(0),
30641
30836
  persisted: boolean()
30642
30837
  });
30643
30838
  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(), {
@@ -31215,7 +31410,7 @@ var StationaryObjectSchema = object({
31215
31410
  /** Enrichment label carried from the source track (identity / plate). */
31216
31411
  label: string().optional(),
31217
31412
  /** Native-resolution key-frame media key for the parked object's best image. */
31218
- keyFrameMediaKey: string().optional()
31413
+ keyFrameMediaId: number().int().optional()
31219
31414
  });
31220
31415
  var CameraOccupancySnapshotSchema = object({
31221
31416
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -32371,6 +32566,12 @@ Object.freeze({
32371
32566
  addonId: null,
32372
32567
  access: "create"
32373
32568
  },
32569
+ "dataStoreProvider.upsert": {
32570
+ capName: "data-store-provider",
32571
+ capScope: "system",
32572
+ addonId: null,
32573
+ access: "create"
32574
+ },
32374
32575
  "dayNight.getOptions": {
32375
32576
  capName: "day-night",
32376
32577
  capScope: "device",
@@ -34807,6 +35008,12 @@ Object.freeze({
34807
35008
  addonId: null,
34808
35009
  access: "view"
34809
35010
  },
35011
+ "pipelineAnalytics.listRecentSummaries": {
35012
+ capName: "pipeline-analytics",
35013
+ capScope: "device",
35014
+ addonId: null,
35015
+ access: "view"
35016
+ },
34810
35017
  "pipelineAnalytics.listRecentTracks": {
34811
35018
  capName: "pipeline-analytics",
34812
35019
  capScope: "device",
@@ -36235,6 +36442,12 @@ Object.freeze({
36235
36442
  addonId: null,
36236
36443
  access: "create"
36237
36444
  },
36445
+ "settingsStore.upsert": {
36446
+ capName: "settings-store",
36447
+ capScope: "system",
36448
+ addonId: null,
36449
+ access: "create"
36450
+ },
36238
36451
  "smtpProvider.getStatus": {
36239
36452
  capName: "smtp-provider",
36240
36453
  capScope: "system",
@@ -38628,6 +38841,11 @@ Object.freeze({
38628
38841
  form: "single",
38629
38842
  optional: true
38630
38843
  }],
38844
+ "pipelineAnalytics.listRecentSummaries": [{
38845
+ name: "deviceIds",
38846
+ form: "array",
38847
+ optional: false
38848
+ }],
38631
38849
  "pipelineAnalytics.listRecentTracks": [{
38632
38850
  name: "deviceIds",
38633
38851
  form: "array",
@@ -72,7 +72,7 @@ function carryForward(base, existing, keys) {
72
72
  return out;
73
73
  }
74
74
  //#endregion
75
- //#region ../types/dist/event-category-DAXzJeTX.mjs
75
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
76
76
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
77
77
  EventCategory["SystemBoot"] = "system.boot";
78
78
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -633,7 +633,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
633
633
  * a package zone — a newly-appeared stationary object of a package class
634
634
  * that cleared the class / zone / dwell / size gates. Payload:
635
635
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
636
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
636
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
637
637
  * Telemetry (D8): the durable record is the `package-events` store row;
638
638
  * this bus topic drives notifier rules + live UI. See
639
639
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5419,7 +5419,7 @@ var ZodIssueCode = {
5419
5419
  var ZodFirstPartyTypeKind;
5420
5420
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5421
5421
  //#endregion
5422
- //#region ../types/dist/sleep-BnujYGPe.mjs
5422
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5423
5423
  /**
5424
5424
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5425
5425
  * window to float samples (D455).
@@ -7939,13 +7939,30 @@ new Set([
7939
7939
  "scene",
7940
7940
  "motion",
7941
7941
  "object",
7942
- "audio"
7942
+ "audio",
7943
+ "mosaic"
7943
7944
  ]);
7944
- new Set([
7945
+ /**
7946
+ * The owner types that are event tables, in the order the event-media resolver
7947
+ * should consider them. Exported so a consumer asking "is this key an event?"
7948
+ * does not re-spell the list and drift from it.
7949
+ */
7950
+ var EVENT_OWNER_TYPES = [
7945
7951
  "motion",
7946
7952
  "object",
7947
7953
  "audio"
7948
- ]);
7954
+ ];
7955
+ /**
7956
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7957
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7958
+ * `(table, id)`).
7959
+ *
7960
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7961
+ * table would otherwise be accepted by the codec and refused at the door, with
7962
+ * nothing failing until a caller asked.
7963
+ */
7964
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7965
+ new Set(EVENT_OWNER_TYPES);
7949
7966
  var EncodeProfileSchema = object({
7950
7967
  video: object({
7951
7968
  codec: _enum([
@@ -13124,6 +13141,16 @@ method(object({
13124
13141
  filter: QueryFilterSchema.optional(),
13125
13142
  columns: array(string()).readonly().optional()
13126
13143
  }), array(SettingsRecordSchema).readonly()), method(object({
13144
+ namespace: string().optional(),
13145
+ collection: string(),
13146
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
13147
+ * index on the collection. Never empty. */
13148
+ conflict: array(string()).readonly(),
13149
+ record: BulkRecordSchema
13150
+ }), object({
13151
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
13152
+ * row's own id on an update. */
13153
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
13127
13154
  namespace: string().optional(),
13128
13155
  collection: string(),
13129
13156
  record: object({
@@ -13258,6 +13285,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
13258
13285
  id: union([string(), number()]) }), {
13259
13286
  kind: "mutation",
13260
13287
  auth: "admin"
13288
+ }), method(object({
13289
+ namespace: string().optional(),
13290
+ collection: string(),
13291
+ conflict: array(string()).readonly(),
13292
+ record: BulkRecordSchema
13293
+ }), object({ id: union([string(), number()]) }), {
13294
+ kind: "mutation",
13295
+ auth: "admin"
13261
13296
  }), method(object({
13262
13297
  namespace: string().optional(),
13263
13298
  collection: string(),
@@ -19555,8 +19590,8 @@ var TrackPositionSchema = object({
19555
19590
  var TrackSnapshotSchema = object({
19556
19591
  timestamp: number(),
19557
19592
  position: TrackPositionSchema,
19558
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19559
- mediaKey: string()
19593
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19594
+ mediaId: number().int()
19560
19595
  });
19561
19596
  /**
19562
19597
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -20160,14 +20195,14 @@ var ObjectEventSchema = object({
20160
20195
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
20161
20196
  frameWidth: number().optional(),
20162
20197
  frameHeight: number().optional(),
20163
- /** MediaStore key for the crop attached to this event (if any). */
20164
- mediaKey: string().optional(),
20165
- /** Design B: MediaStore key of the track's native-resolution key frame (the
20198
+ /** Media row id of the crop attached to this event (if any) — D482. */
20199
+ mediaId: number().int().optional(),
20200
+ /** Design B: media row id of the track's native-resolution key frame (the
20166
20201
  * best-detection full frame). Resolve via the event-media data-plane
20167
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
20202
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
20168
20203
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
20169
- * sources — consumers fall back to `mediaKey` (the tight crop). */
20170
- keyFrameMediaKey: string().optional(),
20204
+ * sources — consumers fall back to `mediaId` (the tight crop). */
20205
+ keyFrameMediaId: number().int().optional(),
20171
20206
  /** Populated by B5 (recording playback URL for this event). */
20172
20207
  mediaUrl: string().optional(),
20173
20208
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -20217,7 +20252,7 @@ var MediaFileKindEnum = _enum([
20217
20252
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
20218
20253
  *
20219
20254
  * `url` points at the `event-media` data plane
20220
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
20255
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
20221
20256
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
20222
20257
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
20223
20258
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -20225,10 +20260,13 @@ var MediaFileKindEnum = _enum([
20225
20260
  * `data-plane-access.ts` for the rule and the one gap it does not close
20226
20261
  * (per-device scoping).
20227
20262
  *
20228
- * The URL is built from the row's **stored** key, which is not always its
20229
- * published `kind`: a track's face/plate crop is stored as `crop` under
20263
+ * The URL is built from the row's own id, which since D482 says nothing about
20264
+ * the row's owner or kind — and that is the point. The published `kind` is not
20265
+ * the stored one (a track's face/plate crop is stored as `crop` under
20230
20266
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
20231
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
20267
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
20268
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
20269
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
20232
20270
  *
20233
20271
  * ## `base64` is TRANSITIONAL and is going away
20234
20272
  *
@@ -20241,7 +20279,19 @@ var MediaFileKindEnum = _enum([
20241
20279
  * `analytics-query-facade.ts`; nothing else reads it.
20242
20280
  */
20243
20281
  var MediaFileSchema = object({
20244
- key: string(),
20282
+ /**
20283
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
20284
+ *
20285
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
20286
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
20287
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
20288
+ * different rows that spell the same number, and a track whose
20289
+ * `bestEventId` was an object event was served a motion event's snapshot —
20290
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
20291
+ * surrogate id cannot name the wrong row's owner, because it names no owner
20292
+ * at all.
20293
+ */
20294
+ mediaId: number().int(),
20245
20295
  kind: MediaFileKindEnum,
20246
20296
  sizeBytes: number(),
20247
20297
  timestamp: number()
@@ -20349,7 +20399,7 @@ var RetrainTrackSchema = object({
20349
20399
  });
20350
20400
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
20351
20401
  var RetrainFrameCandidateSchema = object({
20352
- mediaKey: string(),
20402
+ mediaId: number().int(),
20353
20403
  kind: MediaFileKindEnum,
20354
20404
  timestamp: number(),
20355
20405
  sizeBytes: number().int(),
@@ -20363,7 +20413,7 @@ var RetrainFrameSchema = object({
20363
20413
  deviceId: number(),
20364
20414
  trackId: string(),
20365
20415
  /** Provenance only. It may already point at nothing — that is expected. */
20366
- sourceMediaKey: string(),
20416
+ sourceMediaId: number().int(),
20367
20417
  sourceKind: MediaFileKindEnum,
20368
20418
  sizeBytes: number().int(),
20369
20419
  width: number().int(),
@@ -20379,7 +20429,7 @@ var RetrainCopyRefusalSchema = _enum([
20379
20429
  var RetrainFrameSelectionSchema = object({
20380
20430
  copied: array(RetrainFrameSchema).readonly(),
20381
20431
  refused: array(object({
20382
- sourceMediaKey: string(),
20432
+ sourceMediaId: number().int(),
20383
20433
  reason: RetrainCopyRefusalSchema
20384
20434
  })).readonly()
20385
20435
  });
@@ -20387,7 +20437,7 @@ var RetrainFrameListSchema = object({
20387
20437
  candidates: array(RetrainFrameCandidateSchema).readonly(),
20388
20438
  copies: array(RetrainFrameSchema).readonly(),
20389
20439
  /** What the page pre-selects — the native key frame when one survives. */
20390
- autoPickMediaKey: string().optional()
20440
+ autoPickMediaId: number().int().optional()
20391
20441
  });
20392
20442
  /** What the operator asked the assist to look for. */
20393
20443
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20482,9 +20532,11 @@ var RecentTracksQueryInput = object({
20482
20532
  });
20483
20533
  /**
20484
20534
  * A Summary (D359): the per-camera session envelope that references tracks.
20485
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20486
- * served by the event-media plane by key), null until the first frame with a
20487
- * member visible has been materialised.
20535
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20536
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20537
+ * first frame with a member visible has been materialised (D482 — it was the
20538
+ * row's composite key, which embedded the owner and could name another table's
20539
+ * row once event ids became per-table rowids).
20488
20540
  */
20489
20541
  var SummarySchema = object({
20490
20542
  id: string(),
@@ -20493,19 +20545,115 @@ var SummarySchema = object({
20493
20545
  lastActiveAt: number(),
20494
20546
  sealedAt: number().nullable(),
20495
20547
  memberCount: number().int().nonnegative(),
20496
- groupShotKey: string().nullable()
20548
+ groupShotMediaId: number().int().nullable()
20497
20549
  });
20498
20550
  var SummariesQueryInput = object({
20499
20551
  deviceIds: array(number()).min(1),
20500
20552
  since: number().optional(),
20501
20553
  until: number().optional(),
20502
- limit: number().int().min(1).max(200).default(50)
20554
+ limit: number().int().min(1).max(200).default(50),
20555
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20556
+ * meaning of "dog" on every summary surface. */
20557
+ classes: array(string().min(1).max(60)).max(40).optional()
20503
20558
  });
20504
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20505
20559
  var SummaryDetailSchema = object({
20506
20560
  summary: SummarySchema,
20507
- /** Member track ids, in absorption order. */
20508
- trackIds: array(string()).readonly()
20561
+ /** Members in absorption order, oldest join first. */
20562
+ members: array(object({
20563
+ trackId: string(),
20564
+ /**
20565
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20566
+ * and the track no longer exists to fill it.
20567
+ *
20568
+ * The join, not the latest opinion: class voting can re-decide a track's
20569
+ * class afterwards, and a group's composition is the history of who came
20570
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20571
+ * dog" — rather than being handed a second, denormalised tally that would
20572
+ * drift from the members it claims to describe.
20573
+ */
20574
+ className: string().nullable(),
20575
+ /** `primary` is the one member that carries the group's identity. */
20576
+ role: _enum([
20577
+ "primary",
20578
+ "sibling",
20579
+ "child"
20580
+ ]).nullable(),
20581
+ joinedAt: number(),
20582
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20583
+ * at read time. Absent when nothing resolved, never an empty string. */
20584
+ label: string().optional(),
20585
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20586
+ * recognised nobody, and absent for a member whose track has been pruned —
20587
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20588
+ subLabel: string().optional()
20589
+ })).readonly()
20590
+ });
20591
+ /**
20592
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20593
+ *
20594
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20595
+ * we extend when we notice we need more". Enough to draw the card ("two people
20596
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20597
+ * are on {@link getSummary}: shipping every member of every group so a client
20598
+ * can count them turns a page of two hundred groups into a thousand records to
20599
+ * draw two hundred badges.
20600
+ */
20601
+ var SummaryListItemSchema = object({
20602
+ summary: SummarySchema,
20603
+ /** Class counts, most numerous first; `className: null` is a member whose
20604
+ * class never resolved, counted so the counts add up to `memberCount`. */
20605
+ classes: array(object({
20606
+ className: string().nullable(),
20607
+ count: number().int().positive()
20608
+ })).readonly()
20609
+ });
20610
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20611
+ /**
20612
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20613
+ *
20614
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20615
+ * summaries feed that could only answer "the last N for these cameras"
20616
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20617
+ * else. Two feeds with different ordering contracts would be two ideas of what
20618
+ * "newest" means, on one screen.
20619
+ */
20620
+ var RecentSummariesQueryInput = object({
20621
+ /** Devices to merge. An empty array yields an empty page. */
20622
+ deviceIds: array(number()),
20623
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20624
+ since: number().optional(),
20625
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20626
+ until: number().optional(),
20627
+ limit: number().int().min(1).max(500).default(100),
20628
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20629
+ cursor: string().optional(),
20630
+ /**
20631
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20632
+ * deliberately the same name: the events filter tree emits one list of ticked
20633
+ * taxonomy nodes, and a summaries feed that called it something else would
20634
+ * make the operator's one filter component produce two shapes.
20635
+ *
20636
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20637
+ * me the arrivals with a dog" has to return the group of four people who had
20638
+ * one with them; a filter on the group's own fields could only ever answer
20639
+ * about its primary.
20640
+ *
20641
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20642
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20643
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20644
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20645
+ * that read only the class would answer "animal" and never "dog".
20646
+ *
20647
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20648
+ */
20649
+ classes: array(string().min(1).max(60)).max(40).optional()
20650
+ });
20651
+ var RecentSummariesPageSchema = object({
20652
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20653
+ * {@link listSummaries} — one list shape, so a card renders the same
20654
+ * whichever feed drew it. */
20655
+ summaries: array(SummaryListItemSchema).readonly(),
20656
+ nextCursor: string().nullable()
20509
20657
  });
20510
20658
  var RecentTracksPageSchema = object({
20511
20659
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20919,7 +21067,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20919
21067
  classes: array(string()).optional(),
20920
21068
  /** See {@link ExcludeSourcesDoc}. */
20921
21069
  excludeSources: array(TrackSourceSchema).optional()
20922
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
21070
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20923
21071
  kind: "mutation",
20924
21072
  auth: "admin"
20925
21073
  }), 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({
@@ -21047,7 +21195,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21047
21195
  }), method(object({
21048
21196
  deviceId: number(),
21049
21197
  trackId: string(),
21050
- mediaKeys: array(string()).min(1)
21198
+ mediaIds: array(number().int()).min(1)
21051
21199
  }), RetrainFrameSelectionSchema, {
21052
21200
  kind: "mutation",
21053
21201
  auth: "admin"
@@ -21115,7 +21263,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21115
21263
  kind: "query",
21116
21264
  auth: "admin"
21117
21265
  }), method(object({
21118
- eventId: string(),
21266
+ /** Which of the three event tables owns the row. */
21267
+ ownerType: EventOwnerTypeSchema,
21268
+ /** The event's rowid in that table (D474: an event id is a number). */
21269
+ eventId: number().int(),
21119
21270
  kind: MediaFileKindEnum.optional(),
21120
21271
  deviceId: number()
21121
21272
  }), array(MediaFileSchema).readonly()), method(object({
@@ -21126,7 +21277,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
21126
21277
  trackId: string(),
21127
21278
  deviceId: number()
21128
21279
  }), array(MediaFileInfoSchema).readonly()), method(object({
21129
- eventId: string(),
21280
+ ownerType: EventOwnerTypeSchema,
21281
+ eventId: number().int(),
21130
21282
  deviceId: number()
21131
21283
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21132
21284
  kind: "mutation",
@@ -25609,8 +25761,8 @@ var IdentitySchema = object({
25609
25761
  id: string(),
25610
25762
  name: string(),
25611
25763
  sampleCount: number().int().nonnegative(),
25612
- coverMediaKey: string().optional(),
25613
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25764
+ coverMediaId: number().int().optional(),
25765
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25614
25766
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25615
25767
  coverBase64: string().optional()
25616
25768
  });
@@ -25636,8 +25788,8 @@ var IdentitySampleAuditRowSchema = object({
25636
25788
  enrolledAt: number().int(),
25637
25789
  deviceId: number().int().optional(),
25638
25790
  source: _enum(["detected", "photo"]),
25639
- /** The enrolled crop's media key, so the panel can show the evidence. */
25640
- mediaKey: string().optional(),
25791
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25792
+ mediaId: number().int().optional(),
25641
25793
  /**
25642
25794
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25643
25795
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25706,15 +25858,15 @@ var FaceInfoSchema = object({
25706
25858
  */
25707
25859
  cropUrl: string().optional(),
25708
25860
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25709
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
25861
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25710
25862
  * legacy rows written before design B. */
25711
25863
  faceBbox: BoundingBoxSchema.optional(),
25712
25864
  /** Design B: MediaStore key of the track's native-resolution key frame.
25713
25865
  * Fetch the native JPEG via the event-media data-plane
25714
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
25866
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
25715
25867
  * track produced no key frame (e.g. native/onboard source) — the UI falls
25716
25868
  * back to the inline `base64` face crop. */
25717
- keyFrameMediaKey: string().optional(),
25869
+ keyFrameMediaId: number().int().optional(),
25718
25870
  /** Winning identity-match cosine (0..1) for this face's track, when an
25719
25871
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
25720
25872
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -25783,11 +25935,14 @@ var FaceClusterSchema = object({
25783
25935
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25784
25936
  * dialog already rendering its key FRAME from the `event-media` plane.
25785
25937
  *
25786
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25787
- * media key directly, so this needed no new plane and no new access decision.
25938
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
25939
+ * resolves a media row by its own id, so this needed no new plane and no new
25940
+ * access decision.
25788
25941
  */
25789
25942
  var MediaFileLiteSchema$1 = object({
25790
- key: string(),
25943
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
25944
+ * (D482). It was the row's composite key, which embedded the owner. */
25945
+ mediaId: number().int(),
25791
25946
  kind: string(),
25792
25947
  url: string(),
25793
25948
  sizeBytes: number(),
@@ -25804,7 +25959,7 @@ method(object({
25804
25959
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
25805
25960
  * four times and the viewer holds at `staleTime: 30_000`.
25806
25961
  *
25807
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
25962
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
25808
25963
  * the `event-media` plane serves that key `immutable` with an ETag.
25809
25964
  *
25810
25965
  * **This is an INPUT field, so it does not reach the addon until the
@@ -28288,8 +28443,8 @@ var VehicleSchema = object({
28288
28443
  id: string(),
28289
28444
  name: string(),
28290
28445
  sampleCount: number().int().nonnegative(),
28291
- coverMediaKey: string().optional(),
28292
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
28446
+ coverMediaId: number().int().optional(),
28447
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
28293
28448
  coverBase64: string().optional()
28294
28449
  });
28295
28450
  var VehicleSampleInfoSchema = object({
@@ -28322,7 +28477,7 @@ var PlateInfoSchema = object({
28322
28477
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
28323
28478
  plateBbox: BoundingBoxSchema.optional(),
28324
28479
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
28325
- keyFrameMediaKey: string().optional(),
28480
+ keyFrameMediaId: number().int().optional(),
28326
28481
  base64: string().optional(),
28327
28482
  /**
28328
28483
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -28343,11 +28498,14 @@ var PlateInfoSchema = object({
28343
28498
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
28344
28499
  * dialog already rendering its key FRAME from the `event-media` plane.
28345
28500
  *
28346
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
28347
- * media key directly, so this needed no new plane and no new access decision.
28501
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
28502
+ * resolves a media row by its own id, so this needed no new plane and no new
28503
+ * access decision.
28348
28504
  */
28349
28505
  var MediaFileLiteSchema = object({
28350
- key: string(),
28506
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
28507
+ * (D482). It was the row's composite key, which embedded the owner. */
28508
+ mediaId: number().int(),
28351
28509
  kind: string(),
28352
28510
  url: string(),
28353
28511
  sizeBytes: number(),
@@ -28406,7 +28564,7 @@ method(object({
28406
28564
  }), method(object({
28407
28565
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
28408
28566
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
28409
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
28567
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
28410
28568
  * INPUT field: stripped by the hub router until the train ships, which
28411
28569
  * resolves to `false` and is exactly the intended default. */
28412
28570
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -29730,7 +29888,14 @@ var SceneReferenceSchema = object({
29730
29888
  modelId: string(),
29731
29889
  condition: SceneConditionSchema,
29732
29890
  capturedAt: number(),
29733
- thumbnailMediaId: string().optional(),
29891
+ /**
29892
+ * The reference thumbnail's media ROW id (D482).
29893
+ *
29894
+ * The name already said `mediaId` while the value was the row's composite
29895
+ * key — a leftover that read as done and was not. It is the row's own id
29896
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
29897
+ */
29898
+ thumbnailMediaId: number().int().optional(),
29734
29899
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
29735
29900
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
29736
29901
  * normalized rect frame a different piece of world, and the scene would
@@ -30553,7 +30718,29 @@ var LoggingSettingsPatchSchema = object({
30553
30718
  * disarmed the channels it did not mention would make the Levels page and
30554
30719
  * the Diagnostics page fight over the same value.
30555
30720
  */
30556
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
30721
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
30722
+ /**
30723
+ * How many days the analytics OPS LOG is kept — the audit trail of what
30724
+ * retention actually deleted.
30725
+ *
30726
+ * It lives HERE, on the logging document, and not on the analytics addon's
30727
+ * settings form, on the operator's instruction: every other log-ish window in
30728
+ * this system is configured through this one document, and a retention audit
30729
+ * configured somewhere else is a second place to look when the question is
30730
+ * "what happened to my data".
30731
+ *
30732
+ * That placement has a cost worth stating: this document ships in the
30733
+ * framework closure, so this knob needs a published `@camstack/server` while
30734
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
30735
+ *
30736
+ * `retention-model.ts` argued this one must NOT follow a device window,
30737
+ * because the audit trail is the history of the SWEEP and not of the camera.
30738
+ * That argument stands and is untouched — what changes is that 30 days stops
30739
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
30740
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
30741
+ * state in which the next 9.5 GiB leak cannot be explained.
30742
+ */
30743
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
30557
30744
  });
30558
30745
  /**
30559
30746
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -30626,6 +30813,14 @@ var LoggingSettingsStateSchema = object({
30626
30813
  channels: array(LogChannelDescriptorSchema).readonly(),
30627
30814
  /** The channels ARMED right now, each with its deadline. */
30628
30815
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
30816
+ /**
30817
+ * The ops-log window in days, as it is in force. `0` = kept forever.
30818
+ *
30819
+ * Reported rather than left to the caller's memory of what it wrote: this is
30820
+ * the value the analytics sweep will actually use, and an operator asking how
30821
+ * long their audit trail lives should not have to infer it from a default.
30822
+ */
30823
+ opsLogRetentionDays: number().int().min(0),
30629
30824
  persisted: boolean()
30630
30825
  });
30631
30826
  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(), {
@@ -31203,7 +31398,7 @@ var StationaryObjectSchema = object({
31203
31398
  /** Enrichment label carried from the source track (identity / plate). */
31204
31399
  label: string().optional(),
31205
31400
  /** Native-resolution key-frame media key for the parked object's best image. */
31206
- keyFrameMediaKey: string().optional()
31401
+ keyFrameMediaId: number().int().optional()
31207
31402
  });
31208
31403
  var CameraOccupancySnapshotSchema = object({
31209
31404
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -32359,6 +32554,12 @@ Object.freeze({
32359
32554
  addonId: null,
32360
32555
  access: "create"
32361
32556
  },
32557
+ "dataStoreProvider.upsert": {
32558
+ capName: "data-store-provider",
32559
+ capScope: "system",
32560
+ addonId: null,
32561
+ access: "create"
32562
+ },
32362
32563
  "dayNight.getOptions": {
32363
32564
  capName: "day-night",
32364
32565
  capScope: "device",
@@ -34795,6 +34996,12 @@ Object.freeze({
34795
34996
  addonId: null,
34796
34997
  access: "view"
34797
34998
  },
34999
+ "pipelineAnalytics.listRecentSummaries": {
35000
+ capName: "pipeline-analytics",
35001
+ capScope: "device",
35002
+ addonId: null,
35003
+ access: "view"
35004
+ },
34798
35005
  "pipelineAnalytics.listRecentTracks": {
34799
35006
  capName: "pipeline-analytics",
34800
35007
  capScope: "device",
@@ -36223,6 +36430,12 @@ Object.freeze({
36223
36430
  addonId: null,
36224
36431
  access: "create"
36225
36432
  },
36433
+ "settingsStore.upsert": {
36434
+ capName: "settings-store",
36435
+ capScope: "system",
36436
+ addonId: null,
36437
+ access: "create"
36438
+ },
36226
36439
  "smtpProvider.getStatus": {
36227
36440
  capName: "smtp-provider",
36228
36441
  capScope: "system",
@@ -38616,6 +38829,11 @@ Object.freeze({
38616
38829
  form: "single",
38617
38830
  optional: true
38618
38831
  }],
38832
+ "pipelineAnalytics.listRecentSummaries": [{
38833
+ name: "deviceIds",
38834
+ form: "array",
38835
+ optional: false
38836
+ }],
38619
38837
  "pipelineAnalytics.listRecentTracks": [{
38620
38838
  name: "deviceIds",
38621
38839
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-hap",
3
- "version": "1.2.110",
3
+ "version": "1.2.112",
4
4
  "description": "HomeKit (HAP) exporter for CamStack devices. Publishes each exposed device as its own HomeKit accessory: cameras and doorbells with SRTP streaming, HomeKit Secure Video, motion, two-way audio, PTZ and battery; switches, lights, locks and sensors through a capability→service table.",
5
5
  "keywords": [
6
6
  "camstack",