@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.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- //#region ../types/dist/event-category-DAXzJeTX.mjs
5
+ //#region ../types/dist/event-category-ZyX6jcse.mjs
6
6
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
7
7
  EventCategory["SystemBoot"] = "system.boot";
8
8
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -563,7 +563,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
563
563
  * a package zone — a newly-appeared stationary object of a package class
564
564
  * that cleared the class / zone / dwell / size gates. Payload:
565
565
  * `PipelineAnalyticsPackageDeliveredPayload` carrying `{ deviceId,
566
- * entryId, className, zoneIds, keyFrameMediaKey?, bbox, timestamp }`.
566
+ * entryId, className, zoneIds, keyFrameMediaId?, bbox, timestamp }`.
567
567
  * Telemetry (D8): the durable record is the `package-events` store row;
568
568
  * this bus topic drives notifier rules + live UI. See
569
569
  * docs/superpowers/specs/2026-07-17-package-zones-design.md §5.1.
@@ -5349,7 +5349,7 @@ var ZodIssueCode = {
5349
5349
  var ZodFirstPartyTypeKind;
5350
5350
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5351
5351
  //#endregion
5352
- //#region ../types/dist/sleep-BnujYGPe.mjs
5352
+ //#region ../types/dist/sleep-DBKu2-U5.mjs
5353
5353
  /**
5354
5354
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5355
5355
  * window to float samples (D455).
@@ -7412,13 +7412,30 @@ new Set([
7412
7412
  "scene",
7413
7413
  "motion",
7414
7414
  "object",
7415
- "audio"
7415
+ "audio",
7416
+ "mosaic"
7416
7417
  ]);
7417
- new Set([
7418
+ /**
7419
+ * The owner types that are event tables, in the order the event-media resolver
7420
+ * should consider them. Exported so a consumer asking "is this key an event?"
7421
+ * does not re-spell the list and drift from it.
7422
+ */
7423
+ var EVENT_OWNER_TYPES = [
7418
7424
  "motion",
7419
7425
  "object",
7420
7426
  "audio"
7421
- ]);
7427
+ ];
7428
+ /**
7429
+ * The same list as a Zod enum, for the cap inputs that must NAME the table
7430
+ * (D482: `getEventMedia` / `listEventMedia` take an owner, and an owner is
7431
+ * `(table, id)`).
7432
+ *
7433
+ * Built FROM {@link EVENT_OWNER_TYPES} rather than re-spelled: a fourth event
7434
+ * table would otherwise be accepted by the codec and refused at the door, with
7435
+ * nothing failing until a caller asked.
7436
+ */
7437
+ var EventOwnerTypeSchema = _enum(EVENT_OWNER_TYPES);
7438
+ new Set(EVENT_OWNER_TYPES);
7422
7439
  var EncodeProfileSchema = object({
7423
7440
  video: object({
7424
7441
  codec: _enum([
@@ -7457,7 +7474,23 @@ var EncodeProfileSchema = object({
7457
7474
  "zerolatency",
7458
7475
  "film",
7459
7476
  "animation"
7460
- ]).optional()
7477
+ ]).optional(),
7478
+ /**
7479
+ * ONE slice per access unit.
7480
+ *
7481
+ * `-tune zerolatency` turns on x264's sliced threads, and a frame then leaves
7482
+ * the encoder as five NAL slices that share one RTP timestamp and carry one
7483
+ * marker bit. A libwebrtc depacketiser sees five frame-starts and one
7484
+ * frame-end per frame: the first pictures render and the video then freezes
7485
+ * for good while the audio, on its own plane, plays on. Measured on this hub
7486
+ * 2026-09-13 against the Echo, and reduced to one slice by this flag alone.
7487
+ *
7488
+ * A NAMED field and not a raw flag, because {@link EgressEncodeSchema} omits
7489
+ * `outputArgs` on purpose: an opaque array is part of the sharing key, so two
7490
+ * consumers meaning the same thing spelled differently would stop sharing one
7491
+ * child. Absent means "whatever the encoder does" — today's behaviour.
7492
+ */
7493
+ singleSlicePerFrame: boolean().optional()
7461
7494
  }),
7462
7495
  audio: union([literal("passthrough"), object({
7463
7496
  codec: _enum([
@@ -7507,7 +7540,7 @@ var BASE_LIVE_EGRESS_PROFILE = {
7507
7540
  audio: "passthrough"
7508
7541
  };
7509
7542
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7510
- ({ ...BASE_LIVE_EGRESS_PROFILE });
7543
+ ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
7511
7544
  /**
7512
7545
  * Deep wiring healthcheck — snapshot of active reachability probes across
7513
7546
  * every declared capability + widget of every installed plugin, on every
@@ -13383,6 +13416,59 @@ var settingsStoreCapability = {
13383
13416
  filter: QueryFilterSchema.optional(),
13384
13417
  columns: array(string()).readonly().optional()
13385
13418
  }), array(SettingsRecordSchema).readonly()),
13419
+ /**
13420
+ * Make a SLOT hold this row — insert it, or update whatever already
13421
+ * occupies the slot — and answer with the id the row HAS.
13422
+ *
13423
+ * ## Why the store needed this
13424
+ *
13425
+ * {@link set} is already an upsert, but its slot is the PRIMARY KEY: the
13426
+ * caller has to know the id to address the row. That is exactly what a
13427
+ * collection keyed by an `INTEGER` rowid alias cannot do — SQLite assigns
13428
+ * the id, so a writer whose uniqueness rule is "one row per
13429
+ * `(ownerKind, ownerId, kind, slot)`" has no id to name and no way to
13430
+ * express the rule. {@link insert} can express it (a UNIQUE index
13431
+ * arbitrates) but only by THROWING at the loser of a race, and the loser
13432
+ * here is a legitimate re-capture, not an error.
13433
+ *
13434
+ * Getting there without this method means read-then-write: query the slot,
13435
+ * then `insert` or `update`. Two statements instead of one, a race between
13436
+ * them, and a recovery path that has to recognise a UNIQUE violation by its
13437
+ * MESSAGE — across a process boundary, because the store lives behind an
13438
+ * RPC door. A string match would be the whole gate, and a store that
13439
+ * rephrased its error would lose the row silently. One statement has no
13440
+ * such gate (D482).
13441
+ *
13442
+ * ## The contract
13443
+ *
13444
+ * `conflict` names the DECLARED columns that identify the slot. They must
13445
+ * be covered by a UNIQUE index on the collection — SQLite resolves an
13446
+ * `ON CONFLICT` target against its indexes and REFUSES the statement when
13447
+ * none matches, so a wrong list is a loud error on the first write, never a
13448
+ * silently duplicated row. An empty list is refused rather than passed on:
13449
+ * `ON CONFLICT()` is not valid SQL, and the intent it would most likely
13450
+ * encode ("no slot, just insert") is {@link insert}.
13451
+ *
13452
+ * Row shape is {@link insert}'s: `record.id` is used when the collection
13453
+ * needs a supplied key and IGNORED when the primary key is an `INTEGER`
13454
+ * rowid alias — exactly as `insert` and `insertMany` already treat it.
13455
+ *
13456
+ * Returns the id either way: the one SQLite assigned on an insert, or the
13457
+ * one the row it updated already had. A caller cannot tell which happened,
13458
+ * and deliberately — every caller wants the slot's id, and one that needed
13459
+ * to distinguish creation from replacement would have to read first anyway.
13460
+ */
13461
+ upsert: method(object({
13462
+ namespace: string().optional(),
13463
+ collection: string(),
13464
+ /** Declared columns identifying the slot; must be covered by a UNIQUE
13465
+ * index on the collection. Never empty. */
13466
+ conflict: array(string()).readonly(),
13467
+ record: BulkRecordSchema
13468
+ }), object({
13469
+ /** The id the row HAS: assigned by SQLite on an insert, or the existing
13470
+ * row's own id on an update. */
13471
+ id: union([string(), number()]) }), { kind: "mutation" }),
13386
13472
  /** Insert a new record. */
13387
13473
  insert: method(object({
13388
13474
  namespace: string().optional(),
@@ -13672,6 +13758,20 @@ id: union([string(), number()]) }), {
13672
13758
  kind: "mutation",
13673
13759
  auth: "admin"
13674
13760
  }),
13761
+ /**
13762
+ * Make a slot hold this row, and answer with the id the row HAS. The
13763
+ * engine half of `settings-store.upsert` — read that docblock for why the
13764
+ * store needed a slot that is not the primary key (D482).
13765
+ */
13766
+ upsert: method(object({
13767
+ namespace: string().optional(),
13768
+ collection: string(),
13769
+ conflict: array(string()).readonly(),
13770
+ record: BulkRecordSchema
13771
+ }), object({ id: union([string(), number()]) }), {
13772
+ kind: "mutation",
13773
+ auth: "admin"
13774
+ }),
13675
13775
  /** Insert many records in ONE transaction. All or nothing. */
13676
13776
  insertMany: method(object({
13677
13777
  namespace: string().optional(),
@@ -21856,8 +21956,8 @@ var TrackPositionSchema = object({
21856
21956
  var TrackSnapshotSchema = object({
21857
21957
  timestamp: number(),
21858
21958
  position: TrackPositionSchema,
21859
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
21860
- mediaKey: string()
21959
+ /** The snapshot's media row id (D482); resolve via `listTrackMedia({ trackId })`. */
21960
+ mediaId: number().int()
21861
21961
  });
21862
21962
  /**
21863
21963
  * Normalized 0..1 trajectory envelope (min/max over every position bbox,
@@ -22461,14 +22561,14 @@ var ObjectEventSchema = object({
22461
22561
  * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
22462
22562
  frameWidth: number().optional(),
22463
22563
  frameHeight: number().optional(),
22464
- /** MediaStore key for the crop attached to this event (if any). */
22465
- mediaKey: string().optional(),
22466
- /** Design B: MediaStore key of the track's native-resolution key frame (the
22564
+ /** Media row id of the crop attached to this event (if any) — D482. */
22565
+ mediaId: number().int().optional(),
22566
+ /** Design B: media row id of the track's native-resolution key frame (the
22467
22567
  * best-detection full frame). Resolve via the event-media data-plane
22468
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
22568
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`) for a detail view that
22469
22569
  * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
22470
- * sources — consumers fall back to `mediaKey` (the tight crop). */
22471
- keyFrameMediaKey: string().optional(),
22570
+ * sources — consumers fall back to `mediaId` (the tight crop). */
22571
+ keyFrameMediaId: number().int().optional(),
22472
22572
  /** Populated by B5 (recording playback URL for this event). */
22473
22573
  mediaUrl: string().optional(),
22474
22574
  /** The parent track's key-event importance [0,1], propagated to every object
@@ -22518,7 +22618,7 @@ var MediaFileKindEnum = _enum([
22518
22618
  * hub-main's heap on the way past — for an `<img>` that would have cached it.
22519
22619
  *
22520
22620
  * `url` points at the `event-media` data plane
22521
- * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
22621
+ * (`/addon/<addonId>/event-media/m/<mediaId>` — D482), which serves the same blob
22522
22622
  * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
22523
22623
  * render a `?variant=thumb`, and streams. The hub gate in front of it requires
22524
22624
  * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
@@ -22526,10 +22626,13 @@ var MediaFileKindEnum = _enum([
22526
22626
  * `data-plane-access.ts` for the rule and the one gap it does not close
22527
22627
  * (per-device scoping).
22528
22628
  *
22529
- * The URL is built from the row's **stored** key, which is not always its
22530
- * published `kind`: a track's face/plate crop is stored as `crop` under
22629
+ * The URL is built from the row's own id, which since D482 says nothing about
22630
+ * the row's owner or kind — and that is the point. The published `kind` is not
22631
+ * the stored one (a track's face/plate crop is stored as `crop` under
22531
22632
  * `('face'|'plate', '<prefix>-<trackId>')` and published as
22532
- * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
22633
+ * `faceCrop`/`plateCrop`), and under the old composite key the URL had to be
22634
+ * taken from `row.key` verbatim or it 404'd exactly those two rows. An id
22635
+ * cannot be mis-reconstructed, because there is nothing in it to reconstruct.
22533
22636
  *
22534
22637
  * ## `base64` is TRANSITIONAL and is going away
22535
22638
  *
@@ -22542,7 +22645,19 @@ var MediaFileKindEnum = _enum([
22542
22645
  * `analytics-query-facade.ts`; nothing else reads it.
22543
22646
  */
22544
22647
  var MediaFileSchema = object({
22545
- key: string(),
22648
+ /**
22649
+ * The media row's OWN id — the rowid SQLite assigned it (D482).
22650
+ *
22651
+ * It used to be the row's composite key, `<ownerType>:<ownerId>:<fileKind>
22652
+ * [:<timestampMs>]`, which embedded a FOREIGN id. D474 made event ids
22653
+ * per-table rowid aliases, so `object:101314` and `motion:101314` are two
22654
+ * different rows that spell the same number, and a track whose
22655
+ * `bestEventId` was an object event was served a motion event's snapshot —
22656
+ * a night-time thumbnail on an afternoon track, with no error anywhere. A
22657
+ * surrogate id cannot name the wrong row's owner, because it names no owner
22658
+ * at all.
22659
+ */
22660
+ mediaId: number().int(),
22546
22661
  kind: MediaFileKindEnum,
22547
22662
  sizeBytes: number(),
22548
22663
  timestamp: number()
@@ -22650,7 +22765,7 @@ var RetrainTrackSchema = object({
22650
22765
  });
22651
22766
  /** A frame the picker may offer — an index row, no blob was read to produce it. */
22652
22767
  var RetrainFrameCandidateSchema = object({
22653
- mediaKey: string(),
22768
+ mediaId: number().int(),
22654
22769
  kind: MediaFileKindEnum,
22655
22770
  timestamp: number(),
22656
22771
  sizeBytes: number().int(),
@@ -22664,7 +22779,7 @@ var RetrainFrameSchema = object({
22664
22779
  deviceId: number(),
22665
22780
  trackId: string(),
22666
22781
  /** Provenance only. It may already point at nothing — that is expected. */
22667
- sourceMediaKey: string(),
22782
+ sourceMediaId: number().int(),
22668
22783
  sourceKind: MediaFileKindEnum,
22669
22784
  sizeBytes: number().int(),
22670
22785
  width: number().int(),
@@ -22680,7 +22795,7 @@ var RetrainCopyRefusalSchema = _enum([
22680
22795
  var RetrainFrameSelectionSchema = object({
22681
22796
  copied: array(RetrainFrameSchema).readonly(),
22682
22797
  refused: array(object({
22683
- sourceMediaKey: string(),
22798
+ sourceMediaId: number().int(),
22684
22799
  reason: RetrainCopyRefusalSchema
22685
22800
  })).readonly()
22686
22801
  });
@@ -22688,7 +22803,7 @@ var RetrainFrameListSchema = object({
22688
22803
  candidates: array(RetrainFrameCandidateSchema).readonly(),
22689
22804
  copies: array(RetrainFrameSchema).readonly(),
22690
22805
  /** What the page pre-selects — the native key frame when one survives. */
22691
- autoPickMediaKey: string().optional()
22806
+ autoPickMediaId: number().int().optional()
22692
22807
  });
22693
22808
  /** What the operator asked the assist to look for. */
22694
22809
  var RetrainAssistSubjectSchema = discriminatedUnion("kind", [object({
@@ -22783,9 +22898,11 @@ var RecentTracksQueryInput = object({
22783
22898
  });
22784
22899
  /**
22785
22900
  * A Summary (D359): the per-camera session envelope that references tracks.
22786
- * `groupShotKey` is the media key of its group shot (`ownerKind: 'summary'`,
22787
- * served by the event-media plane by key), null until the first frame with a
22788
- * member visible has been materialised.
22901
+ * `groupShotMediaId` is the media ROW id of its group shot (`ownerKind:
22902
+ * 'summary'`, served by the event-media plane at `/m/<id>`), null until the
22903
+ * first frame with a member visible has been materialised (D482 — it was the
22904
+ * row's composite key, which embedded the owner and could name another table's
22905
+ * row once event ids became per-table rowids).
22789
22906
  */
22790
22907
  var SummarySchema = object({
22791
22908
  id: string(),
@@ -22794,19 +22911,115 @@ var SummarySchema = object({
22794
22911
  lastActiveAt: number(),
22795
22912
  sealedAt: number().nullable(),
22796
22913
  memberCount: number().int().nonnegative(),
22797
- groupShotKey: string().nullable()
22914
+ groupShotMediaId: number().int().nullable()
22798
22915
  });
22799
22916
  var SummariesQueryInput = object({
22800
22917
  deviceIds: array(number()).min(1),
22801
22918
  since: number().optional(),
22802
22919
  until: number().optional(),
22803
- limit: number().int().min(1).max(200).default(50)
22920
+ limit: number().int().min(1).max(200).default(50),
22921
+ /** The same member-wise filter {@link listRecentSummaries} takes — one
22922
+ * meaning of "dog" on every summary surface. */
22923
+ classes: array(string().min(1).max(60)).max(40).optional()
22804
22924
  });
22805
- var SummariesPageSchema = object({ summaries: array(SummarySchema).readonly() });
22806
22925
  var SummaryDetailSchema = object({
22807
22926
  summary: SummarySchema,
22808
- /** Member track ids, in absorption order. */
22809
- trackIds: array(string()).readonly()
22927
+ /** Members in absorption order, oldest join first. */
22928
+ members: array(object({
22929
+ trackId: string(),
22930
+ /**
22931
+ * The track's class at the time it JOINED, or `null` when it was unresolved
22932
+ * and the track no longer exists to fill it.
22933
+ *
22934
+ * The join, not the latest opinion: class voting can re-decide a track's
22935
+ * class afterwards, and a group's composition is the history of who came
22936
+ * together. A consumer counts these to badge a thumbnail — "two people and a
22937
+ * dog" — rather than being handed a second, denormalised tally that would
22938
+ * drift from the members it claims to describe.
22939
+ */
22940
+ className: string().nullable(),
22941
+ /** `primary` is the one member that carries the group's identity. */
22942
+ role: _enum([
22943
+ "primary",
22944
+ "sibling",
22945
+ "child"
22946
+ ]).nullable(),
22947
+ joinedAt: number(),
22948
+ /** Tier 1 — the sub-class (`dog`, `van`), resolved from the member's track
22949
+ * at read time. Absent when nothing resolved, never an empty string. */
22950
+ label: string().optional(),
22951
+ /** Tier 2 — the instance: a person's name, a plate. Absent when the pipeline
22952
+ * recognised nobody, and absent for a member whose track has been pruned —
22953
+ * a thumbnail that names the wrong person is worse than one naming nobody. */
22954
+ subLabel: string().optional()
22955
+ })).readonly()
22956
+ });
22957
+ /**
22958
+ * What a LIST of groups carries: the envelope plus what it is MADE OF.
22959
+ *
22960
+ * Coarse on purpose — the operator's rule is "only the rough data on the list,
22961
+ * we extend when we notice we need more". Enough to draw the card ("two people
22962
+ * and a dog") and no more. The MEMBERS, their names and the per-member links
22963
+ * are on {@link getSummary}: shipping every member of every group so a client
22964
+ * can count them turns a page of two hundred groups into a thousand records to
22965
+ * draw two hundred badges.
22966
+ */
22967
+ var SummaryListItemSchema = object({
22968
+ summary: SummarySchema,
22969
+ /** Class counts, most numerous first; `className: null` is a member whose
22970
+ * class never resolved, counted so the counts add up to `memberCount`. */
22971
+ classes: array(object({
22972
+ className: string().nullable(),
22973
+ count: number().int().positive()
22974
+ })).readonly()
22975
+ });
22976
+ var SummariesPageSchema = object({ summaries: array(SummaryListItemSchema).readonly() });
22977
+ /**
22978
+ * The paged feed, deliberately the SAME shape as {@link RecentTracksQueryInput}.
22979
+ *
22980
+ * The reel on the cameras page and the Events gallery both ride a cursor, so a
22981
+ * summaries feed that could only answer "the last N for these cameras"
22982
+ * (`listSummaries`) would make groups available in the timeline and nowhere
22983
+ * else. Two feeds with different ordering contracts would be two ideas of what
22984
+ * "newest" means, on one screen.
22985
+ */
22986
+ var RecentSummariesQueryInput = object({
22987
+ /** Devices to merge. An empty array yields an empty page. */
22988
+ deviceIds: array(number()),
22989
+ /** Window lower bound on `lastActiveAt` (inclusive). */
22990
+ since: number().optional(),
22991
+ /** Window upper bound on `lastActiveAt` (inclusive). */
22992
+ until: number().optional(),
22993
+ limit: number().int().min(1).max(500).default(100),
22994
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
22995
+ cursor: string().optional(),
22996
+ /**
22997
+ * Restrict to these classes — THE SAME FIELD `listRecentTracks` takes, and
22998
+ * deliberately the same name: the events filter tree emits one list of ticked
22999
+ * taxonomy nodes, and a summaries feed that called it something else would
23000
+ * make the operator's one filter component produce two shapes.
23001
+ *
23002
+ * A question about the MEMBERS: a group matches when ANY of them does. "Show
23003
+ * me the arrivals with a dog" has to return the group of four people who had
23004
+ * one with them; a filter on the group's own fields could only ever answer
23005
+ * about its primary.
23006
+ *
23007
+ * Matched against all THREE tiers of a member — the class (`animal`), the
23008
+ * sub-class (`dog`) and the instance (a name, a plate) — because which tier a
23009
+ * ticked node lives on is the taxonomy's business, not the caller's. `dog` is
23010
+ * an event kind in the tree and a tier-1 label on an `animal` track; a filter
23011
+ * that read only the class would answer "animal" and never "dog".
23012
+ *
23013
+ * ABSENT or EMPTY means no filter — absent is not a selection of none.
23014
+ */
23015
+ classes: array(string().min(1).max(60)).max(40).optional()
23016
+ });
23017
+ var RecentSummariesPageSchema = object({
23018
+ /** Ordered by (`lastActiveAt` DESC, id DESC). Same light item as
23019
+ * {@link listSummaries} — one list shape, so a card renders the same
23020
+ * whichever feed drew it. */
23021
+ summaries: array(SummaryListItemSchema).readonly(),
23022
+ nextCursor: string().nullable()
22810
23023
  });
22811
23024
  var RecentTracksPageSchema = object({
22812
23025
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -23248,6 +23461,11 @@ var pipelineAnalyticsCapability = {
23248
23461
  listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
23249
23462
  /** Newest first by `openedAt`, for a camera set, inside an optional window. */
23250
23463
  listSummaries: method(SummariesQueryInput, SummariesPageSchema),
23464
+ /**
23465
+ * The PAGED feed — the reel and the Events gallery. Same cursor contract as
23466
+ * {@link listRecentTracks}, so one screen cannot hold two ideas of newest.
23467
+ */
23468
+ listRecentSummaries: method(RecentSummariesQueryInput, RecentSummariesPageSchema),
23251
23469
  /** One summary with its member track ids; `null` for an id nobody wrote. */
23252
23470
  getSummary: method(object({ id: string() }), SummaryDetailSchema.nullable()),
23253
23471
  clearTracks: method(object({ deviceId: number() }), _void(), {
@@ -23777,7 +23995,7 @@ var pipelineAnalyticsCapability = {
23777
23995
  selectRetrainFrames: method(object({
23778
23996
  deviceId: number(),
23779
23997
  trackId: string(),
23780
- mediaKeys: array(string()).min(1)
23998
+ mediaIds: array(number().int()).min(1)
23781
23999
  }), RetrainFrameSelectionSchema, {
23782
24000
  kind: "mutation",
23783
24001
  auth: "admin"
@@ -23941,8 +24159,29 @@ var pipelineAnalyticsCapability = {
23941
24159
  kind: "query",
23942
24160
  auth: "admin"
23943
24161
  }),
24162
+ /**
24163
+ * The media an EVENT owns.
24164
+ *
24165
+ * `ownerType` is REQUIRED, and that is the whole correction (D482). An
24166
+ * event is a row in ONE of three tables, and since D474 each numbers from
24167
+ * 1 independently: `motion:101314` and `object:101314` both exist. This
24168
+ * method took a bare `eventId` and the provider probed motion → object →
24169
+ * audio, stopping at the first hit — so on the live hub, where object
24170
+ * events own no media of their own and 299 883 of 300 559 media rows are
24171
+ * motion-owned, EVERY object event resolved to its motion namesake. A
24172
+ * track from 17:34 on camera 615 was served a snapshot from 22:35 eleven
24173
+ * days earlier, silently.
24174
+ *
24175
+ * D476 had already disambiguated the KEY (`motion:` vs `object:`); this is
24176
+ * the API catching up to it. Naming the table is not a "qualification" of
24177
+ * the ambiguity — a listing BY OWNER has no address to make surrogate: the
24178
+ * owner IS (table, id), and half of it is not a question.
24179
+ */
23944
24180
  getEventMedia: method(object({
23945
- eventId: string(),
24181
+ /** Which of the three event tables owns the row. */
24182
+ ownerType: EventOwnerTypeSchema,
24183
+ /** The event's rowid in that table (D474: an event id is a number). */
24184
+ eventId: number().int(),
23946
24185
  kind: MediaFileKindEnum.optional(),
23947
24186
  deviceId: number()
23948
24187
  }), array(MediaFileSchema).readonly()),
@@ -23987,11 +24226,14 @@ var pipelineAnalyticsCapability = {
23987
24226
  * the live hub: one event's `fullFrameBoxed` is 2 824 077 B, base64'd to
23988
24227
  * ~3.8 MB, allocated whole in hub-main's heap, for a list of keys.
23989
24228
  *
23990
- * Same `deviceId` authorization subject as `getEventMedia`, and the same
23991
- * rows — this is a projection of that method, never a different question.
24229
+ * Same `deviceId` authorization subject as `getEventMedia`, the same
24230
+ * REQUIRED `ownerType` (read that method's docblock for the night-time
24231
+ * thumbnail this closes — D482), and the same rows: this is a projection
24232
+ * of that method, never a different question.
23992
24233
  */
23993
24234
  listEventMedia: method(object({
23994
- eventId: string(),
24235
+ ownerType: EventOwnerTypeSchema,
24236
+ eventId: number().int(),
23995
24237
  deviceId: number()
23996
24238
  }), array(MediaFileInfoSchema).readonly()),
23997
24239
  /**
@@ -30618,8 +30860,8 @@ var IdentitySchema = object({
30618
30860
  id: string(),
30619
30861
  name: string(),
30620
30862
  sampleCount: number().int().nonnegative(),
30621
- coverMediaKey: string().optional(),
30622
- /** Inline base64 of the cover sample's crop, resolved from `coverMediaKey`.
30863
+ coverMediaId: number().int().optional(),
30864
+ /** Inline base64 of the cover sample's crop, resolved from `coverMediaId`.
30623
30865
  * Lets the UI render the person's face avatar instead of a placeholder icon. */
30624
30866
  coverBase64: string().optional()
30625
30867
  });
@@ -30645,8 +30887,8 @@ var IdentitySampleAuditRowSchema = object({
30645
30887
  enrolledAt: number().int(),
30646
30888
  deviceId: number().int().optional(),
30647
30889
  source: _enum(["detected", "photo"]),
30648
- /** The enrolled crop's media key, so the panel can show the evidence. */
30649
- mediaKey: string().optional(),
30890
+ /** The enrolled crop's media row id, so the panel can show the evidence. */
30891
+ mediaId: number().int().optional(),
30650
30892
  /**
30651
30893
  * False = the sample is stamped with a DIFFERENT face model, and a cosine
30652
30894
  * across feature spaces is a well-formed float with no meaning. Every number
@@ -30715,15 +30957,15 @@ var FaceInfoSchema = object({
30715
30957
  */
30716
30958
  cropUrl: string().optional(),
30717
30959
  /** Design B: the face bbox (pixel space) on the key frame — lets a detail
30718
- * view draw the box over the native `keyFrameMediaKey` frame. Absent on
30960
+ * view draw the box over the native `keyFrameMediaId` frame. Absent on
30719
30961
  * legacy rows written before design B. */
30720
30962
  faceBbox: BoundingBoxSchema.optional(),
30721
30963
  /** Design B: MediaStore key of the track's native-resolution key frame.
30722
30964
  * Fetch the native JPEG via the event-media data-plane
30723
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
30965
+ * (`/addon/<addonId>/event-media/m/<keyFrameMediaId>`). Absent when the
30724
30966
  * track produced no key frame (e.g. native/onboard source) — the UI falls
30725
30967
  * back to the inline `base64` face crop. */
30726
- keyFrameMediaKey: string().optional(),
30968
+ keyFrameMediaId: number().int().optional(),
30727
30969
  /** Winning identity-match cosine (0..1) for this face's track, when an
30728
30970
  * identity was auto-confirmed. Lets the UI surface WHY a face was assigned
30729
30971
  * (confidence badge / low-confidence audit). Absent on legacy rows and on
@@ -30792,11 +31034,14 @@ var FaceClusterSchema = object({
30792
31034
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
30793
31035
  * dialog already rendering its key FRAME from the `event-media` plane.
30794
31036
  *
30795
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
30796
- * media key directly, so this needed no new plane and no new access decision.
31037
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
31038
+ * resolves a media row by its own id, so this needed no new plane and no new
31039
+ * access decision.
30797
31040
  */
30798
31041
  var MediaFileLiteSchema$1 = object({
30799
- key: string(),
31042
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
31043
+ * (D482). It was the row's composite key, which embedded the owner. */
31044
+ mediaId: number().int(),
30800
31045
  kind: string(),
30801
31046
  url: string(),
30802
31047
  sizeBytes: number(),
@@ -30818,7 +31063,7 @@ var faceGalleryCapability = {
30818
31063
  * covers inline, ~10 KB of base64 per row, on a query this UI mounts
30819
31064
  * four times and the viewer holds at `staleTime: 30_000`.
30820
31065
  *
30821
- * Nothing loses its avatar: `coverMediaKey` is already on every row and
31066
+ * Nothing loses its avatar: `coverMediaId` is already on every row and
30822
31067
  * the `event-media` plane serves that key `immutable` with an ETag.
30823
31068
  *
30824
31069
  * **This is an INPUT field, so it does not reach the addon until the
@@ -34557,8 +34802,8 @@ var VehicleSchema = object({
34557
34802
  id: string(),
34558
34803
  name: string(),
34559
34804
  sampleCount: number().int().nonnegative(),
34560
- coverMediaKey: string().optional(),
34561
- /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
34805
+ coverMediaId: number().int().optional(),
34806
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaId`. */
34562
34807
  coverBase64: string().optional()
34563
34808
  });
34564
34809
  var VehicleSampleInfoSchema = object({
@@ -34591,7 +34836,7 @@ var PlateInfoSchema = object({
34591
34836
  /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
34592
34837
  plateBbox: BoundingBoxSchema.optional(),
34593
34838
  /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
34594
- keyFrameMediaKey: string().optional(),
34839
+ keyFrameMediaId: number().int().optional(),
34595
34840
  base64: string().optional(),
34596
34841
  /**
34597
34842
  * Same crop as a data-plane URL, always present when the plate has a stored
@@ -34612,11 +34857,14 @@ var PlateInfoSchema = object({
34612
34857
  * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
34613
34858
  * dialog already rendering its key FRAME from the `event-media` plane.
34614
34859
  *
34615
- * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
34616
- * media key directly, so this needed no new plane and no new access decision.
34860
+ * `url` is `/addon/<addonId>/event-media/m/<mediaId>` (D482). The plane
34861
+ * resolves a media row by its own id, so this needed no new plane and no new
34862
+ * access decision.
34617
34863
  */
34618
34864
  var MediaFileLiteSchema = object({
34619
- key: string(),
34865
+ /** The media row's own id — the same address `MediaFileRef.mediaId` carries
34866
+ * (D482). It was the row's composite key, which embedded the owner. */
34867
+ mediaId: number().int(),
34620
34868
  kind: string(),
34621
34869
  url: string(),
34622
34870
  sizeBytes: number(),
@@ -34698,7 +34946,7 @@ var plateGalleryCapability = {
34698
34946
  listVehicles: method(object({
34699
34947
  /** Inline {@link VehicleSchema.coverBase64} on every row. Default
34700
34948
  * `false` — the vehicle twin of `faceGallery.listIdentities`'s
34701
- * option; `coverMediaKey` + the `event-media` plane carry the picture.
34949
+ * option; `coverMediaId` + the `event-media` plane carry the picture.
34702
34950
  * INPUT field: stripped by the hub router until the train ships, which
34703
34951
  * resolves to `false` and is exactly the intended default. */
34704
34952
  includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly()),
@@ -36561,7 +36809,14 @@ var SceneReferenceSchema = object({
36561
36809
  modelId: string(),
36562
36810
  condition: SceneConditionSchema,
36563
36811
  capturedAt: number(),
36564
- thumbnailMediaId: string().optional(),
36812
+ /**
36813
+ * The reference thumbnail's media ROW id (D482).
36814
+ *
36815
+ * The name already said `mediaId` while the value was the row's composite
36816
+ * key — a leftover that read as done and was not. It is the row's own id
36817
+ * now, addressed at `/addon/<addonId>/event-media/m/<id>`.
36818
+ */
36819
+ thumbnailMediaId: number().int().optional(),
36565
36820
  /** Whole-frame (downscaled) embedding captured alongside the ROI crop. The
36566
36821
  * anti-view-shift anchor: a bumped camera, a PTZ preset or a re-aim makes the
36567
36822
  * normalized rect frame a different piece of world, and the scene would
@@ -37652,7 +37907,29 @@ var LoggingSettingsPatchSchema = object({
37652
37907
  * disarmed the channels it did not mention would make the Levels page and
37653
37908
  * the Diagnostics page fight over the same value.
37654
37909
  */
37655
- channels: array(LogChannelWindowPatchSchema).readonly().optional()
37910
+ channels: array(LogChannelWindowPatchSchema).readonly().optional(),
37911
+ /**
37912
+ * How many days the analytics OPS LOG is kept — the audit trail of what
37913
+ * retention actually deleted.
37914
+ *
37915
+ * It lives HERE, on the logging document, and not on the analytics addon's
37916
+ * settings form, on the operator's instruction: every other log-ish window in
37917
+ * this system is configured through this one document, and a retention audit
37918
+ * configured somewhere else is a second place to look when the question is
37919
+ * "what happened to my data".
37920
+ *
37921
+ * That placement has a cost worth stating: this document ships in the
37922
+ * framework closure, so this knob needs a published `@camstack/server` while
37923
+ * the four windows beside it (D485) reach a node on `camstack deploy`.
37924
+ *
37925
+ * `retention-model.ts` argued this one must NOT follow a device window,
37926
+ * because the audit trail is the history of the SWEEP and not of the camera.
37927
+ * That argument stands and is untouched — what changes is that 30 days stops
37928
+ * being a constant nobody can reach. `0` keeps it forever. The floor is 1 day
37929
+ * rather than 0-as-delete: an audit log truncated to nothing is precisely the
37930
+ * state in which the next 9.5 GiB leak cannot be explained.
37931
+ */
37932
+ opsLogRetentionDays: number().int().min(0).max(3650).optional()
37656
37933
  });
37657
37934
  /**
37658
37935
  * Which LAYER of the hierarchy is addressed. Absent = the cluster layer.
@@ -37725,6 +38002,14 @@ var LoggingSettingsStateSchema = object({
37725
38002
  channels: array(LogChannelDescriptorSchema).readonly(),
37726
38003
  /** The channels ARMED right now, each with its deadline. */
37727
38004
  activeChannels: array(LogChannelWindowStateSchema).readonly(),
38005
+ /**
38006
+ * The ops-log window in days, as it is in force. `0` = kept forever.
38007
+ *
38008
+ * Reported rather than left to the caller's memory of what it wrote: this is
38009
+ * the value the analytics sweep will actually use, and an operator asking how
38010
+ * long their audit trail lives should not have to infer it from a default.
38011
+ */
38012
+ opsLogRetentionDays: number().int().min(0),
37728
38013
  persisted: boolean()
37729
38014
  });
37730
38015
  var systemCapability = {
@@ -38696,7 +38981,7 @@ var StationaryObjectSchema = object({
38696
38981
  /** Enrichment label carried from the source track (identity / plate). */
38697
38982
  label: string().optional(),
38698
38983
  /** Native-resolution key-frame media key for the parked object's best image. */
38699
- keyFrameMediaKey: string().optional()
38984
+ keyFrameMediaId: number().int().optional()
38700
38985
  });
38701
38986
  var CameraOccupancySnapshotSchema = object({
38702
38987
  /** Frame timestamp of the inference result that produced this snapshot. */
@@ -40199,6 +40484,12 @@ Object.freeze({
40199
40484
  addonId: null,
40200
40485
  access: "create"
40201
40486
  },
40487
+ "dataStoreProvider.upsert": {
40488
+ capName: "data-store-provider",
40489
+ capScope: "system",
40490
+ addonId: null,
40491
+ access: "create"
40492
+ },
40202
40493
  "dayNight.getOptions": {
40203
40494
  capName: "day-night",
40204
40495
  capScope: "device",
@@ -42635,6 +42926,12 @@ Object.freeze({
42635
42926
  addonId: null,
42636
42927
  access: "view"
42637
42928
  },
42929
+ "pipelineAnalytics.listRecentSummaries": {
42930
+ capName: "pipeline-analytics",
42931
+ capScope: "device",
42932
+ addonId: null,
42933
+ access: "view"
42934
+ },
42638
42935
  "pipelineAnalytics.listRecentTracks": {
42639
42936
  capName: "pipeline-analytics",
42640
42937
  capScope: "device",
@@ -44063,6 +44360,12 @@ Object.freeze({
44063
44360
  addonId: null,
44064
44361
  access: "create"
44065
44362
  },
44363
+ "settingsStore.upsert": {
44364
+ capName: "settings-store",
44365
+ capScope: "system",
44366
+ addonId: null,
44367
+ access: "create"
44368
+ },
44066
44369
  "smtpProvider.getStatus": {
44067
44370
  capName: "smtp-provider",
44068
44371
  capScope: "system",
@@ -46456,6 +46759,11 @@ Object.freeze({
46456
46759
  form: "single",
46457
46760
  optional: true
46458
46761
  }],
46762
+ "pipelineAnalytics.listRecentSummaries": [{
46763
+ name: "deviceIds",
46764
+ form: "array",
46765
+ optional: false
46766
+ }],
46459
46767
  "pipelineAnalytics.listRecentTracks": [{
46460
46768
  name: "deviceIds",
46461
46769
  form: "array",