@camstack/addon-notifiers 1.2.103 → 1.2.105

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
@@ -29,7 +29,7 @@ let node_fs = require("node:fs");
29
29
  let node_path = require("node:path");
30
30
  node_path = __toESM(node_path);
31
31
  let node_url = require("node:url");
32
- //#region ../types/dist/event-category-DAXzJeTX.mjs
32
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
33
33
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
34
34
  EventCategory["SystemBoot"] = "system.boot";
35
35
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -590,7 +590,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
590
590
  * a package zone — a newly-appeared stationary object of a package class
591
591
  * that cleared the class / zone / dwell / size gates. Payload:
592
592
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
593
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
593
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
594
594
  * Telemetry (D8): the durable record is the `package-events` store row;
595
595
  * this bus topic drives notifier rules + live UI. See
596
596
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5376,7 +5376,7 @@ var ZodIssueCode = {
5376
5376
  var ZodFirstPartyTypeKind;
5377
5377
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5378
5378
  //#endregion
5379
- //#region ../types/dist/sleep-BnujYGPe.mjs
5379
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5380
5380
  /**
5381
5381
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5382
5382
  * window to float samples (D455).
@@ -7327,13 +7327,30 @@ new Set([
7327
7327
  "scene",
7328
7328
  "motion",
7329
7329
  "object",
7330
- "audio"
7330
+ "audio",
7331
+ "mosaic"
7331
7332
  ]);
7332
- new Set([
7333
+ /**
7334
+ * The owner types that are event tables, in the order the event-media resolver
7335
+ * should consider them. Exported so a consumer asking "is this key an event?"
7336
+ * does not re-spell the list and drift from it.
7337
+ */
7338
+ var EVENT_OWNER_TYPES = [
7333
7339
  "motion",
7334
7340
  "object",
7335
7341
  "audio"
7336
- ]);
7342
+ ];
7343
+ /**
7344
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7345
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7346
+ * `(table, id)`).
7347
+ *
7348
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7349
+ * table would otherwise be accepted by the codec and refused at the door, with
7350
+ * nothing failing until a caller asked.
7351
+ */
7352
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7353
+ new Set(EVENT_OWNER_TYPES);
7337
7354
  /**
7338
7355
  * Build an `IAddonRouteProvider` from a list of routes. Implements
7339
7356
  * both the operator-facing `getRoutes` (returning route descriptors
@@ -7489,7 +7506,23 @@ var EncodeProfileSchema = object({
7489
7506
  "zerolatency",
7490
7507
  "film",
7491
7508
  "animation"
7492
- ]).optional()
7509
+ ]).optional(),
7510
+ /**
7511
+ * ONE slice per access unit.
7512
+ *
7513
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7514
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7515
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7516
+ * frame-end per frame: the first pictures render and the video then freezes
7517
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7518
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7519
+ *
7520
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7521
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7522
+ * consumers meaning the same thing spelled differently would stop sharing one
7523
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7524
+ */
7525
+ singleSlicePerFrame: boolean().optional()
7493
7526
  }),
7494
7527
  audio: union([literal("passthrough"), object({
7495
7528
  codec: _enum([
@@ -7539,7 +7572,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7539
7572
  audio: "passthrough"
7540
7573
  };
7541
7574
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7542
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7575
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7543
7576
  /**
7544
7577
  * Deep wiring healthcheck — snapshot of active reachability probes across
7545
7578
  * every declared capability + widget of every installed plugin, on every
@@ -12588,6 +12621,16 @@ method(object({
12588
12621
  filter: QueryFilterSchema.optional(),
12589
12622
  columns: array(string()).readonly().optional()
12590
12623
  }), array(SettingsRecordSchema).readonly()), method(object({
12624
+ namespace: string().optional(),
12625
+ collection: string(),
12626
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12627
+ * index on the collection. Never empty. */
12628
+ conflict: array(string()).readonly(),
12629
+ record: BulkRecordSchema
12630
+ }), object({
12631
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12632
+ * row's own id on an update. */
12633
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12591
12634
  namespace: string().optional(),
12592
12635
  collection: string(),
12593
12636
  record: object({
@@ -12722,6 +12765,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12722
12765
  id: union([string(), number()]) }), {
12723
12766
  kind: "mutation",
12724
12767
  auth: "admin"
12768
+ }), method(object({
12769
+ namespace: string().optional(),
12770
+ collection: string(),
12771
+ conflict: array(string()).readonly(),
12772
+ record: BulkRecordSchema
12773
+ }), object({ id: union([string(), number()]) }), {
12774
+ kind: "mutation",
12775
+ auth: "admin"
12725
12776
  }), method(object({
12726
12777
  namespace: string().optional(),
12727
12778
  collection: string(),
@@ -18952,8 +19003,8 @@ var TrackPositionSchema = object({
18952
19003
  var TrackSnapshotSchema = object({
18953
19004
  timestamp: number(),
18954
19005
  position: TrackPositionSchema,
18955
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
18956
- mediaKey: string()
19006
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19007
+ mediaId: number().int()
18957
19008
  });
18958
19009
  /**
18959
19010
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19557,14 +19608,14 @@ var ObjectEventSchema = object({
19557
19608
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19558
19609
  frameWidth: number().optional(),
19559
19610
  frameHeight: number().optional(),
19560
- /** MediaStore key for the crop attached to this event (if any). */
19561
- mediaKey: string().optional(),
19562
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19611
+ /** Media row id of the crop attached to this event (if any) — D482. */
19612
+ mediaId: number().int().optional(),
19613
+ /** Design B: media row id of the track's native-resolution key frame (the
19563
19614
  * best-detection full frame). Resolve via the event-media data-plane
19564
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19615
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19565
19616
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19566
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19567
- keyFrameMediaKey: string().optional(),
19617
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19618
+ keyFrameMediaId: number().int().optional(),
19568
19619
  /** Populated by B5 (recording playback URL for this event). */
19569
19620
  mediaUrl: string().optional(),
19570
19621
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19614,7 +19665,7 @@ var MediaFileKindEnum = _enum([
19614
19665
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19615
19666
  *
19616
19667
  * `url` points at the `event-media` data plane
19617
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19668
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19618
19669
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19619
19670
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19620
19671
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19622,10 +19673,13 @@ var MediaFileKindEnum = _enum([
19622
19673
  * `data-plane-access.ts` for the rule and the one gap it does not close
19623
19674
  * (per-device scoping).
19624
19675
  *
19625
- * The URL is built from the row's **stored** key, which is not always its
19626
- * published `kind`: a track's face/plate crop is stored as `crop` under
19676
+ * The URL is built from the row's own id, which since D482 says nothing about
19677
+ * the row's owner or kind — and that is the point. The published `kind` is not
19678
+ * the stored one (a track's face/plate crop is stored as `crop` under
19627
19679
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19628
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19680
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19681
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19682
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19629
19683
  *
19630
19684
  * ## `base64` is TRANSITIONAL and is going away
19631
19685
  *
@@ -19638,7 +19692,19 @@ var MediaFileKindEnum = _enum([
19638
19692
  * `analytics-query-facade.ts`; nothing else reads it.
19639
19693
  */
19640
19694
  var MediaFileSchema = object({
19641
- key: string(),
19695
+ /**
19696
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19697
+ *
19698
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19699
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19700
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19701
+ * different rows that spell the same number, and a track whose
19702
+ * `bestEventId` was an object event was served a motion event's snapshot —
19703
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19704
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19705
+ * at all.
19706
+ */
19707
+ mediaId: number().int(),
19642
19708
  kind: MediaFileKindEnum,
19643
19709
  sizeBytes: number(),
19644
19710
  timestamp: number()
@@ -19746,7 +19812,7 @@ var RetrainTrackSchema = object({
19746
19812
  });
19747
19813
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19748
19814
  var RetrainFrameCandidateSchema = object({
19749
- mediaKey: string(),
19815
+ mediaId: number().int(),
19750
19816
  kind: MediaFileKindEnum,
19751
19817
  timestamp: number(),
19752
19818
  sizeBytes: number().int(),
@@ -19760,7 +19826,7 @@ var RetrainFrameSchema = object({
19760
19826
  deviceId: number(),
19761
19827
  trackId: string(),
19762
19828
  /** Provenance only. It may already point at nothing — that is expected. */
19763
- sourceMediaKey: string(),
19829
+ sourceMediaId: number().int(),
19764
19830
  sourceKind: MediaFileKindEnum,
19765
19831
  sizeBytes: number().int(),
19766
19832
  width: number().int(),
@@ -19776,7 +19842,7 @@ var RetrainCopyRefusalSchema = _enum([
19776
19842
  var RetrainFrameSelectionSchema = object({
19777
19843
  copied: array(RetrainFrameSchema).readonly(),
19778
19844
  refused: array(object({
19779
- sourceMediaKey: string(),
19845
+ sourceMediaId: number().int(),
19780
19846
  reason: RetrainCopyRefusalSchema
19781
19847
  })).readonly()
19782
19848
  });
@@ -19784,7 +19850,7 @@ var RetrainFrameListSchema = object({
19784
19850
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19785
19851
  copies: array(RetrainFrameSchema).readonly(),
19786
19852
  /** What the page pre-selects — the native key frame when one survives. */
19787
- autoPickMediaKey: string().optional()
19853
+ autoPickMediaId: number().int().optional()
19788
19854
  });
19789
19855
  /** What the operator asked the assist to look for. */
19790
19856
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -19879,9 +19945,11 @@ var RecentTracksQueryInput = object({
19879
19945
  });
19880
19946
  /**
19881
19947
  * A Summary (D359): the per-camera session envelope that references tracks.
19882
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
19883
- * served by the event-media plane by key), null until the first frame with a
19884
- * member visible has been materialised.
19948
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
19949
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
19950
+ * first frame with a member visible has been materialised (D482 — it was the
19951
+ * row's composite key, which embedded the owner and could name another table's
19952
+ * row once event ids became per-table rowids).
19885
19953
  */
19886
19954
  var SummarySchema = object({
19887
19955
  id: string(),
@@ -19890,19 +19958,115 @@ var SummarySchema = object({
19890
19958
  lastActiveAt: number(),
19891
19959
  sealedAt: number().nullable(),
19892
19960
  memberCount: number().int().nonnegative(),
19893
- groupShotKey: string().nullable()
19961
+ groupShotMediaId: number().int().nullable()
19894
19962
  });
19895
19963
  var SummariesQueryInput = object({
19896
19964
  deviceIds: array(number()).min(1),
19897
19965
  since: number().optional(),
19898
19966
  until: number().optional(),
19899
- limit: number().int().min(1).max(200).default(50)
19967
+ limit: number().int().min(1).max(200).default(50),
19968
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
19969
+ * meaning of "dog" on every summary surface. */
19970
+ classes: array(string().min(1).max(60)).max(40).optional()
19900
19971
  });
19901
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
19902
19972
  var SummaryDetailSchema = object({
19903
19973
  summary: SummarySchema,
19904
- /** Member track ids, in absorption order. */
19905
- trackIds: array(string()).readonly()
19974
+ /** Members in absorption order, oldest join first. */
19975
+ members: array(object({
19976
+ trackId: string(),
19977
+ /**
19978
+ * The track's class at the time it JOINED, or `null` when it was unresolved
19979
+ * and the track no longer exists to fill it.
19980
+ *
19981
+ * The join, not the latest opinion: class voting can re-decide a track's
19982
+ * class afterwards, and a group's composition is the history of who came
19983
+ * together. A consumer counts these to badge a thumbnail — "two people and a
19984
+ * dog" — rather than being handed a second, denormalised tally that would
19985
+ * drift from the members it claims to describe.
19986
+ */
19987
+ className: string().nullable(),
19988
+ /** `primary` is the one member that carries the group's identity. */
19989
+ role: _enum([
19990
+ "primary",
19991
+ "sibling",
19992
+ "child"
19993
+ ]).nullable(),
19994
+ joinedAt: number(),
19995
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
19996
+ * at read time. Absent when nothing resolved, never an empty string. */
19997
+ label: string().optional(),
19998
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
19999
+ * recognised nobody, and absent for a member whose track has been pruned —
20000
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20001
+ subLabel: string().optional()
20002
+ })).readonly()
20003
+ });
20004
+ /**
20005
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20006
+ *
20007
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20008
+ * we extend when we notice we need more". Enough to draw the card ("two people
20009
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20010
+ * are on {@link getSummary}: shipping every member of every group so a client
20011
+ * can count them turns a page of two hundred groups into a thousand records to
20012
+ * draw two hundred badges.
20013
+ */
20014
+ var SummaryListItemSchema = object({
20015
+ summary: SummarySchema,
20016
+ /** Class counts, most numerous first; `className: null` is a member whose
20017
+ * class never resolved, counted so the counts add up to `memberCount`. */
20018
+ classes: array(object({
20019
+ className: string().nullable(),
20020
+ count: number().int().positive()
20021
+ })).readonly()
20022
+ });
20023
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20024
+ /**
20025
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20026
+ *
20027
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20028
+ * summaries feed that could only answer "the last N for these cameras"
20029
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20030
+ * else. Two feeds with different ordering contracts would be two ideas of what
20031
+ * "newest" means, on one screen.
20032
+ */
20033
+ var RecentSummariesQueryInput = object({
20034
+ /** Devices to merge. An empty array yields an empty page. */
20035
+ deviceIds: array(number()),
20036
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20037
+ since: number().optional(),
20038
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20039
+ until: number().optional(),
20040
+ limit: number().int().min(1).max(500).default(100),
20041
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20042
+ cursor: string().optional(),
20043
+ /**
20044
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20045
+ * deliberately the same name: the events filter tree emits one list of ticked
20046
+ * taxonomy nodes, and a summaries feed that called it something else would
20047
+ * make the operator's one filter component produce two shapes.
20048
+ *
20049
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20050
+ * me the arrivals with a dog" has to return the group of four people who had
20051
+ * one with them; a filter on the group's own fields could only ever answer
20052
+ * about its primary.
20053
+ *
20054
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20055
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20056
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20057
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20058
+ * that read only the class would answer "animal" and never "dog".
20059
+ *
20060
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20061
+ */
20062
+ classes: array(string().min(1).max(60)).max(40).optional()
20063
+ });
20064
+ var RecentSummariesPageSchema = object({
20065
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20066
+ * {@link listSummaries} — one list shape, so a card renders the same
20067
+ * whichever feed drew it. */
20068
+ summaries: array(SummaryListItemSchema).readonly(),
20069
+ nextCursor: string().nullable()
19906
20070
  });
19907
20071
  var RecentTracksPageSchema = object({
19908
20072
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20316,7 +20480,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20316
20480
  classes: array(string()).optional(),
20317
20481
  /** See {@link ExcludeSourcesDoc}. */
20318
20482
  excludeSources: array(TrackSourceSchema).optional()
20319
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20483
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20320
20484
  kind: "mutation",
20321
20485
  auth: "admin"
20322
20486
  }), 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({
@@ -20444,7 +20608,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20444
20608
  }), method(object({
20445
20609
  deviceId: number(),
20446
20610
  trackId: string(),
20447
- mediaKeys: array(string()).min(1)
20611
+ mediaIds: array(number().int()).min(1)
20448
20612
  }), RetrainFrameSelectionSchema, {
20449
20613
  kind: "mutation",
20450
20614
  auth: "admin"
@@ -20512,7 +20676,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20512
20676
  kind: "query",
20513
20677
  auth: "admin"
20514
20678
  }), method(object({
20515
- eventId: string(),
20679
+ /** Which of the three event tables owns the row. */
20680
+ ownerType: EventOwnerTypeSchema,
20681
+ /** The event's rowid in that table (D474: an event id is a number). */
20682
+ eventId: number().int(),
20516
20683
  kind: MediaFileKindEnum.optional(),
20517
20684
  deviceId: number()
20518
20685
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20523,7 +20690,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20523
20690
  trackId: string(),
20524
20691
  deviceId: number()
20525
20692
  }), array(MediaFileInfoSchema).readonly()), method(object({
20526
- eventId: string(),
20693
+ ownerType: EventOwnerTypeSchema,
20694
+ eventId: number().int(),
20527
20695
  deviceId: number()
20528
20696
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20529
20697
  kind: "mutation",
@@ -24986,8 +25154,8 @@ var IdentitySchema = object({
24986
25154
  id: string(),
24987
25155
  name: string(),
24988
25156
  sampleCount: number().int().nonnegative(),
24989
- coverMediaKey: string().optional(),
24990
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25157
+ coverMediaId: number().int().optional(),
25158
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
24991
25159
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
24992
25160
  coverBase64: string().optional()
24993
25161
  });
@@ -25013,8 +25181,8 @@ var IdentitySampleAuditRowSchema = object({
25013
25181
  enrolledAt: number().int(),
25014
25182
  deviceId: number().int().optional(),
25015
25183
  source: _enum(["detected", "photo"]),
25016
- /** The enrolled crop's media key, so the panel can show the evidence. */
25017
- mediaKey: string().optional(),
25184
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25185
+ mediaId: number().int().optional(),
25018
25186
  /**
25019
25187
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25020
25188
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25083,15 +25251,15 @@ var FaceInfoSchema = object({
25083
25251
  */
25084
25252
  cropUrl: string().optional(),
25085
25253
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25086
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
25254
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25087
25255
  * legacy rows written before design B. */
25088
25256
  faceBbox: BoundingBoxSchema.optional(),
25089
25257
  /** Design B: MediaStore key of the track's native-resolution key frame.
25090
25258
  * Fetch the native JPEG via the event-media data-plane
25091
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
25259
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
25092
25260
  * track produced no key frame (e.g. native/onboard source) — the UI falls
25093
25261
  * back to the inline `base64` face crop. */
25094
- keyFrameMediaKey: string().optional(),
25262
+ keyFrameMediaId: number().int().optional(),
25095
25263
  /** Winning identity-match cosine (0..1) for this face's track, when an
25096
25264
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
25097
25265
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -25160,11 +25328,14 @@ var FaceClusterSchema = object({
25160
25328
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25161
25329
  * dialog already rendering its key FRAME from the `event-media` plane.
25162
25330
  *
25163
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25164
- * media key directly, so this needed no new plane and no new access decision.
25331
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
25332
+ * resolves a media row by its own id, so this needed no new plane and no new
25333
+ * access decision.
25165
25334
  */
25166
25335
  var MediaFileLiteSchema$1 = object({
25167
- key: string(),
25336
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
25337
+ * (D482). It was the row's composite key, which embedded the owner. */
25338
+ mediaId: number().int(),
25168
25339
  kind: string(),
25169
25340
  url: string(),
25170
25341
  sizeBytes: number(),
@@ -25181,7 +25352,7 @@ method(object({
25181
25352
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
25182
25353
  * four times and the viewer holds at `staleTime: 30_000`.
25183
25354
  *
25184
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
25355
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
25185
25356
  * the `event-media` plane serves that key `immutable` with an ETag.
25186
25357
  *
25187
25358
  * **This is an INPUT field, so it does not reach the addon until the
@@ -27649,8 +27820,8 @@ var VehicleSchema = object({
27649
27820
  id: string(),
27650
27821
  name: string(),
27651
27822
  sampleCount: number().int().nonnegative(),
27652
- coverMediaKey: string().optional(),
27653
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
27823
+ coverMediaId: number().int().optional(),
27824
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
27654
27825
  coverBase64: string().optional()
27655
27826
  });
27656
27827
  var VehicleSampleInfoSchema = object({
@@ -27683,7 +27854,7 @@ var PlateInfoSchema = object({
27683
27854
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
27684
27855
  plateBbox: BoundingBoxSchema.optional(),
27685
27856
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
27686
- keyFrameMediaKey: string().optional(),
27857
+ keyFrameMediaId: number().int().optional(),
27687
27858
  base64: string().optional(),
27688
27859
  /**
27689
27860
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -27704,11 +27875,14 @@ var PlateInfoSchema = object({
27704
27875
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27705
27876
  * dialog already rendering its key FRAME from the `event-media` plane.
27706
27877
  *
27707
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27708
- * media key directly, so this needed no new plane and no new access decision.
27878
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
27879
+ * resolves a media row by its own id, so this needed no new plane and no new
27880
+ * access decision.
27709
27881
  */
27710
27882
  var MediaFileLiteSchema = object({
27711
- key: string(),
27883
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
27884
+ * (D482). It was the row's composite key, which embedded the owner. */
27885
+ mediaId: number().int(),
27712
27886
  kind: string(),
27713
27887
  url: string(),
27714
27888
  sizeBytes: number(),
@@ -27767,7 +27941,7 @@ method(object({
27767
27941
  }), method(object({
27768
27942
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
27769
27943
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
27770
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
27944
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
27771
27945
  * INPUT field: stripped by the hub router until the train ships, which
27772
27946
  * resolves to `false` and is exactly the intended default. */
27773
27947
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -29091,7 +29265,14 @@ var SceneReferenceSchema = object({
29091
29265
  modelId: string(),
29092
29266
  condition: SceneConditionSchema,
29093
29267
  capturedAt: number(),
29094
- thumbnailMediaId: string().optional(),
29268
+ /**
29269
+ * The reference thumbnail's media ROW id (D482).
29270
+ *
29271
+ * The name already said `mediaId` while the value was the row's composite
29272
+ * key — a leftover that read as done and was not. It is the row's own id
29273
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
29274
+ */
29275
+ thumbnailMediaId: number().int().optional(),
29095
29276
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
29096
29277
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
29097
29278
  * normalized rect frame a different piece of world, and the scene would
@@ -29905,7 +30086,29 @@ var LoggingSettingsPatchSchema = object({
29905
30086
  * disarmed the channels it did not mention would make the Levels page and
29906
30087
  * the Diagnostics page fight over the same value.
29907
30088
  */
29908
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
30089
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
30090
+ /**
30091
+ * How many days the analytics OPS LOG is kept — the audit trail of what
30092
+ * retention actually deleted.
30093
+ *
30094
+ * It lives HERE, on the logging document, and not on the analytics addon's
30095
+ * settings form, on the operator's instruction: every other log-ish window in
30096
+ * this system is configured through this one document, and a retention audit
30097
+ * configured somewhere else is a second place to look when the question is
30098
+ * "what happened to my data".
30099
+ *
30100
+ * That placement has a cost worth stating: this document ships in the
30101
+ * framework closure, so this knob needs a published `@camstack/server` while
30102
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
30103
+ *
30104
+ * `retention-model.ts` argued this one must NOT follow a device window,
30105
+ * because the audit trail is the history of the SWEEP and not of the camera.
30106
+ * That argument stands and is untouched — what changes is that 30 days stops
30107
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
30108
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
30109
+ * state in which the next 9.5 GiB leak cannot be explained.
30110
+ */
30111
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
29909
30112
  });
29910
30113
  /**
29911
30114
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -29978,6 +30181,14 @@ var LoggingSettingsStateSchema = object({
29978
30181
  channels: array(LogChannelDescriptorSchema).readonly(),
29979
30182
  /** The channels ARMED right now, each with its deadline. */
29980
30183
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
30184
+ /**
30185
+ * The ops-log window in days, as it is in force. `0` = kept forever.
30186
+ *
30187
+ * Reported rather than left to the caller's memory of what it wrote: this is
30188
+ * the value the analytics sweep will actually use, and an operator asking how
30189
+ * long their audit trail lives should not have to infer it from a default.
30190
+ */
30191
+ opsLogRetentionDays: number().int().min(0),
29981
30192
  persisted: boolean()
29982
30193
  });
29983
30194
  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(), {
@@ -30539,7 +30750,7 @@ var StationaryObjectSchema = object({
30539
30750
  /** Enrichment label carried from the source track (identity / plate). */
30540
30751
  label: string().optional(),
30541
30752
  /** Native-resolution key-frame media key for the parked object's best image. */
30542
- keyFrameMediaKey: string().optional()
30753
+ keyFrameMediaId: number().int().optional()
30543
30754
  });
30544
30755
  var CameraOccupancySnapshotSchema = object({
30545
30756
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -31695,6 +31906,12 @@ Object.freeze({
31695
31906
  addonId: null,
31696
31907
  access: "create"
31697
31908
  },
31909
+ "dataStoreProvider.upsert": {
31910
+ capName: "data-store-provider",
31911
+ capScope: "system",
31912
+ addonId: null,
31913
+ access: "create"
31914
+ },
31698
31915
  "dayNight.getOptions": {
31699
31916
  capName: "day-night",
31700
31917
  capScope: "device",
@@ -34131,6 +34348,12 @@ Object.freeze({
34131
34348
  addonId: null,
34132
34349
  access: "view"
34133
34350
  },
34351
+ "pipelineAnalytics.listRecentSummaries": {
34352
+ capName: "pipeline-analytics",
34353
+ capScope: "device",
34354
+ addonId: null,
34355
+ access: "view"
34356
+ },
34134
34357
  "pipelineAnalytics.listRecentTracks": {
34135
34358
  capName: "pipeline-analytics",
34136
34359
  capScope: "device",
@@ -35559,6 +35782,12 @@ Object.freeze({
35559
35782
  addonId: null,
35560
35783
  access: "create"
35561
35784
  },
35785
+ "settingsStore.upsert": {
35786
+ capName: "settings-store",
35787
+ capScope: "system",
35788
+ addonId: null,
35789
+ access: "create"
35790
+ },
35562
35791
  "smtpProvider.getStatus": {
35563
35792
  capName: "smtp-provider",
35564
35793
  capScope: "system",
@@ -37952,6 +38181,11 @@ Object.freeze({
37952
38181
  form: "single",
37953
38182
  optional: true
37954
38183
  }],
38184
+ "pipelineAnalytics.listRecentSummaries": [{
38185
+ name: "deviceIds",
38186
+ form: "array",
38187
+ optional: false
38188
+ }],
37955
38189
  "pipelineAnalytics.listRecentTracks": [{
37956
38190
  name: "deviceIds",
37957
38191
  form: "array",
package/dist/addon.mjs CHANGED
@@ -2,7 +2,7 @@ import { brotliCompressSync, deflateSync, gzipSync } from "node:zlib";
2
2
  import { readFileSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- //#region ../types/dist/event-category-DAXzJeTX.mjs
5
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
6
6
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
7
7
  EventCategory["SystemBoot"] = "system.boot";
8
8
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -563,7 +563,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
563
563
  * a package zone — a newly-appeared stationary object of a package class
564
564
  * that cleared the class / zone / dwell / size gates. Payload:
565
565
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
566
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
566
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
567
567
  * Telemetry (D8): the durable record is the `package-events` store row;
568
568
  * this bus topic drives notifier rules + live UI. See
569
569
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5349,7 +5349,7 @@ var ZodIssueCode = {
5349
5349
  var ZodFirstPartyTypeKind;
5350
5350
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5351
5351
  //#endregion
5352
- //#region ../types/dist/sleep-BnujYGPe.mjs
5352
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5353
5353
  /**
5354
5354
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5355
5355
  * window to float samples (D455).
@@ -7300,13 +7300,30 @@ new Set([
7300
7300
  "scene",
7301
7301
  "motion",
7302
7302
  "object",
7303
- "audio"
7303
+ "audio",
7304
+ "mosaic"
7304
7305
  ]);
7305
- new Set([
7306
+ /**
7307
+ * The owner types that are event tables, in the order the event-media resolver
7308
+ * should consider them. Exported so a consumer asking "is this key an event?"
7309
+ * does not re-spell the list and drift from it.
7310
+ */
7311
+ var EVENT_OWNER_TYPES = [
7306
7312
  "motion",
7307
7313
  "object",
7308
7314
  "audio"
7309
- ]);
7315
+ ];
7316
+ /**
7317
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7318
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7319
+ * `(table, id)`).
7320
+ *
7321
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7322
+ * table would otherwise be accepted by the codec and refused at the door, with
7323
+ * nothing failing until a caller asked.
7324
+ */
7325
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7326
+ new Set(EVENT_OWNER_TYPES);
7310
7327
  /**
7311
7328
  * Build an `IAddonRouteProvider` from a list of routes. Implements
7312
7329
  * both the operator-facing `getRoutes` (returning route descriptors
@@ -7462,7 +7479,23 @@ var EncodeProfileSchema = object({
7462
7479
  "zerolatency",
7463
7480
  "film",
7464
7481
  "animation"
7465
- ]).optional()
7482
+ ]).optional(),
7483
+ /**
7484
+ * ONE slice per access unit.
7485
+ *
7486
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7487
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7488
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7489
+ * frame-end per frame: the first pictures render and the video then freezes
7490
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7491
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7492
+ *
7493
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7494
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7495
+ * consumers meaning the same thing spelled differently would stop sharing one
7496
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7497
+ */
7498
+ singleSlicePerFrame: boolean().optional()
7466
7499
  }),
7467
7500
  audio: union([literal("passthrough"), object({
7468
7501
  codec: _enum([
@@ -7512,7 +7545,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7512
7545
  audio: "passthrough"
7513
7546
  };
7514
7547
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7515
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7548
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7516
7549
  /**
7517
7550
  * Deep wiring healthcheck — snapshot of active reachability probes across
7518
7551
  * every declared capability + widget of every installed plugin, on every
@@ -12561,6 +12594,16 @@ method(object({
12561
12594
  filter: QueryFilterSchema.optional(),
12562
12595
  columns: array(string()).readonly().optional()
12563
12596
  }), array(SettingsRecordSchema).readonly()), method(object({
12597
+ namespace: string().optional(),
12598
+ collection: string(),
12599
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12600
+ * index on the collection. Never empty. */
12601
+ conflict: array(string()).readonly(),
12602
+ record: BulkRecordSchema
12603
+ }), object({
12604
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12605
+ * row's own id on an update. */
12606
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12564
12607
  namespace: string().optional(),
12565
12608
  collection: string(),
12566
12609
  record: object({
@@ -12695,6 +12738,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12695
12738
  id: union([string(), number()]) }), {
12696
12739
  kind: "mutation",
12697
12740
  auth: "admin"
12741
+ }), method(object({
12742
+ namespace: string().optional(),
12743
+ collection: string(),
12744
+ conflict: array(string()).readonly(),
12745
+ record: BulkRecordSchema
12746
+ }), object({ id: union([string(), number()]) }), {
12747
+ kind: "mutation",
12748
+ auth: "admin"
12698
12749
  }), method(object({
12699
12750
  namespace: string().optional(),
12700
12751
  collection: string(),
@@ -18925,8 +18976,8 @@ var TrackPositionSchema = object({
18925
18976
  var TrackSnapshotSchema = object({
18926
18977
  timestamp: number(),
18927
18978
  position: TrackPositionSchema,
18928
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
18929
- mediaKey: string()
18979
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
18980
+ mediaId: number().int()
18930
18981
  });
18931
18982
  /**
18932
18983
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19530,14 +19581,14 @@ var ObjectEventSchema = object({
19530
19581
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19531
19582
  frameWidth: number().optional(),
19532
19583
  frameHeight: number().optional(),
19533
- /** MediaStore key for the crop attached to this event (if any). */
19534
- mediaKey: string().optional(),
19535
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19584
+ /** Media row id of the crop attached to this event (if any) — D482. */
19585
+ mediaId: number().int().optional(),
19586
+ /** Design B: media row id of the track's native-resolution key frame (the
19536
19587
  * best-detection full frame). Resolve via the event-media data-plane
19537
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19588
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19538
19589
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19539
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19540
- keyFrameMediaKey: string().optional(),
19590
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19591
+ keyFrameMediaId: number().int().optional(),
19541
19592
  /** Populated by B5 (recording playback URL for this event). */
19542
19593
  mediaUrl: string().optional(),
19543
19594
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19587,7 +19638,7 @@ var MediaFileKindEnum = _enum([
19587
19638
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19588
19639
  *
19589
19640
  * `url` points at the `event-media` data plane
19590
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19641
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19591
19642
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19592
19643
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19593
19644
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19595,10 +19646,13 @@ var MediaFileKindEnum = _enum([
19595
19646
  * `data-plane-access.ts` for the rule and the one gap it does not close
19596
19647
  * (per-device scoping).
19597
19648
  *
19598
- * The URL is built from the row's **stored** key, which is not always its
19599
- * published `kind`: a track's face/plate crop is stored as `crop` under
19649
+ * The URL is built from the row's own id, which since D482 says nothing about
19650
+ * the row's owner or kind — and that is the point. The published `kind` is not
19651
+ * the stored one (a track's face/plate crop is stored as `crop` under
19600
19652
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19601
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19653
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19654
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19655
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19602
19656
  *
19603
19657
  * ## `base64` is TRANSITIONAL and is going away
19604
19658
  *
@@ -19611,7 +19665,19 @@ var MediaFileKindEnum = _enum([
19611
19665
  * `analytics-query-facade.ts`; nothing else reads it.
19612
19666
  */
19613
19667
  var MediaFileSchema = object({
19614
- key: string(),
19668
+ /**
19669
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19670
+ *
19671
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19672
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19673
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19674
+ * different rows that spell the same number, and a track whose
19675
+ * `bestEventId` was an object event was served a motion event's snapshot —
19676
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19677
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19678
+ * at all.
19679
+ */
19680
+ mediaId: number().int(),
19615
19681
  kind: MediaFileKindEnum,
19616
19682
  sizeBytes: number(),
19617
19683
  timestamp: number()
@@ -19719,7 +19785,7 @@ var RetrainTrackSchema = object({
19719
19785
  });
19720
19786
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19721
19787
  var RetrainFrameCandidateSchema = object({
19722
- mediaKey: string(),
19788
+ mediaId: number().int(),
19723
19789
  kind: MediaFileKindEnum,
19724
19790
  timestamp: number(),
19725
19791
  sizeBytes: number().int(),
@@ -19733,7 +19799,7 @@ var RetrainFrameSchema = object({
19733
19799
  deviceId: number(),
19734
19800
  trackId: string(),
19735
19801
  /** Provenance only. It may already point at nothing — that is expected. */
19736
- sourceMediaKey: string(),
19802
+ sourceMediaId: number().int(),
19737
19803
  sourceKind: MediaFileKindEnum,
19738
19804
  sizeBytes: number().int(),
19739
19805
  width: number().int(),
@@ -19749,7 +19815,7 @@ var RetrainCopyRefusalSchema = _enum([
19749
19815
  var RetrainFrameSelectionSchema = object({
19750
19816
  copied: array(RetrainFrameSchema).readonly(),
19751
19817
  refused: array(object({
19752
- sourceMediaKey: string(),
19818
+ sourceMediaId: number().int(),
19753
19819
  reason: RetrainCopyRefusalSchema
19754
19820
  })).readonly()
19755
19821
  });
@@ -19757,7 +19823,7 @@ var RetrainFrameListSchema = object({
19757
19823
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19758
19824
  copies: array(RetrainFrameSchema).readonly(),
19759
19825
  /** What the page pre-selects — the native key frame when one survives. */
19760
- autoPickMediaKey: string().optional()
19826
+ autoPickMediaId: number().int().optional()
19761
19827
  });
19762
19828
  /** What the operator asked the assist to look for. */
19763
19829
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -19852,9 +19918,11 @@ var RecentTracksQueryInput = object({
19852
19918
  });
19853
19919
  /**
19854
19920
  * A Summary (D359): the per-camera session envelope that references tracks.
19855
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
19856
- * served by the event-media plane by key), null until the first frame with a
19857
- * member visible has been materialised.
19921
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
19922
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
19923
+ * first frame with a member visible has been materialised (D482 — it was the
19924
+ * row's composite key, which embedded the owner and could name another table's
19925
+ * row once event ids became per-table rowids).
19858
19926
  */
19859
19927
  var SummarySchema = object({
19860
19928
  id: string(),
@@ -19863,19 +19931,115 @@ var SummarySchema = object({
19863
19931
  lastActiveAt: number(),
19864
19932
  sealedAt: number().nullable(),
19865
19933
  memberCount: number().int().nonnegative(),
19866
- groupShotKey: string().nullable()
19934
+ groupShotMediaId: number().int().nullable()
19867
19935
  });
19868
19936
  var SummariesQueryInput = object({
19869
19937
  deviceIds: array(number()).min(1),
19870
19938
  since: number().optional(),
19871
19939
  until: number().optional(),
19872
- limit: number().int().min(1).max(200).default(50)
19940
+ limit: number().int().min(1).max(200).default(50),
19941
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
19942
+ * meaning of "dog" on every summary surface. */
19943
+ classes: array(string().min(1).max(60)).max(40).optional()
19873
19944
  });
19874
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
19875
19945
  var SummaryDetailSchema = object({
19876
19946
  summary: SummarySchema,
19877
- /** Member track ids, in absorption order. */
19878
- trackIds: array(string()).readonly()
19947
+ /** Members in absorption order, oldest join first. */
19948
+ members: array(object({
19949
+ trackId: string(),
19950
+ /**
19951
+ * The track's class at the time it JOINED, or `null` when it was unresolved
19952
+ * and the track no longer exists to fill it.
19953
+ *
19954
+ * The join, not the latest opinion: class voting can re-decide a track's
19955
+ * class afterwards, and a group's composition is the history of who came
19956
+ * together. A consumer counts these to badge a thumbnail — "two people and a
19957
+ * dog" — rather than being handed a second, denormalised tally that would
19958
+ * drift from the members it claims to describe.
19959
+ */
19960
+ className: string().nullable(),
19961
+ /** `primary` is the one member that carries the group's identity. */
19962
+ role: _enum([
19963
+ "primary",
19964
+ "sibling",
19965
+ "child"
19966
+ ]).nullable(),
19967
+ joinedAt: number(),
19968
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
19969
+ * at read time. Absent when nothing resolved, never an empty string. */
19970
+ label: string().optional(),
19971
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
19972
+ * recognised nobody, and absent for a member whose track has been pruned —
19973
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
19974
+ subLabel: string().optional()
19975
+ })).readonly()
19976
+ });
19977
+ /**
19978
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
19979
+ *
19980
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
19981
+ * we extend when we notice we need more". Enough to draw the card ("two people
19982
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
19983
+ * are on {@link getSummary}: shipping every member of every group so a client
19984
+ * can count them turns a page of two hundred groups into a thousand records to
19985
+ * draw two hundred badges.
19986
+ */
19987
+ var SummaryListItemSchema = object({
19988
+ summary: SummarySchema,
19989
+ /** Class counts, most numerous first; `className: null` is a member whose
19990
+ * class never resolved, counted so the counts add up to `memberCount`. */
19991
+ classes: array(object({
19992
+ className: string().nullable(),
19993
+ count: number().int().positive()
19994
+ })).readonly()
19995
+ });
19996
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
19997
+ /**
19998
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
19999
+ *
20000
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20001
+ * summaries feed that could only answer "the last N for these cameras"
20002
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20003
+ * else. Two feeds with different ordering contracts would be two ideas of what
20004
+ * "newest" means, on one screen.
20005
+ */
20006
+ var RecentSummariesQueryInput = object({
20007
+ /** Devices to merge. An empty array yields an empty page. */
20008
+ deviceIds: array(number()),
20009
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20010
+ since: number().optional(),
20011
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20012
+ until: number().optional(),
20013
+ limit: number().int().min(1).max(500).default(100),
20014
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20015
+ cursor: string().optional(),
20016
+ /**
20017
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20018
+ * deliberately the same name: the events filter tree emits one list of ticked
20019
+ * taxonomy nodes, and a summaries feed that called it something else would
20020
+ * make the operator's one filter component produce two shapes.
20021
+ *
20022
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20023
+ * me the arrivals with a dog" has to return the group of four people who had
20024
+ * one with them; a filter on the group's own fields could only ever answer
20025
+ * about its primary.
20026
+ *
20027
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20028
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20029
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20030
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20031
+ * that read only the class would answer "animal" and never "dog".
20032
+ *
20033
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20034
+ */
20035
+ classes: array(string().min(1).max(60)).max(40).optional()
20036
+ });
20037
+ var RecentSummariesPageSchema = object({
20038
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20039
+ * {@link listSummaries} — one list shape, so a card renders the same
20040
+ * whichever feed drew it. */
20041
+ summaries: array(SummaryListItemSchema).readonly(),
20042
+ nextCursor: string().nullable()
19879
20043
  });
19880
20044
  var RecentTracksPageSchema = object({
19881
20045
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20289,7 +20453,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20289
20453
  classes: array(string()).optional(),
20290
20454
  /** See {@link ExcludeSourcesDoc}. */
20291
20455
  excludeSources: array(TrackSourceSchema).optional()
20292
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20456
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20293
20457
  kind: "mutation",
20294
20458
  auth: "admin"
20295
20459
  }), 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({
@@ -20417,7 +20581,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20417
20581
  }), method(object({
20418
20582
  deviceId: number(),
20419
20583
  trackId: string(),
20420
- mediaKeys: array(string()).min(1)
20584
+ mediaIds: array(number().int()).min(1)
20421
20585
  }), RetrainFrameSelectionSchema, {
20422
20586
  kind: "mutation",
20423
20587
  auth: "admin"
@@ -20485,7 +20649,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20485
20649
  kind: "query",
20486
20650
  auth: "admin"
20487
20651
  }), method(object({
20488
- eventId: string(),
20652
+ /** Which of the three event tables owns the row. */
20653
+ ownerType: EventOwnerTypeSchema,
20654
+ /** The event's rowid in that table (D474: an event id is a number). */
20655
+ eventId: number().int(),
20489
20656
  kind: MediaFileKindEnum.optional(),
20490
20657
  deviceId: number()
20491
20658
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20496,7 +20663,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20496
20663
  trackId: string(),
20497
20664
  deviceId: number()
20498
20665
  }), array(MediaFileInfoSchema).readonly()), method(object({
20499
- eventId: string(),
20666
+ ownerType: EventOwnerTypeSchema,
20667
+ eventId: number().int(),
20500
20668
  deviceId: number()
20501
20669
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20502
20670
  kind: "mutation",
@@ -24959,8 +25127,8 @@ var IdentitySchema = object({
24959
25127
  id: string(),
24960
25128
  name: string(),
24961
25129
  sampleCount: number().int().nonnegative(),
24962
- coverMediaKey: string().optional(),
24963
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25130
+ coverMediaId: number().int().optional(),
25131
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
24964
25132
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
24965
25133
  coverBase64: string().optional()
24966
25134
  });
@@ -24986,8 +25154,8 @@ var IdentitySampleAuditRowSchema = object({
24986
25154
  enrolledAt: number().int(),
24987
25155
  deviceId: number().int().optional(),
24988
25156
  source: _enum(["detected", "photo"]),
24989
- /** The enrolled crop's media key, so the panel can show the evidence. */
24990
- mediaKey: string().optional(),
25157
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25158
+ mediaId: number().int().optional(),
24991
25159
  /**
24992
25160
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
24993
25161
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25056,15 +25224,15 @@ var FaceInfoSchema = object({
25056
25224
  */
25057
25225
  cropUrl: string().optional(),
25058
25226
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25059
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
25227
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25060
25228
  * legacy rows written before design B. */
25061
25229
  faceBbox: BoundingBoxSchema.optional(),
25062
25230
  /** Design B: MediaStore key of the track's native-resolution key frame.
25063
25231
  * Fetch the native JPEG via the event-media data-plane
25064
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
25232
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
25065
25233
  * track produced no key frame (e.g. native/onboard source) — the UI falls
25066
25234
  * back to the inline `base64` face crop. */
25067
- keyFrameMediaKey: string().optional(),
25235
+ keyFrameMediaId: number().int().optional(),
25068
25236
  /** Winning identity-match cosine (0..1) for this face's track, when an
25069
25237
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
25070
25238
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -25133,11 +25301,14 @@ var FaceClusterSchema = object({
25133
25301
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25134
25302
  * dialog already rendering its key FRAME from the `event-media` plane.
25135
25303
  *
25136
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25137
- * media key directly, so this needed no new plane and no new access decision.
25304
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
25305
+ * resolves a media row by its own id, so this needed no new plane and no new
25306
+ * access decision.
25138
25307
  */
25139
25308
  var MediaFileLiteSchema$1 = object({
25140
- key: string(),
25309
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
25310
+ * (D482). It was the row's composite key, which embedded the owner. */
25311
+ mediaId: number().int(),
25141
25312
  kind: string(),
25142
25313
  url: string(),
25143
25314
  sizeBytes: number(),
@@ -25154,7 +25325,7 @@ method(object({
25154
25325
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
25155
25326
  * four times and the viewer holds at `staleTime: 30_000`.
25156
25327
  *
25157
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
25328
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
25158
25329
  * the `event-media` plane serves that key `immutable` with an ETag.
25159
25330
  *
25160
25331
  * **This is an INPUT field, so it does not reach the addon until the
@@ -27622,8 +27793,8 @@ var VehicleSchema = object({
27622
27793
  id: string(),
27623
27794
  name: string(),
27624
27795
  sampleCount: number().int().nonnegative(),
27625
- coverMediaKey: string().optional(),
27626
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
27796
+ coverMediaId: number().int().optional(),
27797
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
27627
27798
  coverBase64: string().optional()
27628
27799
  });
27629
27800
  var VehicleSampleInfoSchema = object({
@@ -27656,7 +27827,7 @@ var PlateInfoSchema = object({
27656
27827
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
27657
27828
  plateBbox: BoundingBoxSchema.optional(),
27658
27829
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
27659
- keyFrameMediaKey: string().optional(),
27830
+ keyFrameMediaId: number().int().optional(),
27660
27831
  base64: string().optional(),
27661
27832
  /**
27662
27833
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -27677,11 +27848,14 @@ var PlateInfoSchema = object({
27677
27848
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27678
27849
  * dialog already rendering its key FRAME from the `event-media` plane.
27679
27850
  *
27680
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27681
- * media key directly, so this needed no new plane and no new access decision.
27851
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
27852
+ * resolves a media row by its own id, so this needed no new plane and no new
27853
+ * access decision.
27682
27854
  */
27683
27855
  var MediaFileLiteSchema = object({
27684
- key: string(),
27856
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
27857
+ * (D482). It was the row's composite key, which embedded the owner. */
27858
+ mediaId: number().int(),
27685
27859
  kind: string(),
27686
27860
  url: string(),
27687
27861
  sizeBytes: number(),
@@ -27740,7 +27914,7 @@ method(object({
27740
27914
  }), method(object({
27741
27915
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
27742
27916
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
27743
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
27917
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
27744
27918
  * INPUT field: stripped by the hub router until the train ships, which
27745
27919
  * resolves to `false` and is exactly the intended default. */
27746
27920
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -29064,7 +29238,14 @@ var SceneReferenceSchema = object({
29064
29238
  modelId: string(),
29065
29239
  condition: SceneConditionSchema,
29066
29240
  capturedAt: number(),
29067
- thumbnailMediaId: string().optional(),
29241
+ /**
29242
+ * The reference thumbnail's media ROW id (D482).
29243
+ *
29244
+ * The name already said `mediaId` while the value was the row's composite
29245
+ * key — a leftover that read as done and was not. It is the row's own id
29246
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
29247
+ */
29248
+ thumbnailMediaId: number().int().optional(),
29068
29249
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
29069
29250
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
29070
29251
  * normalized rect frame a different piece of world, and the scene would
@@ -29878,7 +30059,29 @@ var LoggingSettingsPatchSchema = object({
29878
30059
  * disarmed the channels it did not mention would make the Levels page and
29879
30060
  * the Diagnostics page fight over the same value.
29880
30061
  */
29881
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
30062
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
30063
+ /**
30064
+ * How many days the analytics OPS LOG is kept — the audit trail of what
30065
+ * retention actually deleted.
30066
+ *
30067
+ * It lives HERE, on the logging document, and not on the analytics addon's
30068
+ * settings form, on the operator's instruction: every other log-ish window in
30069
+ * this system is configured through this one document, and a retention audit
30070
+ * configured somewhere else is a second place to look when the question is
30071
+ * "what happened to my data".
30072
+ *
30073
+ * That placement has a cost worth stating: this document ships in the
30074
+ * framework closure, so this knob needs a published `@camstack/server` while
30075
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
30076
+ *
30077
+ * `retention-model.ts` argued this one must NOT follow a device window,
30078
+ * because the audit trail is the history of the SWEEP and not of the camera.
30079
+ * That argument stands and is untouched — what changes is that 30 days stops
30080
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
30081
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
30082
+ * state in which the next 9.5 GiB leak cannot be explained.
30083
+ */
30084
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
29882
30085
  });
29883
30086
  /**
29884
30087
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -29951,6 +30154,14 @@ var LoggingSettingsStateSchema = object({
29951
30154
  channels: array(LogChannelDescriptorSchema).readonly(),
29952
30155
  /** The channels ARMED right now, each with its deadline. */
29953
30156
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
30157
+ /**
30158
+ * The ops-log window in days, as it is in force. `0` = kept forever.
30159
+ *
30160
+ * Reported rather than left to the caller's memory of what it wrote: this is
30161
+ * the value the analytics sweep will actually use, and an operator asking how
30162
+ * long their audit trail lives should not have to infer it from a default.
30163
+ */
30164
+ opsLogRetentionDays: number().int().min(0),
29954
30165
  persisted: boolean()
29955
30166
  });
29956
30167
  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(), {
@@ -30512,7 +30723,7 @@ var StationaryObjectSchema = object({
30512
30723
  /** Enrichment label carried from the source track (identity / plate). */
30513
30724
  label: string().optional(),
30514
30725
  /** Native-resolution key-frame media key for the parked object's best image. */
30515
- keyFrameMediaKey: string().optional()
30726
+ keyFrameMediaId: number().int().optional()
30516
30727
  });
30517
30728
  var CameraOccupancySnapshotSchema = object({
30518
30729
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -31668,6 +31879,12 @@ Object.freeze({
31668
31879
  addonId: null,
31669
31880
  access: "create"
31670
31881
  },
31882
+ "dataStoreProvider.upsert": {
31883
+ capName: "data-store-provider",
31884
+ capScope: "system",
31885
+ addonId: null,
31886
+ access: "create"
31887
+ },
31671
31888
  "dayNight.getOptions": {
31672
31889
  capName: "day-night",
31673
31890
  capScope: "device",
@@ -34104,6 +34321,12 @@ Object.freeze({
34104
34321
  addonId: null,
34105
34322
  access: "view"
34106
34323
  },
34324
+ "pipelineAnalytics.listRecentSummaries": {
34325
+ capName: "pipeline-analytics",
34326
+ capScope: "device",
34327
+ addonId: null,
34328
+ access: "view"
34329
+ },
34107
34330
  "pipelineAnalytics.listRecentTracks": {
34108
34331
  capName: "pipeline-analytics",
34109
34332
  capScope: "device",
@@ -35532,6 +35755,12 @@ Object.freeze({
35532
35755
  addonId: null,
35533
35756
  access: "create"
35534
35757
  },
35758
+ "settingsStore.upsert": {
35759
+ capName: "settings-store",
35760
+ capScope: "system",
35761
+ addonId: null,
35762
+ access: "create"
35763
+ },
35535
35764
  "smtpProvider.getStatus": {
35536
35765
  capName: "smtp-provider",
35537
35766
  capScope: "system",
@@ -37925,6 +38154,11 @@ Object.freeze({
37925
38154
  form: "single",
37926
38155
  optional: true
37927
38156
  }],
38157
+ "pipelineAnalytics.listRecentSummaries": [{
38158
+ name: "deviceIds",
38159
+ form: "array",
38160
+ optional: false
38161
+ }],
37928
38162
  "pipelineAnalytics.listRecentTracks": [{
37929
38163
  name: "deviceIds",
37930
38164
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-notifiers",
3
- "version": "1.2.103",
3
+ "version": "1.2.105",
4
4
  "description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
5
5
  "keywords": [
6
6
  "camstack",