@camstack/addon-export-alexa 1.2.105 → 1.2.107

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.
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  //#endregion
27
27
  let node_crypto = require("node:crypto");
28
28
  node_crypto = __toESM(node_crypto);
29
- //#region ../types/dist/event-category-DAXzJeTX.mjs
29
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
30
30
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
31
31
  EventCategory["SystemBoot"] = "system.boot";
32
32
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -587,7 +587,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
587
587
  * a package zone — a newly-appeared stationary object of a package class
588
588
  * that cleared the class / zone / dwell / size gates. Payload:
589
589
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
590
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
590
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
591
591
  * Telemetry (D8): the durable record is the `package-events` store row;
592
592
  * this bus topic drives notifier rules + live UI. See
593
593
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5373,7 +5373,7 @@ var ZodIssueCode = {
5373
5373
  var ZodFirstPartyTypeKind;
5374
5374
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5375
5375
  //#endregion
5376
- //#region ../types/dist/sleep-BnujYGPe.mjs
5376
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5377
5377
  /**
5378
5378
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5379
5379
  * window to float samples (D455).
@@ -7336,13 +7336,30 @@ new Set([
7336
7336
  "scene",
7337
7337
  "motion",
7338
7338
  "object",
7339
- "audio"
7339
+ "audio",
7340
+ "mosaic"
7340
7341
  ]);
7341
- new Set([
7342
+ /**
7343
+ * The owner types that are event tables, in the order the event-media resolver
7344
+ * should consider them. Exported so a consumer asking "is this key an event?"
7345
+ * does not re-spell the list and drift from it.
7346
+ */
7347
+ var EVENT_OWNER_TYPES = [
7342
7348
  "motion",
7343
7349
  "object",
7344
7350
  "audio"
7345
- ]);
7351
+ ];
7352
+ /**
7353
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7354
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7355
+ * `(table, id)`).
7356
+ *
7357
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7358
+ * table would otherwise be accepted by the codec and refused at the door, with
7359
+ * nothing failing until a caller asked.
7360
+ */
7361
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7362
+ new Set(EVENT_OWNER_TYPES);
7346
7363
  /**
7347
7364
  * Build an `IAddonRouteProvider` from a list of routes. Implements
7348
7365
  * both the operator-facing `getRoutes` (returning route descriptors
@@ -7573,7 +7590,32 @@ var BASE_LIVE_EGRESS_PROFILE = {
7573
7590
  */
7574
7591
  var ALEXA_EGRESS_PROFILE = {
7575
7592
  ...BASE_LIVE_EGRESS_PROFILE,
7576
- audio: { codec: "opus" }
7593
+ audio: { codec: "opus" },
7594
+ /**
7595
+ * ONE slice per access unit.
7596
+ *
7597
+ * `-tune zerolatency` turns on x264's sliced threads, and every access unit
7598
+ * came out of this encode as FIVE slices — measured on the hub 2026-09-13 on
7599
+ * the exact argv this path spawns, and reduced to one by this single flag.
7600
+ *
7601
+ * Five slices share one RTP timestamp and carry one marker bit, so a
7602
+ * libwebrtc depacketiser sees five frame-starts and one frame-end per frame.
7603
+ * The Echo renders the first pictures and then freezes for good, while the
7604
+ * audio — a separate plane — plays on. It is the only structural property
7605
+ * that survived the elimination: the same freeze reproduced at 2304×1296
7606
+ * HEVC with 217 KB keyframes AND at 640×360 H.264 with 67 KB keyframes and a
7607
+ * fifth of the bitrate, so source, resolution, bitrate, keyframe burst,
7608
+ * hardware decode, parameter sets, drift and bitstream conformance are all
7609
+ * out — each ruled out by a measurement, not by elimination.
7610
+ *
7611
+ * The tune STAYS: it also drops the lookahead and the B-frames, which a live
7612
+ * path wants. Only the slicing is unwanted, and this is the flag that says so.
7613
+ *
7614
+ * Scoped to Alexa deliberately. HomeKit and the browser transcode inherit
7615
+ * {@link BASE_LIVE_EGRESS_PROFILE}; whether they want the same shape is a
7616
+ * question for their own evidence, and the browser has not been measured.
7617
+ */
7618
+ outputArgs: ["-x264-params", "sliced-threads=0"]
7577
7619
  };
7578
7620
  /**
7579
7621
  * Deep wiring healthcheck — snapshot of active reachability probes across
@@ -12684,6 +12726,16 @@ method(object({
12684
12726
  filter: QueryFilterSchema.optional(),
12685
12727
  columns: array(string()).readonly().optional()
12686
12728
  }), array(SettingsRecordSchema).readonly()), method(object({
12729
+ namespace: string().optional(),
12730
+ collection: string(),
12731
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12732
+ * index on the collection. Never empty. */
12733
+ conflict: array(string()).readonly(),
12734
+ record: BulkRecordSchema
12735
+ }), object({
12736
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12737
+ * row's own id on an update. */
12738
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12687
12739
  namespace: string().optional(),
12688
12740
  collection: string(),
12689
12741
  record: object({
@@ -12818,6 +12870,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12818
12870
  id: union([string(), number()]) }), {
12819
12871
  kind: "mutation",
12820
12872
  auth: "admin"
12873
+ }), method(object({
12874
+ namespace: string().optional(),
12875
+ collection: string(),
12876
+ conflict: array(string()).readonly(),
12877
+ record: BulkRecordSchema
12878
+ }), object({ id: union([string(), number()]) }), {
12879
+ kind: "mutation",
12880
+ auth: "admin"
12821
12881
  }), method(object({
12822
12882
  namespace: string().optional(),
12823
12883
  collection: string(),
@@ -19075,8 +19135,8 @@ var TrackPositionSchema = object({
19075
19135
  var TrackSnapshotSchema = object({
19076
19136
  timestamp: number(),
19077
19137
  position: TrackPositionSchema,
19078
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19079
- mediaKey: string()
19138
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19139
+ mediaId: number().int()
19080
19140
  });
19081
19141
  /**
19082
19142
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19680,14 +19740,14 @@ var ObjectEventSchema = object({
19680
19740
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19681
19741
  frameWidth: number().optional(),
19682
19742
  frameHeight: number().optional(),
19683
- /** MediaStore key for the crop attached to this event (if any). */
19684
- mediaKey: string().optional(),
19685
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19743
+ /** Media row id of the crop attached to this event (if any) — D482. */
19744
+ mediaId: number().int().optional(),
19745
+ /** Design B: media row id of the track's native-resolution key frame (the
19686
19746
  * best-detection full frame). Resolve via the event-media data-plane
19687
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19747
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19688
19748
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19689
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19690
- keyFrameMediaKey: string().optional(),
19749
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19750
+ keyFrameMediaId: number().int().optional(),
19691
19751
  /** Populated by B5 (recording playback URL for this event). */
19692
19752
  mediaUrl: string().optional(),
19693
19753
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19737,7 +19797,7 @@ var MediaFileKindEnum = _enum([
19737
19797
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19738
19798
  *
19739
19799
  * `url` points at the `event-media` data plane
19740
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19800
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19741
19801
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19742
19802
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19743
19803
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19745,10 +19805,13 @@ var MediaFileKindEnum = _enum([
19745
19805
  * `data-plane-access.ts` for the rule and the one gap it does not close
19746
19806
  * (per-device scoping).
19747
19807
  *
19748
- * The URL is built from the row's **stored** key, which is not always its
19749
- * published `kind`: a track's face/plate crop is stored as `crop` under
19808
+ * The URL is built from the row's own id, which since D482 says nothing about
19809
+ * the row's owner or kind — and that is the point. The published `kind` is not
19810
+ * the stored one (a track's face/plate crop is stored as `crop` under
19750
19811
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19751
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19812
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19813
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19814
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19752
19815
  *
19753
19816
  * ## `base64` is TRANSITIONAL and is going away
19754
19817
  *
@@ -19761,7 +19824,19 @@ var MediaFileKindEnum = _enum([
19761
19824
  * `analytics-query-facade.ts`; nothing else reads it.
19762
19825
  */
19763
19826
  var MediaFileSchema = object({
19764
- key: string(),
19827
+ /**
19828
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19829
+ *
19830
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19831
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19832
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19833
+ * different rows that spell the same number, and a track whose
19834
+ * `bestEventId` was an object event was served a motion event's snapshot —
19835
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19836
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19837
+ * at all.
19838
+ */
19839
+ mediaId: number().int(),
19765
19840
  kind: MediaFileKindEnum,
19766
19841
  sizeBytes: number(),
19767
19842
  timestamp: number()
@@ -19869,7 +19944,7 @@ var RetrainTrackSchema = object({
19869
19944
  });
19870
19945
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19871
19946
  var RetrainFrameCandidateSchema = object({
19872
- mediaKey: string(),
19947
+ mediaId: number().int(),
19873
19948
  kind: MediaFileKindEnum,
19874
19949
  timestamp: number(),
19875
19950
  sizeBytes: number().int(),
@@ -19883,7 +19958,7 @@ var RetrainFrameSchema = object({
19883
19958
  deviceId: number(),
19884
19959
  trackId: string(),
19885
19960
  /** Provenance only. It may already point at nothing — that is expected. */
19886
- sourceMediaKey: string(),
19961
+ sourceMediaId: number().int(),
19887
19962
  sourceKind: MediaFileKindEnum,
19888
19963
  sizeBytes: number().int(),
19889
19964
  width: number().int(),
@@ -19899,7 +19974,7 @@ var RetrainCopyRefusalSchema = _enum([
19899
19974
  var RetrainFrameSelectionSchema = object({
19900
19975
  copied: array(RetrainFrameSchema).readonly(),
19901
19976
  refused: array(object({
19902
- sourceMediaKey: string(),
19977
+ sourceMediaId: number().int(),
19903
19978
  reason: RetrainCopyRefusalSchema
19904
19979
  })).readonly()
19905
19980
  });
@@ -19907,7 +19982,7 @@ var RetrainFrameListSchema = object({
19907
19982
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19908
19983
  copies: array(RetrainFrameSchema).readonly(),
19909
19984
  /** What the page pre-selects — the native key frame when one survives. */
19910
- autoPickMediaKey: string().optional()
19985
+ autoPickMediaId: number().int().optional()
19911
19986
  });
19912
19987
  /** What the operator asked the assist to look for. */
19913
19988
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -20002,9 +20077,11 @@ var RecentTracksQueryInput = object({
20002
20077
  });
20003
20078
  /**
20004
20079
  * A Summary (D359): the per-camera session envelope that references tracks.
20005
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
20006
- * served by the event-media plane by key), null until the first frame with a
20007
- * member visible has been materialised.
20080
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20081
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20082
+ * first frame with a member visible has been materialised (D482 — it was the
20083
+ * row's composite key, which embedded the owner and could name another table's
20084
+ * row once event ids became per-table rowids).
20008
20085
  */
20009
20086
  var SummarySchema = object({
20010
20087
  id: string(),
@@ -20013,19 +20090,115 @@ var SummarySchema = object({
20013
20090
  lastActiveAt: number(),
20014
20091
  sealedAt: number().nullable(),
20015
20092
  memberCount: number().int().nonnegative(),
20016
- groupShotKey: string().nullable()
20093
+ groupShotMediaId: number().int().nullable()
20017
20094
  });
20018
20095
  var SummariesQueryInput = object({
20019
20096
  deviceIds: array(number()).min(1),
20020
20097
  since: number().optional(),
20021
20098
  until: number().optional(),
20022
- limit: number().int().min(1).max(200).default(50)
20099
+ limit: number().int().min(1).max(200).default(50),
20100
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20101
+ * meaning of "dog" on every summary surface. */
20102
+ classes: array(string().min(1).max(60)).max(40).optional()
20023
20103
  });
20024
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
20025
20104
  var SummaryDetailSchema = object({
20026
20105
  summary: SummarySchema,
20027
- /** Member track ids, in absorption order. */
20028
- trackIds: array(string()).readonly()
20106
+ /** Members in absorption order, oldest join first. */
20107
+ members: array(object({
20108
+ trackId: string(),
20109
+ /**
20110
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20111
+ * and the track no longer exists to fill it.
20112
+ *
20113
+ * The join, not the latest opinion: class voting can re-decide a track's
20114
+ * class afterwards, and a group's composition is the history of who came
20115
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20116
+ * dog" — rather than being handed a second, denormalised tally that would
20117
+ * drift from the members it claims to describe.
20118
+ */
20119
+ className: string().nullable(),
20120
+ /** `primary` is the one member that carries the group's identity. */
20121
+ role: _enum([
20122
+ "primary",
20123
+ "sibling",
20124
+ "child"
20125
+ ]).nullable(),
20126
+ joinedAt: number(),
20127
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20128
+ * at read time. Absent when nothing resolved, never an empty string. */
20129
+ label: string().optional(),
20130
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20131
+ * recognised nobody, and absent for a member whose track has been pruned —
20132
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20133
+ subLabel: string().optional()
20134
+ })).readonly()
20135
+ });
20136
+ /**
20137
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20138
+ *
20139
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20140
+ * we extend when we notice we need more". Enough to draw the card ("two people
20141
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20142
+ * are on {@link getSummary}: shipping every member of every group so a client
20143
+ * can count them turns a page of two hundred groups into a thousand records to
20144
+ * draw two hundred badges.
20145
+ */
20146
+ var SummaryListItemSchema = object({
20147
+ summary: SummarySchema,
20148
+ /** Class counts, most numerous first; `className: null` is a member whose
20149
+ * class never resolved, counted so the counts add up to `memberCount`. */
20150
+ classes: array(object({
20151
+ className: string().nullable(),
20152
+ count: number().int().positive()
20153
+ })).readonly()
20154
+ });
20155
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20156
+ /**
20157
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20158
+ *
20159
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20160
+ * summaries feed that could only answer "the last N for these cameras"
20161
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20162
+ * else. Two feeds with different ordering contracts would be two ideas of what
20163
+ * "newest" means, on one screen.
20164
+ */
20165
+ var RecentSummariesQueryInput = object({
20166
+ /** Devices to merge. An empty array yields an empty page. */
20167
+ deviceIds: array(number()),
20168
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20169
+ since: number().optional(),
20170
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20171
+ until: number().optional(),
20172
+ limit: number().int().min(1).max(500).default(100),
20173
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20174
+ cursor: string().optional(),
20175
+ /**
20176
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20177
+ * deliberately the same name: the events filter tree emits one list of ticked
20178
+ * taxonomy nodes, and a summaries feed that called it something else would
20179
+ * make the operator's one filter component produce two shapes.
20180
+ *
20181
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20182
+ * me the arrivals with a dog" has to return the group of four people who had
20183
+ * one with them; a filter on the group's own fields could only ever answer
20184
+ * about its primary.
20185
+ *
20186
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20187
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20188
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20189
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20190
+ * that read only the class would answer "animal" and never "dog".
20191
+ *
20192
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20193
+ */
20194
+ classes: array(string().min(1).max(60)).max(40).optional()
20195
+ });
20196
+ var RecentSummariesPageSchema = object({
20197
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20198
+ * {@link listSummaries} — one list shape, so a card renders the same
20199
+ * whichever feed drew it. */
20200
+ summaries: array(SummaryListItemSchema).readonly(),
20201
+ nextCursor: string().nullable()
20029
20202
  });
20030
20203
  var RecentTracksPageSchema = object({
20031
20204
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20439,7 +20612,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20439
20612
  classes: array(string()).optional(),
20440
20613
  /** See {@link ExcludeSourcesDoc}. */
20441
20614
  excludeSources: array(TrackSourceSchema).optional()
20442
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20615
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20443
20616
  kind: "mutation",
20444
20617
  auth: "admin"
20445
20618
  }), 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({
@@ -20567,7 +20740,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20567
20740
  }), method(object({
20568
20741
  deviceId: number(),
20569
20742
  trackId: string(),
20570
- mediaKeys: array(string()).min(1)
20743
+ mediaIds: array(number().int()).min(1)
20571
20744
  }), RetrainFrameSelectionSchema, {
20572
20745
  kind: "mutation",
20573
20746
  auth: "admin"
@@ -20635,7 +20808,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20635
20808
  kind: "query",
20636
20809
  auth: "admin"
20637
20810
  }), method(object({
20638
- eventId: string(),
20811
+ /** Which of the three event tables owns the row. */
20812
+ ownerType: EventOwnerTypeSchema,
20813
+ /** The event's rowid in that table (D474: an event id is a number). */
20814
+ eventId: number().int(),
20639
20815
  kind: MediaFileKindEnum.optional(),
20640
20816
  deviceId: number()
20641
20817
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20646,7 +20822,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20646
20822
  trackId: string(),
20647
20823
  deviceId: number()
20648
20824
  }), array(MediaFileInfoSchema).readonly()), method(object({
20649
- eventId: string(),
20825
+ ownerType: EventOwnerTypeSchema,
20826
+ eventId: number().int(),
20650
20827
  deviceId: number()
20651
20828
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20652
20829
  kind: "mutation",
@@ -25109,8 +25286,8 @@ var IdentitySchema = object({
25109
25286
  id: string(),
25110
25287
  name: string(),
25111
25288
  sampleCount: number().int().nonnegative(),
25112
- coverMediaKey: string().optional(),
25113
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25289
+ coverMediaId: number().int().optional(),
25290
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25114
25291
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25115
25292
  coverBase64: string().optional()
25116
25293
  });
@@ -25136,8 +25313,8 @@ var IdentitySampleAuditRowSchema = object({
25136
25313
  enrolledAt: number().int(),
25137
25314
  deviceId: number().int().optional(),
25138
25315
  source: _enum(["detected", "photo"]),
25139
- /** The enrolled crop's media key, so the panel can show the evidence. */
25140
- mediaKey: string().optional(),
25316
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25317
+ mediaId: number().int().optional(),
25141
25318
  /**
25142
25319
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25143
25320
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25206,15 +25383,15 @@ var FaceInfoSchema = object({
25206
25383
  */
25207
25384
  cropUrl: string().optional(),
25208
25385
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25209
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
25386
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25210
25387
  * legacy rows written before design B. */
25211
25388
  faceBbox: BoundingBoxSchema.optional(),
25212
25389
  /** Design B: MediaStore key of the track's native-resolution key frame.
25213
25390
  * Fetch the native JPEG via the event-media data-plane
25214
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
25391
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
25215
25392
  * track produced no key frame (e.g. native/onboard source) — the UI falls
25216
25393
  * back to the inline `base64` face crop. */
25217
- keyFrameMediaKey: string().optional(),
25394
+ keyFrameMediaId: number().int().optional(),
25218
25395
  /** Winning identity-match cosine (0..1) for this face's track, when an
25219
25396
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
25220
25397
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -25283,11 +25460,14 @@ var FaceClusterSchema = object({
25283
25460
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25284
25461
  * dialog already rendering its key FRAME from the `event-media` plane.
25285
25462
  *
25286
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25287
- * media key directly, so this needed no new plane and no new access decision.
25463
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
25464
+ * resolves a media row by its own id, so this needed no new plane and no new
25465
+ * access decision.
25288
25466
  */
25289
25467
  var MediaFileLiteSchema$1 = object({
25290
- key: string(),
25468
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
25469
+ * (D482). It was the row's composite key, which embedded the owner. */
25470
+ mediaId: number().int(),
25291
25471
  kind: string(),
25292
25472
  url: string(),
25293
25473
  sizeBytes: number(),
@@ -25304,7 +25484,7 @@ method(object({
25304
25484
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
25305
25485
  * four times and the viewer holds at `staleTime: 30_000`.
25306
25486
  *
25307
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
25487
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
25308
25488
  * the `event-media` plane serves that key `immutable` with an ETag.
25309
25489
  *
25310
25490
  * **This is an INPUT field, so it does not reach the addon until the
@@ -27772,8 +27952,8 @@ var VehicleSchema = object({
27772
27952
  id: string(),
27773
27953
  name: string(),
27774
27954
  sampleCount: number().int().nonnegative(),
27775
- coverMediaKey: string().optional(),
27776
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
27955
+ coverMediaId: number().int().optional(),
27956
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
27777
27957
  coverBase64: string().optional()
27778
27958
  });
27779
27959
  var VehicleSampleInfoSchema = object({
@@ -27806,7 +27986,7 @@ var PlateInfoSchema = object({
27806
27986
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
27807
27987
  plateBbox: BoundingBoxSchema.optional(),
27808
27988
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
27809
- keyFrameMediaKey: string().optional(),
27989
+ keyFrameMediaId: number().int().optional(),
27810
27990
  base64: string().optional(),
27811
27991
  /**
27812
27992
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -27827,11 +28007,14 @@ var PlateInfoSchema = object({
27827
28007
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27828
28008
  * dialog already rendering its key FRAME from the `event-media` plane.
27829
28009
  *
27830
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27831
- * media key directly, so this needed no new plane and no new access decision.
28010
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
28011
+ * resolves a media row by its own id, so this needed no new plane and no new
28012
+ * access decision.
27832
28013
  */
27833
28014
  var MediaFileLiteSchema = object({
27834
- key: string(),
28015
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
28016
+ * (D482). It was the row's composite key, which embedded the owner. */
28017
+ mediaId: number().int(),
27835
28018
  kind: string(),
27836
28019
  url: string(),
27837
28020
  sizeBytes: number(),
@@ -27890,7 +28073,7 @@ method(object({
27890
28073
  }), method(object({
27891
28074
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
27892
28075
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
27893
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
28076
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
27894
28077
  * INPUT field: stripped by the hub router until the train ships, which
27895
28078
  * resolves to `false` and is exactly the intended default. */
27896
28079
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -29214,7 +29397,14 @@ var SceneReferenceSchema = object({
29214
29397
  modelId: string(),
29215
29398
  condition: SceneConditionSchema,
29216
29399
  capturedAt: number(),
29217
- thumbnailMediaId: string().optional(),
29400
+ /**
29401
+ * The reference thumbnail's media ROW id (D482).
29402
+ *
29403
+ * The name already said `mediaId` while the value was the row's composite
29404
+ * key — a leftover that read as done and was not. It is the row's own id
29405
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
29406
+ */
29407
+ thumbnailMediaId: number().int().optional(),
29218
29408
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
29219
29409
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
29220
29410
  * normalized rect frame a different piece of world, and the scene would
@@ -30028,7 +30218,29 @@ var LoggingSettingsPatchSchema = object({
30028
30218
  * disarmed the channels it did not mention would make the Levels page and
30029
30219
  * the Diagnostics page fight over the same value.
30030
30220
  */
30031
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
30221
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
30222
+ /**
30223
+ * How many days the analytics OPS LOG is kept — the audit trail of what
30224
+ * retention actually deleted.
30225
+ *
30226
+ * It lives HERE, on the logging document, and not on the analytics addon's
30227
+ * settings form, on the operator's instruction: every other log-ish window in
30228
+ * this system is configured through this one document, and a retention audit
30229
+ * configured somewhere else is a second place to look when the question is
30230
+ * "what happened to my data".
30231
+ *
30232
+ * That placement has a cost worth stating: this document ships in the
30233
+ * framework closure, so this knob needs a published `@camstack/server` while
30234
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
30235
+ *
30236
+ * `retention-model.ts` argued this one must NOT follow a device window,
30237
+ * because the audit trail is the history of the SWEEP and not of the camera.
30238
+ * That argument stands and is untouched — what changes is that 30 days stops
30239
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
30240
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
30241
+ * state in which the next 9.5 GiB leak cannot be explained.
30242
+ */
30243
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
30032
30244
  });
30033
30245
  /**
30034
30246
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -30101,6 +30313,14 @@ var LoggingSettingsStateSchema = object({
30101
30313
  channels: array(LogChannelDescriptorSchema).readonly(),
30102
30314
  /** The channels ARMED right now, each with its deadline. */
30103
30315
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
30316
+ /**
30317
+ * The ops-log window in days, as it is in force. `0` = kept forever.
30318
+ *
30319
+ * Reported rather than left to the caller's memory of what it wrote: this is
30320
+ * the value the analytics sweep will actually use, and an operator asking how
30321
+ * long their audit trail lives should not have to infer it from a default.
30322
+ */
30323
+ opsLogRetentionDays: number().int().min(0),
30104
30324
  persisted: boolean()
30105
30325
  });
30106
30326
  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(), {
@@ -30662,7 +30882,7 @@ var StationaryObjectSchema = object({
30662
30882
  /** Enrichment label carried from the source track (identity / plate). */
30663
30883
  label: string().optional(),
30664
30884
  /** Native-resolution key-frame media key for the parked object's best image. */
30665
- keyFrameMediaKey: string().optional()
30885
+ keyFrameMediaId: number().int().optional()
30666
30886
  });
30667
30887
  var CameraOccupancySnapshotSchema = object({
30668
30888
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -31818,6 +32038,12 @@ Object.freeze({
31818
32038
  addonId: null,
31819
32039
  access: "create"
31820
32040
  },
32041
+ "dataStoreProvider.upsert": {
32042
+ capName: "data-store-provider",
32043
+ capScope: "system",
32044
+ addonId: null,
32045
+ access: "create"
32046
+ },
31821
32047
  "dayNight.getOptions": {
31822
32048
  capName: "day-night",
31823
32049
  capScope: "device",
@@ -34254,6 +34480,12 @@ Object.freeze({
34254
34480
  addonId: null,
34255
34481
  access: "view"
34256
34482
  },
34483
+ "pipelineAnalytics.listRecentSummaries": {
34484
+ capName: "pipeline-analytics",
34485
+ capScope: "device",
34486
+ addonId: null,
34487
+ access: "view"
34488
+ },
34257
34489
  "pipelineAnalytics.listRecentTracks": {
34258
34490
  capName: "pipeline-analytics",
34259
34491
  capScope: "device",
@@ -35682,6 +35914,12 @@ Object.freeze({
35682
35914
  addonId: null,
35683
35915
  access: "create"
35684
35916
  },
35917
+ "settingsStore.upsert": {
35918
+ capName: "settings-store",
35919
+ capScope: "system",
35920
+ addonId: null,
35921
+ access: "create"
35922
+ },
35685
35923
  "smtpProvider.getStatus": {
35686
35924
  capName: "smtp-provider",
35687
35925
  capScope: "system",
@@ -38075,6 +38313,11 @@ Object.freeze({
38075
38313
  form: "single",
38076
38314
  optional: true
38077
38315
  }],
38316
+ "pipelineAnalytics.listRecentSummaries": [{
38317
+ name: "deviceIds",
38318
+ form: "array",
38319
+ optional: false
38320
+ }],
38078
38321
  "pipelineAnalytics.listRecentTracks": [{
38079
38322
  name: "deviceIds",
38080
38323
  form: "array",
@@ -1,5 +1,5 @@
1
1
  import * as crypto$1 from "node:crypto";
2
- //#region ../types/dist/event-category-DAXzJeTX.mjs
2
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -560,7 +560,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
560
560
  * a package zone — a newly-appeared stationary object of a package class
561
561
  * that cleared the class / zone / dwell / size gates. Payload:
562
562
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
563
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
563
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
564
564
  * Telemetry (D8): the durable record is the `package-events` store row;
565
565
  * this bus topic drives notifier rules + live UI. See
566
566
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5346,7 +5346,7 @@ var ZodIssueCode = {
5346
5346
  var ZodFirstPartyTypeKind;
5347
5347
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5348
5348
  //#endregion
5349
- //#region ../types/dist/sleep-BnujYGPe.mjs
5349
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5350
5350
  /**
5351
5351
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5352
5352
  * window to float samples (D455).
@@ -7309,13 +7309,30 @@ new Set([
7309
7309
  "scene",
7310
7310
  "motion",
7311
7311
  "object",
7312
- "audio"
7312
+ "audio",
7313
+ "mosaic"
7313
7314
  ]);
7314
- new Set([
7315
+ /**
7316
+ * The owner types that are event tables, in the order the event-media resolver
7317
+ * should consider them. Exported so a consumer asking "is this key an event?"
7318
+ * does not re-spell the list and drift from it.
7319
+ */
7320
+ var EVENT_OWNER_TYPES = [
7315
7321
  "motion",
7316
7322
  "object",
7317
7323
  "audio"
7318
- ]);
7324
+ ];
7325
+ /**
7326
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7327
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7328
+ * `(table, id)`).
7329
+ *
7330
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7331
+ * table would otherwise be accepted by the codec and refused at the door, with
7332
+ * nothing failing until a caller asked.
7333
+ */
7334
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7335
+ new Set(EVENT_OWNER_TYPES);
7319
7336
  /**
7320
7337
  * Build an `IAddonRouteProvider` from a list of routes. Implements
7321
7338
  * both the operator-facing `getRoutes` (returning route descriptors
@@ -7546,7 +7563,32 @@ var BASE_LIVE_EGRESS_PROFILE = {
7546
7563
  */
7547
7564
  var ALEXA_EGRESS_PROFILE = {
7548
7565
  ...BASE_LIVE_EGRESS_PROFILE,
7549
- audio: { codec: "opus" }
7566
+ audio: { codec: "opus" },
7567
+ /**
7568
+ * ONE slice per access unit.
7569
+ *
7570
+ * `-tune zerolatency` turns on x264's sliced threads, and every access unit
7571
+ * came out of this encode as FIVE slices — measured on the hub 2026-09-13 on
7572
+ * the exact argv this path spawns, and reduced to one by this single flag.
7573
+ *
7574
+ * Five slices share one RTP timestamp and carry one marker bit, so a
7575
+ * libwebrtc depacketiser sees five frame-starts and one frame-end per frame.
7576
+ * The Echo renders the first pictures and then freezes for good, while the
7577
+ * audio — a separate plane — plays on. It is the only structural property
7578
+ * that survived the elimination: the same freeze reproduced at 2304×1296
7579
+ * HEVC with 217 KB keyframes AND at 640×360 H.264 with 67 KB keyframes and a
7580
+ * fifth of the bitrate, so source, resolution, bitrate, keyframe burst,
7581
+ * hardware decode, parameter sets, drift and bitstream conformance are all
7582
+ * out — each ruled out by a measurement, not by elimination.
7583
+ *
7584
+ * The tune STAYS: it also drops the lookahead and the B-frames, which a live
7585
+ * path wants. Only the slicing is unwanted, and this is the flag that says so.
7586
+ *
7587
+ * Scoped to Alexa deliberately. HomeKit and the browser transcode inherit
7588
+ * {@link BASE_LIVE_EGRESS_PROFILE}; whether they want the same shape is a
7589
+ * question for their own evidence, and the browser has not been measured.
7590
+ */
7591
+ outputArgs: ["-x264-params", "sliced-threads=0"]
7550
7592
  };
7551
7593
  /**
7552
7594
  * Deep wiring healthcheck — snapshot of active reachability probes across
@@ -12657,6 +12699,16 @@ method(object({
12657
12699
  filter: QueryFilterSchema.optional(),
12658
12700
  columns: array(string()).readonly().optional()
12659
12701
  }), array(SettingsRecordSchema).readonly()), method(object({
12702
+ namespace: string().optional(),
12703
+ collection: string(),
12704
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
12705
+ * index on the collection. Never empty. */
12706
+ conflict: array(string()).readonly(),
12707
+ record: BulkRecordSchema
12708
+ }), object({
12709
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
12710
+ * row's own id on an update. */
12711
+ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
12660
12712
  namespace: string().optional(),
12661
12713
  collection: string(),
12662
12714
  record: object({
@@ -12791,6 +12843,14 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
12791
12843
  id: union([string(), number()]) }), {
12792
12844
  kind: "mutation",
12793
12845
  auth: "admin"
12846
+ }), method(object({
12847
+ namespace: string().optional(),
12848
+ collection: string(),
12849
+ conflict: array(string()).readonly(),
12850
+ record: BulkRecordSchema
12851
+ }), object({ id: union([string(), number()]) }), {
12852
+ kind: "mutation",
12853
+ auth: "admin"
12794
12854
  }), method(object({
12795
12855
  namespace: string().optional(),
12796
12856
  collection: string(),
@@ -19048,8 +19108,8 @@ var TrackPositionSchema = object({
19048
19108
  var TrackSnapshotSchema = object({
19049
19109
  timestamp: number(),
19050
19110
  position: TrackPositionSchema,
19051
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
19052
- mediaKey: string()
19111
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
19112
+ mediaId: number().int()
19053
19113
  });
19054
19114
  /**
19055
19115
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -19653,14 +19713,14 @@ var ObjectEventSchema = object({
19653
19713
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
19654
19714
  frameWidth: number().optional(),
19655
19715
  frameHeight: number().optional(),
19656
- /** MediaStore key for the crop attached to this event (if any). */
19657
- mediaKey: string().optional(),
19658
- /** Design B: MediaStore key of the track's native-resolution key frame (the
19716
+ /** Media row id of the crop attached to this event (if any) — D482. */
19717
+ mediaId: number().int().optional(),
19718
+ /** Design B: media row id of the track's native-resolution key frame (the
19659
19719
  * best-detection full frame). Resolve via the event-media data-plane
19660
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
19720
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
19661
19721
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
19662
- * sources — consumers fall back to `mediaKey` (the tight crop). */
19663
- keyFrameMediaKey: string().optional(),
19722
+ * sources — consumers fall back to `mediaId` (the tight crop). */
19723
+ keyFrameMediaId: number().int().optional(),
19664
19724
  /** Populated by B5 (recording playback URL for this event). */
19665
19725
  mediaUrl: string().optional(),
19666
19726
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -19710,7 +19770,7 @@ var MediaFileKindEnum = _enum([
19710
19770
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
19711
19771
  *
19712
19772
  * `url` points at the `event-media` data plane
19713
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19773
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
19714
19774
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19715
19775
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19716
19776
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -19718,10 +19778,13 @@ var MediaFileKindEnum = _enum([
19718
19778
  * `data-plane-access.ts` for the rule and the one gap it does not close
19719
19779
  * (per-device scoping).
19720
19780
  *
19721
- * The URL is built from the row's **stored** key, which is not always its
19722
- * published `kind`: a track's face/plate crop is stored as `crop` under
19781
+ * The URL is built from the row's own id, which since D482 says nothing about
19782
+ * the row's owner or kind — and that is the point. The published `kind` is not
19783
+ * the stored one (a track's face/plate crop is stored as `crop` under
19723
19784
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
19724
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19785
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
19786
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
19787
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
19725
19788
  *
19726
19789
  * ## `base64` is TRANSITIONAL and is going away
19727
19790
  *
@@ -19734,7 +19797,19 @@ var MediaFileKindEnum = _enum([
19734
19797
  * `analytics-query-facade.ts`; nothing else reads it.
19735
19798
  */
19736
19799
  var MediaFileSchema = object({
19737
- key: string(),
19800
+ /**
19801
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
19802
+ *
19803
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
19804
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
19805
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
19806
+ * different rows that spell the same number, and a track whose
19807
+ * `bestEventId` was an object event was served a motion event's snapshot —
19808
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
19809
+ * surrogate id cannot name the wrong row's owner, because it names no owner
19810
+ * at all.
19811
+ */
19812
+ mediaId: number().int(),
19738
19813
  kind: MediaFileKindEnum,
19739
19814
  sizeBytes: number(),
19740
19815
  timestamp: number()
@@ -19842,7 +19917,7 @@ var RetrainTrackSchema = object({
19842
19917
  });
19843
19918
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
19844
19919
  var RetrainFrameCandidateSchema = object({
19845
- mediaKey: string(),
19920
+ mediaId: number().int(),
19846
19921
  kind: MediaFileKindEnum,
19847
19922
  timestamp: number(),
19848
19923
  sizeBytes: number().int(),
@@ -19856,7 +19931,7 @@ var RetrainFrameSchema = object({
19856
19931
  deviceId: number(),
19857
19932
  trackId: string(),
19858
19933
  /** Provenance only. It may already point at nothing — that is expected. */
19859
- sourceMediaKey: string(),
19934
+ sourceMediaId: number().int(),
19860
19935
  sourceKind: MediaFileKindEnum,
19861
19936
  sizeBytes: number().int(),
19862
19937
  width: number().int(),
@@ -19872,7 +19947,7 @@ var RetrainCopyRefusalSchema = _enum([
19872
19947
  var RetrainFrameSelectionSchema = object({
19873
19948
  copied: array(RetrainFrameSchema).readonly(),
19874
19949
  refused: array(object({
19875
- sourceMediaKey: string(),
19950
+ sourceMediaId: number().int(),
19876
19951
  reason: RetrainCopyRefusalSchema
19877
19952
  })).readonly()
19878
19953
  });
@@ -19880,7 +19955,7 @@ var RetrainFrameListSchema = object({
19880
19955
  candidates: array(RetrainFrameCandidateSchema).readonly(),
19881
19956
  copies: array(RetrainFrameSchema).readonly(),
19882
19957
  /** What the page pre-selects — the native key frame when one survives. */
19883
- autoPickMediaKey: string().optional()
19958
+ autoPickMediaId: number().int().optional()
19884
19959
  });
19885
19960
  /** What the operator asked the assist to look for. */
19886
19961
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -19975,9 +20050,11 @@ var RecentTracksQueryInput = object({
19975
20050
  });
19976
20051
  /**
19977
20052
  * A Summary (D359): the per-camera session envelope that references tracks.
19978
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
19979
- * served by the event-media plane by key), null until the first frame with a
19980
- * member visible has been materialised.
20053
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
20054
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
20055
+ * first frame with a member visible has been materialised (D482 — it was the
20056
+ * row's composite key, which embedded the owner and could name another table's
20057
+ * row once event ids became per-table rowids).
19981
20058
  */
19982
20059
  var SummarySchema = object({
19983
20060
  id: string(),
@@ -19986,19 +20063,115 @@ var SummarySchema = object({
19986
20063
  lastActiveAt: number(),
19987
20064
  sealedAt: number().nullable(),
19988
20065
  memberCount: number().int().nonnegative(),
19989
- groupShotKey: string().nullable()
20066
+ groupShotMediaId: number().int().nullable()
19990
20067
  });
19991
20068
  var SummariesQueryInput = object({
19992
20069
  deviceIds: array(number()).min(1),
19993
20070
  since: number().optional(),
19994
20071
  until: number().optional(),
19995
- limit: number().int().min(1).max(200).default(50)
20072
+ limit: number().int().min(1).max(200).default(50),
20073
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
20074
+ * meaning of "dog" on every summary surface. */
20075
+ classes: array(string().min(1).max(60)).max(40).optional()
19996
20076
  });
19997
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
19998
20077
  var SummaryDetailSchema = object({
19999
20078
  summary: SummarySchema,
20000
- /** Member track ids, in absorption order. */
20001
- trackIds: array(string()).readonly()
20079
+ /** Members in absorption order, oldest join first. */
20080
+ members: array(object({
20081
+ trackId: string(),
20082
+ /**
20083
+ * The track's class at the time it JOINED, or `null` when it was unresolved
20084
+ * and the track no longer exists to fill it.
20085
+ *
20086
+ * The join, not the latest opinion: class voting can re-decide a track's
20087
+ * class afterwards, and a group's composition is the history of who came
20088
+ * together. A consumer counts these to badge a thumbnail — "two people and a
20089
+ * dog" — rather than being handed a second, denormalised tally that would
20090
+ * drift from the members it claims to describe.
20091
+ */
20092
+ className: string().nullable(),
20093
+ /** `primary` is the one member that carries the group's identity. */
20094
+ role: _enum([
20095
+ "primary",
20096
+ "sibling",
20097
+ "child"
20098
+ ]).nullable(),
20099
+ joinedAt: number(),
20100
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
20101
+ * at read time. Absent when nothing resolved, never an empty string. */
20102
+ label: string().optional(),
20103
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
20104
+ * recognised nobody, and absent for a member whose track has been pruned —
20105
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
20106
+ subLabel: string().optional()
20107
+ })).readonly()
20108
+ });
20109
+ /**
20110
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
20111
+ *
20112
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
20113
+ * we extend when we notice we need more". Enough to draw the card ("two people
20114
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
20115
+ * are on {@link getSummary}: shipping every member of every group so a client
20116
+ * can count them turns a page of two hundred groups into a thousand records to
20117
+ * draw two hundred badges.
20118
+ */
20119
+ var SummaryListItemSchema = object({
20120
+ summary: SummarySchema,
20121
+ /** Class counts, most numerous first; `className: null` is a member whose
20122
+ * class never resolved, counted so the counts add up to `memberCount`. */
20123
+ classes: array(object({
20124
+ className: string().nullable(),
20125
+ count: number().int().positive()
20126
+ })).readonly()
20127
+ });
20128
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
20129
+ /**
20130
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
20131
+ *
20132
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
20133
+ * summaries feed that could only answer "the last N for these cameras"
20134
+ * (`listSummaries`) would make groups available in the timeline and nowhere
20135
+ * else. Two feeds with different ordering contracts would be two ideas of what
20136
+ * "newest" means, on one screen.
20137
+ */
20138
+ var RecentSummariesQueryInput = object({
20139
+ /** Devices to merge. An empty array yields an empty page. */
20140
+ deviceIds: array(number()),
20141
+ /** Window lower bound on `lastActiveAt` (inclusive). */
20142
+ since: number().optional(),
20143
+ /** Window upper bound on `lastActiveAt` (inclusive). */
20144
+ until: number().optional(),
20145
+ limit: number().int().min(1).max(500).default(100),
20146
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
20147
+ cursor: string().optional(),
20148
+ /**
20149
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
20150
+ * deliberately the same name: the events filter tree emits one list of ticked
20151
+ * taxonomy nodes, and a summaries feed that called it something else would
20152
+ * make the operator's one filter component produce two shapes.
20153
+ *
20154
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
20155
+ * me the arrivals with a dog" has to return the group of four people who had
20156
+ * one with them; a filter on the group's own fields could only ever answer
20157
+ * about its primary.
20158
+ *
20159
+ * Matched against all THREE tiers of a member — the class (`animal`), the
20160
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
20161
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
20162
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
20163
+ * that read only the class would answer "animal" and never "dog".
20164
+ *
20165
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
20166
+ */
20167
+ classes: array(string().min(1).max(60)).max(40).optional()
20168
+ });
20169
+ var RecentSummariesPageSchema = object({
20170
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
20171
+ * {@link listSummaries} — one list shape, so a card renders the same
20172
+ * whichever feed drew it. */
20173
+ summaries: array(SummaryListItemSchema).readonly(),
20174
+ nextCursor: string().nullable()
20002
20175
  });
20003
20176
  var RecentTracksPageSchema = object({
20004
20177
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -20412,7 +20585,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20412
20585
  classes: array(string()).optional(),
20413
20586
  /** See {@link ExcludeSourcesDoc}. */
20414
20587
  excludeSources: array(TrackSourceSchema).optional()
20415
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20588
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(SummariesQueryInput, SummariesPageSchema), method(RecentSummariesQueryInput, RecentSummariesPageSchema), method(object({ id: string() }), SummaryDetailSchema.nullable()), method(object({ deviceId: number() }), _void(), {
20416
20589
  kind: "mutation",
20417
20590
  auth: "admin"
20418
20591
  }), 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({
@@ -20540,7 +20713,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20540
20713
  }), method(object({
20541
20714
  deviceId: number(),
20542
20715
  trackId: string(),
20543
- mediaKeys: array(string()).min(1)
20716
+ mediaIds: array(number().int()).min(1)
20544
20717
  }), RetrainFrameSelectionSchema, {
20545
20718
  kind: "mutation",
20546
20719
  auth: "admin"
@@ -20608,7 +20781,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20608
20781
  kind: "query",
20609
20782
  auth: "admin"
20610
20783
  }), method(object({
20611
- eventId: string(),
20784
+ /** Which of the three event tables owns the row. */
20785
+ ownerType: EventOwnerTypeSchema,
20786
+ /** The event's rowid in that table (D474: an event id is a number). */
20787
+ eventId: number().int(),
20612
20788
  kind: MediaFileKindEnum.optional(),
20613
20789
  deviceId: number()
20614
20790
  }), array(MediaFileSchema).readonly()), method(object({
@@ -20619,7 +20795,8 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20619
20795
  trackId: string(),
20620
20796
  deviceId: number()
20621
20797
  }), array(MediaFileInfoSchema).readonly()), method(object({
20622
- eventId: string(),
20798
+ ownerType: EventOwnerTypeSchema,
20799
+ eventId: number().int(),
20623
20800
  deviceId: number()
20624
20801
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20625
20802
  kind: "mutation",
@@ -25082,8 +25259,8 @@ var IdentitySchema = object({
25082
25259
  id: string(),
25083
25260
  name: string(),
25084
25261
  sampleCount: number().int().nonnegative(),
25085
- coverMediaKey: string().optional(),
25086
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
25262
+ coverMediaId: number().int().optional(),
25263
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
25087
25264
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
25088
25265
  coverBase64: string().optional()
25089
25266
  });
@@ -25109,8 +25286,8 @@ var IdentitySampleAuditRowSchema = object({
25109
25286
  enrolledAt: number().int(),
25110
25287
  deviceId: number().int().optional(),
25111
25288
  source: _enum(["detected", "photo"]),
25112
- /** The enrolled crop's media key, so the panel can show the evidence. */
25113
- mediaKey: string().optional(),
25289
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
25290
+ mediaId: number().int().optional(),
25114
25291
  /**
25115
25292
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
25116
25293
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -25179,15 +25356,15 @@ var FaceInfoSchema = object({
25179
25356
  */
25180
25357
  cropUrl: string().optional(),
25181
25358
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
25182
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
25359
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
25183
25360
  * legacy rows written before design B. */
25184
25361
  faceBbox: BoundingBoxSchema.optional(),
25185
25362
  /** Design B: MediaStore key of the track's native-resolution key frame.
25186
25363
  * Fetch the native JPEG via the event-media data-plane
25187
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
25364
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
25188
25365
  * track produced no key frame (e.g. native/onboard source) — the UI falls
25189
25366
  * back to the inline `base64` face crop. */
25190
- keyFrameMediaKey: string().optional(),
25367
+ keyFrameMediaId: number().int().optional(),
25191
25368
  /** Winning identity-match cosine (0..1) for this face's track, when an
25192
25369
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
25193
25370
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -25256,11 +25433,14 @@ var FaceClusterSchema = object({
25256
25433
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25257
25434
  * dialog already rendering its key FRAME from the `event-media` plane.
25258
25435
  *
25259
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25260
- * media key directly, so this needed no new plane and no new access decision.
25436
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
25437
+ * resolves a media row by its own id, so this needed no new plane and no new
25438
+ * access decision.
25261
25439
  */
25262
25440
  var MediaFileLiteSchema$1 = object({
25263
- key: string(),
25441
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
25442
+ * (D482). It was the row's composite key, which embedded the owner. */
25443
+ mediaId: number().int(),
25264
25444
  kind: string(),
25265
25445
  url: string(),
25266
25446
  sizeBytes: number(),
@@ -25277,7 +25457,7 @@ method(object({
25277
25457
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
25278
25458
  * four times and the viewer holds at `staleTime: 30_000`.
25279
25459
  *
25280
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
25460
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
25281
25461
  * the `event-media` plane serves that key `immutable` with an ETag.
25282
25462
  *
25283
25463
  * **This is an INPUT field, so it does not reach the addon until the
@@ -27745,8 +27925,8 @@ var VehicleSchema = object({
27745
27925
  id: string(),
27746
27926
  name: string(),
27747
27927
  sampleCount: number().int().nonnegative(),
27748
- coverMediaKey: string().optional(),
27749
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
27928
+ coverMediaId: number().int().optional(),
27929
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
27750
27930
  coverBase64: string().optional()
27751
27931
  });
27752
27932
  var VehicleSampleInfoSchema = object({
@@ -27779,7 +27959,7 @@ var PlateInfoSchema = object({
27779
27959
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
27780
27960
  plateBbox: BoundingBoxSchema.optional(),
27781
27961
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
27782
- keyFrameMediaKey: string().optional(),
27962
+ keyFrameMediaId: number().int().optional(),
27783
27963
  base64: string().optional(),
27784
27964
  /**
27785
27965
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -27800,11 +27980,14 @@ var PlateInfoSchema = object({
27800
27980
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27801
27981
  * dialog already rendering its key FRAME from the `event-media` plane.
27802
27982
  *
27803
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27804
- * media key directly, so this needed no new plane and no new access decision.
27983
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
27984
+ * resolves a media row by its own id, so this needed no new plane and no new
27985
+ * access decision.
27805
27986
  */
27806
27987
  var MediaFileLiteSchema = object({
27807
- key: string(),
27988
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
27989
+ * (D482). It was the row's composite key, which embedded the owner. */
27990
+ mediaId: number().int(),
27808
27991
  kind: string(),
27809
27992
  url: string(),
27810
27993
  sizeBytes: number(),
@@ -27863,7 +28046,7 @@ method(object({
27863
28046
  }), method(object({
27864
28047
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
27865
28048
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
27866
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
28049
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
27867
28050
  * INPUT field: stripped by the hub router until the train ships, which
27868
28051
  * resolves to `false` and is exactly the intended default. */
27869
28052
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
@@ -29187,7 +29370,14 @@ var SceneReferenceSchema = object({
29187
29370
  modelId: string(),
29188
29371
  condition: SceneConditionSchema,
29189
29372
  capturedAt: number(),
29190
- thumbnailMediaId: string().optional(),
29373
+ /**
29374
+ * The reference thumbnail's media ROW id (D482).
29375
+ *
29376
+ * The name already said `mediaId` while the value was the row's composite
29377
+ * key — a leftover that read as done and was not. It is the row's own id
29378
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
29379
+ */
29380
+ thumbnailMediaId: number().int().optional(),
29191
29381
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
29192
29382
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
29193
29383
  * normalized rect frame a different piece of world, and the scene would
@@ -30001,7 +30191,29 @@ var LoggingSettingsPatchSchema = object({
30001
30191
  * disarmed the channels it did not mention would make the Levels page and
30002
30192
  * the Diagnostics page fight over the same value.
30003
30193
  */
30004
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
30194
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
30195
+ /**
30196
+ * How many days the analytics OPS LOG is kept — the audit trail of what
30197
+ * retention actually deleted.
30198
+ *
30199
+ * It lives HERE, on the logging document, and not on the analytics addon's
30200
+ * settings form, on the operator's instruction: every other log-ish window in
30201
+ * this system is configured through this one document, and a retention audit
30202
+ * configured somewhere else is a second place to look when the question is
30203
+ * "what happened to my data".
30204
+ *
30205
+ * That placement has a cost worth stating: this document ships in the
30206
+ * framework closure, so this knob needs a published `@camstack/server` while
30207
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
30208
+ *
30209
+ * `retention-model.ts` argued this one must NOT follow a device window,
30210
+ * because the audit trail is the history of the SWEEP and not of the camera.
30211
+ * That argument stands and is untouched — what changes is that 30 days stops
30212
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
30213
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
30214
+ * state in which the next 9.5 GiB leak cannot be explained.
30215
+ */
30216
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
30005
30217
  });
30006
30218
  /**
30007
30219
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -30074,6 +30286,14 @@ var LoggingSettingsStateSchema = object({
30074
30286
  channels: array(LogChannelDescriptorSchema).readonly(),
30075
30287
  /** The channels ARMED right now, each with its deadline. */
30076
30288
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
30289
+ /**
30290
+ * The ops-log window in days, as it is in force. `0` = kept forever.
30291
+ *
30292
+ * Reported rather than left to the caller's memory of what it wrote: this is
30293
+ * the value the analytics sweep will actually use, and an operator asking how
30294
+ * long their audit trail lives should not have to infer it from a default.
30295
+ */
30296
+ opsLogRetentionDays: number().int().min(0),
30077
30297
  persisted: boolean()
30078
30298
  });
30079
30299
  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(), {
@@ -30635,7 +30855,7 @@ var StationaryObjectSchema = object({
30635
30855
  /** Enrichment label carried from the source track (identity / plate). */
30636
30856
  label: string().optional(),
30637
30857
  /** Native-resolution key-frame media key for the parked object's best image. */
30638
- keyFrameMediaKey: string().optional()
30858
+ keyFrameMediaId: number().int().optional()
30639
30859
  });
30640
30860
  var CameraOccupancySnapshotSchema = object({
30641
30861
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -31791,6 +32011,12 @@ Object.freeze({
31791
32011
  addonId: null,
31792
32012
  access: "create"
31793
32013
  },
32014
+ "dataStoreProvider.upsert": {
32015
+ capName: "data-store-provider",
32016
+ capScope: "system",
32017
+ addonId: null,
32018
+ access: "create"
32019
+ },
31794
32020
  "dayNight.getOptions": {
31795
32021
  capName: "day-night",
31796
32022
  capScope: "device",
@@ -34227,6 +34453,12 @@ Object.freeze({
34227
34453
  addonId: null,
34228
34454
  access: "view"
34229
34455
  },
34456
+ "pipelineAnalytics.listRecentSummaries": {
34457
+ capName: "pipeline-analytics",
34458
+ capScope: "device",
34459
+ addonId: null,
34460
+ access: "view"
34461
+ },
34230
34462
  "pipelineAnalytics.listRecentTracks": {
34231
34463
  capName: "pipeline-analytics",
34232
34464
  capScope: "device",
@@ -35655,6 +35887,12 @@ Object.freeze({
35655
35887
  addonId: null,
35656
35888
  access: "create"
35657
35889
  },
35890
+ "settingsStore.upsert": {
35891
+ capName: "settings-store",
35892
+ capScope: "system",
35893
+ addonId: null,
35894
+ access: "create"
35895
+ },
35658
35896
  "smtpProvider.getStatus": {
35659
35897
  capName: "smtp-provider",
35660
35898
  capScope: "system",
@@ -38048,6 +38286,11 @@ Object.freeze({
38048
38286
  form: "single",
38049
38287
  optional: true
38050
38288
  }],
38289
+ "pipelineAnalytics.listRecentSummaries": [{
38290
+ name: "deviceIds",
38291
+ form: "array",
38292
+ optional: false
38293
+ }],
38051
38294
  "pipelineAnalytics.listRecentTracks": [{
38052
38295
  name: "deviceIds",
38053
38296
  form: "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-export-alexa",
3
- "version": "1.2.105",
3
+ "version": "1.2.107",
4
4
  "description": "Alexa Smart Home Skill export — hub-side OAuth provider + directive handler. The companion AWS Lambda forwards Alexa directives to this addon's /addon/export-alexa/directive endpoint.",
5
5
  "keywords": [
6
6
  "camstack",