@camstack/addon-post-analysis 1.2.286 → 1.2.287

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 (14) hide show
  1. package/dist/{clip-model-registry-D4mC2M7F.mjs → clip-model-registry-BRHeTbFV.mjs} +36 -42
  2. package/dist/{clip-model-registry-CY0FGcpQ.js → clip-model-registry-Cq1X8kTo.js} +36 -42
  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-CkfvSztj.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BpJC8P0K.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-NKbCrEkH.mjs → _virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-3dZf67Z7.mjs} +1 -1
  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-r4OU_ahc.mjs +26 -0
  9. package/dist/pipeline-analytics/{hostInit-CelYj4QN.mjs → hostInit-BIUCfS5T.mjs} +2 -2
  10. package/dist/pipeline-analytics/index.js +605 -62
  11. package/dist/pipeline-analytics/index.mjs +605 -62
  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_ui_mf_2_library__loadShare__.js-BAYoLHsN.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-D4mC2M7F.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-BRHeTbFV.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";
@@ -82,7 +82,7 @@ function motionEpisodeDurationMs(state) {
82
82
  }
83
83
  //#endregion
84
84
  //#region src/pipeline-analytics/motion-episode-tracker.ts
85
- function keyOf$1(deviceId, source) {
85
+ function keyOf$2(deviceId, source) {
86
86
  return `${deviceId}:${source}`;
87
87
  }
88
88
  var MotionEpisodeTracker = class {
@@ -98,7 +98,7 @@ var MotionEpisodeTracker = class {
98
98
  * {@link MotionEpisodeInsertInput}.
99
99
  */
100
100
  async observe(deviceId, source, detected, timestamp, regionData) {
101
- const key = keyOf$1(deviceId, source);
101
+ const key = keyOf$2(deviceId, source);
102
102
  const prior = this.signals.get(key);
103
103
  const decision = decideMotionSignal(prior, detected, timestamp);
104
104
  switch (decision.kind) {
@@ -12773,14 +12773,14 @@ var NcPruneRecordSchema = object({
12773
12773
  });
12774
12774
  /** Rules × deleted subjects — small; a generous ceiling. */
12775
12775
  var LOAD_LIMIT$4 = 1e4;
12776
- function keyOf(row) {
12776
+ function keyOf$1(row) {
12777
12777
  return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
12778
12778
  }
12779
12779
  var NC_DEVICE_PRUNES_SPEC = {
12780
12780
  collection: NC_DEVICE_PRUNES_COLLECTION,
12781
12781
  columns: [...NC_DEVICE_PRUNES_COLUMNS],
12782
12782
  writeMode: "write-through",
12783
- keyOf,
12783
+ keyOf: keyOf$1,
12784
12784
  toValue: (row) => ({
12785
12785
  ruleId: row.ruleId,
12786
12786
  at: row.at,
@@ -12820,7 +12820,7 @@ var NcPruneLedger = class {
12820
12820
  * {@link isLoaded} as it was — see the module docblock.
12821
12821
  */
12822
12822
  async load() {
12823
- const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
12823
+ const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
12824
12824
  const startedAt = this.writes;
12825
12825
  const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
12826
12826
  try {
@@ -12831,7 +12831,7 @@ var NcPruneLedger = class {
12831
12831
  const next = /* @__PURE__ */ new Map();
12832
12832
  for (const record of records) {
12833
12833
  const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
12834
- if (row !== null) next.set(keyOf(row), row);
12834
+ if (row !== null) next.set(keyOf$1(row), row);
12835
12835
  }
12836
12836
  for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
12837
12837
  for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
@@ -12861,7 +12861,7 @@ var NcPruneLedger = class {
12861
12861
  */
12862
12862
  async record(notes, source, at) {
12863
12863
  for (const note of notes) {
12864
- const held = this.ledger.get(keyOf(note));
12864
+ const held = this.ledger.get(keyOf$1(note));
12865
12865
  if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
12866
12866
  const row = NcPruneRecordSchema.parse({
12867
12867
  ...note,
@@ -12869,7 +12869,7 @@ var NcPruneLedger = class {
12869
12869
  source
12870
12870
  });
12871
12871
  this.writes += 1;
12872
- this.writtenAt.set(keyOf(row), this.writes);
12872
+ this.writtenAt.set(keyOf$1(row), this.writes);
12873
12873
  await this.ledger.put(row);
12874
12874
  }
12875
12875
  }
@@ -39685,6 +39685,87 @@ var ReembedFacesInputSchema = object({
39685
39685
  /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39686
39686
  executeOnNodeId: string().optional()
39687
39687
  });
39688
+ /**
39689
+ * D658: re-embed each sample's stored template with ONE pinned model and
39690
+ * compare with the stored vector — does the stamp say what produced it?
39691
+ * Read-only: it reports and writes nothing.
39692
+ */
39693
+ var AuditFaceEmbeddingsInputSchema = object({
39694
+ /** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
39695
+ modelId: string().min(1).optional(),
39696
+ /** Only this identity's samples. */
39697
+ identityId: string().min(1).optional(),
39698
+ /** Stop after this many measured samples. */
39699
+ maxSamples: number().int().positive().optional(),
39700
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39701
+ executeOnNodeId: string().optional()
39702
+ });
39703
+ var AuditFaceEmbeddingsResultSchema = object({
39704
+ auditedModelId: string(),
39705
+ total: number(),
39706
+ ok: number(),
39707
+ mismatch: number(),
39708
+ inconclusive: number(),
39709
+ unmeasurable: number(),
39710
+ complete: boolean(),
39711
+ okMinCosine: number(),
39712
+ mismatchMaxCosine: number(),
39713
+ rows: array(object({
39714
+ sampleId: string(),
39715
+ identityId: string(),
39716
+ identityName: string(),
39717
+ stampedModelId: string(),
39718
+ verdict: _enum([
39719
+ "ok",
39720
+ "mismatch",
39721
+ "inconclusive",
39722
+ "unmeasurable"
39723
+ ]),
39724
+ cosine: number().optional(),
39725
+ reason: _enum([
39726
+ "stamped-other-model",
39727
+ "no-pixels",
39728
+ "no-stored-vector",
39729
+ "embed-failed",
39730
+ "no-capable-node",
39731
+ "dimension-differs"
39732
+ ]).optional(),
39733
+ detail: string().optional()
39734
+ }))
39735
+ });
39736
+ /**
39737
+ * D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
39738
+ * vectors even when already stamped with it — the repair for what
39739
+ * `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
39740
+ */
39741
+ var ReembedSamplesInputSchema = object({
39742
+ sampleIds: array(string().min(1)).min(1).max(200),
39743
+ modelId: string().min(1),
39744
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39745
+ executeOnNodeId: string().optional()
39746
+ });
39747
+ var ReembedSamplesResultSchema = object({
39748
+ pinnedModelId: string(),
39749
+ requested: number(),
39750
+ reembedded: number(),
39751
+ failed: number(),
39752
+ rows: array(object({
39753
+ sampleId: string(),
39754
+ outcome: _enum([
39755
+ "reembedded",
39756
+ "not-found",
39757
+ "duplicate",
39758
+ "no-pixels",
39759
+ "embed-failed",
39760
+ "no-capable-node",
39761
+ "no-model-stamp",
39762
+ "not-landed"
39763
+ ]),
39764
+ previousModelId: string().optional(),
39765
+ modelId: string().optional(),
39766
+ detail: string().optional()
39767
+ }))
39768
+ });
39688
39769
  /** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
39689
39770
  var ReembedFacesResultSchema = object({
39690
39771
  targetModelId: string(),
@@ -39936,11 +40017,256 @@ var embeddingActions = defineCustomActions({
39936
40017
  "embedding.faceAlignmentStatus": customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
39937
40018
  "embedding.backfillPhotoSources": customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
39938
40019
  "faceGallery.sourceFrameStats": customAction(NoInputSchema$1, SourceFrameStatsSchema),
40020
+ "faceGallery.reembedSamples": customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
40021
+ kind: "mutation",
40022
+ auth: "admin"
40023
+ }),
40024
+ "faceGallery.auditEmbeddings": customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
39939
40025
  "faceGallery.resetToCore": customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
39940
40026
  kind: "mutation",
39941
40027
  auth: "admin"
39942
40028
  })
39943
40029
  });
40030
+ async function reembedOne(deps, sample, pinned) {
40031
+ const base = {
40032
+ sampleId: sample.sampleId,
40033
+ previousModelId: sample.modelId
40034
+ };
40035
+ const crop = await deps.loadAlignedCrop(sample);
40036
+ if (crop === null) return {
40037
+ ...base,
40038
+ outcome: "no-pixels"
40039
+ };
40040
+ const outcome = await deps.embed(sample, crop);
40041
+ if (outcome.kind === "failed") return {
40042
+ ...base,
40043
+ outcome: "embed-failed",
40044
+ detail: outcome.reason
40045
+ };
40046
+ if (outcome.kind === "no-capable-node") return {
40047
+ ...base,
40048
+ outcome: "no-capable-node",
40049
+ detail: outcome.reason
40050
+ };
40051
+ if (outcome.modelId === void 0 || outcome.modelId === "") return {
40052
+ ...base,
40053
+ outcome: "no-model-stamp",
40054
+ detail: "the embed answered no model id"
40055
+ };
40056
+ if (outcome.modelId !== pinned) return {
40057
+ ...base,
40058
+ outcome: "embed-failed",
40059
+ detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
40060
+ };
40061
+ const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
40062
+ if (!write.landed) return {
40063
+ ...base,
40064
+ outcome: "not-landed",
40065
+ detail: write.reason ?? "the write did not land"
40066
+ };
40067
+ return {
40068
+ ...base,
40069
+ outcome: "reembedded",
40070
+ modelId: outcome.modelId
40071
+ };
40072
+ }
40073
+ async function reembedFaceSamples(deps, options) {
40074
+ const pinned = options.modelId;
40075
+ const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
40076
+ const pacingMs = options.pacingMs ?? 25;
40077
+ const seen = /* @__PURE__ */ new Set();
40078
+ const rows = [];
40079
+ let noNode = null;
40080
+ deps.logger.info("face sample re-embed starting", { meta: {
40081
+ pinnedModelId: pinned,
40082
+ requested: options.sampleIds.length
40083
+ } });
40084
+ for (const sampleId of options.sampleIds) {
40085
+ if (seen.has(sampleId)) {
40086
+ rows.push({
40087
+ sampleId,
40088
+ outcome: "duplicate"
40089
+ });
40090
+ continue;
40091
+ }
40092
+ seen.add(sampleId);
40093
+ const sample = byId.get(sampleId);
40094
+ if (sample === void 0) {
40095
+ rows.push({
40096
+ sampleId,
40097
+ outcome: "not-found"
40098
+ });
40099
+ continue;
40100
+ }
40101
+ if (noNode !== null) {
40102
+ rows.push({
40103
+ sampleId,
40104
+ previousModelId: sample.modelId,
40105
+ outcome: "no-capable-node",
40106
+ detail: noNode
40107
+ });
40108
+ continue;
40109
+ }
40110
+ const row = await reembedOne(deps, sample, pinned);
40111
+ if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
40112
+ if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
40113
+ sampleId,
40114
+ outcome: row.outcome,
40115
+ detail: row.detail ?? null,
40116
+ pinnedModelId: pinned
40117
+ } });
40118
+ rows.push(row);
40119
+ await deps.pause(pacingMs);
40120
+ }
40121
+ const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
40122
+ deps.logger.info("face sample re-embed finished", { meta: {
40123
+ pinnedModelId: pinned,
40124
+ requested: options.sampleIds.length,
40125
+ reembedded,
40126
+ failed: rows.length - reembedded
40127
+ } });
40128
+ return {
40129
+ pinnedModelId: pinned,
40130
+ requested: options.sampleIds.length,
40131
+ reembedded,
40132
+ failed: rows.length - reembedded,
40133
+ rows
40134
+ };
40135
+ }
40136
+ //#endregion
40137
+ //#region src/pipeline-analytics/face-embedding-audit.ts
40138
+ /** At or above: the stored vector is this model's. */
40139
+ var AUDIT_OK_MIN_COSINE = .98;
40140
+ /** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
40141
+ var AUDIT_MISMATCH_MAX_COSINE = .9;
40142
+ /** Cosine of two vectors of equal length; `null` when either has no length. */
40143
+ function cosine(a, b) {
40144
+ let dot = 0;
40145
+ let na = 0;
40146
+ let nb = 0;
40147
+ for (let i = 0; i < a.length; i++) {
40148
+ const x = a[i] ?? 0;
40149
+ const y = b[i] ?? 0;
40150
+ dot += x * y;
40151
+ na += x * x;
40152
+ nb += y * y;
40153
+ }
40154
+ if (na === 0 || nb === 0) return null;
40155
+ return dot / Math.sqrt(na * nb);
40156
+ }
40157
+ /** The verdict a measured cosine earns. Pure. */
40158
+ function verdictOf(c) {
40159
+ if (c >= .98) return "ok";
40160
+ if (c < .9) return "mismatch";
40161
+ return "inconclusive";
40162
+ }
40163
+ function rowBase(s) {
40164
+ return {
40165
+ sampleId: s.sampleId,
40166
+ identityId: s.identityId,
40167
+ identityName: s.identityName,
40168
+ stampedModelId: s.modelId
40169
+ };
40170
+ }
40171
+ async function measure$2(deps, sample, stored) {
40172
+ const base = rowBase(sample);
40173
+ if (stored === void 0) return {
40174
+ ...base,
40175
+ verdict: "unmeasurable",
40176
+ reason: "no-stored-vector"
40177
+ };
40178
+ const crop = await deps.loadAlignedCrop(sample);
40179
+ if (crop === null) return {
40180
+ ...base,
40181
+ verdict: "unmeasurable",
40182
+ reason: "no-pixels"
40183
+ };
40184
+ const outcome = await deps.embed(sample, crop);
40185
+ if (outcome.kind === "failed") return {
40186
+ ...base,
40187
+ verdict: "unmeasurable",
40188
+ reason: "embed-failed",
40189
+ detail: outcome.reason
40190
+ };
40191
+ if (outcome.kind === "no-capable-node") return {
40192
+ ...base,
40193
+ verdict: "unmeasurable",
40194
+ reason: "no-capable-node",
40195
+ detail: outcome.reason
40196
+ };
40197
+ if (outcome.embedding.length !== stored.length) return {
40198
+ ...base,
40199
+ verdict: "unmeasurable",
40200
+ reason: "dimension-differs",
40201
+ detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
40202
+ };
40203
+ const c = cosine(outcome.embedding, stored);
40204
+ if (c === null) return {
40205
+ ...base,
40206
+ verdict: "unmeasurable",
40207
+ reason: "no-stored-vector",
40208
+ detail: "zero vector"
40209
+ };
40210
+ return {
40211
+ ...base,
40212
+ verdict: verdictOf(c),
40213
+ cosine: Math.round(c * 1e4) / 1e4
40214
+ };
40215
+ }
40216
+ /**
40217
+ * Audit every sample stamped with `auditedModelId`. Read-only; bounded by
40218
+ * `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
40219
+ * the rest as unmeasurable rather than asking again per sample.
40220
+ */
40221
+ async function auditFaceEmbeddings(deps, options) {
40222
+ const all = await deps.listSamples();
40223
+ const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
40224
+ const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
40225
+ const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
40226
+ const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
40227
+ const rows = [];
40228
+ let measured = 0;
40229
+ let complete = true;
40230
+ let noNode = null;
40231
+ for (const sample of samples) {
40232
+ if (sample.modelId !== options.auditedModelId) {
40233
+ rows.push({
40234
+ ...rowBase(sample),
40235
+ verdict: "unmeasurable",
40236
+ reason: "stamped-other-model"
40237
+ });
40238
+ continue;
40239
+ }
40240
+ if (measured >= budget) {
40241
+ complete = false;
40242
+ continue;
40243
+ }
40244
+ if (noNode !== null) {
40245
+ rows.push({
40246
+ ...rowBase(sample),
40247
+ verdict: "unmeasurable",
40248
+ reason: "no-capable-node",
40249
+ detail: noNode
40250
+ });
40251
+ continue;
40252
+ }
40253
+ const row = await measure$2(deps, sample, stored.get(sample.sampleId));
40254
+ if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
40255
+ if (row.cosine !== void 0) measured += 1;
40256
+ rows.push(row);
40257
+ }
40258
+ const count = (v) => rows.filter((r) => r.verdict === v).length;
40259
+ return {
40260
+ auditedModelId: options.auditedModelId,
40261
+ total: samples.length,
40262
+ ok: count("ok"),
40263
+ mismatch: count("mismatch"),
40264
+ inconclusive: count("inconclusive"),
40265
+ unmeasurable: count("unmeasurable"),
40266
+ complete,
40267
+ rows
40268
+ };
40269
+ }
39944
40270
  //#endregion
39945
40271
  //#region src/pipeline-analytics/face-alignment-version.ts
39946
40272
  /**
@@ -65931,7 +66257,8 @@ var FaceReembedEmbedder = class {
65931
66257
  return {
65932
66258
  kind: "embedded",
65933
66259
  embedding: vector,
65934
- dim: vector.length
66260
+ dim: vector.length,
66261
+ modelId: result.modelId
65935
66262
  };
65936
66263
  }
65937
66264
  }
@@ -84222,6 +84549,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
84222
84549
  frameHeight: dims.h
84223
84550
  };
84224
84551
  }
84552
+ var MESSAGES = {
84553
+ "dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
84554
+ "no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
84555
+ "no-model-id": "object embeddings refused — the result names no model"
84556
+ };
84557
+ function keyOf(r) {
84558
+ return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
84559
+ }
84560
+ var EmbeddingRefusalReport = class {
84561
+ logger;
84562
+ intervalMs;
84563
+ now;
84564
+ keys = /* @__PURE__ */ new Map();
84565
+ constructor(options) {
84566
+ this.logger = options.logger;
84567
+ this.intervalMs = options.intervalMs ?? 3e5;
84568
+ this.now = options.now ?? (() => Date.now());
84569
+ }
84570
+ /** Count one refusal; write a line if this key is new or its interval passed. */
84571
+ note(refusal) {
84572
+ const key = keyOf(refusal);
84573
+ const at = this.now();
84574
+ const held = this.keys.get(key);
84575
+ const pending = (held?.pending ?? 0) + 1;
84576
+ if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
84577
+ this.write(refusal, pending, held === void 0 ? "first" : "interval");
84578
+ this.keys = new Map(this.keys).set(key, {
84579
+ last: refusal,
84580
+ pending: 0,
84581
+ lastLineAtMs: at
84582
+ });
84583
+ return;
84584
+ }
84585
+ this.keys = new Map(this.keys).set(key, {
84586
+ ...held,
84587
+ last: refusal,
84588
+ pending
84589
+ });
84590
+ }
84591
+ /** Report every key whose interval passed with refusals still pending. */
84592
+ sweep() {
84593
+ const at = this.now();
84594
+ for (const [key, held] of this.keys) {
84595
+ if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
84596
+ this.write(held.last, held.pending, "interval");
84597
+ this.keys = new Map(this.keys).set(key, {
84598
+ ...held,
84599
+ pending: 0,
84600
+ lastLineAtMs: at
84601
+ });
84602
+ }
84603
+ }
84604
+ /** Report every pending count now, whatever its interval (shutdown). */
84605
+ flush() {
84606
+ for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
84607
+ this.keys = /* @__PURE__ */ new Map();
84608
+ }
84609
+ write(r, refused, closing) {
84610
+ this.logger.warn(MESSAGES[r.reason], {
84611
+ tags: { deviceId: r.deviceId },
84612
+ meta: {
84613
+ reason: r.reason,
84614
+ modelId: r.modelId,
84615
+ refused,
84616
+ windowMs: this.intervalMs,
84617
+ closing,
84618
+ lastTrackId: r.trackId,
84619
+ ...r.dim !== void 0 ? { dim: r.dim } : {},
84620
+ ...r.expected !== void 0 ? { expected: r.expected } : {}
84621
+ }
84622
+ });
84623
+ }
84624
+ };
84225
84625
  //#endregion
84226
84626
  //#region src/pipeline-analytics/store/object-embedding-store.ts
84227
84627
  /**
@@ -84284,10 +84684,33 @@ var ObjectEmbeddingStore = class {
84284
84684
  clipModels;
84285
84685
  /** index → the dim this process declared it at. Per index, per process. */
84286
84686
  declaredDims = /* @__PURE__ */ new Map();
84687
+ /**
84688
+ * Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
84689
+ * — a runner stamping the wrong model refuses every vector it sends — so a
84690
+ * per-track WARN was a flood that hid the one fact that mattered (D658).
84691
+ */
84692
+ refusals;
84287
84693
  constructor(deps) {
84288
84694
  this.vectorStore = deps.vectorStore;
84289
84695
  this.logger = deps.logger;
84290
84696
  this.clipModels = deps.clipModels;
84697
+ this.refusals = new EmbeddingRefusalReport({
84698
+ logger: deps.logger,
84699
+ ...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
84700
+ ...deps.now !== void 0 ? { now: deps.now } : {}
84701
+ });
84702
+ }
84703
+ /**
84704
+ * Count a write refused BEFORE it reached this store — a detail whose result
84705
+ * named no model (D658). Same report, same rate limit, as the refusals the
84706
+ * store makes itself.
84707
+ */
84708
+ noteRefused(refusal) {
84709
+ this.refusals.note(refusal);
84710
+ }
84711
+ /** Report every refusal count still pending (shutdown). */
84712
+ flushRefusals() {
84713
+ this.refusals.flush();
84291
84714
  }
84292
84715
  /** Every index a delete, scan or wipe must reach. */
84293
84716
  indexes() {
@@ -84385,19 +84808,28 @@ var ObjectEmbeddingStore = class {
84385
84808
  * and dropped", because a counter that cannot reads as verification.
84386
84809
  */
84387
84810
  async upsertIfBetter(input) {
84811
+ this.refusals.sweep();
84388
84812
  const vector = resolveVectorPayload(input);
84389
84813
  if (vector === null) return "failed";
84390
- const space = this.spaceOrWarn(input.modelId, "upsert", input.deviceId);
84391
- if (space === null) return "failed";
84814
+ const space = this.clipModels.spaceFor(input.modelId);
84815
+ if (space === null) {
84816
+ this.refusals.note({
84817
+ deviceId: input.deviceId,
84818
+ modelId: input.modelId,
84819
+ reason: "no-clip-metadata",
84820
+ trackId: input.trackId,
84821
+ dim: vector.dim
84822
+ });
84823
+ return "failed";
84824
+ }
84392
84825
  if (vector.dim !== space.dim) {
84393
- this.logger.warn("object embedding refused — its length is not its model's dimension", {
84394
- tags: { deviceId: input.deviceId },
84395
- meta: {
84396
- trackId: input.trackId,
84397
- modelId: input.modelId,
84398
- dim: vector.dim,
84399
- expected: space.dim
84400
- }
84826
+ this.refusals.note({
84827
+ deviceId: input.deviceId,
84828
+ modelId: input.modelId,
84829
+ reason: "dimension-mismatch",
84830
+ trackId: input.trackId,
84831
+ dim: vector.dim,
84832
+ expected: space.dim
84401
84833
  });
84402
84834
  return "failed";
84403
84835
  }
@@ -84871,6 +85303,30 @@ function rowFromMetadata(id, metadata) {
84871
85303
  };
84872
85304
  }
84873
85305
  //#endregion
85306
+ //#region src/pipeline-analytics/clip-detail-route.ts
85307
+ async function routeClipDetailToStore(store, input) {
85308
+ const modelId = input.embeddingModelId;
85309
+ if (modelId === void 0 || modelId === "") {
85310
+ store.noteRefused({
85311
+ deviceId: input.deviceId,
85312
+ modelId: null,
85313
+ reason: "no-model-id",
85314
+ trackId: input.trackId
85315
+ });
85316
+ return "failed";
85317
+ }
85318
+ return store.upsertIfBetter({
85319
+ trackId: input.trackId,
85320
+ deviceId: input.deviceId,
85321
+ timestamp: input.timestamp,
85322
+ className: input.className,
85323
+ embeddingBase64: input.embeddingBase64,
85324
+ modelId,
85325
+ confidence: input.score,
85326
+ ...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
85327
+ });
85328
+ }
85329
+ //#endregion
84874
85330
  //#region src/pipeline-analytics/clip-space-plan.ts
84875
85331
  /**
84876
85332
  * Which CLIP feature space a search reads and a rebuild fills — decided in one
@@ -87082,9 +87538,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
87082
87538
  * covers a healthy trail many times over; past it the honest answer is "no
87083
87539
  * evidence" and the temporal bound takes over. */
87084
87540
  var KEYFRAME_CROP_MAX_SAMPLE_GAP_MS = 1e3;
87085
- /** How long the active CLIP model id (from the embedding-encoder) is cached
87086
- * before re-reading. */
87087
- var CLIP_MODEL_ID_CACHE_TTL_MS = 6e4;
87088
87541
  var SETTINGS_CACHE_TTL_MS = 5e3;
87089
87542
  /** TTL for the per-node confirmation-gate detector-step cache (the node's
87090
87543
  * default object-detector step rarely changes). */
@@ -87375,10 +87828,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
87375
87828
  overlayState = new OverlayDetailStateStore();
87376
87829
  /** Throttle state for the "overlay synthesis failed" warn, one per device. */
87377
87830
  overlaySynthesisWarnAt = /* @__PURE__ */ new Map();
87378
- /** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
87379
- * Stamped on object-embedding rows from the detail plane so semantic search's
87380
- * same-model gate keeps matching. */
87381
- clipModelIdCache = null;
87382
87831
  /** Frame-based event/track media (crop + boxed full-frame) from the
87383
87832
  * detection-pipeline DECODED frame — the ONLY image source (never the
87384
87833
  * snapshot cap). Null when shm frame access is unavailable. */
@@ -91254,6 +91703,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
91254
91703
  await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
91255
91704
  await this.embeddingDispatcher?.stop();
91256
91705
  this.embeddingDispatcher = null;
91706
+ this.objectEmbeddingStore?.flushRefusals();
91257
91707
  await this.notificationCenter?.stop();
91258
91708
  this.notificationCenter = null;
91259
91709
  for (const id of this.proxies.keys()) this.releaseProxy(id);
@@ -92936,31 +93386,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
92936
93386
  });
92937
93387
  }
92938
93388
  /** CLIP object-embedding detail → the object-embedding store (semantic
92939
- * search). Stamps the active encoder's model id so the same-model search
92940
- * gate keeps matching. */
93389
+ * search), under the model the RUNNER stamped — never a guess (D658). */
92941
93390
  async routeClipDetail(deviceId, trackId, detail, timestamp) {
92942
93391
  const store = this.objectEmbeddingStore;
92943
93392
  if (!store || detail.embedding === void 0) return;
92944
- const modelId = detail.embeddingModelId ?? await this.resolveClipModelId();
92945
- if (modelId === null) {
92946
- this.ctx.logger.debug("clip detail dropped — no active embedding model id", {
92947
- tags: { deviceId },
92948
- meta: {
92949
- trackId,
92950
- stepId: detail.stepId
92951
- }
92952
- });
92953
- return;
92954
- }
92955
93393
  const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
92956
- await store.upsertIfBetter({
92957
- trackId,
93394
+ await routeClipDetailToStore(store, {
92958
93395
  deviceId,
93396
+ trackId,
92959
93397
  timestamp,
92960
93398
  className: detail.className,
92961
- embedding: decodeEmbeddingBase64(detail.embedding),
92962
- modelId,
92963
- confidence: detail.score,
93399
+ score: detail.score,
93400
+ embeddingBase64: detail.embedding,
93401
+ embeddingModelId: detail.embeddingModelId,
92964
93402
  ...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
92965
93403
  });
92966
93404
  }
@@ -92989,23 +93427,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
92989
93427
  ...identityId !== void 0 ? { identityId } : {}
92990
93428
  });
92991
93429
  }
92992
- /** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
92993
- * Returns null when the embedding-encoder cap is unavailable. */
92994
- async resolveClipModelId() {
92995
- const now = Date.now();
92996
- if (this.clipModelIdCache && now < this.clipModelIdCache.expiresAt) return this.clipModelIdCache.value;
92997
- let value = null;
92998
- try {
92999
- value = (await this.ctx.api.embeddingEncoder.getInfo.query())?.modelId ?? null;
93000
- } catch (err) {
93001
- this.ctx.logger.debug("resolveClipModelId: getInfo failed", { meta: { error: errMsg$1(err) } });
93002
- }
93003
- this.clipModelIdCache = {
93004
- value,
93005
- expiresAt: now + CLIP_MODEL_ID_CACHE_TTL_MS
93006
- };
93007
- return value;
93008
- }
93009
93430
  /**
93010
93431
  * §5 — decide which active tracks need periodic media THIS frame. Pure over
93011
93432
  * TrackStore.lastSnapshotAt + the per-track best-confidence map:
@@ -96931,6 +97352,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
96931
97352
  }
96932
97353
  }
96933
97354
  /**
97355
+ * D658: re-embed EXACTLY the named samples with ONE pinned model, forced
97356
+ * (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
97357
+ * the gallery-job exclusion with the re-embed pass, the photo pass and the
97358
+ * reset, and reloads the matcher's gallery when anything landed.
97359
+ */
97360
+ async reembedFaceGallerySamples(input) {
97361
+ const api = this.ctx.api;
97362
+ if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
97363
+ const identityStore = this.identityStore;
97364
+ if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
97365
+ const mediaStore = this.mediaStore;
97366
+ if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
97367
+ if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
97368
+ if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
97369
+ if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
97370
+ this.faceReembedRunning = true;
97371
+ try {
97372
+ const embedder = new FaceReembedEmbedder({
97373
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97374
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97375
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
97376
+ const result = await reembedFaceSamples({
97377
+ listSamples: () => identityStore.listSamplesForReembed(),
97378
+ loadAlignedCrop: async (sample) => {
97379
+ if (sample.mediaId === void 0) return null;
97380
+ try {
97381
+ const file = await mediaStore.getById(sample.mediaId);
97382
+ return file === null ? null : Buffer.from(file.base64, "base64");
97383
+ } catch (err) {
97384
+ this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
97385
+ sampleId: sample.sampleId,
97386
+ error: String(err)
97387
+ } });
97388
+ return null;
97389
+ }
97390
+ },
97391
+ embed: (sample, crop) => embedder.embed(sample, crop),
97392
+ store: (sample, embedding, modelId, dim) => identityStore.restampSample({
97393
+ sampleId: sample.sampleId,
97394
+ identityId: sample.identityId,
97395
+ embedding,
97396
+ modelId,
97397
+ dim
97398
+ }),
97399
+ pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
97400
+ logger: {
97401
+ info: (m, e) => this.ctx.logger.info(m, e),
97402
+ warn: (m, e) => this.ctx.logger.warn(m, e)
97403
+ }
97404
+ }, {
97405
+ sampleIds: input.sampleIds,
97406
+ modelId: input.modelId
97407
+ });
97408
+ if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
97409
+ return {
97410
+ ...result,
97411
+ rows: [...result.rows]
97412
+ };
97413
+ } finally {
97414
+ this.faceReembedRunning = false;
97415
+ }
97416
+ }
97417
+ /**
97418
+ * D658: re-embed each sample's stored template with ONE pinned model and
97419
+ * compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
97420
+ * nothing is restamped, re-embedded or removed. The pass pins the model the
97421
+ * caller names (default `auraface-r100`), not the cluster row: the question
97422
+ * is whether a STAMP is true, whatever the cluster runs today.
97423
+ */
97424
+ async auditFaceGalleryEmbeddings(input) {
97425
+ const api = this.ctx.api;
97426
+ if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
97427
+ const identityStore = this.identityStore;
97428
+ if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
97429
+ const mediaStore = this.mediaStore;
97430
+ if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
97431
+ const auditedModelId = input.modelId ?? "auraface-r100";
97432
+ const embedder = new FaceReembedEmbedder({
97433
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97434
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97435
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
97436
+ const report = await auditFaceEmbeddings({
97437
+ listSamples: () => identityStore.listSamplesForReembed(),
97438
+ loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
97439
+ loadAlignedCrop: async (sample) => {
97440
+ if (sample.mediaId === void 0) return null;
97441
+ try {
97442
+ const file = await mediaStore.getById(sample.mediaId);
97443
+ return file === null ? null : Buffer.from(file.base64, "base64");
97444
+ } catch (err) {
97445
+ this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
97446
+ sampleId: sample.sampleId,
97447
+ error: String(err)
97448
+ } });
97449
+ return null;
97450
+ }
97451
+ },
97452
+ embed: (sample, crop) => embedder.embed(sample, crop)
97453
+ }, {
97454
+ auditedModelId,
97455
+ ...input.identityId !== void 0 ? { identityId: input.identityId } : {},
97456
+ ...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
97457
+ });
97458
+ this.ctx.logger.info("face embedding audit finished", { meta: {
97459
+ auditedModelId,
97460
+ total: report.total,
97461
+ ok: report.ok,
97462
+ mismatch: report.mismatch,
97463
+ inconclusive: report.inconclusive,
97464
+ unmeasurable: report.unmeasurable,
97465
+ complete: report.complete
97466
+ } });
97467
+ return {
97468
+ ...report,
97469
+ okMinCosine: AUDIT_OK_MIN_COSINE,
97470
+ mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
97471
+ rows: [...report.rows]
97472
+ };
97473
+ }
97474
+ /**
96934
97475
  * The server-side half of "change the model from any UI and the gallery
96935
97476
  * follows": if a sample sits outside the cluster space and a pass could
96936
97477
  * still bring it in, start one. Runs on a timer; converges and goes quiet.
@@ -97453,6 +97994,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97453
97994
  samples: [...result.samples]
97454
97995
  };
97455
97996
  },
97997
+ "faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
97998
+ "faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
97456
97999
  "faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
97457
98000
  ...d,
97458
98001
  dropped: { ...d.dropped }