@camstack/addon-osd-manager 0.1.96 → 0.1.98

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 (18) hide show
  1. package/dist/{MotionZonesSettings-DiPEp4sf.mjs → MotionZonesSettings-DpjulO5F.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-DtN4fpc6.mjs → PrivacyMaskSettings-DaxBEzfV.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-DYVrXZTY.mjs → SceneMonitorEditor-DoZLlL7g.mjs} +258 -255
  4. package/dist/_stub.js +301 -351
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-CMxHfgf-.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_osd_manager_page-BAwkeBnB.mjs} +4 -4
  6. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-0OJdCNRg.mjs +26 -0
  7. package/dist/{hostInit-dn3yZuiW.mjs → hostInit-wtg1CgtS.mjs} +3 -3
  8. package/dist/index.js +367 -59
  9. package/dist/index.mjs +367 -59
  10. package/dist/{player-overlays-CwrMj6Pr.mjs → player-overlays-CKl-z5dT.mjs} +2 -2
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-RmJIe_kG.mjs → responsive-Db-2LAhn.mjs} +3 -3
  13. package/dist/{square-CwZjiwHQ.mjs → square-D4bK2uSS.mjs} +1 -1
  14. package/dist/{trash-2-BTG6P2f3.mjs → trash-2-CkBUBr8-.mjs} +1 -1
  15. package/dist/{use-device-snapshot-Woc9fBcw.mjs → use-device-snapshot-VChilLVp.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-BmqbXL9l.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_osd_manager_page__remoteEntry_js-Cx68KkGj.mjs} +1 -1
  17. package/package.json +1 -1
  18. package/dist/_virtual_mf___mfe_internal__addon_osd_manager_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DZnyQitE.mjs +0 -26
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- //#region ../types/dist/event-category-DAXzJeTX.mjs
1
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
2
2
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
3
3
  EventCategory["SystemBoot"] = "system.boot";
4
4
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -559,7 +559,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
559
559
  * a package zone — a newly-appeared stationary object of a package class
560
560
  * that cleared the class / zone / dwell / size gates. Payload:
561
561
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
562
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
562
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
563
563
  * Telemetry (D8): the durable record is the `package-events` store row;
564
564
  * this bus topic drives notifier rules + live UI. See
565
565
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5345,7 +5345,7 @@ var ZodIssueCode = {
5345
5345
  var ZodFirstPartyTypeKind;
5346
5346
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5347
5347
  //#endregion
5348
- //#region ../types/dist/sleep-BnujYGPe.mjs
5348
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5349
5349
  /**
5350
5350
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5351
5351
  * window to float samples (D455).
@@ -7408,13 +7408,30 @@ new Set([
7408
7408
  "scene",
7409
7409
  "motion",
7410
7410
  "object",
7411
- "audio"
7411
+ "audio",
7412
+ "mosaic"
7412
7413
  ]);
7413
- new Set([
7414
+ /**
7415
+ * The owner types that are event tables, in the order the event-media resolver
7416
+ * should consider them. Exported so a consumer asking "is this key an event?"
7417
+ * does not re-spell the list and drift from it.
7418
+ */
7419
+ var EVENT_OWNER_TYPES = [
7414
7420
  "motion",
7415
7421
  "object",
7416
7422
  "audio"
7417
- ]);
7423
+ ];
7424
+ /**
7425
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7426
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7427
+ * `(table, id)`).
7428
+ *
7429
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7430
+ * table would otherwise be accepted by the codec and refused at the door, with
7431
+ * nothing failing until a caller asked.
7432
+ */
7433
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7434
+ new Set(EVENT_OWNER_TYPES);
7418
7435
  var EncodeProfileSchema = object({
7419
7436
  video: object({
7420
7437
  codec: _enum([
@@ -7453,7 +7470,23 @@ var EncodeProfileSchema = object({
7453
7470
  "zerolatency",
7454
7471
  "film",
7455
7472
  "animation"
7456
- ]).optional()
7473
+ ]).optional(),
7474
+ /**
7475
+ * ONE slice per access unit.
7476
+ *
7477
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7478
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7479
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7480
+ * frame-end per frame: the first pictures render and the video then freezes
7481
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7482
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7483
+ *
7484
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7485
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7486
+ * consumers meaning the same thing spelled differently would stop sharing one
7487
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7488
+ */
7489
+ singleSlicePerFrame: boolean().optional()
7457
7490
  }),
7458
7491
  audio: union([literal("passthrough"), object({
7459
7492
  codec: _enum([
@@ -7503,7 +7536,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7503
7536
  audio: "passthrough"
7504
7537
  };
7505
7538
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7506
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7539
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7507
7540
  /**
7508
7541
  * Deep wiring healthcheck — snapshot of active reachability probes across
7509
7542
  * every declared capability + widget of every installed plugin, on every
@@ -13379,6 +13412,59 @@ var settingsStoreCapability = {
13379
13412
  filter: QueryFilterSchema.optional(),
13380
13413
  columns: array(string()).readonly().optional()
13381
13414
  }), array(SettingsRecordSchema).readonly()),
13415
+ /**
13416
+ * Make a SLOT hold this row — insert it, or update whatever already
13417
+ * occupies the slot — and answer with the id the row HAS.
13418
+ *
13419
+ * ## Why the store needed this
13420
+ *
13421
+ * {@link set} is already an upsert, but its slot is the PRIMARY KEY: the
13422
+ * caller has to know the id to address the row. That is exactly what a
13423
+ * collection keyed by an `INTEGER` rowid alias cannot do — SQLite assigns
13424
+ * the id, so a writer whose uniqueness rule is "one row per
13425
+ * `(ownerKind, ownerId, kind, slot)`" has no id to name and no way to
13426
+ * express the rule. {@link insert} can express it (a UNIQUE index
13427
+ * arbitrates) but only by THROWING at the loser of a race, and the loser
13428
+ * here is a legitimate re-capture, not an error.
13429
+ *
13430
+ * Getting there without this method means read-then-write: query the slot,
13431
+ * then `insert` or `update`. Two statements instead of one, a race between
13432
+ * them, and a recovery path that has to recognise a UNIQUE violation by its
13433
+ * MESSAGE — across a process boundary, because the store lives behind an
13434
+ * RPC door. A string match would be the whole gate, and a store that
13435
+ * rephrased its error would lose the row silently. One statement has no
13436
+ * such gate (D482).
13437
+ *
13438
+ * ## The contract
13439
+ *
13440
+ * `conflict` names the DECLARED columns that identify the slot. They must
13441
+ * be covered by a UNIQUE index on the collection — SQLite resolves an
13442
+ * `ON CONFLICT` target against its indexes and REFUSES the statement when
13443
+ * none matches, so a wrong list is a loud error on the first write, never a
13444
+ * silently duplicated row. An empty list is refused rather than passed on:
13445
+ * `ON CONFLICT()` is not valid SQL, and the intent it would most likely
13446
+ * encode ("no slot, just insert") is {@link insert}.
13447
+ *
13448
+ * Row shape is {@link insert}'s: `record.id` is used when the collection
13449
+ * needs a supplied key and IGNORED when the primary key is an `INTEGER`
13450
+ * rowid alias — exactly as `insert` and `insertMany` already treat it.
13451
+ *
13452
+ * Returns the id either way: the one SQLite assigned on an insert, or the
13453
+ * one the row it updated already had. A caller cannot tell which happened,
13454
+ * and deliberately — every caller wants the slot's id, and one that needed
13455
+ * to distinguish creation from replacement would have to read first anyway.
13456
+ */
13457
+ upsert: method(object({
13458
+ namespace: string().optional(),
13459
+ collection: string(),
13460
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
13461
+ * index on the collection. Never empty. */
13462
+ conflict: array(string()).readonly(),
13463
+ record: BulkRecordSchema
13464
+ }), object({
13465
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
13466
+ * row's own id on an update. */
13467
+ id: union([string(), number()]) }), { kind: "mutation" }),
13382
13468
  /** Insert a new record. */
13383
13469
  insert: method(object({
13384
13470
  namespace: string().optional(),
@@ -13668,6 +13754,20 @@ id: union([string(), number()]) }), {
13668
13754
  kind: "mutation",
13669
13755
  auth: "admin"
13670
13756
  }),
13757
+ /**
13758
+ * Make a slot hold this row, and answer with the id the row HAS. The
13759
+ * engine half of `settings-store.upsert` — read that docblock for why the
13760
+ * store needed a slot that is not the primary key (D482).
13761
+ */
13762
+ upsert: method(object({
13763
+ namespace: string().optional(),
13764
+ collection: string(),
13765
+ conflict: array(string()).readonly(),
13766
+ record: BulkRecordSchema
13767
+ }), object({ id: union([string(), number()]) }), {
13768
+ kind: "mutation",
13769
+ auth: "admin"
13770
+ }),
13671
13771
  /** Insert many records in ONE transaction. All or nothing. */
13672
13772
  insertMany: method(object({
13673
13773
  namespace: string().optional(),
@@ -21852,8 +21952,8 @@ var TrackPositionSchema = object({
21852
21952
  var TrackSnapshotSchema = object({
21853
21953
  timestamp: number(),
21854
21954
  position: TrackPositionSchema,
21855
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
21856
- mediaKey: string()
21955
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
21956
+ mediaId: number().int()
21857
21957
  });
21858
21958
  /**
21859
21959
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -22457,14 +22557,14 @@ var ObjectEventSchema = object({
22457
22557
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
22458
22558
  frameWidth: number().optional(),
22459
22559
  frameHeight: number().optional(),
22460
- /** MediaStore key for the crop attached to this event (if any). */
22461
- mediaKey: string().optional(),
22462
- /** Design B: MediaStore key of the track's native-resolution key frame (the
22560
+ /** Media row id of the crop attached to this event (if any) — D482. */
22561
+ mediaId: number().int().optional(),
22562
+ /** Design B: media row id of the track's native-resolution key frame (the
22463
22563
  * best-detection full frame). Resolve via the event-media data-plane
22464
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
22564
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
22465
22565
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
22466
- * sources — consumers fall back to `mediaKey` (the tight crop). */
22467
- keyFrameMediaKey: string().optional(),
22566
+ * sources — consumers fall back to `mediaId` (the tight crop). */
22567
+ keyFrameMediaId: number().int().optional(),
22468
22568
  /** Populated by B5 (recording playback URL for this event). */
22469
22569
  mediaUrl: string().optional(),
22470
22570
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -22514,7 +22614,7 @@ var MediaFileKindEnum = _enum([
22514
22614
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
22515
22615
  *
22516
22616
  * `url` points at the `event-media` data plane
22517
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
22617
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
22518
22618
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
22519
22619
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
22520
22620
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -22522,10 +22622,13 @@ var MediaFileKindEnum = _enum([
22522
22622
  * `data-plane-access.ts` for the rule and the one gap it does not close
22523
22623
  * (per-device scoping).
22524
22624
  *
22525
- * The URL is built from the row's **stored** key, which is not always its
22526
- * published `kind`: a track's face/plate crop is stored as `crop` under
22625
+ * The URL is built from the row's own id, which since D482 says nothing about
22626
+ * the row's owner or kind — and that is the point. The published `kind` is not
22627
+ * the stored one (a track's face/plate crop is stored as `crop` under
22527
22628
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
22528
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
22629
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
22630
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
22631
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
22529
22632
  *
22530
22633
  * ## `base64` is TRANSITIONAL and is going away
22531
22634
  *
@@ -22538,7 +22641,19 @@ var MediaFileKindEnum = _enum([
22538
22641
  * `analytics-query-facade.ts`; nothing else reads it.
22539
22642
  */
22540
22643
  var MediaFileSchema = object({
22541
- key: string(),
22644
+ /**
22645
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
22646
+ *
22647
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
22648
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
22649
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
22650
+ * different rows that spell the same number, and a track whose
22651
+ * `bestEventId` was an object event was served a motion event's snapshot —
22652
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
22653
+ * surrogate id cannot name the wrong row's owner, because it names no owner
22654
+ * at all.
22655
+ */
22656
+ mediaId: number().int(),
22542
22657
  kind: MediaFileKindEnum,
22543
22658
  sizeBytes: number(),
22544
22659
  timestamp: number()
@@ -22646,7 +22761,7 @@ var RetrainTrackSchema = object({
22646
22761
  });
22647
22762
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
22648
22763
  var RetrainFrameCandidateSchema = object({
22649
- mediaKey: string(),
22764
+ mediaId: number().int(),
22650
22765
  kind: MediaFileKindEnum,
22651
22766
  timestamp: number(),
22652
22767
  sizeBytes: number().int(),
@@ -22660,7 +22775,7 @@ var RetrainFrameSchema = object({
22660
22775
  deviceId: number(),
22661
22776
  trackId: string(),
22662
22777
  /** Provenance only. It may already point at nothing — that is expected. */
22663
- sourceMediaKey: string(),
22778
+ sourceMediaId: number().int(),
22664
22779
  sourceKind: MediaFileKindEnum,
22665
22780
  sizeBytes: number().int(),
22666
22781
  width: number().int(),
@@ -22676,7 +22791,7 @@ var RetrainCopyRefusalSchema = _enum([
22676
22791
  var RetrainFrameSelectionSchema = object({
22677
22792
  copied: array(RetrainFrameSchema).readonly(),
22678
22793
  refused: array(object({
22679
- sourceMediaKey: string(),
22794
+ sourceMediaId: number().int(),
22680
22795
  reason: RetrainCopyRefusalSchema
22681
22796
  })).readonly()
22682
22797
  });
@@ -22684,7 +22799,7 @@ var RetrainFrameListSchema = object({
22684
22799
  candidates: array(RetrainFrameCandidateSchema).readonly(),
22685
22800
  copies: array(RetrainFrameSchema).readonly(),
22686
22801
  /** What the page pre-selects — the native key frame when one survives. */
22687
- autoPickMediaKey: string().optional()
22802
+ autoPickMediaId: number().int().optional()
22688
22803
  });
22689
22804
  /** What the operator asked the assist to look for. */
22690
22805
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -22779,9 +22894,11 @@ var RecentTracksQueryInput = object({
22779
22894
  });
22780
22895
  /**
22781
22896
  * A Summary (D359): the per-camera session envelope that references tracks.
22782
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
22783
- * served by the event-media plane by key), null until the first frame with a
22784
- * member visible has been materialised.
22897
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
22898
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
22899
+ * first frame with a member visible has been materialised (D482 — it was the
22900
+ * row's composite key, which embedded the owner and could name another table's
22901
+ * row once event ids became per-table rowids).
22785
22902
  */
22786
22903
  var SummarySchema = object({
22787
22904
  id: string(),
@@ -22790,19 +22907,115 @@ var SummarySchema = object({
22790
22907
  lastActiveAt: number(),
22791
22908
  sealedAt: number().nullable(),
22792
22909
  memberCount: number().int().nonnegative(),
22793
- groupShotKey: string().nullable()
22910
+ groupShotMediaId: number().int().nullable()
22794
22911
  });
22795
22912
  var SummariesQueryInput = object({
22796
22913
  deviceIds: array(number()).min(1),
22797
22914
  since: number().optional(),
22798
22915
  until: number().optional(),
22799
- limit: number().int().min(1).max(200).default(50)
22916
+ limit: number().int().min(1).max(200).default(50),
22917
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
22918
+ * meaning of "dog" on every summary surface. */
22919
+ classes: array(string().min(1).max(60)).max(40).optional()
22800
22920
  });
22801
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
22802
22921
  var SummaryDetailSchema = object({
22803
22922
  summary: SummarySchema,
22804
- /** Member track ids, in absorption order. */
22805
- trackIds: array(string()).readonly()
22923
+ /** Members in absorption order, oldest join first. */
22924
+ members: array(object({
22925
+ trackId: string(),
22926
+ /**
22927
+ * The track's class at the time it JOINED, or `null` when it was unresolved
22928
+ * and the track no longer exists to fill it.
22929
+ *
22930
+ * The join, not the latest opinion: class voting can re-decide a track's
22931
+ * class afterwards, and a group's composition is the history of who came
22932
+ * together. A consumer counts these to badge a thumbnail — "two people and a
22933
+ * dog" — rather than being handed a second, denormalised tally that would
22934
+ * drift from the members it claims to describe.
22935
+ */
22936
+ className: string().nullable(),
22937
+ /** `primary` is the one member that carries the group's identity. */
22938
+ role: _enum([
22939
+ "primary",
22940
+ "sibling",
22941
+ "child"
22942
+ ]).nullable(),
22943
+ joinedAt: number(),
22944
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
22945
+ * at read time. Absent when nothing resolved, never an empty string. */
22946
+ label: string().optional(),
22947
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
22948
+ * recognised nobody, and absent for a member whose track has been pruned —
22949
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
22950
+ subLabel: string().optional()
22951
+ })).readonly()
22952
+ });
22953
+ /**
22954
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
22955
+ *
22956
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
22957
+ * we extend when we notice we need more". Enough to draw the card ("two people
22958
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
22959
+ * are on {@link getSummary}: shipping every member of every group so a client
22960
+ * can count them turns a page of two hundred groups into a thousand records to
22961
+ * draw two hundred badges.
22962
+ */
22963
+ var SummaryListItemSchema = object({
22964
+ summary: SummarySchema,
22965
+ /** Class counts, most numerous first; `className: null` is a member whose
22966
+ * class never resolved, counted so the counts add up to `memberCount`. */
22967
+ classes: array(object({
22968
+ className: string().nullable(),
22969
+ count: number().int().positive()
22970
+ })).readonly()
22971
+ });
22972
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
22973
+ /**
22974
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
22975
+ *
22976
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
22977
+ * summaries feed that could only answer "the last N for these cameras"
22978
+ * (`listSummaries`) would make groups available in the timeline and nowhere
22979
+ * else. Two feeds with different ordering contracts would be two ideas of what
22980
+ * "newest" means, on one screen.
22981
+ */
22982
+ var RecentSummariesQueryInput = object({
22983
+ /** Devices to merge. An empty array yields an empty page. */
22984
+ deviceIds: array(number()),
22985
+ /** Window lower bound on `lastActiveAt` (inclusive). */
22986
+ since: number().optional(),
22987
+ /** Window upper bound on `lastActiveAt` (inclusive). */
22988
+ until: number().optional(),
22989
+ limit: number().int().min(1).max(500).default(100),
22990
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
22991
+ cursor: string().optional(),
22992
+ /**
22993
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
22994
+ * deliberately the same name: the events filter tree emits one list of ticked
22995
+ * taxonomy nodes, and a summaries feed that called it something else would
22996
+ * make the operator's one filter component produce two shapes.
22997
+ *
22998
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
22999
+ * me the arrivals with a dog" has to return the group of four people who had
23000
+ * one with them; a filter on the group's own fields could only ever answer
23001
+ * about its primary.
23002
+ *
23003
+ * Matched against all THREE tiers of a member — the class (`animal`), the
23004
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
23005
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
23006
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
23007
+ * that read only the class would answer "animal" and never "dog".
23008
+ *
23009
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
23010
+ */
23011
+ classes: array(string().min(1).max(60)).max(40).optional()
23012
+ });
23013
+ var RecentSummariesPageSchema = object({
23014
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
23015
+ * {@link listSummaries} — one list shape, so a card renders the same
23016
+ * whichever feed drew it. */
23017
+ summaries: array(SummaryListItemSchema).readonly(),
23018
+ nextCursor: string().nullable()
22806
23019
  });
22807
23020
  var RecentTracksPageSchema = object({
22808
23021
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -23244,6 +23457,11 @@ var pipelineAnalyticsCapability = {
23244
23457
  listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
23245
23458
  /** Newest first by `openedAt`, for a camera set, inside an optional window. */
23246
23459
  listSummaries: method(SummariesQueryInput, SummariesPageSchema),
23460
+ /**
23461
+ * The PAGED feed — the reel and the Events gallery. Same cursor contract as
23462
+ * {@link listRecentTracks}, so one screen cannot hold two ideas of newest.
23463
+ */
23464
+ listRecentSummaries: method(RecentSummariesQueryInput, RecentSummariesPageSchema),
23247
23465
  /** One summary with its member track ids; `null` for an id nobody wrote. */
23248
23466
  getSummary: method(object({ id: string() }), SummaryDetailSchema.nullable()),
23249
23467
  clearTracks: method(object({ deviceId: number() }), _void(), {
@@ -23773,7 +23991,7 @@ var pipelineAnalyticsCapability = {
23773
23991
  selectRetrainFrames: method(object({
23774
23992
  deviceId: number(),
23775
23993
  trackId: string(),
23776
- mediaKeys: array(string()).min(1)
23994
+ mediaIds: array(number().int()).min(1)
23777
23995
  }), RetrainFrameSelectionSchema, {
23778
23996
  kind: "mutation",
23779
23997
  auth: "admin"
@@ -23937,8 +24155,29 @@ var pipelineAnalyticsCapability = {
23937
24155
  kind: "query",
23938
24156
  auth: "admin"
23939
24157
  }),
24158
+ /**
24159
+ * The media an EVENT owns.
24160
+ *
24161
+ * `ownerType` is REQUIRED, and that is the whole correction (D482). An
24162
+ * event is a row in ONE of three tables, and since D474 each numbers from
24163
+ * 1 independently: `motion:101314` and `object:101314` both exist. This
24164
+ * method took a bare `eventId` and the provider probed motion → object →
24165
+ * audio, stopping at the first hit — so on the live hub, where object
24166
+ * events own no media of their own and 299 883 of 300 559 media rows are
24167
+ * motion-owned, EVERY object event resolved to its motion namesake. A
24168
+ * track from 17:34 on camera 615 was served a snapshot from 22:35 eleven
24169
+ * days earlier, silently.
24170
+ *
24171
+ * D476 had already disambiguated the KEY (`motion:` vs `object:`); this is
24172
+ * the API catching up to it. Naming the table is not a "qualification" of
24173
+ * the ambiguity — a listing BY OWNER has no address to make surrogate: the
24174
+ * owner IS (table, id), and half of it is not a question.
24175
+ */
23940
24176
  getEventMedia: method(object({
23941
- eventId: string(),
24177
+ /** Which of the three event tables owns the row. */
24178
+ ownerType: EventOwnerTypeSchema,
24179
+ /** The event's rowid in that table (D474: an event id is a number). */
24180
+ eventId: number().int(),
23942
24181
  kind: MediaFileKindEnum.optional(),
23943
24182
  deviceId: number()
23944
24183
  }), array(MediaFileSchema).readonly()),
@@ -23983,11 +24222,14 @@ var pipelineAnalyticsCapability = {
23983
24222
  * the live hub: one event's `fullFrameBoxed` is 2 824 077 B, base64'd to
23984
24223
  * ~3.8 MB, allocated whole in hub-main's heap, for a list of keys.
23985
24224
  *
23986
- * Same `deviceId` authorization subject as `getEventMedia`, and the same
23987
- * rows — this is a projection of that method, never a different question.
24225
+ * Same `deviceId` authorization subject as `getEventMedia`, the same
24226
+ * REQUIRED `ownerType` (read that method's docblock for the night-time
24227
+ * thumbnail this closes — D482), and the same rows: this is a projection
24228
+ * of that method, never a different question.
23988
24229
  */
23989
24230
  listEventMedia: method(object({
23990
- eventId: string(),
24231
+ ownerType: EventOwnerTypeSchema,
24232
+ eventId: number().int(),
23991
24233
  deviceId: number()
23992
24234
  }), array(MediaFileInfoSchema).readonly()),
23993
24235
  /**
@@ -30614,8 +30856,8 @@ var IdentitySchema = object({
30614
30856
  id: string(),
30615
30857
  name: string(),
30616
30858
  sampleCount: number().int().nonnegative(),
30617
- coverMediaKey: string().optional(),
30618
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
30859
+ coverMediaId: number().int().optional(),
30860
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
30619
30861
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
30620
30862
  coverBase64: string().optional()
30621
30863
  });
@@ -30641,8 +30883,8 @@ var IdentitySampleAuditRowSchema = object({
30641
30883
  enrolledAt: number().int(),
30642
30884
  deviceId: number().int().optional(),
30643
30885
  source: _enum(["detected", "photo"]),
30644
- /** The enrolled crop's media key, so the panel can show the evidence. */
30645
- mediaKey: string().optional(),
30886
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
30887
+ mediaId: number().int().optional(),
30646
30888
  /**
30647
30889
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
30648
30890
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -30711,15 +30953,15 @@ var FaceInfoSchema = object({
30711
30953
  */
30712
30954
  cropUrl: string().optional(),
30713
30955
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
30714
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
30956
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
30715
30957
  * legacy rows written before design B. */
30716
30958
  faceBbox: BoundingBoxSchema.optional(),
30717
30959
  /** Design B: MediaStore key of the track's native-resolution key frame.
30718
30960
  * Fetch the native JPEG via the event-media data-plane
30719
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
30961
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
30720
30962
  * track produced no key frame (e.g. native/onboard source) — the UI falls
30721
30963
  * back to the inline `base64` face crop. */
30722
- keyFrameMediaKey: string().optional(),
30964
+ keyFrameMediaId: number().int().optional(),
30723
30965
  /** Winning identity-match cosine (0..1) for this face's track, when an
30724
30966
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
30725
30967
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -30788,11 +31030,14 @@ var FaceClusterSchema = object({
30788
31030
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
30789
31031
  * dialog already rendering its key FRAME from the `event-media` plane.
30790
31032
  *
30791
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
30792
- * media key directly, so this needed no new plane and no new access decision.
31033
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
31034
+ * resolves a media row by its own id, so this needed no new plane and no new
31035
+ * access decision.
30793
31036
  */
30794
31037
  var MediaFileLiteSchema$1 = object({
30795
- key: string(),
31038
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
31039
+ * (D482). It was the row's composite key, which embedded the owner. */
31040
+ mediaId: number().int(),
30796
31041
  kind: string(),
30797
31042
  url: string(),
30798
31043
  sizeBytes: number(),
@@ -30814,7 +31059,7 @@ var faceGalleryCapability = {
30814
31059
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
30815
31060
  * four times and the viewer holds at `staleTime: 30_000`.
30816
31061
  *
30817
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
31062
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
30818
31063
  * the `event-media` plane serves that key `immutable` with an ETag.
30819
31064
  *
30820
31065
  * **This is an INPUT field, so it does not reach the addon until the
@@ -34553,8 +34798,8 @@ var VehicleSchema = object({
34553
34798
  id: string(),
34554
34799
  name: string(),
34555
34800
  sampleCount: number().int().nonnegative(),
34556
- coverMediaKey: string().optional(),
34557
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
34801
+ coverMediaId: number().int().optional(),
34802
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
34558
34803
  coverBase64: string().optional()
34559
34804
  });
34560
34805
  var VehicleSampleInfoSchema = object({
@@ -34587,7 +34832,7 @@ var PlateInfoSchema = object({
34587
34832
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
34588
34833
  plateBbox: BoundingBoxSchema.optional(),
34589
34834
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
34590
- keyFrameMediaKey: string().optional(),
34835
+ keyFrameMediaId: number().int().optional(),
34591
34836
  base64: string().optional(),
34592
34837
  /**
34593
34838
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -34608,11 +34853,14 @@ var PlateInfoSchema = object({
34608
34853
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
34609
34854
  * dialog already rendering its key FRAME from the `event-media` plane.
34610
34855
  *
34611
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
34612
- * media key directly, so this needed no new plane and no new access decision.
34856
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
34857
+ * resolves a media row by its own id, so this needed no new plane and no new
34858
+ * access decision.
34613
34859
  */
34614
34860
  var MediaFileLiteSchema = object({
34615
- key: string(),
34861
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
34862
+ * (D482). It was the row's composite key, which embedded the owner. */
34863
+ mediaId: number().int(),
34616
34864
  kind: string(),
34617
34865
  url: string(),
34618
34866
  sizeBytes: number(),
@@ -34694,7 +34942,7 @@ var plateGalleryCapability = {
34694
34942
  listVehicles: method(object({
34695
34943
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
34696
34944
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
34697
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
34945
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
34698
34946
  * INPUT field: stripped by the hub router until the train ships, which
34699
34947
  * resolves to `false` and is exactly the intended default. */
34700
34948
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()),
@@ -36557,7 +36805,14 @@ var SceneReferenceSchema = object({
36557
36805
  modelId: string(),
36558
36806
  condition: SceneConditionSchema,
36559
36807
  capturedAt: number(),
36560
- thumbnailMediaId: string().optional(),
36808
+ /**
36809
+ * The reference thumbnail's media ROW id (D482).
36810
+ *
36811
+ * The name already said `mediaId` while the value was the row's composite
36812
+ * key — a leftover that read as done and was not. It is the row's own id
36813
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
36814
+ */
36815
+ thumbnailMediaId: number().int().optional(),
36561
36816
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
36562
36817
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
36563
36818
  * normalized rect frame a different piece of world, and the scene would
@@ -37648,7 +37903,29 @@ var LoggingSettingsPatchSchema = object({
37648
37903
  * disarmed the channels it did not mention would make the Levels page and
37649
37904
  * the Diagnostics page fight over the same value.
37650
37905
  */
37651
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
37906
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
37907
+ /**
37908
+ * How many days the analytics OPS LOG is kept — the audit trail of what
37909
+ * retention actually deleted.
37910
+ *
37911
+ * It lives HERE, on the logging document, and not on the analytics addon's
37912
+ * settings form, on the operator's instruction: every other log-ish window in
37913
+ * this system is configured through this one document, and a retention audit
37914
+ * configured somewhere else is a second place to look when the question is
37915
+ * "what happened to my data".
37916
+ *
37917
+ * That placement has a cost worth stating: this document ships in the
37918
+ * framework closure, so this knob needs a published `@camstack/server` while
37919
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
37920
+ *
37921
+ * `retention-model.ts` argued this one must NOT follow a device window,
37922
+ * because the audit trail is the history of the SWEEP and not of the camera.
37923
+ * That argument stands and is untouched — what changes is that 30 days stops
37924
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
37925
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
37926
+ * state in which the next 9.5 GiB leak cannot be explained.
37927
+ */
37928
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
37652
37929
  });
37653
37930
  /**
37654
37931
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -37721,6 +37998,14 @@ var LoggingSettingsStateSchema = object({
37721
37998
  channels: array(LogChannelDescriptorSchema).readonly(),
37722
37999
  /** The channels ARMED right now, each with its deadline. */
37723
38000
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
38001
+ /**
38002
+ * The ops-log window in days, as it is in force. `0` = kept forever.
38003
+ *
38004
+ * Reported rather than left to the caller's memory of what it wrote: this is
38005
+ * the value the analytics sweep will actually use, and an operator asking how
38006
+ * long their audit trail lives should not have to infer it from a default.
38007
+ */
38008
+ opsLogRetentionDays: number().int().min(0),
37724
38009
  persisted: boolean()
37725
38010
  });
37726
38011
  var systemCapability = {
@@ -38692,7 +38977,7 @@ var StationaryObjectSchema = object({
38692
38977
  /** Enrichment label carried from the source track (identity / plate). */
38693
38978
  label: string().optional(),
38694
38979
  /** Native-resolution key-frame media key for the parked object's best image. */
38695
- keyFrameMediaKey: string().optional()
38980
+ keyFrameMediaId: number().int().optional()
38696
38981
  });
38697
38982
  var CameraOccupancySnapshotSchema = object({
38698
38983
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -40195,6 +40480,12 @@ Object.freeze({
40195
40480
  addonId: null,
40196
40481
  access: "create"
40197
40482
  },
40483
+ "dataStoreProvider.upsert": {
40484
+ capName: "data-store-provider",
40485
+ capScope: "system",
40486
+ addonId: null,
40487
+ access: "create"
40488
+ },
40198
40489
  "dayNight.getOptions": {
40199
40490
  capName: "day-night",
40200
40491
  capScope: "device",
@@ -42631,6 +42922,12 @@ Object.freeze({
42631
42922
  addonId: null,
42632
42923
  access: "view"
42633
42924
  },
42925
+ "pipelineAnalytics.listRecentSummaries": {
42926
+ capName: "pipeline-analytics",
42927
+ capScope: "device",
42928
+ addonId: null,
42929
+ access: "view"
42930
+ },
42634
42931
  "pipelineAnalytics.listRecentTracks": {
42635
42932
  capName: "pipeline-analytics",
42636
42933
  capScope: "device",
@@ -44059,6 +44356,12 @@ Object.freeze({
44059
44356
  addonId: null,
44060
44357
  access: "create"
44061
44358
  },
44359
+ "settingsStore.upsert": {
44360
+ capName: "settings-store",
44361
+ capScope: "system",
44362
+ addonId: null,
44363
+ access: "create"
44364
+ },
44062
44365
  "smtpProvider.getStatus": {
44063
44366
  capName: "smtp-provider",
44064
44367
  capScope: "system",
@@ -46452,6 +46755,11 @@ Object.freeze({
46452
46755
  form: "single",
46453
46756
  optional: true
46454
46757
  }],
46758
+ "pipelineAnalytics.listRecentSummaries": [{
46759
+ name: "deviceIds",
46760
+ form: "array",
46761
+ optional: false
46762
+ }],
46455
46763
  "pipelineAnalytics.listRecentTracks": [{
46456
46764
  name: "deviceIds",
46457
46765
  form: "array",