@camstack/addon-post-analysis 1.2.286 → 1.2.288

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-CY0FGcpQ.js → clip-model-registry-eFgKIlKE.js} +63 -42
  2. package/dist/{clip-model-registry-D4mC2M7F.mjs → clip-model-registry-hdhXFofg.mjs} +63 -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-CBo7IWoj.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-XcjvUbbR.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-r4OU_ahc.mjs +26 -0
  9. package/dist/pipeline-analytics/{hostInit-CelYj4QN.mjs → hostInit-Fpmz4x_M.mjs} +2 -2
  10. package/dist/pipeline-analytics/index.js +636 -74
  11. package/dist/pipeline-analytics/index.mjs +636 -74
  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-NKbCrEkH.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-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-hdhXFofg.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) {
@@ -229,6 +229,34 @@ var MotionEpisodeTracker = class {
229
229
  }
230
230
  };
231
231
  //#endregion
232
+ //#region src/pipeline-analytics/audio-classify-outcome.ts
233
+ /**
234
+ * The Notification Center's audio sample for one analyzer frame: its level,
235
+ * the labels that clear the SAME per-device floor the router uses (so a rule
236
+ * and the event log never disagree about what was heard), and whether the
237
+ * model ran at all.
238
+ */
239
+ function ncAudioSampleFromFrame(frame, timestampMs, floor) {
240
+ const dbfs = frame.level?.dbfs;
241
+ return {
242
+ timestampMs,
243
+ ...dbfs !== void 0 ? { dbfs } : {},
244
+ labels: (frame.detections ?? []).filter((d) => d.score >= floor.classificationMinScore).map((d) => d.macroClass).filter((macroClass) => floor.audioAllowedClasses.length === 0 || floor.audioAllowedClasses.includes(macroClass)),
245
+ ...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
246
+ };
247
+ }
248
+ /** The level-path audio row for one frame — the one row kind with no label to explain it. */
249
+ function levelAudioEventDraft(deviceId, timestamp, frame) {
250
+ return {
251
+ deviceId,
252
+ timestamp,
253
+ kind: "audio",
254
+ rms: frame.level?.rms ?? 0,
255
+ dbfs: frame.level?.dbfs ?? 0,
256
+ ...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
257
+ };
258
+ }
259
+ //#endregion
232
260
  //#region src/notification-center/alarm/alarm-mode-coverage.ts
233
261
  /**
234
262
  * Whether the panel just BECAME armed, in the sense a person means.
@@ -12773,14 +12801,14 @@ var NcPruneRecordSchema = object({
12773
12801
  });
12774
12802
  /** Rules × deleted subjects — small; a generous ceiling. */
12775
12803
  var LOAD_LIMIT$4 = 1e4;
12776
- function keyOf(row) {
12804
+ function keyOf$1(row) {
12777
12805
  return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
12778
12806
  }
12779
12807
  var NC_DEVICE_PRUNES_SPEC = {
12780
12808
  collection: NC_DEVICE_PRUNES_COLLECTION,
12781
12809
  columns: [...NC_DEVICE_PRUNES_COLUMNS],
12782
12810
  writeMode: "write-through",
12783
- keyOf,
12811
+ keyOf: keyOf$1,
12784
12812
  toValue: (row) => ({
12785
12813
  ruleId: row.ruleId,
12786
12814
  at: row.at,
@@ -12820,7 +12848,7 @@ var NcPruneLedger = class {
12820
12848
  * {@link isLoaded} as it was — see the module docblock.
12821
12849
  */
12822
12850
  async load() {
12823
- const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
12851
+ const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
12824
12852
  const startedAt = this.writes;
12825
12853
  const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
12826
12854
  try {
@@ -12831,7 +12859,7 @@ var NcPruneLedger = class {
12831
12859
  const next = /* @__PURE__ */ new Map();
12832
12860
  for (const record of records) {
12833
12861
  const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
12834
- if (row !== null) next.set(keyOf(row), row);
12862
+ if (row !== null) next.set(keyOf$1(row), row);
12835
12863
  }
12836
12864
  for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
12837
12865
  for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
@@ -12861,7 +12889,7 @@ var NcPruneLedger = class {
12861
12889
  */
12862
12890
  async record(notes, source, at) {
12863
12891
  for (const note of notes) {
12864
- const held = this.ledger.get(keyOf(note));
12892
+ const held = this.ledger.get(keyOf$1(note));
12865
12893
  if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
12866
12894
  const row = NcPruneRecordSchema.parse({
12867
12895
  ...note,
@@ -12869,7 +12897,7 @@ var NcPruneLedger = class {
12869
12897
  source
12870
12898
  });
12871
12899
  this.writes += 1;
12872
- this.writtenAt.set(keyOf(row), this.writes);
12900
+ this.writtenAt.set(keyOf$1(row), this.writes);
12873
12901
  await this.ledger.put(row);
12874
12902
  }
12875
12903
  }
@@ -39685,6 +39713,87 @@ var ReembedFacesInputSchema = object({
39685
39713
  /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39686
39714
  executeOnNodeId: string().optional()
39687
39715
  });
39716
+ /**
39717
+ * D658: re-embed each sample's stored template with ONE pinned model and
39718
+ * compare with the stored vector — does the stamp say what produced it?
39719
+ * Read-only: it reports and writes nothing.
39720
+ */
39721
+ var AuditFaceEmbeddingsInputSchema = object({
39722
+ /** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
39723
+ modelId: string().min(1).optional(),
39724
+ /** Only this identity's samples. */
39725
+ identityId: string().min(1).optional(),
39726
+ /** Stop after this many measured samples. */
39727
+ maxSamples: number().int().positive().optional(),
39728
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39729
+ executeOnNodeId: string().optional()
39730
+ });
39731
+ var AuditFaceEmbeddingsResultSchema = object({
39732
+ auditedModelId: string(),
39733
+ total: number(),
39734
+ ok: number(),
39735
+ mismatch: number(),
39736
+ inconclusive: number(),
39737
+ unmeasurable: number(),
39738
+ complete: boolean(),
39739
+ okMinCosine: number(),
39740
+ mismatchMaxCosine: number(),
39741
+ rows: array(object({
39742
+ sampleId: string(),
39743
+ identityId: string(),
39744
+ identityName: string(),
39745
+ stampedModelId: string(),
39746
+ verdict: _enum([
39747
+ "ok",
39748
+ "mismatch",
39749
+ "inconclusive",
39750
+ "unmeasurable"
39751
+ ]),
39752
+ cosine: number().optional(),
39753
+ reason: _enum([
39754
+ "stamped-other-model",
39755
+ "no-pixels",
39756
+ "no-stored-vector",
39757
+ "embed-failed",
39758
+ "no-capable-node",
39759
+ "dimension-differs"
39760
+ ]).optional(),
39761
+ detail: string().optional()
39762
+ }))
39763
+ });
39764
+ /**
39765
+ * D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
39766
+ * vectors even when already stamped with it — the repair for what
39767
+ * `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
39768
+ */
39769
+ var ReembedSamplesInputSchema = object({
39770
+ sampleIds: array(string().min(1)).min(1).max(200),
39771
+ modelId: string().min(1),
39772
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39773
+ executeOnNodeId: string().optional()
39774
+ });
39775
+ var ReembedSamplesResultSchema = object({
39776
+ pinnedModelId: string(),
39777
+ requested: number(),
39778
+ reembedded: number(),
39779
+ failed: number(),
39780
+ rows: array(object({
39781
+ sampleId: string(),
39782
+ outcome: _enum([
39783
+ "reembedded",
39784
+ "not-found",
39785
+ "duplicate",
39786
+ "no-pixels",
39787
+ "embed-failed",
39788
+ "no-capable-node",
39789
+ "no-model-stamp",
39790
+ "not-landed"
39791
+ ]),
39792
+ previousModelId: string().optional(),
39793
+ modelId: string().optional(),
39794
+ detail: string().optional()
39795
+ }))
39796
+ });
39688
39797
  /** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
39689
39798
  var ReembedFacesResultSchema = object({
39690
39799
  targetModelId: string(),
@@ -39936,11 +40045,256 @@ var embeddingActions = defineCustomActions({
39936
40045
  "embedding.faceAlignmentStatus": customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
39937
40046
  "embedding.backfillPhotoSources": customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
39938
40047
  "faceGallery.sourceFrameStats": customAction(NoInputSchema$1, SourceFrameStatsSchema),
40048
+ "faceGallery.reembedSamples": customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
40049
+ kind: "mutation",
40050
+ auth: "admin"
40051
+ }),
40052
+ "faceGallery.auditEmbeddings": customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
39939
40053
  "faceGallery.resetToCore": customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
39940
40054
  kind: "mutation",
39941
40055
  auth: "admin"
39942
40056
  })
39943
40057
  });
40058
+ async function reembedOne(deps, sample, pinned) {
40059
+ const base = {
40060
+ sampleId: sample.sampleId,
40061
+ previousModelId: sample.modelId
40062
+ };
40063
+ const crop = await deps.loadAlignedCrop(sample);
40064
+ if (crop === null) return {
40065
+ ...base,
40066
+ outcome: "no-pixels"
40067
+ };
40068
+ const outcome = await deps.embed(sample, crop);
40069
+ if (outcome.kind === "failed") return {
40070
+ ...base,
40071
+ outcome: "embed-failed",
40072
+ detail: outcome.reason
40073
+ };
40074
+ if (outcome.kind === "no-capable-node") return {
40075
+ ...base,
40076
+ outcome: "no-capable-node",
40077
+ detail: outcome.reason
40078
+ };
40079
+ if (outcome.modelId === void 0 || outcome.modelId === "") return {
40080
+ ...base,
40081
+ outcome: "no-model-stamp",
40082
+ detail: "the embed answered no model id"
40083
+ };
40084
+ if (outcome.modelId !== pinned) return {
40085
+ ...base,
40086
+ outcome: "embed-failed",
40087
+ detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
40088
+ };
40089
+ const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
40090
+ if (!write.landed) return {
40091
+ ...base,
40092
+ outcome: "not-landed",
40093
+ detail: write.reason ?? "the write did not land"
40094
+ };
40095
+ return {
40096
+ ...base,
40097
+ outcome: "reembedded",
40098
+ modelId: outcome.modelId
40099
+ };
40100
+ }
40101
+ async function reembedFaceSamples(deps, options) {
40102
+ const pinned = options.modelId;
40103
+ const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
40104
+ const pacingMs = options.pacingMs ?? 25;
40105
+ const seen = /* @__PURE__ */ new Set();
40106
+ const rows = [];
40107
+ let noNode = null;
40108
+ deps.logger.info("face sample re-embed starting", { meta: {
40109
+ pinnedModelId: pinned,
40110
+ requested: options.sampleIds.length
40111
+ } });
40112
+ for (const sampleId of options.sampleIds) {
40113
+ if (seen.has(sampleId)) {
40114
+ rows.push({
40115
+ sampleId,
40116
+ outcome: "duplicate"
40117
+ });
40118
+ continue;
40119
+ }
40120
+ seen.add(sampleId);
40121
+ const sample = byId.get(sampleId);
40122
+ if (sample === void 0) {
40123
+ rows.push({
40124
+ sampleId,
40125
+ outcome: "not-found"
40126
+ });
40127
+ continue;
40128
+ }
40129
+ if (noNode !== null) {
40130
+ rows.push({
40131
+ sampleId,
40132
+ previousModelId: sample.modelId,
40133
+ outcome: "no-capable-node",
40134
+ detail: noNode
40135
+ });
40136
+ continue;
40137
+ }
40138
+ const row = await reembedOne(deps, sample, pinned);
40139
+ if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
40140
+ if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
40141
+ sampleId,
40142
+ outcome: row.outcome,
40143
+ detail: row.detail ?? null,
40144
+ pinnedModelId: pinned
40145
+ } });
40146
+ rows.push(row);
40147
+ await deps.pause(pacingMs);
40148
+ }
40149
+ const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
40150
+ deps.logger.info("face sample re-embed finished", { meta: {
40151
+ pinnedModelId: pinned,
40152
+ requested: options.sampleIds.length,
40153
+ reembedded,
40154
+ failed: rows.length - reembedded
40155
+ } });
40156
+ return {
40157
+ pinnedModelId: pinned,
40158
+ requested: options.sampleIds.length,
40159
+ reembedded,
40160
+ failed: rows.length - reembedded,
40161
+ rows
40162
+ };
40163
+ }
40164
+ //#endregion
40165
+ //#region src/pipeline-analytics/face-embedding-audit.ts
40166
+ /** At or above: the stored vector is this model's. */
40167
+ var AUDIT_OK_MIN_COSINE = .98;
40168
+ /** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
40169
+ var AUDIT_MISMATCH_MAX_COSINE = .9;
40170
+ /** Cosine of two vectors of equal length; `null` when either has no length. */
40171
+ function cosine(a, b) {
40172
+ let dot = 0;
40173
+ let na = 0;
40174
+ let nb = 0;
40175
+ for (let i = 0; i < a.length; i++) {
40176
+ const x = a[i] ?? 0;
40177
+ const y = b[i] ?? 0;
40178
+ dot += x * y;
40179
+ na += x * x;
40180
+ nb += y * y;
40181
+ }
40182
+ if (na === 0 || nb === 0) return null;
40183
+ return dot / Math.sqrt(na * nb);
40184
+ }
40185
+ /** The verdict a measured cosine earns. Pure. */
40186
+ function verdictOf(c) {
40187
+ if (c >= .98) return "ok";
40188
+ if (c < .9) return "mismatch";
40189
+ return "inconclusive";
40190
+ }
40191
+ function rowBase(s) {
40192
+ return {
40193
+ sampleId: s.sampleId,
40194
+ identityId: s.identityId,
40195
+ identityName: s.identityName,
40196
+ stampedModelId: s.modelId
40197
+ };
40198
+ }
40199
+ async function measure$2(deps, sample, stored) {
40200
+ const base = rowBase(sample);
40201
+ if (stored === void 0) return {
40202
+ ...base,
40203
+ verdict: "unmeasurable",
40204
+ reason: "no-stored-vector"
40205
+ };
40206
+ const crop = await deps.loadAlignedCrop(sample);
40207
+ if (crop === null) return {
40208
+ ...base,
40209
+ verdict: "unmeasurable",
40210
+ reason: "no-pixels"
40211
+ };
40212
+ const outcome = await deps.embed(sample, crop);
40213
+ if (outcome.kind === "failed") return {
40214
+ ...base,
40215
+ verdict: "unmeasurable",
40216
+ reason: "embed-failed",
40217
+ detail: outcome.reason
40218
+ };
40219
+ if (outcome.kind === "no-capable-node") return {
40220
+ ...base,
40221
+ verdict: "unmeasurable",
40222
+ reason: "no-capable-node",
40223
+ detail: outcome.reason
40224
+ };
40225
+ if (outcome.embedding.length !== stored.length) return {
40226
+ ...base,
40227
+ verdict: "unmeasurable",
40228
+ reason: "dimension-differs",
40229
+ detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
40230
+ };
40231
+ const c = cosine(outcome.embedding, stored);
40232
+ if (c === null) return {
40233
+ ...base,
40234
+ verdict: "unmeasurable",
40235
+ reason: "no-stored-vector",
40236
+ detail: "zero vector"
40237
+ };
40238
+ return {
40239
+ ...base,
40240
+ verdict: verdictOf(c),
40241
+ cosine: Math.round(c * 1e4) / 1e4
40242
+ };
40243
+ }
40244
+ /**
40245
+ * Audit every sample stamped with `auditedModelId`. Read-only; bounded by
40246
+ * `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
40247
+ * the rest as unmeasurable rather than asking again per sample.
40248
+ */
40249
+ async function auditFaceEmbeddings(deps, options) {
40250
+ const all = await deps.listSamples();
40251
+ const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
40252
+ const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
40253
+ const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
40254
+ const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
40255
+ const rows = [];
40256
+ let measured = 0;
40257
+ let complete = true;
40258
+ let noNode = null;
40259
+ for (const sample of samples) {
40260
+ if (sample.modelId !== options.auditedModelId) {
40261
+ rows.push({
40262
+ ...rowBase(sample),
40263
+ verdict: "unmeasurable",
40264
+ reason: "stamped-other-model"
40265
+ });
40266
+ continue;
40267
+ }
40268
+ if (measured >= budget) {
40269
+ complete = false;
40270
+ continue;
40271
+ }
40272
+ if (noNode !== null) {
40273
+ rows.push({
40274
+ ...rowBase(sample),
40275
+ verdict: "unmeasurable",
40276
+ reason: "no-capable-node",
40277
+ detail: noNode
40278
+ });
40279
+ continue;
40280
+ }
40281
+ const row = await measure$2(deps, sample, stored.get(sample.sampleId));
40282
+ if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
40283
+ if (row.cosine !== void 0) measured += 1;
40284
+ rows.push(row);
40285
+ }
40286
+ const count = (v) => rows.filter((r) => r.verdict === v).length;
40287
+ return {
40288
+ auditedModelId: options.auditedModelId,
40289
+ total: samples.length,
40290
+ ok: count("ok"),
40291
+ mismatch: count("mismatch"),
40292
+ inconclusive: count("inconclusive"),
40293
+ unmeasurable: count("unmeasurable"),
40294
+ complete,
40295
+ rows
40296
+ };
40297
+ }
39944
40298
  //#endregion
39945
40299
  //#region src/pipeline-analytics/face-alignment-version.ts
39946
40300
  /**
@@ -65931,7 +66285,8 @@ var FaceReembedEmbedder = class {
65931
66285
  return {
65932
66286
  kind: "embedded",
65933
66287
  embedding: vector,
65934
- dim: vector.length
66288
+ dim: vector.length,
66289
+ modelId: result.modelId
65935
66290
  };
65936
66291
  }
65937
66292
  }
@@ -84222,6 +84577,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
84222
84577
  frameHeight: dims.h
84223
84578
  };
84224
84579
  }
84580
+ var MESSAGES = {
84581
+ "dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
84582
+ "no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
84583
+ "no-model-id": "object embeddings refused — the result names no model"
84584
+ };
84585
+ function keyOf(r) {
84586
+ return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
84587
+ }
84588
+ var EmbeddingRefusalReport = class {
84589
+ logger;
84590
+ intervalMs;
84591
+ now;
84592
+ keys = /* @__PURE__ */ new Map();
84593
+ constructor(options) {
84594
+ this.logger = options.logger;
84595
+ this.intervalMs = options.intervalMs ?? 3e5;
84596
+ this.now = options.now ?? (() => Date.now());
84597
+ }
84598
+ /** Count one refusal; write a line if this key is new or its interval passed. */
84599
+ note(refusal) {
84600
+ const key = keyOf(refusal);
84601
+ const at = this.now();
84602
+ const held = this.keys.get(key);
84603
+ const pending = (held?.pending ?? 0) + 1;
84604
+ if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
84605
+ this.write(refusal, pending, held === void 0 ? "first" : "interval");
84606
+ this.keys = new Map(this.keys).set(key, {
84607
+ last: refusal,
84608
+ pending: 0,
84609
+ lastLineAtMs: at
84610
+ });
84611
+ return;
84612
+ }
84613
+ this.keys = new Map(this.keys).set(key, {
84614
+ ...held,
84615
+ last: refusal,
84616
+ pending
84617
+ });
84618
+ }
84619
+ /** Report every key whose interval passed with refusals still pending. */
84620
+ sweep() {
84621
+ const at = this.now();
84622
+ for (const [key, held] of this.keys) {
84623
+ if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
84624
+ this.write(held.last, held.pending, "interval");
84625
+ this.keys = new Map(this.keys).set(key, {
84626
+ ...held,
84627
+ pending: 0,
84628
+ lastLineAtMs: at
84629
+ });
84630
+ }
84631
+ }
84632
+ /** Report every pending count now, whatever its interval (shutdown). */
84633
+ flush() {
84634
+ for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
84635
+ this.keys = /* @__PURE__ */ new Map();
84636
+ }
84637
+ write(r, refused, closing) {
84638
+ this.logger.warn(MESSAGES[r.reason], {
84639
+ tags: { deviceId: r.deviceId },
84640
+ meta: {
84641
+ reason: r.reason,
84642
+ modelId: r.modelId,
84643
+ refused,
84644
+ windowMs: this.intervalMs,
84645
+ closing,
84646
+ lastTrackId: r.trackId,
84647
+ ...r.dim !== void 0 ? { dim: r.dim } : {},
84648
+ ...r.expected !== void 0 ? { expected: r.expected } : {}
84649
+ }
84650
+ });
84651
+ }
84652
+ };
84225
84653
  //#endregion
84226
84654
  //#region src/pipeline-analytics/store/object-embedding-store.ts
84227
84655
  /**
@@ -84284,10 +84712,33 @@ var ObjectEmbeddingStore = class {
84284
84712
  clipModels;
84285
84713
  /** index → the dim this process declared it at. Per index, per process. */
84286
84714
  declaredDims = /* @__PURE__ */ new Map();
84715
+ /**
84716
+ * Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
84717
+ * — a runner stamping the wrong model refuses every vector it sends — so a
84718
+ * per-track WARN was a flood that hid the one fact that mattered (D658).
84719
+ */
84720
+ refusals;
84287
84721
  constructor(deps) {
84288
84722
  this.vectorStore = deps.vectorStore;
84289
84723
  this.logger = deps.logger;
84290
84724
  this.clipModels = deps.clipModels;
84725
+ this.refusals = new EmbeddingRefusalReport({
84726
+ logger: deps.logger,
84727
+ ...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
84728
+ ...deps.now !== void 0 ? { now: deps.now } : {}
84729
+ });
84730
+ }
84731
+ /**
84732
+ * Count a write refused BEFORE it reached this store — a detail whose result
84733
+ * named no model (D658). Same report, same rate limit, as the refusals the
84734
+ * store makes itself.
84735
+ */
84736
+ noteRefused(refusal) {
84737
+ this.refusals.note(refusal);
84738
+ }
84739
+ /** Report every refusal count still pending (shutdown). */
84740
+ flushRefusals() {
84741
+ this.refusals.flush();
84291
84742
  }
84292
84743
  /** Every index a delete, scan or wipe must reach. */
84293
84744
  indexes() {
@@ -84385,19 +84836,28 @@ var ObjectEmbeddingStore = class {
84385
84836
  * and dropped", because a counter that cannot reads as verification.
84386
84837
  */
84387
84838
  async upsertIfBetter(input) {
84839
+ this.refusals.sweep();
84388
84840
  const vector = resolveVectorPayload(input);
84389
84841
  if (vector === null) return "failed";
84390
- const space = this.spaceOrWarn(input.modelId, "upsert", input.deviceId);
84391
- if (space === null) return "failed";
84842
+ const space = this.clipModels.spaceFor(input.modelId);
84843
+ if (space === null) {
84844
+ this.refusals.note({
84845
+ deviceId: input.deviceId,
84846
+ modelId: input.modelId,
84847
+ reason: "no-clip-metadata",
84848
+ trackId: input.trackId,
84849
+ dim: vector.dim
84850
+ });
84851
+ return "failed";
84852
+ }
84392
84853
  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
- }
84854
+ this.refusals.note({
84855
+ deviceId: input.deviceId,
84856
+ modelId: input.modelId,
84857
+ reason: "dimension-mismatch",
84858
+ trackId: input.trackId,
84859
+ dim: vector.dim,
84860
+ expected: space.dim
84401
84861
  });
84402
84862
  return "failed";
84403
84863
  }
@@ -84871,6 +85331,30 @@ function rowFromMetadata(id, metadata) {
84871
85331
  };
84872
85332
  }
84873
85333
  //#endregion
85334
+ //#region src/pipeline-analytics/clip-detail-route.ts
85335
+ async function routeClipDetailToStore(store, input) {
85336
+ const modelId = input.embeddingModelId;
85337
+ if (modelId === void 0 || modelId === "") {
85338
+ store.noteRefused({
85339
+ deviceId: input.deviceId,
85340
+ modelId: null,
85341
+ reason: "no-model-id",
85342
+ trackId: input.trackId
85343
+ });
85344
+ return "failed";
85345
+ }
85346
+ return store.upsertIfBetter({
85347
+ trackId: input.trackId,
85348
+ deviceId: input.deviceId,
85349
+ timestamp: input.timestamp,
85350
+ className: input.className,
85351
+ embeddingBase64: input.embeddingBase64,
85352
+ modelId,
85353
+ confidence: input.score,
85354
+ ...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
85355
+ });
85356
+ }
85357
+ //#endregion
84874
85358
  //#region src/pipeline-analytics/clip-space-plan.ts
84875
85359
  /**
84876
85360
  * Which CLIP feature space a search reads and a rebuild fills — decided in one
@@ -87082,9 +87566,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
87082
87566
  * covers a healthy trail many times over; past it the honest answer is "no
87083
87567
  * evidence" and the temporal bound takes over. */
87084
87568
  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
87569
  var SETTINGS_CACHE_TTL_MS = 5e3;
87089
87570
  /** TTL for the per-node confirmation-gate detector-step cache (the node's
87090
87571
  * default object-detector step rarely changes). */
@@ -87375,10 +87856,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
87375
87856
  overlayState = new OverlayDetailStateStore();
87376
87857
  /** Throttle state for the "overlay synthesis failed" warn, one per device. */
87377
87858
  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
87859
  /** Frame-based event/track media (crop + boxed full-frame) from the
87383
87860
  * detection-pipeline DECODED frame — the ONLY image source (never the
87384
87861
  * snapshot cap). Null when shm frame access is unavailable. */
@@ -91254,6 +91731,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
91254
91731
  await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
91255
91732
  await this.embeddingDispatcher?.stop();
91256
91733
  this.embeddingDispatcher = null;
91734
+ this.objectEmbeddingStore?.flushRefusals();
91257
91735
  await this.notificationCenter?.stop();
91258
91736
  this.notificationCenter = null;
91259
91737
  for (const id of this.proxies.keys()) this.releaseProxy(id);
@@ -92936,31 +93414,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
92936
93414
  });
92937
93415
  }
92938
93416
  /** 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. */
93417
+ * search), under the model the RUNNER stamped — never a guess (D658). */
92941
93418
  async routeClipDetail(deviceId, trackId, detail, timestamp) {
92942
93419
  const store = this.objectEmbeddingStore;
92943
93420
  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
93421
  const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
92956
- await store.upsertIfBetter({
92957
- trackId,
93422
+ await routeClipDetailToStore(store, {
92958
93423
  deviceId,
93424
+ trackId,
92959
93425
  timestamp,
92960
93426
  className: detail.className,
92961
- embedding: decodeEmbeddingBase64(detail.embedding),
92962
- modelId,
92963
- confidence: detail.score,
93427
+ score: detail.score,
93428
+ embeddingBase64: detail.embedding,
93429
+ embeddingModelId: detail.embeddingModelId,
92964
93430
  ...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
92965
93431
  });
92966
93432
  }
@@ -92989,23 +93455,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
92989
93455
  ...identityId !== void 0 ? { identityId } : {}
92990
93456
  });
92991
93457
  }
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
93458
  /**
93010
93459
  * §5 — decide which active tracks need periodic media THIS frame. Pure over
93011
93460
  * TrackStore.lastSnapshotAt + the per-track best-confidence map:
@@ -93901,6 +94350,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
93901
94350
  kind: "audio",
93902
94351
  rms: input.rms,
93903
94352
  dbfs: input.dbfs,
94353
+ classifyOutcome: { state: "classified" },
93904
94354
  classification: {
93905
94355
  className: input.className,
93906
94356
  ...input.originalClass !== void 0 ? { originalClass: input.originalClass } : {},
@@ -94048,11 +94498,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
94048
94498
  score: topClassification.score
94049
94499
  } : {}
94050
94500
  }, this.audioMarkerConfig(settings));
94051
- this.notificationCenter?.observeAudio(deviceId, {
94052
- timestampMs: timestamp,
94053
- ...level?.dbfs !== void 0 ? { dbfs: level.dbfs } : {},
94054
- labels: (frame.detections ?? []).filter((d) => d.score >= settings.classificationMinScore).map((d) => d.macroClass).filter((macroClass) => settings.audioAllowedClasses.length === 0 || settings.audioAllowedClasses.includes(macroClass))
94055
- });
94501
+ this.notificationCenter?.observeAudio(deviceId, ncAudioSampleFromFrame(frame, timestamp, settings));
94056
94502
  const route = classifyAudioFrame(topClassification ? {
94057
94503
  className: topClassification.macroClass,
94058
94504
  score: topClassification.score
@@ -94127,13 +94573,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
94127
94573
  });
94128
94574
  this.levelStateByDevice.set(deviceId, res.nextState);
94129
94575
  if (!res.emit) return;
94130
- const draft = {
94131
- deviceId,
94132
- timestamp,
94133
- kind: "audio",
94134
- rms: level?.rms ?? 0,
94135
- dbfs: level?.dbfs ?? 0
94136
- };
94576
+ const draft = levelAudioEventDraft(deviceId, timestamp, frame);
94137
94577
  this.lastAudioInsertByDevice.set(deviceId, {
94138
94578
  className: void 0,
94139
94579
  atMs: timestamp
@@ -96931,6 +97371,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
96931
97371
  }
96932
97372
  }
96933
97373
  /**
97374
+ * D658: re-embed EXACTLY the named samples with ONE pinned model, forced
97375
+ * (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
97376
+ * the gallery-job exclusion with the re-embed pass, the photo pass and the
97377
+ * reset, and reloads the matcher's gallery when anything landed.
97378
+ */
97379
+ async reembedFaceGallerySamples(input) {
97380
+ const api = this.ctx.api;
97381
+ if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
97382
+ const identityStore = this.identityStore;
97383
+ if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
97384
+ const mediaStore = this.mediaStore;
97385
+ if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
97386
+ if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
97387
+ if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
97388
+ if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
97389
+ this.faceReembedRunning = true;
97390
+ try {
97391
+ const embedder = new FaceReembedEmbedder({
97392
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97393
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97394
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
97395
+ const result = await reembedFaceSamples({
97396
+ listSamples: () => identityStore.listSamplesForReembed(),
97397
+ loadAlignedCrop: async (sample) => {
97398
+ if (sample.mediaId === void 0) return null;
97399
+ try {
97400
+ const file = await mediaStore.getById(sample.mediaId);
97401
+ return file === null ? null : Buffer.from(file.base64, "base64");
97402
+ } catch (err) {
97403
+ this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
97404
+ sampleId: sample.sampleId,
97405
+ error: String(err)
97406
+ } });
97407
+ return null;
97408
+ }
97409
+ },
97410
+ embed: (sample, crop) => embedder.embed(sample, crop),
97411
+ store: (sample, embedding, modelId, dim) => identityStore.restampSample({
97412
+ sampleId: sample.sampleId,
97413
+ identityId: sample.identityId,
97414
+ embedding,
97415
+ modelId,
97416
+ dim
97417
+ }),
97418
+ pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
97419
+ logger: {
97420
+ info: (m, e) => this.ctx.logger.info(m, e),
97421
+ warn: (m, e) => this.ctx.logger.warn(m, e)
97422
+ }
97423
+ }, {
97424
+ sampleIds: input.sampleIds,
97425
+ modelId: input.modelId
97426
+ });
97427
+ if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
97428
+ return {
97429
+ ...result,
97430
+ rows: [...result.rows]
97431
+ };
97432
+ } finally {
97433
+ this.faceReembedRunning = false;
97434
+ }
97435
+ }
97436
+ /**
97437
+ * D658: re-embed each sample's stored template with ONE pinned model and
97438
+ * compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
97439
+ * nothing is restamped, re-embedded or removed. The pass pins the model the
97440
+ * caller names (default `auraface-r100`), not the cluster row: the question
97441
+ * is whether a STAMP is true, whatever the cluster runs today.
97442
+ */
97443
+ async auditFaceGalleryEmbeddings(input) {
97444
+ const api = this.ctx.api;
97445
+ if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
97446
+ const identityStore = this.identityStore;
97447
+ if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
97448
+ const mediaStore = this.mediaStore;
97449
+ if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
97450
+ const auditedModelId = input.modelId ?? "auraface-r100";
97451
+ const embedder = new FaceReembedEmbedder({
97452
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97453
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97454
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
97455
+ const report = await auditFaceEmbeddings({
97456
+ listSamples: () => identityStore.listSamplesForReembed(),
97457
+ loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
97458
+ loadAlignedCrop: async (sample) => {
97459
+ if (sample.mediaId === void 0) return null;
97460
+ try {
97461
+ const file = await mediaStore.getById(sample.mediaId);
97462
+ return file === null ? null : Buffer.from(file.base64, "base64");
97463
+ } catch (err) {
97464
+ this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
97465
+ sampleId: sample.sampleId,
97466
+ error: String(err)
97467
+ } });
97468
+ return null;
97469
+ }
97470
+ },
97471
+ embed: (sample, crop) => embedder.embed(sample, crop)
97472
+ }, {
97473
+ auditedModelId,
97474
+ ...input.identityId !== void 0 ? { identityId: input.identityId } : {},
97475
+ ...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
97476
+ });
97477
+ this.ctx.logger.info("face embedding audit finished", { meta: {
97478
+ auditedModelId,
97479
+ total: report.total,
97480
+ ok: report.ok,
97481
+ mismatch: report.mismatch,
97482
+ inconclusive: report.inconclusive,
97483
+ unmeasurable: report.unmeasurable,
97484
+ complete: report.complete
97485
+ } });
97486
+ return {
97487
+ ...report,
97488
+ okMinCosine: AUDIT_OK_MIN_COSINE,
97489
+ mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
97490
+ rows: [...report.rows]
97491
+ };
97492
+ }
97493
+ /**
96934
97494
  * The server-side half of "change the model from any UI and the gallery
96935
97495
  * follows": if a sample sits outside the cluster space and a pass could
96936
97496
  * still bring it in, start one. Runs on a timer; converges and goes quiet.
@@ -97453,6 +98013,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
97453
98013
  samples: [...result.samples]
97454
98014
  };
97455
98015
  },
98016
+ "faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
98017
+ "faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
97456
98018
  "faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
97457
98019
  ...d,
97458
98020
  dropped: { ...d.dropped }