@camstack/addon-post-analysis 1.2.79 → 1.2.81

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.
@@ -11176,6 +11176,8 @@ var QueryFilterSchema = object({
11176
11176
  where: record(string(), unknown()).optional(),
11177
11177
  whereIn: record(string(), array(unknown())).optional(),
11178
11178
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11179
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
11180
+ whereNot: record(string(), unknown()).optional(),
11179
11181
  orderBy: object({
11180
11182
  field: string(),
11181
11183
  direction: _enum(["asc", "desc"])
@@ -11195,7 +11197,8 @@ var QueryFilterSchema = object({
11195
11197
  var MutationFilterSchema = object({
11196
11198
  where: record(string(), unknown()).optional(),
11197
11199
  whereIn: record(string(), array(unknown())).optional(),
11198
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
11200
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11201
+ whereNot: record(string(), unknown()).optional()
11199
11202
  });
11200
11203
  /** A single stored record: `{ id, data }`. */
11201
11204
  var SettingsRecordSchema = object({
@@ -17824,7 +17827,10 @@ var RecentTracksQueryInput = object({
17824
17827
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
17825
17828
  cursor: string().optional(),
17826
17829
  /** See {@link TrackProjectionSchema}. Default `full`. */
17827
- projection: TrackProjectionSchema.optional()
17830
+ projection: TrackProjectionSchema.optional(),
17831
+ /** Include stationary-promoted rows (parked objects). Default false: the
17832
+ * feed lists passages; parking records live on the stationary registry. */
17833
+ includeStationary: boolean().optional()
17828
17834
  });
17829
17835
  var RecentTracksPageSchema = object({
17830
17836
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -18056,7 +18062,11 @@ var pipelineAnalyticsCapability = {
18056
18062
  zone: TrackZoneFilterSchema.optional(),
18057
18063
  /** See {@link TrackProjectionSchema}. Default `full` (backward
18058
18064
  * compatible — omitting the field keeps today's exact behaviour). */
18059
- projection: TrackProjectionSchema.optional()
18065
+ projection: TrackProjectionSchema.optional(),
18066
+ /** Include stationary-promoted rows (parked objects handed to the
18067
+ * stationary registry). Default false: the timeline lists passages,
18068
+ * not parking records (operator decision, 2026-08-15). */
18069
+ includeStationary: boolean().optional()
18060
18070
  }), array(TrackSchema).readonly()),
18061
18071
  /**
18062
18072
  * Batched cluster-wide track listing — ONE call for the events page /
@@ -18547,7 +18557,8 @@ var pipelineAnalyticsCapability = {
18547
18557
  }),
18548
18558
  getEventMedia: method(object({
18549
18559
  eventId: string(),
18550
- kind: MediaFileKindEnum.optional()
18560
+ kind: MediaFileKindEnum.optional(),
18561
+ deviceId: number()
18551
18562
  }), array(MediaFileSchema).readonly()),
18552
18563
  /** All media rows owned by a track. `kinds` narrows to a kind subset so a
18553
18564
  * client can fetch the SMALL display variants on open and pull the
@@ -18555,7 +18566,8 @@ var pipelineAnalyticsCapability = {
18555
18566
  * Absent ⇒ every kind (back-compat). */
18556
18567
  getTrackMedia: method(object({
18557
18568
  trackId: string(),
18558
- kinds: array(MediaFileKindEnum).optional()
18569
+ kinds: array(MediaFileKindEnum).optional(),
18570
+ deviceId: number()
18559
18571
  }), array(MediaFileSchema).readonly()),
18560
18572
  /**
18561
18573
  * What media a track HAS, without any of it.
@@ -18572,7 +18584,10 @@ var pipelineAnalyticsCapability = {
18572
18584
  * tell a full-resolution variant existed — and the affordance that opens it
18573
18585
  * would silently disappear.
18574
18586
  */
18575
- listTrackMedia: method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()),
18587
+ listTrackMedia: method(object({
18588
+ trackId: string(),
18589
+ deviceId: number()
18590
+ }), array(MediaFileInfoSchema).readonly()),
18576
18591
  /**
18577
18592
  * Search object events by text query using CLIP cosine similarity.
18578
18593
  * Encodes `text` via the `embedding-encoder` cap, queries the
@@ -20652,7 +20667,16 @@ targets: array(object({
20652
20667
  /** A sleeping battery camera: the frame is deliberately stale and will
20653
20668
  * NOT refresh in the background. A surface should say so rather than
20654
20669
  * present it as current. */
20655
- sleeping: boolean()
20670
+ sleeping: boolean(),
20671
+ /** Current device state rendered over the cached frame. State images
20672
+ * remain authoritative even when their photographic background is
20673
+ * old; null means the link must carry a current camera frame. */
20674
+ stateReason: _enum([
20675
+ "disabled",
20676
+ "sleeping",
20677
+ "unreachable",
20678
+ "waking"
20679
+ ]).nullable()
20656
20680
  })));
20657
20681
  /**
20658
20682
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -37418,6 +37442,11 @@ Object.freeze({
37418
37442
  form: "single",
37419
37443
  optional: false
37420
37444
  }],
37445
+ "pipelineAnalytics.getEventMedia": [{
37446
+ name: "deviceId",
37447
+ form: "single",
37448
+ optional: false
37449
+ }],
37421
37450
  "pipelineAnalytics.getKeyEvents": [{
37422
37451
  name: "deviceId",
37423
37452
  form: "single",
@@ -37448,6 +37477,11 @@ Object.freeze({
37448
37477
  form: "single",
37449
37478
  optional: false
37450
37479
  }],
37480
+ "pipelineAnalytics.getTrackMedia": [{
37481
+ name: "deviceId",
37482
+ form: "single",
37483
+ optional: false
37484
+ }],
37451
37485
  "pipelineAnalytics.getTrainingExportSummary": [{
37452
37486
  name: "deviceIds",
37453
37487
  form: "array",
@@ -37483,6 +37517,11 @@ Object.freeze({
37483
37517
  form: "array",
37484
37518
  optional: true
37485
37519
  }],
37520
+ "pipelineAnalytics.listTrackMedia": [{
37521
+ name: "deviceId",
37522
+ form: "single",
37523
+ optional: false
37524
+ }],
37486
37525
  "pipelineAnalytics.listTracks": [{
37487
37526
  name: "deviceId",
37488
37527
  form: "single",
@@ -37923,6 +37962,12 @@ Object.freeze({
37923
37962
  form: "single",
37924
37963
  optional: false
37925
37964
  }],
37965
+ "snapshot.getSnapshotLinks": [{
37966
+ name: "targets",
37967
+ form: "object-array",
37968
+ optional: false,
37969
+ itemField: "deviceId"
37970
+ }],
37926
37971
  "snapshot.getSnapshotOverview": [{
37927
37972
  name: "deviceIds",
37928
37973
  form: "array",
@@ -11207,6 +11207,8 @@ var QueryFilterSchema = object({
11207
11207
  where: record(string(), unknown()).optional(),
11208
11208
  whereIn: record(string(), array(unknown())).optional(),
11209
11209
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11210
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
11211
+ whereNot: record(string(), unknown()).optional(),
11210
11212
  orderBy: object({
11211
11213
  field: string(),
11212
11214
  direction: _enum(["asc", "desc"])
@@ -11226,7 +11228,8 @@ var QueryFilterSchema = object({
11226
11228
  var MutationFilterSchema = object({
11227
11229
  where: record(string(), unknown()).optional(),
11228
11230
  whereIn: record(string(), array(unknown())).optional(),
11229
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
11231
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11232
+ whereNot: record(string(), unknown()).optional()
11230
11233
  });
11231
11234
  /** A single stored record: `{ id, data }`. */
11232
11235
  var SettingsRecordSchema = object({
@@ -17855,7 +17858,10 @@ var RecentTracksQueryInput = object({
17855
17858
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
17856
17859
  cursor: string().optional(),
17857
17860
  /** See {@link TrackProjectionSchema}. Default `full`. */
17858
- projection: TrackProjectionSchema.optional()
17861
+ projection: TrackProjectionSchema.optional(),
17862
+ /** Include stationary-promoted rows (parked objects). Default false: the
17863
+ * feed lists passages; parking records live on the stationary registry. */
17864
+ includeStationary: boolean().optional()
17859
17865
  });
17860
17866
  var RecentTracksPageSchema = object({
17861
17867
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -18087,7 +18093,11 @@ var pipelineAnalyticsCapability = {
18087
18093
  zone: TrackZoneFilterSchema.optional(),
18088
18094
  /** See {@link TrackProjectionSchema}. Default `full` (backward
18089
18095
  * compatible — omitting the field keeps today's exact behaviour). */
18090
- projection: TrackProjectionSchema.optional()
18096
+ projection: TrackProjectionSchema.optional(),
18097
+ /** Include stationary-promoted rows (parked objects handed to the
18098
+ * stationary registry). Default false: the timeline lists passages,
18099
+ * not parking records (operator decision, 2026-08-15). */
18100
+ includeStationary: boolean().optional()
18091
18101
  }), array(TrackSchema).readonly()),
18092
18102
  /**
18093
18103
  * Batched cluster-wide track listing — ONE call for the events page /
@@ -18578,7 +18588,8 @@ var pipelineAnalyticsCapability = {
18578
18588
  }),
18579
18589
  getEventMedia: method(object({
18580
18590
  eventId: string(),
18581
- kind: MediaFileKindEnum.optional()
18591
+ kind: MediaFileKindEnum.optional(),
18592
+ deviceId: number()
18582
18593
  }), array(MediaFileSchema).readonly()),
18583
18594
  /** All media rows owned by a track. `kinds` narrows to a kind subset so a
18584
18595
  * client can fetch the SMALL display variants on open and pull the
@@ -18586,7 +18597,8 @@ var pipelineAnalyticsCapability = {
18586
18597
  * Absent ⇒ every kind (back-compat). */
18587
18598
  getTrackMedia: method(object({
18588
18599
  trackId: string(),
18589
- kinds: array(MediaFileKindEnum).optional()
18600
+ kinds: array(MediaFileKindEnum).optional(),
18601
+ deviceId: number()
18590
18602
  }), array(MediaFileSchema).readonly()),
18591
18603
  /**
18592
18604
  * What media a track HAS, without any of it.
@@ -18603,7 +18615,10 @@ var pipelineAnalyticsCapability = {
18603
18615
  * tell a full-resolution variant existed — and the affordance that opens it
18604
18616
  * would silently disappear.
18605
18617
  */
18606
- listTrackMedia: method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()),
18618
+ listTrackMedia: method(object({
18619
+ trackId: string(),
18620
+ deviceId: number()
18621
+ }), array(MediaFileInfoSchema).readonly()),
18607
18622
  /**
18608
18623
  * Search object events by text query using CLIP cosine similarity.
18609
18624
  * Encodes `text` via the `embedding-encoder` cap, queries the
@@ -20683,7 +20698,16 @@ targets: array(object({
20683
20698
  /** A sleeping battery camera: the frame is deliberately stale and will
20684
20699
  * NOT refresh in the background. A surface should say so rather than
20685
20700
  * present it as current. */
20686
- sleeping: boolean()
20701
+ sleeping: boolean(),
20702
+ /** Current device state rendered over the cached frame. State images
20703
+ * remain authoritative even when their photographic background is
20704
+ * old; null means the link must carry a current camera frame. */
20705
+ stateReason: _enum([
20706
+ "disabled",
20707
+ "sleeping",
20708
+ "unreachable",
20709
+ "waking"
20710
+ ]).nullable()
20687
20711
  })));
20688
20712
  /**
20689
20713
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -37449,6 +37473,11 @@ Object.freeze({
37449
37473
  form: "single",
37450
37474
  optional: false
37451
37475
  }],
37476
+ "pipelineAnalytics.getEventMedia": [{
37477
+ name: "deviceId",
37478
+ form: "single",
37479
+ optional: false
37480
+ }],
37452
37481
  "pipelineAnalytics.getKeyEvents": [{
37453
37482
  name: "deviceId",
37454
37483
  form: "single",
@@ -37479,6 +37508,11 @@ Object.freeze({
37479
37508
  form: "single",
37480
37509
  optional: false
37481
37510
  }],
37511
+ "pipelineAnalytics.getTrackMedia": [{
37512
+ name: "deviceId",
37513
+ form: "single",
37514
+ optional: false
37515
+ }],
37482
37516
  "pipelineAnalytics.getTrainingExportSummary": [{
37483
37517
  name: "deviceIds",
37484
37518
  form: "array",
@@ -37514,6 +37548,11 @@ Object.freeze({
37514
37548
  form: "array",
37515
37549
  optional: true
37516
37550
  }],
37551
+ "pipelineAnalytics.listTrackMedia": [{
37552
+ name: "deviceId",
37553
+ form: "single",
37554
+ optional: false
37555
+ }],
37517
37556
  "pipelineAnalytics.listTracks": [{
37518
37557
  name: "deviceId",
37519
37558
  form: "single",
@@ -37954,6 +37993,12 @@ Object.freeze({
37954
37993
  form: "single",
37955
37994
  optional: false
37956
37995
  }],
37996
+ "snapshot.getSnapshotLinks": [{
37997
+ name: "targets",
37998
+ form: "object-array",
37999
+ optional: false,
38000
+ itemField: "deviceId"
38001
+ }],
37957
38002
  "snapshot.getSnapshotOverview": [{
37958
38003
  name: "deviceIds",
37959
38004
  form: "array",
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CWoaZE47.js");
5
+ const require_dist = require("../dist-XdSS4Y-z.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_fs$1 = require_dist.__toESM(node_fs, 1);
8
8
  node_fs = require_dist.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { $ as hfModelUrl, X as embeddingEncoderCapability, mt as BaseAddon } from "../dist-D6_m_zg1.mjs";
1
+ import { $ as hfModelUrl, X as embeddingEncoderCapability, mt as BaseAddon } from "../dist-CeJ9gG8F.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import "node:fs";
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
3
3
  var e = {
4
4
  "@camstack/sdk": {
5
5
  name: "@camstack/sdk",
6
- version: "1.2.19",
6
+ version: "1.2.20",
7
7
  scope: ["default"],
8
8
  loaded: !1,
9
9
  from: "addon_pipeline_analytics_widgets",
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.77",
21
+ version: "1.2.80",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_analytics_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.54",
36
+ version: "1.2.55",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_pipeline_analytics_widgets",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.77",
39
+ version: "1.2.80",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  singleton: !0,
@@ -45,7 +45,7 @@ async function r() {
45
45
  }
46
46
  },
47
47
  "@camstack/sdk": {
48
- version: "1.2.19",
48
+ version: "1.2.20",
49
49
  scope: "default",
50
50
  shareConfig: {
51
51
  singleton: !0,
@@ -81,7 +81,7 @@ async function r() {
81
81
  }
82
82
  },
83
83
  "@camstack/ui-library": {
84
- version: "1.2.54",
84
+ version: "1.2.55",
85
85
  scope: "default",
86
86
  shareConfig: {
87
87
  singleton: !0,
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CWoaZE47.js");
5
+ const require_dist = require("../dist-XdSS4Y-z.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_dist.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -29050,7 +29050,12 @@ var AnalyticsQueryFacade = class {
29050
29050
  return named ?? track;
29051
29051
  }
29052
29052
  async listTracks(input) {
29053
- return this.withZoneNames(await this.deps.trackStore()?.queryHistorical(input) ?? []);
29053
+ const store = this.deps.trackStore();
29054
+ if (!store) return [];
29055
+ return this.withZoneNames(await store.queryHistorical({
29056
+ ...input,
29057
+ excludeStationaryPromotions: input.includeStationary !== true
29058
+ }));
29054
29059
  }
29055
29060
  /**
29056
29061
  * Batched cluster-wide track listing — one merged (`lastSeen` DESC,
@@ -29060,7 +29065,10 @@ var AnalyticsQueryFacade = class {
29060
29065
  * indexed page + k-way merge and the cursor encoding.
29061
29066
  */
29062
29067
  async listRecentTracks(input) {
29063
- const page = await this.deps.trackStore()?.queryRecent(input) ?? {
29068
+ const page = await this.deps.trackStore()?.queryRecent({
29069
+ ...input,
29070
+ excludeStationaryPromotions: input.includeStationary !== true
29071
+ }) ?? {
29064
29072
  tracks: [],
29065
29073
  nextCursor: null
29066
29074
  };
@@ -29092,11 +29100,11 @@ var AnalyticsQueryFacade = class {
29092
29100
  return this.withMediaUrl(events);
29093
29101
  }
29094
29102
  async getEventMedia(input) {
29095
- const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId) ?? Promise.resolve([]));
29103
+ const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId, input.deviceId) ?? Promise.resolve([]));
29096
29104
  return input.kind ? all.filter((m) => m.kind === input.kind) : all;
29097
29105
  }
29098
29106
  async getTrackMedia(input) {
29099
- const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId) ?? Promise.resolve([]));
29107
+ const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]));
29100
29108
  const kinds = input.kinds;
29101
29109
  return kinds && kinds.length > 0 ? all.filter((m) => kinds.includes(m.kind)) : all;
29102
29110
  }
@@ -29110,7 +29118,7 @@ var AnalyticsQueryFacade = class {
29110
29118
  * ~10 MB of string allocated, to answer a question the index already holds.
29111
29119
  */
29112
29120
  async listTrackMedia(input) {
29113
- return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId) ?? Promise.resolve([]);
29121
+ return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]);
29114
29122
  }
29115
29123
  async pruneEventsBefore(input) {
29116
29124
  const eventStore = this.deps.eventStore();
@@ -34551,13 +34559,23 @@ var MediaStore = class {
34551
34559
  } });
34552
34560
  }
34553
34561
  }
34554
- async listByOwner(ownerKind, ownerId) {
34562
+ /**
34563
+ * `deviceId` is the AUTHORIZATION filter: every media row is stamped with
34564
+ * its device at write time, so scoping the query by it makes a media read
34565
+ * for a device the caller cannot name return nothing — the DB itself
34566
+ * enforces the boundary, no post-hoc ownership check to forget.
34567
+ */
34568
+ async listByOwner(ownerKind, ownerId, deviceId) {
34555
34569
  const rows = await this.store.query.query({
34556
34570
  collection: MEDIA_COLLECTION,
34557
34571
  filter: {
34558
- where: {
34572
+ where: deviceId === void 0 ? {
34559
34573
  ownerKind,
34560
34574
  ownerId
34575
+ } : {
34576
+ ownerKind,
34577
+ ownerId,
34578
+ deviceId
34561
34579
  },
34562
34580
  orderBy: {
34563
34581
  field: "timestamp",
@@ -34608,13 +34626,17 @@ var MediaStore = class {
34608
34626
  * crops), and retention deletes it later — a manifest frozen at close would
34609
34627
  * advertise tiles that 404 and hide ones that exist.
34610
34628
  */
34611
- async listInfoByOwner(ownerKind, ownerId) {
34629
+ async listInfoByOwner(ownerKind, ownerId, deviceId) {
34612
34630
  return (await this.store.query.query({
34613
34631
  collection: MEDIA_COLLECTION,
34614
34632
  filter: {
34615
- where: {
34633
+ where: deviceId === void 0 ? {
34616
34634
  ownerKind,
34617
34635
  ownerId
34636
+ } : {
34637
+ ownerKind,
34638
+ ownerId,
34639
+ deviceId
34618
34640
  },
34619
34641
  orderBy: {
34620
34642
  field: "timestamp",
@@ -41082,11 +41104,12 @@ function isAfterCursor(row, cursor) {
41082
41104
  * `'stationary-promotion'` is the only member and means: the tracker decided
41083
41105
  * this subject had PARKED, handed it to the stationary registry, and the row
41084
41106
  * was written so its media, events and vector keep an owner (D161). It is not a
41085
- * quality signal and it never hides the track — a parked car is still a track,
41086
- * still on the events page, still swept by the same retention. It exists so a
41087
- * consumer that wants PASSAGES can say so exactly, instead of re-deriving
41088
- * "did it move" from a trajectory downstream and getting it wrong on the
41089
- * subject that stood still for one honest reason.
41107
+ * quality signal — and since 2026-08-15 it is NOT on the default read path:
41108
+ * the parked row exists to own media and serve the stationary registry, while
41109
+ * the events feed and the per-camera timeline exclude it by default
41110
+ * (`excludeStationaryPromotions` — operator decision: a parked object updates
41111
+ * the stationary record, it is not a passage). Retention and forensics read
41112
+ * the store WITHOUT the exclusion, so the row keeps its D161 cascade.
41090
41113
  *
41091
41114
  * An ABSENT value means "expired normally, or written before this marker
41092
41115
  * existed". Never read an absence as a positive — same rule as `hasFace`.
@@ -42268,11 +42291,13 @@ var TrackStore = class {
42268
42291
  async queryHistorical(params) {
42269
42292
  const limit = params.limit ?? 50;
42270
42293
  const timeBetween = params.since !== void 0 || params.until !== void 0 ? { firstSeen: [params.since ?? 0, params.until ?? Date.now()] } : {};
42294
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42271
42295
  if (params.zone === void 0) return (await this.store.query.query({
42272
42296
  collection: TRACKS_COLLECTION,
42273
42297
  filter: {
42274
42298
  where: { deviceId: params.deviceId },
42275
42299
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42300
+ ...stationaryExclusion,
42276
42301
  orderBy: {
42277
42302
  field: "firstSeen",
42278
42303
  direction: "desc"
@@ -42286,6 +42311,7 @@ var TrackStore = class {
42286
42311
  collection: TRACKS_COLLECTION,
42287
42312
  filter: {
42288
42313
  where: { deviceId: params.deviceId },
42314
+ ...stationaryExclusion,
42289
42315
  whereBetween: {
42290
42316
  ...timeBetween,
42291
42317
  envMinX: [-1e6, bounds.maxX],
@@ -42304,6 +42330,7 @@ var TrackStore = class {
42304
42330
  collection: TRACKS_COLLECTION,
42305
42331
  filter: {
42306
42332
  where: { deviceId: params.deviceId },
42333
+ ...stationaryExclusion,
42307
42334
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42308
42335
  orderBy: {
42309
42336
  field: "firstSeen",
@@ -42386,10 +42413,12 @@ var TrackStore = class {
42386
42413
  tracks: [],
42387
42414
  nextCursor: null
42388
42415
  };
42416
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42389
42417
  const fetchRange = async (deviceId, range, pageLimit) => this.store.query.query({
42390
42418
  collection: TRACKS_COLLECTION,
42391
42419
  filter: {
42392
42420
  where: { deviceId },
42421
+ ...stationaryExclusion,
42393
42422
  whereBetween: { lastSeen: range },
42394
42423
  orderBy: {
42395
42424
  field: "lastSeen",
@@ -49899,11 +49928,22 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
49899
49928
  async onInitialize() {
49900
49929
  const api = this.ctx.api;
49901
49930
  if (!api) throw new Error("pipeline-analytics requires ctx.api (device-manager + settings-store)");
49902
- await this.declareCollections(api);
49903
- await this.declareAlarmPanel(api);
49931
+ const bootT0 = Date.now();
49932
+ const step = async (name, fn) => {
49933
+ const t0 = Date.now();
49934
+ const out = await fn();
49935
+ this.ctx.logger.info("pipeline-analytics boot step", { meta: {
49936
+ step: name,
49937
+ ms: Date.now() - t0,
49938
+ sinceBootMs: Date.now() - bootT0
49939
+ } });
49940
+ return out;
49941
+ };
49942
+ await step("declareCollections", () => this.declareCollections(api));
49943
+ await step("declareAlarmPanel", () => this.declareAlarmPanel(api));
49904
49944
  const logger = this.ctx.logger;
49905
- const storage = await this.resolveMediaStorage(logger);
49906
- const stores = await this.buildStores(api, logger, storage);
49945
+ const storage = await step("resolveMediaStorage", () => this.resolveMediaStorage(logger));
49946
+ const stores = await step("buildStores", () => this.buildStores(api, logger, storage));
49907
49947
  const rawNodeId = this.ctx.kernel.localNodeId ?? this.ctx.id;
49908
49948
  const ownNodeId = rawNodeId.includes("/") ? rawNodeId.split("/")[0] : rawNodeId;
49909
49949
  this.eventsOpsLog = new OpsLogStore({
@@ -49912,7 +49952,7 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
49912
49952
  nodeId: ownNodeId
49913
49953
  });
49914
49954
  {
49915
- const designated = await this.postProcessingNodeState.get();
49955
+ const designated = await step("postProcessingNodeState", () => this.postProcessingNodeState.get());
49916
49956
  this.isPostProcessingNode = ownNodeId === designated;
49917
49957
  if (!this.isPostProcessingNode) logger.info("pipeline-analytics INERT on this node — post-processing assigned elsewhere", { meta: {
49918
49958
  ownNodeId,
@@ -49936,8 +49976,8 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
49936
49976
  this.buildRecognizers(api, logger, stores, transport);
49937
49977
  this.buildDetailDispatcher(api, logger, transport);
49938
49978
  const capProviders = this.buildAnalyticsProviders(api, logger);
49939
- await this.buildNotificationCenter(api, logger, stores);
49940
- await this.serveEventMediaDataPlane();
49979
+ await step("buildNotificationCenter", () => this.buildNotificationCenter(api, logger, stores));
49980
+ await step("serveEventMediaDataPlane", () => this.serveEventMediaDataPlane());
49941
49981
  this.subscriptionUnsubs = wireSubscriptions({
49942
49982
  eventBus: this.ctx.eventBus,
49943
49983
  isPostProcessingNode: this.isPostProcessingNode,
@@ -49953,11 +49993,11 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
49953
49993
  onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now()),
49954
49994
  onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event)
49955
49995
  });
49956
- if (this.isPostProcessingNode) await this.startEmbeddingDispatcher(logger, transport);
49957
- await this.buildSceneMonitor(api, logger);
49996
+ if (this.isPostProcessingNode) await step("startEmbeddingDispatcher", () => this.startEmbeddingDispatcher(logger, transport));
49997
+ await step("buildSceneMonitor", () => this.buildSceneMonitor(api, logger));
49958
49998
  this.startSweepTimers();
49959
49999
  this.ctx.logger.info("pipeline-analytics subscribers installed");
49960
- const providers = this.buildProviderRegistrations(api, stores, capProviders);
50000
+ const providers = await step("buildProviderRegistrations", () => this.buildProviderRegistrations(api, stores, capProviders));
49961
50001
  const isHub = ownNodeId === "hub";
49962
50002
  const ncHandlers = isHub ? this.buildNcActionHandlers(api) : void 0;
49963
50003
  return {
@@ -50132,26 +50172,30 @@ var PipelineAnalyticsAddon = class extends require_dist.BaseAddon {
50132
50172
  }
50133
50173
  async declareCollections(api) {
50134
50174
  this.settingsStore = api.settingsStore;
50135
- await TrackStore.declare(api.settingsStore);
50136
- await MediaStore.declare(api.settingsStore);
50137
- await EventStore.declare(api.settingsStore);
50138
- await SensorEventStore.declare(api.settingsStore);
50139
- await IdentityStore.declare(api.settingsStore);
50140
- await FaceStore.declare(api.settingsStore);
50141
- await PlateStore.declare(api.settingsStore);
50142
- await VehicleStore.declare(api.settingsStore);
50143
- await StationaryObjectRegistry.declare(api.settingsStore);
50144
- await RetrainFrameStore.declare(api.settingsStore);
50145
- await RetrainAnnotationStore.declare(api.settingsStore);
50146
- await OpsLogStore.declare(api.settingsStore);
50147
- await AnalyticsLts.declare(api.settingsStore);
50148
- await SceneStore.declare(api.settingsStore);
50149
- await NotificationCenter.declare(api.settingsStore);
50150
- await runLabelTierMigration({
50175
+ await Promise.all([
50176
+ TrackStore.declare(api.settingsStore),
50177
+ MediaStore.declare(api.settingsStore),
50178
+ EventStore.declare(api.settingsStore),
50179
+ SensorEventStore.declare(api.settingsStore),
50180
+ IdentityStore.declare(api.settingsStore),
50181
+ FaceStore.declare(api.settingsStore),
50182
+ PlateStore.declare(api.settingsStore),
50183
+ VehicleStore.declare(api.settingsStore),
50184
+ StationaryObjectRegistry.declare(api.settingsStore),
50185
+ RetrainFrameStore.declare(api.settingsStore),
50186
+ RetrainAnnotationStore.declare(api.settingsStore),
50187
+ OpsLogStore.declare(api.settingsStore),
50188
+ AnalyticsLts.declare(api.settingsStore),
50189
+ SceneStore.declare(api.settingsStore),
50190
+ NotificationCenter.declare(api.settingsStore)
50191
+ ]);
50192
+ runLabelTierMigration({
50151
50193
  store: api.settingsStore,
50152
50194
  logger: this.ctx.logger,
50153
50195
  now: () => Date.now()
50154
- }, [TRACKS_COLLECTION, OBJECT_EVENTS_COLLECTION]);
50196
+ }, [TRACKS_COLLECTION, OBJECT_EVENTS_COLLECTION]).catch((err) => {
50197
+ this.ctx.logger.warn("label tier migration failed — will retry next boot", { meta: { error: err instanceof Error ? err.message : String(err) } });
50198
+ });
50155
50199
  }
50156
50200
  /** Event-media storage. By default it uses `ctx.kernel.storage`, rooted at
50157
50201
  * the node data dir (appData) → heavy thumbnail/track-frame churn hammers
@@ -1,4 +1,4 @@
1
- import { At as string, B as addonWidgetsSourceCapability, C as NcSnoozeSchema, Ct as boolean, D as OpsLogEntrySchema, Dt as object, E as NcTaxonomySchema, Et as number, F as TIMELAPSE_DENSE_FLOOR_SEC, G as buildEventKindDescriptor, H as assertTimelapseCadences, I as TimelapseRuleInputSchema, J as defineCustomActions, K as cosineSimilarity$1, L as TimelapseRulePatchSchema, M as SCENE_DEFAULT_UNCOVERED_POLICY, Mt as EventCategory, N as SCENE_DIVERGED, O as RECORDING_EXPORT_MAX_READ_BYTES, Ot as partialRecord, P as SceneMonitorSchema, Q as faceGalleryCapability, R as TimelapseRuleSchema, S as NcSnoozeInputSchema, St as array, T as NcSystemEventKindSchema, Tt as literal, U as audioMetricsCapability, V as alarmPanelCapability, W as audioModeOf, Y as deriveRecordingMode, Z as encodeVectorBase64, _ as NcRuleInputSchema, _t as hydrateSchema, at as plateGalleryCapability, b as NcRuleTargetSchema, bt as sleep, c as EVENT_PAD_MS, ct as sceneMonitorCapability, d as NC_ALARM_SYSTEM_EVENT_KINDS, dt as videoclipsCapability, et as isDetectionMacroClass, f as NC_CONDITION_CATALOG, ft as zoneAnalyticsCapability, g as NcConditionDescriptorSchema, gt as createEvent, h as NC_TAXONOMY, ht as DeviceType, i as DETECTION_MACRO_CLASSES, it as pipelineAnalyticsCapability, j as SCENE_DEFAULT_ANCHOR_THRESHOLD, jt as unknown, k as RetrainStatusSchema, kt as record, l as LabelAttributionSchema, lt as subKindsOf, mt as BaseAddon, n as DEFAULT_EVENT_COLOR, nt as kebabToCamel, o as DeclaredDevices, ot as readDeviceStateFrom, p as NC_DEFAULT_SNOOZE_MINUTES, pt as errMsg, q as customAction, rt as notificationRulesCapability, s as EVENT_KIND_BY_CAP, st as readTimelapseGeneratedAt, t as BaseDevice, tt as isScheduleActive, u as MACRO_LABELS, ut as vectorDimFromBase64, v as NcRulePatchSchema, vt as isDeviceScopedCap, w as NcSnoozeSuppressedSchema, wt as discriminatedUnion, x as NcScheduleSchema, xt as _enum, y as NcRuleSchema, yt as nodePin, z as TrackSourceSchema } from "../dist-D6_m_zg1.mjs";
1
+ import { At as string, B as addonWidgetsSourceCapability, C as NcSnoozeSchema, Ct as boolean, D as OpsLogEntrySchema, Dt as object, E as NcTaxonomySchema, Et as number, F as TIMELAPSE_DENSE_FLOOR_SEC, G as buildEventKindDescriptor, H as assertTimelapseCadences, I as TimelapseRuleInputSchema, J as defineCustomActions, K as cosineSimilarity$1, L as TimelapseRulePatchSchema, M as SCENE_DEFAULT_UNCOVERED_POLICY, Mt as EventCategory, N as SCENE_DIVERGED, O as RECORDING_EXPORT_MAX_READ_BYTES, Ot as partialRecord, P as SceneMonitorSchema, Q as faceGalleryCapability, R as TimelapseRuleSchema, S as NcSnoozeInputSchema, St as array, T as NcSystemEventKindSchema, Tt as literal, U as audioMetricsCapability, V as alarmPanelCapability, W as audioModeOf, Y as deriveRecordingMode, Z as encodeVectorBase64, _ as NcRuleInputSchema, _t as hydrateSchema, at as plateGalleryCapability, b as NcRuleTargetSchema, bt as sleep, c as EVENT_PAD_MS, ct as sceneMonitorCapability, d as NC_ALARM_SYSTEM_EVENT_KINDS, dt as videoclipsCapability, et as isDetectionMacroClass, f as NC_CONDITION_CATALOG, ft as zoneAnalyticsCapability, g as NcConditionDescriptorSchema, gt as createEvent, h as NC_TAXONOMY, ht as DeviceType, i as DETECTION_MACRO_CLASSES, it as pipelineAnalyticsCapability, j as SCENE_DEFAULT_ANCHOR_THRESHOLD, jt as unknown, k as RetrainStatusSchema, kt as record, l as LabelAttributionSchema, lt as subKindsOf, mt as BaseAddon, n as DEFAULT_EVENT_COLOR, nt as kebabToCamel, o as DeclaredDevices, ot as readDeviceStateFrom, p as NC_DEFAULT_SNOOZE_MINUTES, pt as errMsg, q as customAction, rt as notificationRulesCapability, s as EVENT_KIND_BY_CAP, st as readTimelapseGeneratedAt, t as BaseDevice, tt as isScheduleActive, u as MACRO_LABELS, ut as vectorDimFromBase64, v as NcRulePatchSchema, vt as isDeviceScopedCap, w as NcSnoozeSuppressedSchema, wt as discriminatedUnion, x as NcScheduleSchema, xt as _enum, y as NcRuleSchema, yt as nodePin, z as TrackSourceSchema } from "../dist-CeJ9gG8F.mjs";
2
2
  import { t as __exportAll } from "../embedding-encoder/index.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { promises } from "node:fs";
@@ -28992,7 +28992,12 @@ var AnalyticsQueryFacade = class {
28992
28992
  return named ?? track;
28993
28993
  }
28994
28994
  async listTracks(input) {
28995
- return this.withZoneNames(await this.deps.trackStore()?.queryHistorical(input) ?? []);
28995
+ const store = this.deps.trackStore();
28996
+ if (!store) return [];
28997
+ return this.withZoneNames(await store.queryHistorical({
28998
+ ...input,
28999
+ excludeStationaryPromotions: input.includeStationary !== true
29000
+ }));
28996
29001
  }
28997
29002
  /**
28998
29003
  * Batched cluster-wide track listing — one merged (`lastSeen` DESC,
@@ -29002,7 +29007,10 @@ var AnalyticsQueryFacade = class {
29002
29007
  * indexed page + k-way merge and the cursor encoding.
29003
29008
  */
29004
29009
  async listRecentTracks(input) {
29005
- const page = await this.deps.trackStore()?.queryRecent(input) ?? {
29010
+ const page = await this.deps.trackStore()?.queryRecent({
29011
+ ...input,
29012
+ excludeStationaryPromotions: input.includeStationary !== true
29013
+ }) ?? {
29006
29014
  tracks: [],
29007
29015
  nextCursor: null
29008
29016
  };
@@ -29034,11 +29042,11 @@ var AnalyticsQueryFacade = class {
29034
29042
  return this.withMediaUrl(events);
29035
29043
  }
29036
29044
  async getEventMedia(input) {
29037
- const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId) ?? Promise.resolve([]));
29045
+ const all = await (this.deps.mediaStore()?.listByOwner("event", input.eventId, input.deviceId) ?? Promise.resolve([]));
29038
29046
  return input.kind ? all.filter((m) => m.kind === input.kind) : all;
29039
29047
  }
29040
29048
  async getTrackMedia(input) {
29041
- const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId) ?? Promise.resolve([]));
29049
+ const all = await (this.deps.mediaStore()?.listByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]));
29042
29050
  const kinds = input.kinds;
29043
29051
  return kinds && kinds.length > 0 ? all.filter((m) => kinds.includes(m.kind)) : all;
29044
29052
  }
@@ -29052,7 +29060,7 @@ var AnalyticsQueryFacade = class {
29052
29060
  * ~10 MB of string allocated, to answer a question the index already holds.
29053
29061
  */
29054
29062
  async listTrackMedia(input) {
29055
- return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId) ?? Promise.resolve([]);
29063
+ return this.deps.mediaStore()?.listInfoByOwner("track", input.trackId, input.deviceId) ?? Promise.resolve([]);
29056
29064
  }
29057
29065
  async pruneEventsBefore(input) {
29058
29066
  const eventStore = this.deps.eventStore();
@@ -34493,13 +34501,23 @@ var MediaStore = class {
34493
34501
  } });
34494
34502
  }
34495
34503
  }
34496
- async listByOwner(ownerKind, ownerId) {
34504
+ /**
34505
+ * `deviceId` is the AUTHORIZATION filter: every media row is stamped with
34506
+ * its device at write time, so scoping the query by it makes a media read
34507
+ * for a device the caller cannot name return nothing — the DB itself
34508
+ * enforces the boundary, no post-hoc ownership check to forget.
34509
+ */
34510
+ async listByOwner(ownerKind, ownerId, deviceId) {
34497
34511
  const rows = await this.store.query.query({
34498
34512
  collection: MEDIA_COLLECTION,
34499
34513
  filter: {
34500
- where: {
34514
+ where: deviceId === void 0 ? {
34501
34515
  ownerKind,
34502
34516
  ownerId
34517
+ } : {
34518
+ ownerKind,
34519
+ ownerId,
34520
+ deviceId
34503
34521
  },
34504
34522
  orderBy: {
34505
34523
  field: "timestamp",
@@ -34550,13 +34568,17 @@ var MediaStore = class {
34550
34568
  * crops), and retention deletes it later — a manifest frozen at close would
34551
34569
  * advertise tiles that 404 and hide ones that exist.
34552
34570
  */
34553
- async listInfoByOwner(ownerKind, ownerId) {
34571
+ async listInfoByOwner(ownerKind, ownerId, deviceId) {
34554
34572
  return (await this.store.query.query({
34555
34573
  collection: MEDIA_COLLECTION,
34556
34574
  filter: {
34557
- where: {
34575
+ where: deviceId === void 0 ? {
34558
34576
  ownerKind,
34559
34577
  ownerId
34578
+ } : {
34579
+ ownerKind,
34580
+ ownerId,
34581
+ deviceId
34560
34582
  },
34561
34583
  orderBy: {
34562
34584
  field: "timestamp",
@@ -41024,11 +41046,12 @@ function isAfterCursor(row, cursor) {
41024
41046
  * `'stationary-promotion'` is the only member and means: the tracker decided
41025
41047
  * this subject had PARKED, handed it to the stationary registry, and the row
41026
41048
  * was written so its media, events and vector keep an owner (D161). It is not a
41027
- * quality signal and it never hides the track — a parked car is still a track,
41028
- * still on the events page, still swept by the same retention. It exists so a
41029
- * consumer that wants PASSAGES can say so exactly, instead of re-deriving
41030
- * "did it move" from a trajectory downstream and getting it wrong on the
41031
- * subject that stood still for one honest reason.
41049
+ * quality signal — and since 2026-08-15 it is NOT on the default read path:
41050
+ * the parked row exists to own media and serve the stationary registry, while
41051
+ * the events feed and the per-camera timeline exclude it by default
41052
+ * (`excludeStationaryPromotions` — operator decision: a parked object updates
41053
+ * the stationary record, it is not a passage). Retention and forensics read
41054
+ * the store WITHOUT the exclusion, so the row keeps its D161 cascade.
41032
41055
  *
41033
41056
  * An ABSENT value means "expired normally, or written before this marker
41034
41057
  * existed". Never read an absence as a positive — same rule as `hasFace`.
@@ -42210,11 +42233,13 @@ var TrackStore = class {
42210
42233
  async queryHistorical(params) {
42211
42234
  const limit = params.limit ?? 50;
42212
42235
  const timeBetween = params.since !== void 0 || params.until !== void 0 ? { firstSeen: [params.since ?? 0, params.until ?? Date.now()] } : {};
42236
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42213
42237
  if (params.zone === void 0) return (await this.store.query.query({
42214
42238
  collection: TRACKS_COLLECTION,
42215
42239
  filter: {
42216
42240
  where: { deviceId: params.deviceId },
42217
42241
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42242
+ ...stationaryExclusion,
42218
42243
  orderBy: {
42219
42244
  field: "firstSeen",
42220
42245
  direction: "desc"
@@ -42228,6 +42253,7 @@ var TrackStore = class {
42228
42253
  collection: TRACKS_COLLECTION,
42229
42254
  filter: {
42230
42255
  where: { deviceId: params.deviceId },
42256
+ ...stationaryExclusion,
42231
42257
  whereBetween: {
42232
42258
  ...timeBetween,
42233
42259
  envMinX: [-1e6, bounds.maxX],
@@ -42246,6 +42272,7 @@ var TrackStore = class {
42246
42272
  collection: TRACKS_COLLECTION,
42247
42273
  filter: {
42248
42274
  where: { deviceId: params.deviceId },
42275
+ ...stationaryExclusion,
42249
42276
  ...Object.keys(timeBetween).length > 0 ? { whereBetween: timeBetween } : {},
42250
42277
  orderBy: {
42251
42278
  field: "firstSeen",
@@ -42328,10 +42355,12 @@ var TrackStore = class {
42328
42355
  tracks: [],
42329
42356
  nextCursor: null
42330
42357
  };
42358
+ const stationaryExclusion = params.excludeStationaryPromotions === true ? { whereNot: { closedReason: "stationary-promotion" } } : {};
42331
42359
  const fetchRange = async (deviceId, range, pageLimit) => this.store.query.query({
42332
42360
  collection: TRACKS_COLLECTION,
42333
42361
  filter: {
42334
42362
  where: { deviceId },
42363
+ ...stationaryExclusion,
42335
42364
  whereBetween: { lastSeen: range },
42336
42365
  orderBy: {
42337
42366
  field: "lastSeen",
@@ -49841,11 +49870,22 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
49841
49870
  async onInitialize() {
49842
49871
  const api = this.ctx.api;
49843
49872
  if (!api) throw new Error("pipeline-analytics requires ctx.api (device-manager + settings-store)");
49844
- await this.declareCollections(api);
49845
- await this.declareAlarmPanel(api);
49873
+ const bootT0 = Date.now();
49874
+ const step = async (name, fn) => {
49875
+ const t0 = Date.now();
49876
+ const out = await fn();
49877
+ this.ctx.logger.info("pipeline-analytics boot step", { meta: {
49878
+ step: name,
49879
+ ms: Date.now() - t0,
49880
+ sinceBootMs: Date.now() - bootT0
49881
+ } });
49882
+ return out;
49883
+ };
49884
+ await step("declareCollections", () => this.declareCollections(api));
49885
+ await step("declareAlarmPanel", () => this.declareAlarmPanel(api));
49846
49886
  const logger = this.ctx.logger;
49847
- const storage = await this.resolveMediaStorage(logger);
49848
- const stores = await this.buildStores(api, logger, storage);
49887
+ const storage = await step("resolveMediaStorage", () => this.resolveMediaStorage(logger));
49888
+ const stores = await step("buildStores", () => this.buildStores(api, logger, storage));
49849
49889
  const rawNodeId = this.ctx.kernel.localNodeId ?? this.ctx.id;
49850
49890
  const ownNodeId = rawNodeId.includes("/") ? rawNodeId.split("/")[0] : rawNodeId;
49851
49891
  this.eventsOpsLog = new OpsLogStore({
@@ -49854,7 +49894,7 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
49854
49894
  nodeId: ownNodeId
49855
49895
  });
49856
49896
  {
49857
- const designated = await this.postProcessingNodeState.get();
49897
+ const designated = await step("postProcessingNodeState", () => this.postProcessingNodeState.get());
49858
49898
  this.isPostProcessingNode = ownNodeId === designated;
49859
49899
  if (!this.isPostProcessingNode) logger.info("pipeline-analytics INERT on this node — post-processing assigned elsewhere", { meta: {
49860
49900
  ownNodeId,
@@ -49878,8 +49918,8 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
49878
49918
  this.buildRecognizers(api, logger, stores, transport);
49879
49919
  this.buildDetailDispatcher(api, logger, transport);
49880
49920
  const capProviders = this.buildAnalyticsProviders(api, logger);
49881
- await this.buildNotificationCenter(api, logger, stores);
49882
- await this.serveEventMediaDataPlane();
49921
+ await step("buildNotificationCenter", () => this.buildNotificationCenter(api, logger, stores));
49922
+ await step("serveEventMediaDataPlane", () => this.serveEventMediaDataPlane());
49883
49923
  this.subscriptionUnsubs = wireSubscriptions({
49884
49924
  eventBus: this.ctx.eventBus,
49885
49925
  isPostProcessingNode: this.isPostProcessingNode,
@@ -49895,11 +49935,11 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
49895
49935
  onClusterTopology: (nodes) => this.nodeLiveness.applySnapshot(nodes, Date.now()),
49896
49936
  onSystemEvent: (event) => this.notificationCenter?.onSystemEvent(event)
49897
49937
  });
49898
- if (this.isPostProcessingNode) await this.startEmbeddingDispatcher(logger, transport);
49899
- await this.buildSceneMonitor(api, logger);
49938
+ if (this.isPostProcessingNode) await step("startEmbeddingDispatcher", () => this.startEmbeddingDispatcher(logger, transport));
49939
+ await step("buildSceneMonitor", () => this.buildSceneMonitor(api, logger));
49900
49940
  this.startSweepTimers();
49901
49941
  this.ctx.logger.info("pipeline-analytics subscribers installed");
49902
- const providers = this.buildProviderRegistrations(api, stores, capProviders);
49942
+ const providers = await step("buildProviderRegistrations", () => this.buildProviderRegistrations(api, stores, capProviders));
49903
49943
  const isHub = ownNodeId === "hub";
49904
49944
  const ncHandlers = isHub ? this.buildNcActionHandlers(api) : void 0;
49905
49945
  return {
@@ -50074,26 +50114,30 @@ var PipelineAnalyticsAddon = class extends BaseAddon {
50074
50114
  }
50075
50115
  async declareCollections(api) {
50076
50116
  this.settingsStore = api.settingsStore;
50077
- await TrackStore.declare(api.settingsStore);
50078
- await MediaStore.declare(api.settingsStore);
50079
- await EventStore.declare(api.settingsStore);
50080
- await SensorEventStore.declare(api.settingsStore);
50081
- await IdentityStore.declare(api.settingsStore);
50082
- await FaceStore.declare(api.settingsStore);
50083
- await PlateStore.declare(api.settingsStore);
50084
- await VehicleStore.declare(api.settingsStore);
50085
- await StationaryObjectRegistry.declare(api.settingsStore);
50086
- await RetrainFrameStore.declare(api.settingsStore);
50087
- await RetrainAnnotationStore.declare(api.settingsStore);
50088
- await OpsLogStore.declare(api.settingsStore);
50089
- await AnalyticsLts.declare(api.settingsStore);
50090
- await SceneStore.declare(api.settingsStore);
50091
- await NotificationCenter.declare(api.settingsStore);
50092
- await runLabelTierMigration({
50117
+ await Promise.all([
50118
+ TrackStore.declare(api.settingsStore),
50119
+ MediaStore.declare(api.settingsStore),
50120
+ EventStore.declare(api.settingsStore),
50121
+ SensorEventStore.declare(api.settingsStore),
50122
+ IdentityStore.declare(api.settingsStore),
50123
+ FaceStore.declare(api.settingsStore),
50124
+ PlateStore.declare(api.settingsStore),
50125
+ VehicleStore.declare(api.settingsStore),
50126
+ StationaryObjectRegistry.declare(api.settingsStore),
50127
+ RetrainFrameStore.declare(api.settingsStore),
50128
+ RetrainAnnotationStore.declare(api.settingsStore),
50129
+ OpsLogStore.declare(api.settingsStore),
50130
+ AnalyticsLts.declare(api.settingsStore),
50131
+ SceneStore.declare(api.settingsStore),
50132
+ NotificationCenter.declare(api.settingsStore)
50133
+ ]);
50134
+ runLabelTierMigration({
50093
50135
  store: api.settingsStore,
50094
50136
  logger: this.ctx.logger,
50095
50137
  now: () => Date.now()
50096
- }, [TRACKS_COLLECTION, OBJECT_EVENTS_COLLECTION]);
50138
+ }, [TRACKS_COLLECTION, OBJECT_EVENTS_COLLECTION]).catch((err) => {
50139
+ this.ctx.logger.warn("label tier migration failed — will retry next boot", { meta: { error: err instanceof Error ? err.message : String(err) } });
50140
+ });
50097
50141
  }
50098
50142
  /** Event-media storage. By default it uses `ctx.kernel.storage`, rooted at
50099
50143
  * the node data dir (appData) → heavy thumbnail/track-frame churn hammers
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DhzYNXUk.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DyVoMMtp.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.79",
3
+ "version": "1.2.81",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",