@camstack/addon-post-analysis 1.2.289 → 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.
Files changed (15) hide show
  1. package/dist/{clip-model-registry-JFtcAZEB.js → clip-model-registry-CTY5co6P.js} +472 -27
  2. package/dist/{clip-model-registry-g3-Yz08Y.mjs → clip-model-registry-Cudz--Ql.mjs} +472 -27
  3. package/dist/embedding-encoder/index.js +1 -1
  4. package/dist/embedding-encoder/index.mjs +1 -1
  5. package/dist/pipeline-analytics/_stub.js +2 -2
  6. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-D5OpYfsh.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CBHxwCSj.mjs} +2 -2
  7. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-2OwTV_D2.mjs +26 -0
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DAAKQ3sC.mjs +26 -0
  9. package/dist/pipeline-analytics/{hostInit-BsQiXbDx.mjs → hostInit-DSKT2dE4.mjs} +2 -2
  10. package/dist/pipeline-analytics/index.js +322 -59
  11. package/dist/pipeline-analytics/index.mjs +322 -59
  12. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  13. package/package.json +1 -1
  14. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CfU5NAwo.mjs +0 -26
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-r4OU_ahc.mjs +0 -26
@@ -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-g3-Yz08Y.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,
@@ -62920,8 +63002,6 @@ var FaceSettingsSchema = object({
62920
63002
  * (2026-07-23), then →0.12 (2026-08-18) alongside the threshold retune —
62921
63003
  * same story and the same recalibration plan as {@link similarityThreshold}. */
62922
63004
  margin: number().min(0).max(1).default(.12),
62923
- /** Minimum face-detection confidence for a face to be considered. */
62924
- minFaceConfidence: number().min(0).max(1).default(.5),
62925
63005
  /**
62926
63006
  * Minimum face bbox size (px, shorter side, NATIVE scale when the runner
62927
63007
  * measured it, else detection-frame space) for a face to be DETECTED and
@@ -63003,7 +63083,6 @@ function resolveFaceSettings(raw) {
63003
63083
  enabled: pick("enabled"),
63004
63084
  similarityThreshold: pick("similarityThreshold"),
63005
63085
  margin: pick("margin"),
63006
- minFaceConfidence: pick("minFaceConfidence"),
63007
63086
  minFacePx: pick("minFacePx"),
63008
63087
  recognitionMinFacePx: pick("recognitionMinFacePx"),
63009
63088
  minIdentitySamples: pick("minIdentitySamples"),
@@ -63076,6 +63155,17 @@ function deriveSuggestedBufferCap(bufferMaxPerDevice) {
63076
63155
  * it from a store.
63077
63156
  */
63078
63157
  var STORED_SUGGESTION_MIN_COSINE_KEY = "suggestionMinCosine";
63158
+ /** The settings key that held the retired recognizer face-confidence bar (D662). */
63159
+ var STORED_MIN_FACE_CONFIDENCE_KEY = "minFaceConfidence";
63160
+ /** Plan the removal of ONE retired key from a settings blob; `null` = absent. */
63161
+ function planRetiredKeyPurge(raw, key) {
63162
+ const was = raw[key];
63163
+ if (was === void 0) return null;
63164
+ return {
63165
+ was,
63166
+ patch: { [key]: void 0 }
63167
+ };
63168
+ }
63079
63169
  /**
63080
63170
  * Plan the removal of a persisted suggestion floor from one settings blob.
63081
63171
  *
@@ -63090,12 +63180,15 @@ var STORED_SUGGESTION_MIN_COSINE_KEY = "suggestionMinCosine";
63090
63180
  * serialisation the blob round-trips through.
63091
63181
  */
63092
63182
  function planStoredSuggestionPurge(raw) {
63093
- const was = raw[STORED_SUGGESTION_MIN_COSINE_KEY];
63094
- if (was === void 0) return null;
63095
- return {
63096
- was,
63097
- patch: { [STORED_SUGGESTION_MIN_COSINE_KEY]: void 0 }
63098
- };
63183
+ return planRetiredKeyPurge(raw, STORED_SUGGESTION_MIN_COSINE_KEY);
63184
+ }
63185
+ /**
63186
+ * Plan the removal of a persisted `minFaceConfidence` (D662). Same contract as
63187
+ * {@link planStoredSuggestionPurge}: absence of the key is the marker, and the
63188
+ * patch unsets with `undefined`.
63189
+ */
63190
+ function planStoredMinFaceConfidencePurge(raw) {
63191
+ return planRetiredKeyPurge(raw, STORED_MIN_FACE_CONFIDENCE_KEY);
63099
63192
  }
63100
63193
  /**
63101
63194
  * Map a detection-frame face short side to the 0..1 `sizeScore` the
@@ -63384,7 +63477,7 @@ var FaceRecognizer = class {
63384
63477
  async processFrame(input) {
63385
63478
  const { settings } = input;
63386
63479
  for (const t of input.tracked) if (t.resurrectionScene === "multi-subject") this.quarantineHeldFace(input.deviceId, t.trackId);
63387
- const candidates = input.tracked.filter((t) => Array.isArray(t.embedding) && t.embedding.length > 0 && typeof t.embeddingModelId === "string" && t.confidence >= settings.minFaceConfidence && (t.nativeFaceShortSidePx ?? (t.faceBbox ? Math.min(t.faceBbox.w, t.faceBbox.h) : void 0) ?? Number.POSITIVE_INFINITY) >= settings.minFacePx);
63480
+ const candidates = input.tracked.filter((t) => Array.isArray(t.embedding) && t.embedding.length > 0 && typeof t.embeddingModelId === "string" && (t.nativeFaceShortSidePx ?? (t.faceBbox ? Math.min(t.faceBbox.w, t.faceBbox.h) : void 0) ?? Number.POSITIVE_INFINITY) >= settings.minFacePx);
63388
63481
  if (candidates.length === 0) return;
63389
63482
  this.deps.logger.debug("face: frame candidates", {
63390
63483
  tags: { deviceId: input.deviceId },
@@ -72951,6 +73044,58 @@ function hasKeyFrame(source) {
72951
73044
  }
72952
73045
  //#endregion
72953
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
+ }
72954
73099
  /**
72955
73100
  * Assemble one track's rebuild inputs, reading at most ONE blob.
72956
73101
  *
@@ -72958,7 +73103,8 @@ function hasKeyFrame(source) {
72958
73103
  * to describe, and the caller counts it. A track WITH an event but WITHOUT a
72959
73104
  * key frame still returns a source (with `keyFrame: null`) so the caller's
72960
73105
  * `missingKeyFrame` counter stays the one place that decision is made; no blob
72961
- * 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.
72962
73108
  */
72963
73109
  async function loadRebuildSource(deps, trackId) {
72964
73110
  const media = await deps.media.listInfoByOwner("track", trackId);
@@ -72966,26 +73112,44 @@ async function loadRebuildSource(deps, trackId) {
72966
73112
  const cropInfo = media.find((m) => m.kind === "thumbnail");
72967
73113
  const peak = await deps.events.peakForTrack(trackId);
72968
73114
  const events = await deps.events.queryObjectByTrackIds([trackId]);
72969
- 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];
72970
73116
  if (best === void 0) return null;
72971
73117
  const common = {
72972
73118
  trackId,
72973
73119
  deviceId: best.deviceId,
72974
- timestamp: best.timestamp,
72975
73120
  className: best.className,
72976
73121
  confidence: best.confidence ?? 0,
72977
- bbox: normaliseEventBbox(best),
72978
73122
  ...cropInfo !== void 0 ? { mediaId: cropInfo.mediaId } : {}
72979
73123
  };
72980
- 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 = {
72981
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,
72982
73147
  keyFrame: null
72983
73148
  };
72984
73149
  const keyFrameFile = await deps.media.getById(keyFrameInfo.mediaId);
72985
73150
  return {
72986
- ...common,
72987
- keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null,
72988
- keyFrameMediaId: keyFrameInfo.mediaId
73151
+ ...keyed,
73152
+ keyFrame: keyFrameFile ? Buffer.from(keyFrameFile.base64, "base64") : null
72989
73153
  };
72990
73154
  }
72991
73155
  //#endregion
@@ -83534,7 +83698,7 @@ function buildGlobalSettingsSchema(cap) {
83534
83698
  addonId: "pipeline-analytics",
83535
83699
  action: "embedding.rebuildStatus",
83536
83700
  buttonLabel: "Check progress",
83537
- 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}"
83538
83702
  },
83539
83703
  {
83540
83704
  type: "addon-action-button",
@@ -84086,16 +84250,6 @@ function buildGlobalSettingsSchema(cap) {
84086
84250
  step: .01,
84087
84251
  default: FACE_DEFAULTS.margin
84088
84252
  },
84089
- {
84090
- type: "number",
84091
- key: "minFaceConfidence",
84092
- label: "Min face confidence",
84093
- description: "Minimum face-detection confidence (0–1) for a face to be considered.",
84094
- min: 0,
84095
- max: 1,
84096
- step: .05,
84097
- default: FACE_DEFAULTS.minFaceConfidence
84098
- },
84099
84253
  {
84100
84254
  type: "number",
84101
84255
  key: "minFacePx",
@@ -94875,6 +95029,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
94875
95029
  const deviceIds = await this.retentionDeviceIds();
94876
95030
  await this.migrateRetiredRetentionSettings(deviceIds);
94877
95031
  await this.purgeStoredSuggestionThreshold(deviceIds);
95032
+ await this.purgeStoredMinFaceConfidence(deviceIds);
94878
95033
  await this.runTrackRetentionSweep(deviceIds, now);
94879
95034
  const eventStore = this.eventStore;
94880
95035
  const sensorEventStore = this.sensorEventStore;
@@ -95002,6 +95157,80 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
95002
95157
  * this process, and which devices have. One announcement, not a heartbeat. */
95003
95158
  storedSuggestionCheckedGlobal = false;
95004
95159
  storedSuggestionCheckedDevices = /* @__PURE__ */ new Set();
95160
+ storedMinFaceConfidenceCheckedGlobal = false;
95161
+ storedMinFaceConfidenceCheckedDevices = /* @__PURE__ */ new Set();
95162
+ /**
95163
+ * One-time: announce the recognizer face-confidence bar D662 retired, then
95164
+ * unset it — globally and per device. Same shape as
95165
+ * {@link purgeStoredSuggestionThreshold}: absence of the key is the marker,
95166
+ * the write goes through `updateGlobalSettings` / `writeDeviceStore`, and the
95167
+ * patch unsets with `undefined`.
95168
+ *
95169
+ * Why it must leave the store and not merely go unread: the bar compared the
95170
+ * DETECTOR's score without knowing which detector produced it, so its units
95171
+ * changed under it when YuNet became the default (D645). A number left in the
95172
+ * blob is an authority the next reader who greps for it would re-adopt — at a
95173
+ * scale nobody can name. The hub carried an interim 0.85 set by hand on
95174
+ * 2026-09-27; the one bar in force is now the detector model's own floor.
95175
+ */
95176
+ async purgeStoredMinFaceConfidence(deviceIds) {
95177
+ const settings = this.ctxIfReady?.settings;
95178
+ if (!settings) return;
95179
+ if (!this.storedMinFaceConfidenceCheckedGlobal) {
95180
+ this.storedMinFaceConfidenceCheckedGlobal = true;
95181
+ try {
95182
+ const plan = planStoredMinFaceConfidencePurge(await this.resolveGlobalStore());
95183
+ if (plan !== null) {
95184
+ this.announceRetiredFaceConfidenceBar({
95185
+ scope: "global",
95186
+ was: plan.was
95187
+ });
95188
+ await this.updateGlobalSettings(plan.patch);
95189
+ }
95190
+ } catch (err) {
95191
+ this.storedMinFaceConfidenceCheckedGlobal = false;
95192
+ this.ctx.logger.warn("face confidence bar purge failed — will retry", { meta: {
95193
+ scope: "global",
95194
+ error: String(err)
95195
+ } });
95196
+ }
95197
+ }
95198
+ for (const deviceId of deviceIds) {
95199
+ if (this.storedMinFaceConfidenceCheckedDevices.has(deviceId)) continue;
95200
+ this.storedMinFaceConfidenceCheckedDevices.add(deviceId);
95201
+ try {
95202
+ const plan = planStoredMinFaceConfidencePurge(await this.readDeviceBlob(deviceId));
95203
+ if (plan === null) continue;
95204
+ this.announceRetiredFaceConfidenceBar({
95205
+ scope: "device",
95206
+ was: plan.was,
95207
+ deviceId
95208
+ });
95209
+ await settings.writeDeviceStore(deviceId, plan.patch);
95210
+ this.rawDeviceBlobMirror.invalidate(deviceId);
95211
+ } catch (err) {
95212
+ this.storedMinFaceConfidenceCheckedDevices.delete(deviceId);
95213
+ this.ctx.logger.warn("face confidence bar purge failed — will retry", {
95214
+ tags: { deviceId },
95215
+ meta: {
95216
+ scope: "device",
95217
+ error: String(err)
95218
+ }
95219
+ });
95220
+ }
95221
+ }
95222
+ }
95223
+ announceRetiredFaceConfidenceBar(found) {
95224
+ this.ctx.logger.warn("face confidence bar RETIRED — the detector model floor on the runner is the one bar now", {
95225
+ ...found.deviceId !== void 0 ? { tags: { deviceId: found.deviceId } } : {},
95226
+ meta: {
95227
+ scope: found.scope,
95228
+ setting: STORED_MIN_FACE_CONFIDENCE_KEY,
95229
+ wasSetTo: found.was,
95230
+ why: "the recognizer compared the detector score without knowing which detector ran; after YuNet became the default its 0.5 was SCRFD’s bar on YuNet’s scale (D662)."
95231
+ }
95232
+ });
95233
+ }
95005
95234
  /**
95006
95235
  * One-time: announce the suggestion floor D222 retired, then unset it —
95007
95236
  * globally and per device.
@@ -97056,6 +97285,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97056
97285
  refusal: "stores-unavailable"
97057
97286
  };
97058
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
+ }
97059
97297
  const confirmation = await this.getClusterPinConfirmer(api, "clip-rebuild").confirm();
97060
97298
  if (confirmation.kind === "refused") {
97061
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 } });
@@ -97099,16 +97337,17 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97099
97337
  logger: this.ctx.logger
97100
97338
  }));
97101
97339
  const deps = {
97102
- listTrackIdPage: async (cursor) => trackStore.queryRecentIds({
97340
+ listTrackIdPage: async (cursor) => trackStore.queryRecentIds(rebuildTrackPageQuery({
97103
97341
  deviceIds,
97104
97342
  ...input.since !== void 0 ? { since: input.since } : {},
97105
97343
  ...input.until !== void 0 ? { until: input.until } : {},
97106
97344
  ...cursor !== null ? { cursor } : {},
97107
97345
  limit: REBUILD_PAGE_SIZE
97108
- }),
97346
+ })),
97109
97347
  loadSource: (trackId) => loadRebuildSource({
97110
97348
  media: mediaStore,
97111
- events: eventStore
97349
+ events: eventStore,
97350
+ tracks: trackStore
97112
97351
  }, trackId),
97113
97352
  recoverKeyFrame: async (source) => {
97114
97353
  const recovered = await recordedStills.recover(source.deviceId, source.timestamp, RECOVERED_KEY_FRAME_MAX_WIDTH);
@@ -97134,7 +97373,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97134
97373
  if (split === null) return null;
97135
97374
  return {
97136
97375
  outOfSpace: split.outOfSpaceIds,
97137
- alreadyInSpace: split.inSpace
97376
+ alreadyInSpace: split.inSpace,
97377
+ inSpace: split.inSpaceIds
97138
97378
  };
97139
97379
  },
97140
97380
  logger: {
@@ -97146,6 +97386,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97146
97386
  running: true,
97147
97387
  modelId,
97148
97388
  index,
97389
+ force,
97149
97390
  startedAtMs: Date.now(),
97150
97391
  progress: {
97151
97392
  scanned: 0,
@@ -97154,6 +97395,14 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97154
97395
  recoveredKeyFrame: 0,
97155
97396
  missingEvent: 0,
97156
97397
  missingBbox: 0,
97398
+ boxSource: {
97399
+ subjectBox: 0,
97400
+ positions: 0,
97401
+ event: 0,
97402
+ refused: 0
97403
+ },
97404
+ forcedInSpace: 0,
97405
+ skippedOutOfSpace: 0,
97157
97406
  alreadyInSpace: 0,
97158
97407
  notRunnable: 0,
97159
97408
  noCapableNode: 0,
@@ -97161,6 +97410,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97161
97410
  }
97162
97411
  };
97163
97412
  rebuildObjectEmbeddings(deps, {
97413
+ force,
97164
97414
  ...input.maxTracks !== void 0 ? { maxTracks: input.maxTracks } : {},
97165
97415
  ...input.pacingMs !== void 0 ? { pacingMs: input.pacingMs } : {},
97166
97416
  onProgress: (progress) => {
@@ -97172,6 +97422,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97172
97422
  running: false,
97173
97423
  modelId,
97174
97424
  index,
97425
+ force,
97175
97426
  startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
97176
97427
  finishedAtMs: Date.now(),
97177
97428
  progress: outcome,
@@ -97187,6 +97438,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97187
97438
  running: false,
97188
97439
  modelId,
97189
97440
  index,
97441
+ force,
97190
97442
  startedAtMs: this.rebuildState.startedAtMs ?? Date.now(),
97191
97443
  finishedAtMs: Date.now(),
97192
97444
  error: String(err)
@@ -97201,7 +97453,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97201
97453
  modelId,
97202
97454
  index,
97203
97455
  deviceId: input.deviceId ?? "all",
97204
- maxTracks: input.maxTracks ?? "unbounded"
97456
+ maxTracks: input.maxTracks ?? "unbounded",
97457
+ force
97205
97458
  } });
97206
97459
  return {
97207
97460
  started: true,
@@ -98203,8 +98456,18 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
98203
98456
  running: state.running,
98204
98457
  scanned: p?.scanned ?? 0,
98205
98458
  rebuilt: p?.rebuilt ?? 0,
98206
- missingKeyFrame: (p?.missingKeyFrame ?? 0) + (p?.missingEvent ?? 0),
98459
+ missingKeyFrame: p?.missingKeyFrame ?? 0,
98460
+ missingEvent: p?.missingEvent ?? 0,
98207
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,
98208
98471
  notRunnable: p?.notRunnable ?? 0,
98209
98472
  noCapableNode: p?.noCapableNode ?? 0,
98210
98473
  failed: p?.failed ?? 0,