@camstack/addon-post-analysis 1.2.290 → 1.2.291

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.
@@ -23215,7 +23215,15 @@ var RebuildObjectEmbeddingsInput = object({
23215
23215
  * force is logged at start and finish so a deliberately slow pass reads
23216
23216
  * differently from a stalled one.
23217
23217
  */
23218
- pacingMs: number().int().nonnegative().optional()
23218
+ pacingMs: number().int().nonnegative().optional(),
23219
+ /**
23220
+ * Re-embed ONLY the tracks whose vector is ALREADY in the pinned model's
23221
+ * space (D664) — the in-space skip inverted. It exists to redo vectors
23222
+ * written from the wrong box, which a normal pass would skip as done
23223
+ * forever. Requires `maxTracks`: a forced pass without a bound is refused
23224
+ * (`force-requires-max-tracks`).
23225
+ */
23226
+ force: boolean().optional()
23219
23227
  });
23220
23228
  /**
23221
23229
  * Result of emptying the CLIP index.
@@ -23251,6 +23259,7 @@ var WipeObjectEmbeddingsResultSchema = object({
23251
23259
  * - `reads-disagree` — the two reads named different choices.
23252
23260
  * - `not-a-clip-model` — the CONFIRMED id has no CLIP metadata, so there is no
23253
23261
  * index to fill and no dimension to check against (D649).
23262
+ * - `force-requires-max-tracks` — `force: true` with no `maxTracks` (D664).
23254
23263
  */
23255
23264
  var RebuildObjectEmbeddingsRefusalSchema = _enum([
23256
23265
  "stores-unavailable",
@@ -23258,7 +23267,8 @@ var RebuildObjectEmbeddingsRefusalSchema = _enum([
23258
23267
  "method-unavailable",
23259
23268
  "read-failed",
23260
23269
  "reads-disagree",
23261
- "not-a-clip-model"
23270
+ "not-a-clip-model",
23271
+ "force-requires-max-tracks"
23262
23272
  ]);
23263
23273
  /**
23264
23274
  * Acknowledgement that a rebuild STARTED.
@@ -23283,11 +23293,32 @@ var RebuildStatusSchema = object({
23283
23293
  running: boolean(),
23284
23294
  scanned: number(),
23285
23295
  rebuilt: number(),
23286
- /** Tracks whose key frame is gone — nothing to re-embed from. */
23296
+ /** Tracks whose key frame is gone — nothing to re-embed from. Pixels ONLY:
23297
+ * `missingEvent` is its own field since D664, never summed in. */
23287
23298
  missingKeyFrame: number(),
23288
- /** Tracks with no usable detection box. */
23299
+ /** Tracks with no object event at all (D664). */
23300
+ missingEvent: number(),
23301
+ /** Tracks with no box measured on the pixels to cut — see `boxSource.refused`. */
23289
23302
  missingBbox: number(),
23290
23303
  /**
23304
+ * Which rule placed each judged track's box (D664): the key frame row's own
23305
+ * `subjectBox`, `track.positions` at exactly its instant (older key frames),
23306
+ * the event box (a key frame recovered from the recording, at the event's
23307
+ * instant), or `refused` by name — the reasons are in the tagged log lines.
23308
+ */
23309
+ boxSource: object({
23310
+ subjectBox: number(),
23311
+ positions: number(),
23312
+ event: number(),
23313
+ refused: number()
23314
+ }),
23315
+ /** Tracks skipped because their vector is already in the pinned space. */
23316
+ alreadyInSpace: number(),
23317
+ /** Whether the current / last pass was a `force` pass; `null` before any (D664). */
23318
+ force: boolean().nullable(),
23319
+ /** In-space tracks a `force` pass selected for re-embedding. */
23320
+ forcedInSpace: number(),
23321
+ /**
23291
23322
  * Tracks an executing node REFUSED rather than broke on — an unreadable key
23292
23323
  * frame, a step that threw. Separate from `failed` because the remedy is
23293
23324
  * different, and because a whole camera silently contributing zero vectors
@@ -23184,7 +23184,15 @@ var RebuildObjectEmbeddingsInput = object({
23184
23184
  * force is logged at start and finish so a deliberately slow pass reads
23185
23185
  * differently from a stalled one.
23186
23186
  */
23187
- pacingMs: number().int().nonnegative().optional()
23187
+ pacingMs: number().int().nonnegative().optional(),
23188
+ /**
23189
+ * Re-embed ONLY the tracks whose vector is ALREADY in the pinned model's
23190
+ * space (D664) — the in-space skip inverted. It exists to redo vectors
23191
+ * written from the wrong box, which a normal pass would skip as done
23192
+ * forever. Requires `maxTracks`: a forced pass without a bound is refused
23193
+ * (`force-requires-max-tracks`).
23194
+ */
23195
+ force: boolean().optional()
23188
23196
  });
23189
23197
  /**
23190
23198
  * Result of emptying the CLIP index.
@@ -23220,6 +23228,7 @@ var WipeObjectEmbeddingsResultSchema = object({
23220
23228
  * - `reads-disagree` — the two reads named different choices.
23221
23229
  * - `not-a-clip-model` — the CONFIRMED id has no CLIP metadata, so there is no
23222
23230
  * index to fill and no dimension to check against (D649).
23231
+ * - `force-requires-max-tracks` — `force: true` with no `maxTracks` (D664).
23223
23232
  */
23224
23233
  var RebuildObjectEmbeddingsRefusalSchema = _enum([
23225
23234
  "stores-unavailable",
@@ -23227,7 +23236,8 @@ var RebuildObjectEmbeddingsRefusalSchema = _enum([
23227
23236
  "method-unavailable",
23228
23237
  "read-failed",
23229
23238
  "reads-disagree",
23230
- "not-a-clip-model"
23239
+ "not-a-clip-model",
23240
+ "force-requires-max-tracks"
23231
23241
  ]);
23232
23242
  /**
23233
23243
  * Acknowledgement that a rebuild STARTED.
@@ -23252,11 +23262,32 @@ var RebuildStatusSchema = object({
23252
23262
  running: boolean(),
23253
23263
  scanned: number(),
23254
23264
  rebuilt: number(),
23255
- /** Tracks whose key frame is gone — nothing to re-embed from. */
23265
+ /** Tracks whose key frame is gone — nothing to re-embed from. Pixels ONLY:
23266
+ * `missingEvent` is its own field since D664, never summed in. */
23256
23267
  missingKeyFrame: number(),
23257
- /** Tracks with no usable detection box. */
23268
+ /** Tracks with no object event at all (D664). */
23269
+ missingEvent: number(),
23270
+ /** Tracks with no box measured on the pixels to cut — see `boxSource.refused`. */
23258
23271
  missingBbox: number(),
23259
23272
  /**
23273
+ * Which rule placed each judged track's box (D664): the key frame row's own
23274
+ * `subjectBox`, `track.positions` at exactly its instant (older key frames),
23275
+ * the event box (a key frame recovered from the recording, at the event's
23276
+ * instant), or `refused` by name — the reasons are in the tagged log lines.
23277
+ */
23278
+ boxSource: object({
23279
+ subjectBox: number(),
23280
+ positions: number(),
23281
+ event: number(),
23282
+ refused: number()
23283
+ }),
23284
+ /** Tracks skipped because their vector is already in the pinned space. */
23285
+ alreadyInSpace: number(),
23286
+ /** Whether the current / last pass was a `force` pass; `null` before any (D664). */
23287
+ force: boolean().nullable(),
23288
+ /** In-space tracks a `force` pass selected for re-embedding. */
23289
+ forcedInSpace: number(),
23290
+ /**
23260
23291
  * Tracks an executing node REFUSED rather than broke on — an unreadable key
23261
23292
  * frame, a step that threw. Separate from `failed` because the remedy is
23262
23293
  * different, and because a whole camera silently contributing zero vectors
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_clip_model_registry = require("../clip-model-registry--sFeijO7.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-CTY5co6P.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs);
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Bn as normalizeNodeId, Ht as embeddingRequirementsUnavailableError, Rt as embeddingEncoderCapability, Vt as embeddingRequirementsFailedError, a as CLIP_EMBEDDING_STEP_ID, c as resolveClusterModelPin, ft as PoolMemoryWatchdog, i as CLIP_TEXT_MODELS, jn as BaseAddon, mn as resolvePoolMemoryPolicy, n as DEFAULT_CLIP_MODEL, r as CLIP_IMAGE_MODELS, sn as parseProcStatus, t as BUILTIN_CLIP_MODELS, zt as embeddingModelFailedError } from "../clip-model-registry-CoYBCvtr.mjs";
1
+ import { Bn as normalizeNodeId, Ht as embeddingRequirementsUnavailableError, Rt as embeddingEncoderCapability, Vt as embeddingRequirementsFailedError, a as CLIP_EMBEDDING_STEP_ID, c as resolveClusterModelPin, ft as PoolMemoryWatchdog, i as CLIP_TEXT_MODELS, jn as BaseAddon, mn as resolvePoolMemoryPolicy, n as DEFAULT_CLIP_MODEL, r as CLIP_IMAGE_MODELS, sn as parseProcStatus, t as BUILTIN_CLIP_MODELS, zt as embeddingModelFailedError } from "../clip-model-registry-Cudz--Ql.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import * as path$1 from "node:path";
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.266",
21
+ version: "1.2.267",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_analytics_widgets",
@@ -36,7 +36,7 @@ async function r() {
36
36
  }
37
37
  },
38
38
  "@camstack/types": {
39
- version: "1.2.266",
39
+ version: "1.2.267",
40
40
  scope: "default",
41
41
  shareConfig: {
42
42
  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_clip_model_registry = require("../clip-model-registry--sFeijO7.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-CTY5co6P.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -39677,7 +39677,12 @@ var RebuildInputSchema = require_clip_model_registry.object({
39677
39677
  * Milliseconds between tracks; omit for the default, `0` to run flat out.
39678
39678
  * A rebuild shares hub-main's only thread with every live request.
39679
39679
  */
39680
- pacingMs: require_clip_model_registry.number().int().nonnegative().optional()
39680
+ pacingMs: require_clip_model_registry.number().int().nonnegative().optional(),
39681
+ /**
39682
+ * Re-embed ONLY tracks already in the pinned space — to redo vectors cut
39683
+ * from the wrong box (D664). Requires `maxTracks`.
39684
+ */
39685
+ force: require_clip_model_registry.boolean().optional()
39681
39686
  });
39682
39687
  var RebuildStartedSchema = require_clip_model_registry.object({
39683
39688
  started: require_clip_model_registry.boolean(),
@@ -39697,7 +39702,20 @@ var RebuildStatusSchema = require_clip_model_registry.object({
39697
39702
  scanned: require_clip_model_registry.number(),
39698
39703
  rebuilt: require_clip_model_registry.number(),
39699
39704
  missingKeyFrame: require_clip_model_registry.number(),
39705
+ /** No object event at all — its own number since D664. */
39706
+ missingEvent: require_clip_model_registry.number(),
39700
39707
  missingBbox: require_clip_model_registry.number(),
39708
+ /** Which rule placed each judged track's box (D664). */
39709
+ boxSource: require_clip_model_registry.object({
39710
+ subjectBox: require_clip_model_registry.number(),
39711
+ positions: require_clip_model_registry.number(),
39712
+ event: require_clip_model_registry.number(),
39713
+ refused: require_clip_model_registry.number()
39714
+ }),
39715
+ alreadyInSpace: require_clip_model_registry.number(),
39716
+ /** Whether the current / last pass was a `force` pass (D664). */
39717
+ force: require_clip_model_registry.boolean().nullable(),
39718
+ forcedInSpace: require_clip_model_registry.number(),
39701
39719
  /** Refused by the executing node (unreadable key frame / bad box), not broken. */
39702
39720
  notRunnable: require_clip_model_registry.number(),
39703
39721
  /** The pass STOPPED: no node in the cluster could serve the pinned model. */
@@ -40972,7 +40990,7 @@ function toPixelBox(b) {
40972
40990
  h: b.height
40973
40991
  };
40974
40992
  }
40975
- function refused(reason, detail) {
40993
+ function refused$1(reason, detail) {
40976
40994
  return {
40977
40995
  kind: "refused",
40978
40996
  reason,
@@ -41000,8 +41018,8 @@ function comparableFaces(input) {
41000
41018
  /** Refuse before choosing when nothing in the photo can become the sample. */
41001
41019
  function precondition(input) {
41002
41020
  const enrollable = input.faces.filter((f) => f.enrollable && f.embedding !== void 0);
41003
- if (enrollable.length === 0) return refused("no-enrollable-face", `${input.faces.length} face(s) found, none enrollable (${input.faces.map((f) => f.refusedReason ?? "unknown").join(", ")})`);
41004
- if (!enrollable.some((f) => isEnrollableIn(f, input.modelId))) return refused("model-mismatch", `faces were embedded by ${[...new Set(enrollable.map((f) => f.modelId))].join(", ")}, the old sample by ${input.modelId} — re-embed the gallery first`);
41021
+ if (enrollable.length === 0) return refused$1("no-enrollable-face", `${input.faces.length} face(s) found, none enrollable (${input.faces.map((f) => f.refusedReason ?? "unknown").join(", ")})`);
41022
+ if (!enrollable.some((f) => isEnrollableIn(f, input.modelId))) return refused$1("model-mismatch", `faces were embedded by ${[...new Set(enrollable.map((f) => f.modelId))].join(", ")}, the old sample by ${input.modelId} — re-embed the gallery first`);
41005
41023
  return null;
41006
41024
  }
41007
41025
  function chooseByIou(input, oldBox, scored) {
@@ -41010,13 +41028,13 @@ function chooseByIou(input, oldBox, scored) {
41010
41028
  face,
41011
41029
  iou: iou$3(box, toPixelBox(face.bbox))
41012
41030
  })).filter((c) => c.iou >= PHOTO_REENROL_MIN_IOU);
41013
- if (overlapping.length === 0) return refused("no-iou-match", `no face overlaps the old box at IoU ≥ ${PHOTO_REENROL_MIN_IOU}`);
41014
- if (overlapping.length > 1) return refused("ambiguous", `${overlapping.length} faces overlap the old box (IoU ${overlapping.map((c) => c.iou.toFixed(2)).join(", ")})`);
41031
+ if (overlapping.length === 0) return refused$1("no-iou-match", `no face overlaps the old box at IoU ≥ ${PHOTO_REENROL_MIN_IOU}`);
41032
+ if (overlapping.length > 1) return refused$1("ambiguous", `${overlapping.length} faces overlap the old box (IoU ${overlapping.map((c) => c.iou.toFixed(2)).join(", ")})`);
41015
41033
  const [only] = overlapping;
41016
- if (only === void 0) return refused("no-iou-match", "no overlapping face");
41017
- if (!isEnrollableIn(only.face, input.modelId)) return refused("no-enrollable-face", `the face on the old box is not enrollable (${only.face.refusedReason ?? `model ${only.face.modelId ?? "none"}`})`);
41034
+ if (only === void 0) return refused$1("no-iou-match", "no overlapping face");
41035
+ if (!isEnrollableIn(only.face, input.modelId)) return refused$1("no-enrollable-face", `the face on the old box is not enrollable (${only.face.refusedReason ?? `model ${only.face.modelId ?? "none"}`})`);
41018
41036
  const cosine = scored.find((s) => s.face === only.face)?.cosine;
41019
- if (cosine === void 0 || cosine < .5) return refused("below-floor", `the overlapping face scores cosine ${cosine?.toFixed(3) ?? "n/a"} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41037
+ if (cosine === void 0 || cosine < .5) return refused$1("below-floor", `the overlapping face scores cosine ${cosine?.toFixed(3) ?? "n/a"} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41020
41038
  return {
41021
41039
  kind: "chosen",
41022
41040
  face: only.face,
@@ -41027,10 +41045,10 @@ function chooseByIou(input, oldBox, scored) {
41027
41045
  }
41028
41046
  function chooseByCosine(input, scored) {
41029
41047
  const [best, runnerUp] = [...scored].toSorted((a, b) => b.cosine - a.cosine);
41030
- if (best === void 0) return refused("model-mismatch", "no face has a comparable vector");
41031
- if (best.cosine < .5) return refused("below-floor", `best cosine ${best.cosine.toFixed(3)} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41032
- if (runnerUp !== void 0 && runnerUp.cosine >= .5 && best.cosine - runnerUp.cosine < .1) return refused("ambiguous", `two faces clear the floor within ${PHOTO_REENROL_AMBIGUITY_MARGIN}: ${best.cosine.toFixed(3)} vs ${runnerUp.cosine.toFixed(3)}`);
41033
- if (!isEnrollableIn(best.face, input.modelId)) return refused("ambiguous", `the closest face (cosine ${best.cosine.toFixed(3)}) is not enrollable (${best.face.refusedReason ?? "unknown"}) — the enrollable ones may be someone else`);
41048
+ if (best === void 0) return refused$1("model-mismatch", "no face has a comparable vector");
41049
+ if (best.cosine < .5) return refused$1("below-floor", `best cosine ${best.cosine.toFixed(3)} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41050
+ if (runnerUp !== void 0 && runnerUp.cosine >= .5 && best.cosine - runnerUp.cosine < .1) return refused$1("ambiguous", `two faces clear the floor within ${PHOTO_REENROL_AMBIGUITY_MARGIN}: ${best.cosine.toFixed(3)} vs ${runnerUp.cosine.toFixed(3)}`);
41051
+ if (!isEnrollableIn(best.face, input.modelId)) return refused$1("ambiguous", `the closest face (cosine ${best.cosine.toFixed(3)}) is not enrollable (${best.face.refusedReason ?? "unknown"}) — the enrollable ones may be someone else`);
41034
41052
  return {
41035
41053
  kind: "chosen",
41036
41054
  face: best.face,
@@ -53746,11 +53764,10 @@ var EventStore = class {
53746
53764
  let entryId;
53747
53765
  for (const row of rows) {
53748
53766
  const bbox = row.data["bbox"];
53749
- if (bbox !== null && typeof bbox === "object") {
53750
- const bw = "w" in bbox && typeof bbox.w === "number" ? bbox.w : 0;
53751
- const bh = "h" in bbox && typeof bbox.h === "number" ? bbox.h : 0;
53752
- if (bw <= 0 || bh <= 0) continue;
53753
- }
53767
+ if (bbox === null || typeof bbox !== "object") continue;
53768
+ const bw = "w" in bbox && typeof bbox.w === "number" ? bbox.w : 0;
53769
+ const bh = "h" in bbox && typeof bbox.h === "number" ? bbox.h : 0;
53770
+ if (bw <= 0 || bh <= 0) continue;
53754
53771
  const conf = typeof row.data["confidence"] === "number" ? row.data["confidence"] : 0;
53755
53772
  if (conf > bestConf) {
53756
53773
  bestConf = conf;
@@ -62453,6 +62470,26 @@ var EmbeddingDispatcher = class {
62453
62470
  return this.pendingCrops.size;
62454
62471
  }
62455
62472
  };
62473
+ //#endregion
62474
+ //#region src/pipeline-analytics/embedding-rebuild.ts
62475
+ /**
62476
+ * Track sources a rebuild never lists (D664). An audio track and a synthetic
62477
+ * sensor track have no object event and no subject to embed — on the live hub
62478
+ * 6,726 + 180 of 42,254 tracks — and walking them only inflated the skip
62479
+ * counters an operator reads to judge media coverage.
62480
+ */
62481
+ var REBUILD_EXCLUDED_SOURCES = ["audio", "sensor"];
62482
+ /**
62483
+ * One page of the rebuild listing as a `queryRecentIds` query. Pure, so the
62484
+ * exclusion is assertable: dropping it would look identical in every result
62485
+ * except the skip counters.
62486
+ */
62487
+ function rebuildTrackPageQuery(window) {
62488
+ return {
62489
+ ...window,
62490
+ excludeSources: REBUILD_EXCLUDED_SOURCES
62491
+ };
62492
+ }
62456
62493
  /**
62457
62494
  * Re-embed every listed track from its key frame.
62458
62495
  *
@@ -62468,6 +62505,14 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62468
62505
  let recoveredKeyFrame = 0;
62469
62506
  let missingEvent = 0;
62470
62507
  let missingBbox = 0;
62508
+ let boxSubject = 0;
62509
+ let boxPositions = 0;
62510
+ let boxEvent = 0;
62511
+ let boxRefused = 0;
62512
+ let forcedInSpace = 0;
62513
+ let skippedOutOfSpace = 0;
62514
+ const refusalsByDevice = /* @__PURE__ */ new Map();
62515
+ const force = options.force === true;
62471
62516
  let alreadyInSpace = 0;
62472
62517
  let notRunnable = 0;
62473
62518
  let noCapableNode = 0;
@@ -62478,6 +62523,12 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62478
62523
  let budgetExhausted = false;
62479
62524
  /** Set when the cluster ran out of nodes — the pass stops, `complete` is false. */
62480
62525
  let abandoned = null;
62526
+ const boxSourceCounts = () => ({
62527
+ subjectBox: boxSubject,
62528
+ positions: boxPositions,
62529
+ event: boxEvent,
62530
+ refused: boxRefused
62531
+ });
62481
62532
  while (scanned < maxTracks && abandoned === null) {
62482
62533
  const page = await deps.listTrackIdPage(cursor);
62483
62534
  if (page.trackIds.length === 0) {
@@ -62485,9 +62536,14 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62485
62536
  break;
62486
62537
  }
62487
62538
  let pageIds = page.trackIds;
62539
+ let forcedPage = false;
62488
62540
  if (deps.filterOutOfSpace) {
62489
62541
  const split = await deps.filterOutOfSpace(page.trackIds);
62490
- if (split !== null) {
62542
+ if (split !== null && force) {
62543
+ skippedOutOfSpace += split.outOfSpace.length;
62544
+ pageIds = split.inSpace;
62545
+ forcedPage = true;
62546
+ } else if (split !== null) {
62491
62547
  alreadyInSpace += split.alreadyInSpace;
62492
62548
  pageIds = split.outOfSpace;
62493
62549
  }
@@ -62503,6 +62559,7 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62503
62559
  const room = maxTracks - scanned;
62504
62560
  const batch = pageIds.slice(0, Number.isFinite(room) ? room : void 0);
62505
62561
  if (batch.length < pageIds.length) budgetExhausted = true;
62562
+ if (forcedPage) forcedInSpace += batch.length;
62506
62563
  for (const trackId of batch) {
62507
62564
  if (!deps.canDispatch()) {
62508
62565
  abandoned = "no node left in the rotation can serve the pinned model — stopped before loading any further media";
@@ -62521,6 +62578,25 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62521
62578
  missingEvent += 1;
62522
62579
  return;
62523
62580
  }
62581
+ if (source.bbox === null || source.bbox.w <= 0 || source.bbox.h <= 0) {
62582
+ missingBbox += 1;
62583
+ boxRefused += 1;
62584
+ const reason = source.boxSource.kind === "refused" ? source.boxSource.reason : "degenerate-box";
62585
+ const n = (refusalsByDevice.get(source.deviceId) ?? 0) + 1;
62586
+ refusalsByDevice.set(source.deviceId, n);
62587
+ if (n === 1 || n % 100 === 0) deps.logger.warn("embedding rebuild: no box measured on the key frame — track refused (D303)", {
62588
+ tags: { deviceId: source.deviceId },
62589
+ meta: {
62590
+ trackId,
62591
+ reason,
62592
+ refusedOnThisCamera: n
62593
+ }
62594
+ });
62595
+ return;
62596
+ }
62597
+ if (source.boxSource.kind === "subjectBox") boxSubject += 1;
62598
+ else if (source.boxSource.kind === "positions") boxPositions += 1;
62599
+ else boxEvent += 1;
62524
62600
  let keyFrame = source.keyFrame;
62525
62601
  let recovered = false;
62526
62602
  if (keyFrame === null && deps.recoverKeyFrame) {
@@ -62532,10 +62608,6 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62532
62608
  return;
62533
62609
  }
62534
62610
  if (recovered) recoveredKeyFrame += 1;
62535
- if (source.bbox === null || source.bbox.w <= 0 || source.bbox.h <= 0) {
62536
- missingBbox += 1;
62537
- return;
62538
- }
62539
62611
  const outcome = await deps.embed(recovered ? {
62540
62612
  ...source,
62541
62613
  keyFrame
@@ -62603,6 +62675,9 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62603
62675
  recoveredKeyFrame,
62604
62676
  missingEvent,
62605
62677
  missingBbox,
62678
+ boxSource: boxSourceCounts(),
62679
+ forcedInSpace,
62680
+ skippedOutOfSpace,
62606
62681
  alreadyInSpace,
62607
62682
  notRunnable,
62608
62683
  noCapableNode,
@@ -62626,6 +62701,10 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62626
62701
  recoveredKeyFrame,
62627
62702
  missingEvent,
62628
62703
  missingBbox,
62704
+ boxSource: boxSourceCounts(),
62705
+ force,
62706
+ forcedInSpace,
62707
+ skippedOutOfSpace,
62629
62708
  alreadyInSpace,
62630
62709
  notRunnable,
62631
62710
  noCapableNode,
@@ -62640,6 +62719,9 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62640
62719
  recoveredKeyFrame,
62641
62720
  missingEvent,
62642
62721
  missingBbox,
62722
+ boxSource: boxSourceCounts(),
62723
+ forcedInSpace,
62724
+ skippedOutOfSpace,
62643
62725
  alreadyInSpace,
62644
62726
  notRunnable,
62645
62727
  noCapableNode,
@@ -73038,6 +73120,58 @@ function hasKeyFrame(source) {
73038
73120
  }
73039
73121
  //#endregion
73040
73122
  //#region src/pipeline-analytics/rebuild-source-loader.ts
73123
+ function refused(reason) {
73124
+ return {
73125
+ bbox: null,
73126
+ boxSource: {
73127
+ kind: "refused",
73128
+ reason
73129
+ }
73130
+ };
73131
+ }
73132
+ function normaliseMeasured(box) {
73133
+ if (box.frameWidth <= 0 || box.frameHeight <= 0 || box.w <= 0 || box.h <= 0) return null;
73134
+ return {
73135
+ x: box.x / box.frameWidth,
73136
+ y: box.y / box.frameHeight,
73137
+ w: box.w / box.frameWidth,
73138
+ h: box.h / box.frameHeight
73139
+ };
73140
+ }
73141
+ /**
73142
+ * The box measured on THIS key frame: its own `subjectBox`, else the track
73143
+ * position at exactly its instant, else a named refusal. Never another
73144
+ * frame's box (D303).
73145
+ */
73146
+ async function keyFrameBox(deps, trackId, keyFrame) {
73147
+ const own = (await deps.media.listSubjectBoxesByOwner("track", trackId)).find((row) => row.mediaId === keyFrame.mediaId);
73148
+ if (own !== void 0) {
73149
+ const bbox = normaliseMeasured(own.subjectBox);
73150
+ if (bbox !== null) return {
73151
+ bbox,
73152
+ boxSource: { kind: "subjectBox" }
73153
+ };
73154
+ }
73155
+ const track = await deps.tracks.getPersistedByTrackId(trackId);
73156
+ if (track === null) return refused("track-row-missing");
73157
+ const { frameWidth, frameHeight } = track;
73158
+ if (frameWidth === void 0 || frameHeight === void 0) return refused("track-frame-size-unknown");
73159
+ const at = track.positions.find((p) => p.timestamp === keyFrame.timestamp);
73160
+ if (at === void 0) return refused("no-position-at-key-frame");
73161
+ const bbox = normaliseMeasured({
73162
+ ...at.bbox,
73163
+ frameWidth,
73164
+ frameHeight
73165
+ });
73166
+ if (bbox === null) return refused("no-position-at-key-frame");
73167
+ return {
73168
+ bbox,
73169
+ boxSource: { kind: "positions" }
73170
+ };
73171
+ }
73172
+ function hasBox(event) {
73173
+ return event.bbox !== void 0 && event.bbox.w > 0 && event.bbox.h > 0;
73174
+ }
73041
73175
  /**
73042
73176
  * Assemble one track's rebuild inputs, reading at most ONE blob.
73043
73177
  *
@@ -73045,7 +73179,8 @@ function hasKeyFrame(source) {
73045
73179
  * to describe, and the caller counts it. A track WITH an event but WITHOUT a
73046
73180
  * key frame still returns a source (with `keyFrame: null`) so the caller's
73047
73181
  * `missingKeyFrame` counter stays the one place that decision is made; no blob
73048
- * is read in that case either.
73182
+ * is read in that case either. A key frame with no box measured on it returns
73183
+ * `bbox: null` and a named refusal, also without reading the blob.
73049
73184
  */
73050
73185
  async function loadRebuildSource(deps, trackId) {
73051
73186
  const media = await deps.media.listInfoByOwner("track", trackId);
@@ -73053,26 +73188,44 @@ async function loadRebuildSource(deps, trackId) {
73053
73188
  const cropInfo = media.find((m) => m.kind === "thumbnail");
73054
73189
  const peak = await deps.events.peakForTrack(trackId);
73055
73190
  const events = await deps.events.queryObjectByTrackIds([trackId]);
73056
- const best = events.find((e) => String(e.id) === peak.bestEventId) ?? events[0];
73191
+ const best = events.find((e) => String(e.id) === peak.bestEventId) ?? events.find(hasBox) ?? events[0];
73057
73192
  if (best === void 0) return null;
73058
73193
  const common = {
73059
73194
  trackId,
73060
73195
  deviceId: best.deviceId,
73061
- timestamp: best.timestamp,
73062
73196
  className: best.className,
73063
73197
  confidence: best.confidence ?? 0,
73064
- bbox: normaliseEventBbox(best),
73065
73198
  ...cropInfo !== void 0 ? { mediaId: cropInfo.mediaId } : {}
73066
73199
  };
73067
- if (keyFrameInfo === void 0) return {
73200
+ if (keyFrameInfo === void 0) {
73201
+ const bbox = normaliseEventBbox(best);
73202
+ return {
73203
+ ...common,
73204
+ timestamp: best.timestamp,
73205
+ bbox,
73206
+ boxSource: bbox !== null ? { kind: "event" } : {
73207
+ kind: "refused",
73208
+ reason: "event-without-box"
73209
+ },
73210
+ keyFrame: null
73211
+ };
73212
+ }
73213
+ const box = await keyFrameBox(deps, trackId, keyFrameInfo);
73214
+ const keyed = {
73068
73215
  ...common,
73216
+ timestamp: keyFrameInfo.timestamp,
73217
+ bbox: box.bbox,
73218
+ boxSource: box.boxSource,
73219
+ keyFrameMediaId: keyFrameInfo.mediaId
73220
+ };
73221
+ if (box.bbox === null) return {
73222
+ ...keyed,
73069
73223
  keyFrame: null
73070
73224
  };
73071
73225
  const keyFrameFile = await deps.media.getById(keyFrameInfo.mediaId);
73072
73226
  return {
73073
- ...common,
73074
- keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null,
73075
- keyFrameMediaId: keyFrameInfo.mediaId
73227
+ ...keyed,
73228
+ keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null
73076
73229
  };
73077
73230
  }
73078
73231
  //#endregion
@@ -83621,7 +83774,7 @@ function buildGlobalSettingsSchema(cap) {
83621
83774
  addonId: "pipeline-analytics",
83622
83775
  action: "embedding.rebuildStatus",
83623
83776
  buttonLabel: "Check progress",
83624
- successMessage: "{modelId} → {index}: indexed {indexed} · scanned {scanned} · rebuilt {rebuilt} · no key frame {missingKeyFrame} · not runnable {notRunnable} · failed {failed}"
83777
+ successMessage: "{modelId} → {index}: indexed {indexed} · scanned {scanned} · rebuilt {rebuilt} · no key frame {missingKeyFrame} · no box {missingBbox} · no event {missingEvent} · not runnable {notRunnable} · failed {failed}"
83625
83778
  },
83626
83779
  {
83627
83780
  type: "addon-action-button",
@@ -97208,6 +97361,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97208
97361
  refusal: "stores-unavailable"
97209
97362
  };
97210
97363
  }
97364
+ const force = input.force === true;
97365
+ if (force && input.maxTracks === void 0) {
97366
+ this.ctx.logger.warn("rebuildObjectEmbeddings: refusing a force pass with no maxTracks", { meta: { refusal: "force-requires-max-tracks" } });
97367
+ return {
97368
+ started: false,
97369
+ alreadyRunning: false,
97370
+ refusal: "force-requires-max-tracks"
97371
+ };
97372
+ }
97211
97373
  const confirmation = await this.getClusterPinConfirmer(api, "clip-rebuild").confirm();
97212
97374
  if (confirmation.kind === "refused") {
97213
97375
  this.ctx.logger.warn("rebuildObjectEmbeddings: the cluster CLIP model is not confirmed — refusing to start a pass that cannot pin one", { meta: { refusal: confirmation.reason } });
@@ -97251,16 +97413,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97251
97413
  logger: this.ctx.logger
97252
97414
  }));
97253
97415
  const deps = {
97254
- listTrackIdPage: async (cursor) => trackStore.queryRecentIds({
97416
+ listTrackIdPage: async (cursor) => trackStore.queryRecentIds(rebuildTrackPageQuery({
97255
97417
  deviceIds,
97256
97418
  ...input.since !== void 0 ? { since: input.since } : {},
97257
97419
  ...input.until !== void 0 ? { until: input.until } : {},
97258
97420
  ...cursor !== null ? { cursor } : {},
97259
97421
  limit: REBUILD_PAGE_SIZE
97260
- }),
97422
+ })),
97261
97423
  loadSource: (trackId) => loadRebuildSource({
97262
97424
  media: mediaStore,
97263
- events: eventStore
97425
+ events: eventStore,
97426
+ tracks: trackStore
97264
97427
  }, trackId),
97265
97428
  recoverKeyFrame: async (source) => {
97266
97429
  const recovered = await recordedStills.recover(source.deviceId, source.timestamp, RECOVERED_KEY_FRAME_MAX_WIDTH);
@@ -97286,7 +97449,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97286
97449
  if (split === null) return null;
97287
97450
  return {
97288
97451
  outOfSpace: split.outOfSpaceIds,
97289
- alreadyInSpace: split.inSpace
97452
+ alreadyInSpace: split.inSpace,
97453
+ inSpace: split.inSpaceIds
97290
97454
  };
97291
97455
  },
97292
97456
  logger: {
@@ -97298,6 +97462,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97298
97462
  running: true,
97299
97463
  modelId,
97300
97464
  index,
97465
+ force,
97301
97466
  startedAtMs: Date.now(),
97302
97467
  progress: {
97303
97468
  scanned: 0,
@@ -97306,6 +97471,14 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97306
97471
  recoveredKeyFrame: 0,
97307
97472
  missingEvent: 0,
97308
97473
  missingBbox: 0,
97474
+ boxSource: {
97475
+ subjectBox: 0,
97476
+ positions: 0,
97477
+ event: 0,
97478
+ refused: 0
97479
+ },
97480
+ forcedInSpace: 0,
97481
+ skippedOutOfSpace: 0,
97309
97482
  alreadyInSpace: 0,
97310
97483
  notRunnable: 0,
97311
97484
  noCapableNode: 0,
@@ -97313,6 +97486,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97313
97486
  }
97314
97487
  };
97315
97488
  rebuildObjectEmbeddings(deps, {
97489
+ force,
97316
97490
  ...input.maxTracks !== void 0 ? { maxTracks: input.maxTracks } : {},
97317
97491
  ...input.pacingMs !== void 0 ? { pacingMs: input.pacingMs } : {},
97318
97492
  onProgress: (progress) => {
@@ -97324,6 +97498,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97324
97498
  running: false,
97325
97499
  modelId,
97326
97500
  index,
97501
+ force,
97327
97502
  startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
97328
97503
  finishedAtMs: Date.now(),
97329
97504
  progress: outcome,
@@ -97339,6 +97514,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97339
97514
  running: false,
97340
97515
  modelId,
97341
97516
  index,
97517
+ force,
97342
97518
  startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
97343
97519
  finishedAtMs: Date.now(),
97344
97520
  error: String(err)
@@ -97353,7 +97529,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97353
97529
  modelId,
97354
97530
  index,
97355
97531
  deviceId: input.deviceId ?? "all",
97356
- maxTracks: input.maxTracks ?? "unbounded"
97532
+ maxTracks: input.maxTracks ?? "unbounded",
97533
+ force
97357
97534
  } });
97358
97535
  return {
97359
97536
  started: true,
@@ -98355,8 +98532,18 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
98355
98532
  running: state.running,
98356
98533
  scanned: p?.scanned ?? 0,
98357
98534
  rebuilt: p?.rebuilt ?? 0,
98358
- missingKeyFrame: (p?.missingKeyFrame ?? 0) + (p?.missingEvent ?? 0),
98535
+ missingKeyFrame: p?.missingKeyFrame ?? 0,
98536
+ missingEvent: p?.missingEvent ?? 0,
98359
98537
  missingBbox: p?.missingBbox ?? 0,
98538
+ boxSource: p?.boxSource ?? {
98539
+ subjectBox: 0,
98540
+ positions: 0,
98541
+ event: 0,
98542
+ refused: 0
98543
+ },
98544
+ alreadyInSpace: p?.alreadyInSpace ?? 0,
98545
+ force: state.force ?? null,
98546
+ forcedInSpace: p?.forcedInSpace ?? 0,
98360
98547
  notRunnable: p?.notRunnable ?? 0,
98361
98548
  noCapableNode: p?.noCapableNode ?? 0,
98362
98549
  failed: p?.failed ?? 0,
@@ -1,4 +1,4 @@
1
- import { $ as NcRuleSchema, $n as EventCategory, $t as isSourceCap, A as LabelAttributionSchema, An as canonicalHash, At as audioMetricsCapability, B as NC_DEFAULT_SNOOZE_MINUTES, Bt as embeddingRefusalOf, C as EMBEDDING_REQUIREMENTS_FAILED, Cn as systemEventFilterApplies, Ct as TimelapseRuleInputSchema, D as FIRST_LEVEL_MACRO_CLASSES, Dn as vectorDimFromBase64, Dt as addonWidgetsSourceCapability, E as EVENT_OWNER_TYPES, En as templateVarsFor, Et as TrackSourceSchema, F as MOTION_CLOSE_AFTER_MS, Fn as customAction, Ft as deriveRecordingMode, G as NC_TAXONOMY, Gn as discriminatedUnion, Gt as evictionPolicyOfLocation, Hn as _enum, I as MediaFileKindEnum, In as defineCustomActions, It as describeSystemEventDeliveryProblem, J as NcAlarmSettingsPatchSchema, Jn as object, Jt as isDetectionMacroClass, K as NC_TEMPLATE_VARS, Kn as literal, Kt as faceGalleryCapability, L as NC_ALARM_SYSTEM_EVENT_KINDS, Ln as hydrateSchema, Lt as describeSystemEventFamilyProblem, M as MAX_ARCHIVED_DEBUG_NOTES, Mn as CamProfileSchema, Mt as buildEventKindDescriptor, N as MAX_BIRTH_DECISION_RECORDS, Nn as DeviceType, Nt as consumableLowPercentOf, O as FULL_IMAGE_BBOX, On as zoneAnalyticsCapability, Ot as alarmPanelCapability, Pn as createEvent, Pt as cosineSimilarity$1, Q as NcRulePatchSchema, Qn as unknown, Qt as isScheduleActive, R as NC_CONDITION_CATALOG, Rn as isDeviceScopedCap, S as EMBEDDING_MODEL_FAILED, Sn as systemEventFamilyProblems, St as TIMELAPSE_DENSE_FLOOR_SEC, T as EVENT_KIND_BY_CAP, Tn as templateCarriesSummary, Tt as TimelapseRuleSchema, U as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, Un as array, Ut as encodeVectorBase64, V as NC_SENSOR_TRANSITION_KINDS, Vn as sleep$1, W as NC_TANK_SYSTEM_EVENT_KINDS, Wn as boolean, Wt as evaluateSensorEdge, X as NcConditionsSchema, Xn as record, Xt as isKnownTemplateVar, Y as NcConditionDescriptorSchema, Yn as partialRecord, Yt as isEventOwnerType, Z as NcRuleInputSchema, Zn as string, Zt as isMediaOwnerType, _n as sensorRaisedOf, a as CLIP_EMBEDDING_STEP_ID, an as notificationRulesCapability, at as NcSnoozeSuppressedSchema, bn as subKindsOf, bt as SCENE_DIVERGED, c as resolveClusterModelPin, cn as pipelineAnalyticsCapability, ct as NcTemplatePreviewInputSchema, d as ArchivedDebugNoteSchema, dn as readTimelapseGeneratedAt, dt as OpsLogEntrySchema, en as isSystemDelivery, et as NcRuleTargetSchema, f as BaseDevice, fn as resolveClipSearchMinScore, g as DEFAULT_EVENT_COLOR, gn as sceneMonitorCapability, gt as RetrainStatusSchema, h as CoverStateSchema, hn as ruleBelongsToAlarm$1, ht as RebuildObjectEmbeddingsRefusalSchema, in as mayWriteToLocation, it as NcSnoozeSchema, j as LockStateSchema, jn as BaseAddon, jt as audioModeOf, k as FailureCounters, kn as errMsg$1, kt as assertTimelapseCadences, l as AUDIO_MACRO_LABELS, ln as plateGalleryCapability, lt as NcTemplatePreviewSchema, m as COCO_TO_MACRO, mt as RECORDING_SOURCE_CAMSTACK_ADDON, nn as isVectorIndexDimConflict, nt as NcScheduleSchema, o as FACE_EMBEDDING_STEP_ID, on as occupancyScope, ot as NcSystemEventKindSchema, p as BirthDecisionRecordSchema, pn as resolveLocationMode, pt as RECORDING_EXPORT_MAX_READ_BYTES, q as NcAlarmConfigSchema, qn as number, qt as failureContributionCapability, rn as kebabToCamel, rt as NcSnoozeInputSchema, s as readClusterModelPinStrict, st as NcTaxonomySchema, t as BUILTIN_CLIP_MODELS, tn as isSystemEventSummaryRule, tt as NcRuleUpdateInputSchema, u as AlarmArmModeSchema, un as readDeviceStateFrom, ut as NcTemplateVarDescriptorSchema, vn as servedTemplateVars, vt as SCENE_DEFAULT_ANCHOR_THRESHOLD, w as EMBEDDING_REQUIREMENTS_UNAVAILABLE, wn as systemEventSummaryScheduleOf, wt as TimelapseRulePatchSchema, x as DeclaredDevices, xn as systemEventDeliveryProblems, xt as SceneMonitorSchema, y as DETECTION_MACRO_CLASSES, yn as storageOccupancyCapability, yt as SCENE_DEFAULT_UNCOVERED_POLICY, z as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, zn as nodePin } from "../clip-model-registry-CoYBCvtr.mjs";
1
+ import { $ as NcRuleSchema, $n as EventCategory, $t as isSourceCap, A as LabelAttributionSchema, An as canonicalHash, At as audioMetricsCapability, B as NC_DEFAULT_SNOOZE_MINUTES, Bt as embeddingRefusalOf, C as EMBEDDING_REQUIREMENTS_FAILED, Cn as systemEventFilterApplies, Ct as TimelapseRuleInputSchema, D as FIRST_LEVEL_MACRO_CLASSES, Dn as vectorDimFromBase64, Dt as addonWidgetsSourceCapability, E as EVENT_OWNER_TYPES, En as templateVarsFor, Et as TrackSourceSchema, F as MOTION_CLOSE_AFTER_MS, Fn as customAction, Ft as deriveRecordingMode, G as NC_TAXONOMY, Gn as discriminatedUnion, Gt as evictionPolicyOfLocation, Hn as _enum, I as MediaFileKindEnum, In as defineCustomActions, It as describeSystemEventDeliveryProblem, J as NcAlarmSettingsPatchSchema, Jn as object, Jt as isDetectionMacroClass, K as NC_TEMPLATE_VARS, Kn as literal, Kt as faceGalleryCapability, L as NC_ALARM_SYSTEM_EVENT_KINDS, Ln as hydrateSchema, Lt as describeSystemEventFamilyProblem, M as MAX_ARCHIVED_DEBUG_NOTES, Mn as CamProfileSchema, Mt as buildEventKindDescriptor, N as MAX_BIRTH_DECISION_RECORDS, Nn as DeviceType, Nt as consumableLowPercentOf, O as FULL_IMAGE_BBOX, On as zoneAnalyticsCapability, Ot as alarmPanelCapability, Pn as createEvent, Pt as cosineSimilarity$1, Q as NcRulePatchSchema, Qn as unknown, Qt as isScheduleActive, R as NC_CONDITION_CATALOG, Rn as isDeviceScopedCap, S as EMBEDDING_MODEL_FAILED, Sn as systemEventFamilyProblems, St as TIMELAPSE_DENSE_FLOOR_SEC, T as EVENT_KIND_BY_CAP, Tn as templateCarriesSummary, Tt as TimelapseRuleSchema, U as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, Un as array, Ut as encodeVectorBase64, V as NC_SENSOR_TRANSITION_KINDS, Vn as sleep$1, W as NC_TANK_SYSTEM_EVENT_KINDS, Wn as boolean, Wt as evaluateSensorEdge, X as NcConditionsSchema, Xn as record, Xt as isKnownTemplateVar, Y as NcConditionDescriptorSchema, Yn as partialRecord, Yt as isEventOwnerType, Z as NcRuleInputSchema, Zn as string, Zt as isMediaOwnerType, _n as sensorRaisedOf, a as CLIP_EMBEDDING_STEP_ID, an as notificationRulesCapability, at as NcSnoozeSuppressedSchema, bn as subKindsOf, bt as SCENE_DIVERGED, c as resolveClusterModelPin, cn as pipelineAnalyticsCapability, ct as NcTemplatePreviewInputSchema, d as ArchivedDebugNoteSchema, dn as readTimelapseGeneratedAt, dt as OpsLogEntrySchema, en as isSystemDelivery, et as NcRuleTargetSchema, f as BaseDevice, fn as resolveClipSearchMinScore, g as DEFAULT_EVENT_COLOR, gn as sceneMonitorCapability, gt as RetrainStatusSchema, h as CoverStateSchema, hn as ruleBelongsToAlarm$1, ht as RebuildObjectEmbeddingsRefusalSchema, in as mayWriteToLocation, it as NcSnoozeSchema, j as LockStateSchema, jn as BaseAddon, jt as audioModeOf, k as FailureCounters, kn as errMsg$1, kt as assertTimelapseCadences, l as AUDIO_MACRO_LABELS, ln as plateGalleryCapability, lt as NcTemplatePreviewSchema, m as COCO_TO_MACRO, mt as RECORDING_SOURCE_CAMSTACK_ADDON, nn as isVectorIndexDimConflict, nt as NcScheduleSchema, o as FACE_EMBEDDING_STEP_ID, on as occupancyScope, ot as NcSystemEventKindSchema, p as BirthDecisionRecordSchema, pn as resolveLocationMode, pt as RECORDING_EXPORT_MAX_READ_BYTES, q as NcAlarmConfigSchema, qn as number, qt as failureContributionCapability, rn as kebabToCamel, rt as NcSnoozeInputSchema, s as readClusterModelPinStrict, st as NcTaxonomySchema, t as BUILTIN_CLIP_MODELS, tn as isSystemEventSummaryRule, tt as NcRuleUpdateInputSchema, u as AlarmArmModeSchema, un as readDeviceStateFrom, ut as NcTemplateVarDescriptorSchema, vn as servedTemplateVars, vt as SCENE_DEFAULT_ANCHOR_THRESHOLD, w as EMBEDDING_REQUIREMENTS_UNAVAILABLE, wn as systemEventSummaryScheduleOf, wt as TimelapseRulePatchSchema, x as DeclaredDevices, xn as systemEventDeliveryProblems, xt as SceneMonitorSchema, y as DETECTION_MACRO_CLASSES, yn as storageOccupancyCapability, yt as SCENE_DEFAULT_UNCOVERED_POLICY, z as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, zn as nodePin } from "../clip-model-registry-Cudz--Ql.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";
@@ -39654,7 +39654,12 @@ var RebuildInputSchema = object({
39654
39654
  * Milliseconds between tracks; omit for the default, `0` to run flat out.
39655
39655
  * A rebuild shares hub-main's only thread with every live request.
39656
39656
  */
39657
- pacingMs: number().int().nonnegative().optional()
39657
+ pacingMs: number().int().nonnegative().optional(),
39658
+ /**
39659
+ * Re-embed ONLY tracks already in the pinned space — to redo vectors cut
39660
+ * from the wrong box (D664). Requires `maxTracks`.
39661
+ */
39662
+ force: boolean().optional()
39658
39663
  });
39659
39664
  var RebuildStartedSchema = object({
39660
39665
  started: boolean(),
@@ -39674,7 +39679,20 @@ var RebuildStatusSchema = object({
39674
39679
  scanned: number(),
39675
39680
  rebuilt: number(),
39676
39681
  missingKeyFrame: number(),
39682
+ /** No object event at all — its own number since D664. */
39683
+ missingEvent: number(),
39677
39684
  missingBbox: number(),
39685
+ /** Which rule placed each judged track's box (D664). */
39686
+ boxSource: object({
39687
+ subjectBox: number(),
39688
+ positions: number(),
39689
+ event: number(),
39690
+ refused: number()
39691
+ }),
39692
+ alreadyInSpace: number(),
39693
+ /** Whether the current / last pass was a `force` pass (D664). */
39694
+ force: boolean().nullable(),
39695
+ forcedInSpace: number(),
39678
39696
  /** Refused by the executing node (unreadable key frame / bad box), not broken. */
39679
39697
  notRunnable: number(),
39680
39698
  /** The pass STOPPED: no node in the cluster could serve the pinned model. */
@@ -40949,7 +40967,7 @@ function toPixelBox(b) {
40949
40967
  h: b.height
40950
40968
  };
40951
40969
  }
40952
- function refused(reason, detail) {
40970
+ function refused$1(reason, detail) {
40953
40971
  return {
40954
40972
  kind: "refused",
40955
40973
  reason,
@@ -40977,8 +40995,8 @@ function comparableFaces(input) {
40977
40995
  /** Refuse before choosing when nothing in the photo can become the sample. */
40978
40996
  function precondition(input) {
40979
40997
  const enrollable = input.faces.filter((f) => f.enrollable && f.embedding !== void 0);
40980
- if (enrollable.length === 0) return refused("no-enrollable-face", `${input.faces.length} face(s) found, none enrollable (${input.faces.map((f) => f.refusedReason ?? "unknown").join(", ")})`);
40981
- if (!enrollable.some((f) => isEnrollableIn(f, input.modelId))) return refused("model-mismatch", `faces were embedded by ${[...new Set(enrollable.map((f) => f.modelId))].join(", ")}, the old sample by ${input.modelId} — re-embed the gallery first`);
40998
+ if (enrollable.length === 0) return refused$1("no-enrollable-face", `${input.faces.length} face(s) found, none enrollable (${input.faces.map((f) => f.refusedReason ?? "unknown").join(", ")})`);
40999
+ if (!enrollable.some((f) => isEnrollableIn(f, input.modelId))) return refused$1("model-mismatch", `faces were embedded by ${[...new Set(enrollable.map((f) => f.modelId))].join(", ")}, the old sample by ${input.modelId} — re-embed the gallery first`);
40982
41000
  return null;
40983
41001
  }
40984
41002
  function chooseByIou(input, oldBox, scored) {
@@ -40987,13 +41005,13 @@ function chooseByIou(input, oldBox, scored) {
40987
41005
  face,
40988
41006
  iou: iou$3(box, toPixelBox(face.bbox))
40989
41007
  })).filter((c) => c.iou >= PHOTO_REENROL_MIN_IOU);
40990
- if (overlapping.length === 0) return refused("no-iou-match", `no face overlaps the old box at IoU ≥ ${PHOTO_REENROL_MIN_IOU}`);
40991
- if (overlapping.length > 1) return refused("ambiguous", `${overlapping.length} faces overlap the old box (IoU ${overlapping.map((c) => c.iou.toFixed(2)).join(", ")})`);
41008
+ if (overlapping.length === 0) return refused$1("no-iou-match", `no face overlaps the old box at IoU ≥ ${PHOTO_REENROL_MIN_IOU}`);
41009
+ if (overlapping.length > 1) return refused$1("ambiguous", `${overlapping.length} faces overlap the old box (IoU ${overlapping.map((c) => c.iou.toFixed(2)).join(", ")})`);
40992
41010
  const [only] = overlapping;
40993
- if (only === void 0) return refused("no-iou-match", "no overlapping face");
40994
- if (!isEnrollableIn(only.face, input.modelId)) return refused("no-enrollable-face", `the face on the old box is not enrollable (${only.face.refusedReason ?? `model ${only.face.modelId ?? "none"}`})`);
41011
+ if (only === void 0) return refused$1("no-iou-match", "no overlapping face");
41012
+ if (!isEnrollableIn(only.face, input.modelId)) return refused$1("no-enrollable-face", `the face on the old box is not enrollable (${only.face.refusedReason ?? `model ${only.face.modelId ?? "none"}`})`);
40995
41013
  const cosine = scored.find((s) => s.face === only.face)?.cosine;
40996
- if (cosine === void 0 || cosine < .5) return refused("below-floor", `the overlapping face scores cosine ${cosine?.toFixed(3) ?? "n/a"} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41014
+ if (cosine === void 0 || cosine < .5) return refused$1("below-floor", `the overlapping face scores cosine ${cosine?.toFixed(3) ?? "n/a"} < ${PHOTO_REENROL_COSINE_FLOOR}`);
40997
41015
  return {
40998
41016
  kind: "chosen",
40999
41017
  face: only.face,
@@ -41004,10 +41022,10 @@ function chooseByIou(input, oldBox, scored) {
41004
41022
  }
41005
41023
  function chooseByCosine(input, scored) {
41006
41024
  const [best, runnerUp] = [...scored].toSorted((a, b) => b.cosine - a.cosine);
41007
- if (best === void 0) return refused("model-mismatch", "no face has a comparable vector");
41008
- if (best.cosine < .5) return refused("below-floor", `best cosine ${best.cosine.toFixed(3)} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41009
- if (runnerUp !== void 0 && runnerUp.cosine >= .5 && best.cosine - runnerUp.cosine < .1) return refused("ambiguous", `two faces clear the floor within ${PHOTO_REENROL_AMBIGUITY_MARGIN}: ${best.cosine.toFixed(3)} vs ${runnerUp.cosine.toFixed(3)}`);
41010
- if (!isEnrollableIn(best.face, input.modelId)) return refused("ambiguous", `the closest face (cosine ${best.cosine.toFixed(3)}) is not enrollable (${best.face.refusedReason ?? "unknown"}) — the enrollable ones may be someone else`);
41025
+ if (best === void 0) return refused$1("model-mismatch", "no face has a comparable vector");
41026
+ if (best.cosine < .5) return refused$1("below-floor", `best cosine ${best.cosine.toFixed(3)} < ${PHOTO_REENROL_COSINE_FLOOR}`);
41027
+ if (runnerUp !== void 0 && runnerUp.cosine >= .5 && best.cosine - runnerUp.cosine < .1) return refused$1("ambiguous", `two faces clear the floor within ${PHOTO_REENROL_AMBIGUITY_MARGIN}: ${best.cosine.toFixed(3)} vs ${runnerUp.cosine.toFixed(3)}`);
41028
+ if (!isEnrollableIn(best.face, input.modelId)) return refused$1("ambiguous", `the closest face (cosine ${best.cosine.toFixed(3)}) is not enrollable (${best.face.refusedReason ?? "unknown"}) — the enrollable ones may be someone else`);
41011
41029
  return {
41012
41030
  kind: "chosen",
41013
41031
  face: best.face,
@@ -53721,11 +53739,10 @@ var EventStore = class {
53721
53739
  let entryId;
53722
53740
  for (const row of rows) {
53723
53741
  const bbox = row.data["bbox"];
53724
- if (bbox !== null && typeof bbox === "object") {
53725
- const bw = "w" in bbox && typeof bbox.w === "number" ? bbox.w : 0;
53726
- const bh = "h" in bbox && typeof bbox.h === "number" ? bbox.h : 0;
53727
- if (bw <= 0 || bh <= 0) continue;
53728
- }
53742
+ if (bbox === null || typeof bbox !== "object") continue;
53743
+ const bw = "w" in bbox && typeof bbox.w === "number" ? bbox.w : 0;
53744
+ const bh = "h" in bbox && typeof bbox.h === "number" ? bbox.h : 0;
53745
+ if (bw <= 0 || bh <= 0) continue;
53729
53746
  const conf = typeof row.data["confidence"] === "number" ? row.data["confidence"] : 0;
53730
53747
  if (conf > bestConf) {
53731
53748
  bestConf = conf;
@@ -62377,6 +62394,26 @@ var EmbeddingDispatcher = class {
62377
62394
  return this.pendingCrops.size;
62378
62395
  }
62379
62396
  };
62397
+ //#endregion
62398
+ //#region src/pipeline-analytics/embedding-rebuild.ts
62399
+ /**
62400
+ * Track sources a rebuild never lists (D664). An audio track and a synthetic
62401
+ * sensor track have no object event and no subject to embed — on the live hub
62402
+ * 6,726 + 180 of 42,254 tracks — and walking them only inflated the skip
62403
+ * counters an operator reads to judge media coverage.
62404
+ */
62405
+ var REBUILD_EXCLUDED_SOURCES = ["audio", "sensor"];
62406
+ /**
62407
+ * One page of the rebuild listing as a `queryRecentIds` query. Pure, so the
62408
+ * exclusion is assertable: dropping it would look identical in every result
62409
+ * except the skip counters.
62410
+ */
62411
+ function rebuildTrackPageQuery(window) {
62412
+ return {
62413
+ ...window,
62414
+ excludeSources: REBUILD_EXCLUDED_SOURCES
62415
+ };
62416
+ }
62380
62417
  /**
62381
62418
  * Re-embed every listed track from its key frame.
62382
62419
  *
@@ -62392,6 +62429,14 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62392
62429
  let recoveredKeyFrame = 0;
62393
62430
  let missingEvent = 0;
62394
62431
  let missingBbox = 0;
62432
+ let boxSubject = 0;
62433
+ let boxPositions = 0;
62434
+ let boxEvent = 0;
62435
+ let boxRefused = 0;
62436
+ let forcedInSpace = 0;
62437
+ let skippedOutOfSpace = 0;
62438
+ const refusalsByDevice = /* @__PURE__ */ new Map();
62439
+ const force = options.force === true;
62395
62440
  let alreadyInSpace = 0;
62396
62441
  let notRunnable = 0;
62397
62442
  let noCapableNode = 0;
@@ -62402,6 +62447,12 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62402
62447
  let budgetExhausted = false;
62403
62448
  /** Set when the cluster ran out of nodes — the pass stops, `complete` is false. */
62404
62449
  let abandoned = null;
62450
+ const boxSourceCounts = () => ({
62451
+ subjectBox: boxSubject,
62452
+ positions: boxPositions,
62453
+ event: boxEvent,
62454
+ refused: boxRefused
62455
+ });
62405
62456
  while (scanned < maxTracks && abandoned === null) {
62406
62457
  const page = await deps.listTrackIdPage(cursor);
62407
62458
  if (page.trackIds.length === 0) {
@@ -62409,9 +62460,14 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62409
62460
  break;
62410
62461
  }
62411
62462
  let pageIds = page.trackIds;
62463
+ let forcedPage = false;
62412
62464
  if (deps.filterOutOfSpace) {
62413
62465
  const split = await deps.filterOutOfSpace(page.trackIds);
62414
- if (split !== null) {
62466
+ if (split !== null && force) {
62467
+ skippedOutOfSpace += split.outOfSpace.length;
62468
+ pageIds = split.inSpace;
62469
+ forcedPage = true;
62470
+ } else if (split !== null) {
62415
62471
  alreadyInSpace += split.alreadyInSpace;
62416
62472
  pageIds = split.outOfSpace;
62417
62473
  }
@@ -62427,6 +62483,7 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62427
62483
  const room = maxTracks - scanned;
62428
62484
  const batch = pageIds.slice(0, Number.isFinite(room) ? room : void 0);
62429
62485
  if (batch.length < pageIds.length) budgetExhausted = true;
62486
+ if (forcedPage) forcedInSpace += batch.length;
62430
62487
  for (const trackId of batch) {
62431
62488
  if (!deps.canDispatch()) {
62432
62489
  abandoned = "no node left in the rotation can serve the pinned model — stopped before loading any further media";
@@ -62445,6 +62502,25 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62445
62502
  missingEvent += 1;
62446
62503
  return;
62447
62504
  }
62505
+ if (source.bbox === null || source.bbox.w <= 0 || source.bbox.h <= 0) {
62506
+ missingBbox += 1;
62507
+ boxRefused += 1;
62508
+ const reason = source.boxSource.kind === "refused" ? source.boxSource.reason : "degenerate-box";
62509
+ const n = (refusalsByDevice.get(source.deviceId) ?? 0) + 1;
62510
+ refusalsByDevice.set(source.deviceId, n);
62511
+ if (n === 1 || n % 100 === 0) deps.logger.warn("embedding rebuild: no box measured on the key frame — track refused (D303)", {
62512
+ tags: { deviceId: source.deviceId },
62513
+ meta: {
62514
+ trackId,
62515
+ reason,
62516
+ refusedOnThisCamera: n
62517
+ }
62518
+ });
62519
+ return;
62520
+ }
62521
+ if (source.boxSource.kind === "subjectBox") boxSubject += 1;
62522
+ else if (source.boxSource.kind === "positions") boxPositions += 1;
62523
+ else boxEvent += 1;
62448
62524
  let keyFrame = source.keyFrame;
62449
62525
  let recovered = false;
62450
62526
  if (keyFrame === null && deps.recoverKeyFrame) {
@@ -62456,10 +62532,6 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62456
62532
  return;
62457
62533
  }
62458
62534
  if (recovered) recoveredKeyFrame += 1;
62459
- if (source.bbox === null || source.bbox.w <= 0 || source.bbox.h <= 0) {
62460
- missingBbox += 1;
62461
- return;
62462
- }
62463
62535
  const outcome = await deps.embed(recovered ? {
62464
62536
  ...source,
62465
62537
  keyFrame
@@ -62527,6 +62599,9 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62527
62599
  recoveredKeyFrame,
62528
62600
  missingEvent,
62529
62601
  missingBbox,
62602
+ boxSource: boxSourceCounts(),
62603
+ forcedInSpace,
62604
+ skippedOutOfSpace,
62530
62605
  alreadyInSpace,
62531
62606
  notRunnable,
62532
62607
  noCapableNode,
@@ -62550,6 +62625,10 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62550
62625
  recoveredKeyFrame,
62551
62626
  missingEvent,
62552
62627
  missingBbox,
62628
+ boxSource: boxSourceCounts(),
62629
+ force,
62630
+ forcedInSpace,
62631
+ skippedOutOfSpace,
62553
62632
  alreadyInSpace,
62554
62633
  notRunnable,
62555
62634
  noCapableNode,
@@ -62564,6 +62643,9 @@ async function rebuildObjectEmbeddings(deps, options = {}) {
62564
62643
  recoveredKeyFrame,
62565
62644
  missingEvent,
62566
62645
  missingBbox,
62646
+ boxSource: boxSourceCounts(),
62647
+ forcedInSpace,
62648
+ skippedOutOfSpace,
62567
62649
  alreadyInSpace,
62568
62650
  notRunnable,
62569
62651
  noCapableNode,
@@ -72962,6 +73044,58 @@ function hasKeyFrame(source) {
72962
73044
  }
72963
73045
  //#endregion
72964
73046
  //#region src/pipeline-analytics/rebuild-source-loader.ts
73047
+ function refused(reason) {
73048
+ return {
73049
+ bbox: null,
73050
+ boxSource: {
73051
+ kind: "refused",
73052
+ reason
73053
+ }
73054
+ };
73055
+ }
73056
+ function normaliseMeasured(box) {
73057
+ if (box.frameWidth <= 0 || box.frameHeight <= 0 || box.w <= 0 || box.h <= 0) return null;
73058
+ return {
73059
+ x: box.x / box.frameWidth,
73060
+ y: box.y / box.frameHeight,
73061
+ w: box.w / box.frameWidth,
73062
+ h: box.h / box.frameHeight
73063
+ };
73064
+ }
73065
+ /**
73066
+ * The box measured on THIS key frame: its own `subjectBox`, else the track
73067
+ * position at exactly its instant, else a named refusal. Never another
73068
+ * frame's box (D303).
73069
+ */
73070
+ async function keyFrameBox(deps, trackId, keyFrame) {
73071
+ const own = (await deps.media.listSubjectBoxesByOwner("track", trackId)).find((row) => row.mediaId === keyFrame.mediaId);
73072
+ if (own !== void 0) {
73073
+ const bbox = normaliseMeasured(own.subjectBox);
73074
+ if (bbox !== null) return {
73075
+ bbox,
73076
+ boxSource: { kind: "subjectBox" }
73077
+ };
73078
+ }
73079
+ const track = await deps.tracks.getPersistedByTrackId(trackId);
73080
+ if (track === null) return refused("track-row-missing");
73081
+ const { frameWidth, frameHeight } = track;
73082
+ if (frameWidth === void 0 || frameHeight === void 0) return refused("track-frame-size-unknown");
73083
+ const at = track.positions.find((p) => p.timestamp === keyFrame.timestamp);
73084
+ if (at === void 0) return refused("no-position-at-key-frame");
73085
+ const bbox = normaliseMeasured({
73086
+ ...at.bbox,
73087
+ frameWidth,
73088
+ frameHeight
73089
+ });
73090
+ if (bbox === null) return refused("no-position-at-key-frame");
73091
+ return {
73092
+ bbox,
73093
+ boxSource: { kind: "positions" }
73094
+ };
73095
+ }
73096
+ function hasBox(event) {
73097
+ return event.bbox !== void 0 && event.bbox.w > 0 && event.bbox.h > 0;
73098
+ }
72965
73099
  /**
72966
73100
  * Assemble one track's rebuild inputs, reading at most ONE blob.
72967
73101
  *
@@ -72969,7 +73103,8 @@ function hasKeyFrame(source) {
72969
73103
  * to describe, and the caller counts it. A track WITH an event but WITHOUT a
72970
73104
  * key frame still returns a source (with `keyFrame: null`) so the caller's
72971
73105
  * `missingKeyFrame` counter stays the one place that decision is made; no blob
72972
- * is read in that case either.
73106
+ * is read in that case either. A key frame with no box measured on it returns
73107
+ * `bbox: null` and a named refusal, also without reading the blob.
72973
73108
  */
72974
73109
  async function loadRebuildSource(deps, trackId) {
72975
73110
  const media = await deps.media.listInfoByOwner("track", trackId);
@@ -72977,26 +73112,44 @@ async function loadRebuildSource(deps, trackId) {
72977
73112
  const cropInfo = media.find((m) => m.kind === "thumbnail");
72978
73113
  const peak = await deps.events.peakForTrack(trackId);
72979
73114
  const events = await deps.events.queryObjectByTrackIds([trackId]);
72980
- const best = events.find((e) => String(e.id) === peak.bestEventId) ?? events[0];
73115
+ const best = events.find((e) => String(e.id) === peak.bestEventId) ?? events.find(hasBox) ?? events[0];
72981
73116
  if (best === void 0) return null;
72982
73117
  const common = {
72983
73118
  trackId,
72984
73119
  deviceId: best.deviceId,
72985
- timestamp: best.timestamp,
72986
73120
  className: best.className,
72987
73121
  confidence: best.confidence ?? 0,
72988
- bbox: normaliseEventBbox(best),
72989
73122
  ...cropInfo !== void 0 ? { mediaId: cropInfo.mediaId } : {}
72990
73123
  };
72991
- if (keyFrameInfo === void 0) return {
73124
+ if (keyFrameInfo === void 0) {
73125
+ const bbox = normaliseEventBbox(best);
73126
+ return {
73127
+ ...common,
73128
+ timestamp: best.timestamp,
73129
+ bbox,
73130
+ boxSource: bbox !== null ? { kind: "event" } : {
73131
+ kind: "refused",
73132
+ reason: "event-without-box"
73133
+ },
73134
+ keyFrame: null
73135
+ };
73136
+ }
73137
+ const box = await keyFrameBox(deps, trackId, keyFrameInfo);
73138
+ const keyed = {
72992
73139
  ...common,
73140
+ timestamp: keyFrameInfo.timestamp,
73141
+ bbox: box.bbox,
73142
+ boxSource: box.boxSource,
73143
+ keyFrameMediaId: keyFrameInfo.mediaId
73144
+ };
73145
+ if (box.bbox === null) return {
73146
+ ...keyed,
72993
73147
  keyFrame: null
72994
73148
  };
72995
73149
  const keyFrameFile = await deps.media.getById(keyFrameInfo.mediaId);
72996
73150
  return {
72997
- ...common,
72998
- keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null,
72999
- keyFrameMediaId: keyFrameInfo.mediaId
73151
+ ...keyed,
73152
+ keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null
73000
73153
  };
73001
73154
  }
73002
73155
  //#endregion
@@ -83545,7 +83698,7 @@ function buildGlobalSettingsSchema(cap) {
83545
83698
  addonId: "pipeline-analytics",
83546
83699
  action: "embedding.rebuildStatus",
83547
83700
  buttonLabel: "Check progress",
83548
- successMessage: "{modelId} → {index}: indexed {indexed} · scanned {scanned} · rebuilt {rebuilt} · no key frame {missingKeyFrame} · not runnable {notRunnable} · failed {failed}"
83701
+ successMessage: "{modelId} → {index}: indexed {indexed} · scanned {scanned} · rebuilt {rebuilt} · no key frame {missingKeyFrame} · no box {missingBbox} · no event {missingEvent} · not runnable {notRunnable} · failed {failed}"
83549
83702
  },
83550
83703
  {
83551
83704
  type: "addon-action-button",
@@ -97132,6 +97285,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97132
97285
  refusal: "stores-unavailable"
97133
97286
  };
97134
97287
  }
97288
+ const force = input.force === true;
97289
+ if (force && input.maxTracks === void 0) {
97290
+ this.ctx.logger.warn("rebuildObjectEmbeddings: refusing a force pass with no maxTracks", { meta: { refusal: "force-requires-max-tracks" } });
97291
+ return {
97292
+ started: false,
97293
+ alreadyRunning: false,
97294
+ refusal: "force-requires-max-tracks"
97295
+ };
97296
+ }
97135
97297
  const confirmation = await this.getClusterPinConfirmer(api, "clip-rebuild").confirm();
97136
97298
  if (confirmation.kind === "refused") {
97137
97299
  this.ctx.logger.warn("rebuildObjectEmbeddings: the cluster CLIP model is not confirmed — refusing to start a pass that cannot pin one", { meta: { refusal: confirmation.reason } });
@@ -97175,16 +97337,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97175
97337
  logger: this.ctx.logger
97176
97338
  }));
97177
97339
  const deps = {
97178
- listTrackIdPage: async (cursor) => trackStore.queryRecentIds({
97340
+ listTrackIdPage: async (cursor) => trackStore.queryRecentIds(rebuildTrackPageQuery({
97179
97341
  deviceIds,
97180
97342
  ...input.since !== void 0 ? { since: input.since } : {},
97181
97343
  ...input.until !== void 0 ? { until: input.until } : {},
97182
97344
  ...cursor !== null ? { cursor } : {},
97183
97345
  limit: REBUILD_PAGE_SIZE
97184
- }),
97346
+ })),
97185
97347
  loadSource: (trackId) => loadRebuildSource({
97186
97348
  media: mediaStore,
97187
- events: eventStore
97349
+ events: eventStore,
97350
+ tracks: trackStore
97188
97351
  }, trackId),
97189
97352
  recoverKeyFrame: async (source) => {
97190
97353
  const recovered = await recordedStills.recover(source.deviceId, source.timestamp, RECOVERED_KEY_FRAME_MAX_WIDTH);
@@ -97210,7 +97373,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97210
97373
  if (split === null) return null;
97211
97374
  return {
97212
97375
  outOfSpace: split.outOfSpaceIds,
97213
- alreadyInSpace: split.inSpace
97376
+ alreadyInSpace: split.inSpace,
97377
+ inSpace: split.inSpaceIds
97214
97378
  };
97215
97379
  },
97216
97380
  logger: {
@@ -97222,6 +97386,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97222
97386
  running: true,
97223
97387
  modelId,
97224
97388
  index,
97389
+ force,
97225
97390
  startedAtMs: Date.now(),
97226
97391
  progress: {
97227
97392
  scanned: 0,
@@ -97230,6 +97395,14 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97230
97395
  recoveredKeyFrame: 0,
97231
97396
  missingEvent: 0,
97232
97397
  missingBbox: 0,
97398
+ boxSource: {
97399
+ subjectBox: 0,
97400
+ positions: 0,
97401
+ event: 0,
97402
+ refused: 0
97403
+ },
97404
+ forcedInSpace: 0,
97405
+ skippedOutOfSpace: 0,
97233
97406
  alreadyInSpace: 0,
97234
97407
  notRunnable: 0,
97235
97408
  noCapableNode: 0,
@@ -97237,6 +97410,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97237
97410
  }
97238
97411
  };
97239
97412
  rebuildObjectEmbeddings(deps, {
97413
+ force,
97240
97414
  ...input.maxTracks !== void 0 ? { maxTracks: input.maxTracks } : {},
97241
97415
  ...input.pacingMs !== void 0 ? { pacingMs: input.pacingMs } : {},
97242
97416
  onProgress: (progress) => {
@@ -97248,6 +97422,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97248
97422
  running: false,
97249
97423
  modelId,
97250
97424
  index,
97425
+ force,
97251
97426
  startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
97252
97427
  finishedAtMs: Date.now(),
97253
97428
  progress: outcome,
@@ -97263,6 +97438,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97263
97438
  running: false,
97264
97439
  modelId,
97265
97440
  index,
97441
+ force,
97266
97442
  startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
97267
97443
  finishedAtMs: Date.now(),
97268
97444
  error: String(err)
@@ -97277,7 +97453,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97277
97453
  modelId,
97278
97454
  index,
97279
97455
  deviceId: input.deviceId ?? "all",
97280
- maxTracks: input.maxTracks ?? "unbounded"
97456
+ maxTracks: input.maxTracks ?? "unbounded",
97457
+ force
97281
97458
  } });
97282
97459
  return {
97283
97460
  started: true,
@@ -98279,8 +98456,18 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
98279
98456
  running: state.running,
98280
98457
  scanned: p?.scanned ?? 0,
98281
98458
  rebuilt: p?.rebuilt ?? 0,
98282
- missingKeyFrame: (p?.missingKeyFrame ?? 0) + (p?.missingEvent ?? 0),
98459
+ missingKeyFrame: p?.missingKeyFrame ?? 0,
98460
+ missingEvent: p?.missingEvent ?? 0,
98283
98461
  missingBbox: p?.missingBbox ?? 0,
98462
+ boxSource: p?.boxSource ?? {
98463
+ subjectBox: 0,
98464
+ positions: 0,
98465
+ event: 0,
98466
+ refused: 0
98467
+ },
98468
+ alreadyInSpace: p?.alreadyInSpace ?? 0,
98469
+ force: state.force ?? null,
98470
+ forcedInSpace: p?.forcedInSpace ?? 0,
98284
98471
  notRunnable: p?.notRunnable ?? 0,
98285
98472
  noCapableNode: p?.noCapableNode ?? 0,
98286
98473
  failed: p?.failed ?? 0,
@@ -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-r3ZZOSzj.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CBHxwCSj.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.290",
3
+ "version": "1.2.291",
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",