@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
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_clip_model_registry = require("../clip-model-registry-CY0FGcpQ.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-eFgKIlKE.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -88,7 +88,7 @@ function motionEpisodeDurationMs(state) {
88
88
  }
89
89
  //#endregion
90
90
  //#region src/pipeline-analytics/motion-episode-tracker.ts
91
- function keyOf$1(deviceId, source) {
91
+ function keyOf$2(deviceId, source) {
92
92
  return `${deviceId}:${source}`;
93
93
  }
94
94
  var MotionEpisodeTracker = class {
@@ -104,7 +104,7 @@ var MotionEpisodeTracker = class {
104
104
  * {@link MotionEpisodeInsertInput}.
105
105
  */
106
106
  async observe(deviceId, source, detected, timestamp, regionData) {
107
- const key = keyOf$1(deviceId, source);
107
+ const key = keyOf$2(deviceId, source);
108
108
  const prior = this.signals.get(key);
109
109
  const decision = decideMotionSignal(prior, detected, timestamp);
110
110
  switch (decision.kind) {
@@ -235,6 +235,34 @@ var MotionEpisodeTracker = class {
235
235
  }
236
236
  };
237
237
  //#endregion
238
+ //#region src/pipeline-analytics/audio-classify-outcome.ts
239
+ /**
240
+ * The Notification Center's audio sample for one analyzer frame: its level,
241
+ * the labels that clear the SAME per-device floor the router uses (so a rule
242
+ * and the event log never disagree about what was heard), and whether the
243
+ * model ran at all.
244
+ */
245
+ function ncAudioSampleFromFrame(frame, timestampMs, floor) {
246
+ const dbfs = frame.level?.dbfs;
247
+ return {
248
+ timestampMs,
249
+ ...dbfs !== void 0 ? { dbfs } : {},
250
+ labels: (frame.detections ?? []).filter((d) => d.score >= floor.classificationMinScore).map((d) => d.macroClass).filter((macroClass) => floor.audioAllowedClasses.length === 0 || floor.audioAllowedClasses.includes(macroClass)),
251
+ ...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
252
+ };
253
+ }
254
+ /** The level-path audio row for one frame — the one row kind with no label to explain it. */
255
+ function levelAudioEventDraft(deviceId, timestamp, frame) {
256
+ return {
257
+ deviceId,
258
+ timestamp,
259
+ kind: "audio",
260
+ rms: frame.level?.rms ?? 0,
261
+ dbfs: frame.level?.dbfs ?? 0,
262
+ ...frame.classifyOutcome !== void 0 ? { classifyOutcome: frame.classifyOutcome } : {}
263
+ };
264
+ }
265
+ //#endregion
238
266
  //#region src/notification-center/alarm/alarm-mode-coverage.ts
239
267
  /**
240
268
  * Whether the panel just BECAME armed, in the sense a person means.
@@ -12780,14 +12808,14 @@ var NcPruneRecordSchema = require_clip_model_registry.object({
12780
12808
  });
12781
12809
  /** Rules × deleted subjects — small; a generous ceiling. */
12782
12810
  var LOAD_LIMIT$4 = 1e4;
12783
- function keyOf(row) {
12811
+ function keyOf$1(row) {
12784
12812
  return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
12785
12813
  }
12786
12814
  var NC_DEVICE_PRUNES_SPEC = {
12787
12815
  collection: NC_DEVICE_PRUNES_COLLECTION,
12788
12816
  columns: [...NC_DEVICE_PRUNES_COLUMNS],
12789
12817
  writeMode: "write-through",
12790
- keyOf,
12818
+ keyOf: keyOf$1,
12791
12819
  toValue: (row) => ({
12792
12820
  ruleId: row.ruleId,
12793
12821
  at: row.at,
@@ -12827,7 +12855,7 @@ var NcPruneLedger = class {
12827
12855
  * {@link isLoaded} as it was — see the module docblock.
12828
12856
  */
12829
12857
  async load() {
12830
- const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
12858
+ const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
12831
12859
  const startedAt = this.writes;
12832
12860
  const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
12833
12861
  try {
@@ -12838,7 +12866,7 @@ var NcPruneLedger = class {
12838
12866
  const next = /* @__PURE__ */ new Map();
12839
12867
  for (const record of records) {
12840
12868
  const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
12841
- if (row !== null) next.set(keyOf(row), row);
12869
+ if (row !== null) next.set(keyOf$1(row), row);
12842
12870
  }
12843
12871
  for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
12844
12872
  for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
@@ -12868,7 +12896,7 @@ var NcPruneLedger = class {
12868
12896
  */
12869
12897
  async record(notes, source, at) {
12870
12898
  for (const note of notes) {
12871
- const held = this.ledger.get(keyOf(note));
12899
+ const held = this.ledger.get(keyOf$1(note));
12872
12900
  if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
12873
12901
  const row = NcPruneRecordSchema.parse({
12874
12902
  ...note,
@@ -12876,7 +12904,7 @@ var NcPruneLedger = class {
12876
12904
  source
12877
12905
  });
12878
12906
  this.writes += 1;
12879
- this.writtenAt.set(keyOf(row), this.writes);
12907
+ this.writtenAt.set(keyOf$1(row), this.writes);
12880
12908
  await this.ledger.put(row);
12881
12909
  }
12882
12910
  }
@@ -39708,6 +39736,87 @@ var ReembedFacesInputSchema = require_clip_model_registry.object({
39708
39736
  /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39709
39737
  executeOnNodeId: require_clip_model_registry.string().optional()
39710
39738
  });
39739
+ /**
39740
+ * D658: re-embed each sample's stored template with ONE pinned model and
39741
+ * compare with the stored vector — does the stamp say what produced it?
39742
+ * Read-only: it reports and writes nothing.
39743
+ */
39744
+ var AuditFaceEmbeddingsInputSchema = require_clip_model_registry.object({
39745
+ /** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
39746
+ modelId: require_clip_model_registry.string().min(1).optional(),
39747
+ /** Only this identity's samples. */
39748
+ identityId: require_clip_model_registry.string().min(1).optional(),
39749
+ /** Stop after this many measured samples. */
39750
+ maxSamples: require_clip_model_registry.number().int().positive().optional(),
39751
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39752
+ executeOnNodeId: require_clip_model_registry.string().optional()
39753
+ });
39754
+ var AuditFaceEmbeddingsResultSchema = require_clip_model_registry.object({
39755
+ auditedModelId: require_clip_model_registry.string(),
39756
+ total: require_clip_model_registry.number(),
39757
+ ok: require_clip_model_registry.number(),
39758
+ mismatch: require_clip_model_registry.number(),
39759
+ inconclusive: require_clip_model_registry.number(),
39760
+ unmeasurable: require_clip_model_registry.number(),
39761
+ complete: require_clip_model_registry.boolean(),
39762
+ okMinCosine: require_clip_model_registry.number(),
39763
+ mismatchMaxCosine: require_clip_model_registry.number(),
39764
+ rows: require_clip_model_registry.array(require_clip_model_registry.object({
39765
+ sampleId: require_clip_model_registry.string(),
39766
+ identityId: require_clip_model_registry.string(),
39767
+ identityName: require_clip_model_registry.string(),
39768
+ stampedModelId: require_clip_model_registry.string(),
39769
+ verdict: require_clip_model_registry._enum([
39770
+ "ok",
39771
+ "mismatch",
39772
+ "inconclusive",
39773
+ "unmeasurable"
39774
+ ]),
39775
+ cosine: require_clip_model_registry.number().optional(),
39776
+ reason: require_clip_model_registry._enum([
39777
+ "stamped-other-model",
39778
+ "no-pixels",
39779
+ "no-stored-vector",
39780
+ "embed-failed",
39781
+ "no-capable-node",
39782
+ "dimension-differs"
39783
+ ]).optional(),
39784
+ detail: require_clip_model_registry.string().optional()
39785
+ }))
39786
+ });
39787
+ /**
39788
+ * D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
39789
+ * vectors even when already stamped with it — the repair for what
39790
+ * `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
39791
+ */
39792
+ var ReembedSamplesInputSchema = require_clip_model_registry.object({
39793
+ sampleIds: require_clip_model_registry.array(require_clip_model_registry.string().min(1)).min(1).max(200),
39794
+ modelId: require_clip_model_registry.string().min(1),
39795
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39796
+ executeOnNodeId: require_clip_model_registry.string().optional()
39797
+ });
39798
+ var ReembedSamplesResultSchema = require_clip_model_registry.object({
39799
+ pinnedModelId: require_clip_model_registry.string(),
39800
+ requested: require_clip_model_registry.number(),
39801
+ reembedded: require_clip_model_registry.number(),
39802
+ failed: require_clip_model_registry.number(),
39803
+ rows: require_clip_model_registry.array(require_clip_model_registry.object({
39804
+ sampleId: require_clip_model_registry.string(),
39805
+ outcome: require_clip_model_registry._enum([
39806
+ "reembedded",
39807
+ "not-found",
39808
+ "duplicate",
39809
+ "no-pixels",
39810
+ "embed-failed",
39811
+ "no-capable-node",
39812
+ "no-model-stamp",
39813
+ "not-landed"
39814
+ ]),
39815
+ previousModelId: require_clip_model_registry.string().optional(),
39816
+ modelId: require_clip_model_registry.string().optional(),
39817
+ detail: require_clip_model_registry.string().optional()
39818
+ }))
39819
+ });
39711
39820
  /** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
39712
39821
  var ReembedFacesResultSchema = require_clip_model_registry.object({
39713
39822
  targetModelId: require_clip_model_registry.string(),
@@ -39959,11 +40068,256 @@ var embeddingActions = require_clip_model_registry.defineCustomActions({
39959
40068
  "embedding.faceAlignmentStatus": require_clip_model_registry.customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
39960
40069
  "embedding.backfillPhotoSources": require_clip_model_registry.customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
39961
40070
  "faceGallery.sourceFrameStats": require_clip_model_registry.customAction(NoInputSchema$1, SourceFrameStatsSchema),
40071
+ "faceGallery.reembedSamples": require_clip_model_registry.customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
40072
+ kind: "mutation",
40073
+ auth: "admin"
40074
+ }),
40075
+ "faceGallery.auditEmbeddings": require_clip_model_registry.customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
39962
40076
  "faceGallery.resetToCore": require_clip_model_registry.customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
39963
40077
  kind: "mutation",
39964
40078
  auth: "admin"
39965
40079
  })
39966
40080
  });
40081
+ async function reembedOne(deps, sample, pinned) {
40082
+ const base = {
40083
+ sampleId: sample.sampleId,
40084
+ previousModelId: sample.modelId
40085
+ };
40086
+ const crop = await deps.loadAlignedCrop(sample);
40087
+ if (crop === null) return {
40088
+ ...base,
40089
+ outcome: "no-pixels"
40090
+ };
40091
+ const outcome = await deps.embed(sample, crop);
40092
+ if (outcome.kind === "failed") return {
40093
+ ...base,
40094
+ outcome: "embed-failed",
40095
+ detail: outcome.reason
40096
+ };
40097
+ if (outcome.kind === "no-capable-node") return {
40098
+ ...base,
40099
+ outcome: "no-capable-node",
40100
+ detail: outcome.reason
40101
+ };
40102
+ if (outcome.modelId === void 0 || outcome.modelId === "") return {
40103
+ ...base,
40104
+ outcome: "no-model-stamp",
40105
+ detail: "the embed answered no model id"
40106
+ };
40107
+ if (outcome.modelId !== pinned) return {
40108
+ ...base,
40109
+ outcome: "embed-failed",
40110
+ detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
40111
+ };
40112
+ const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
40113
+ if (!write.landed) return {
40114
+ ...base,
40115
+ outcome: "not-landed",
40116
+ detail: write.reason ?? "the write did not land"
40117
+ };
40118
+ return {
40119
+ ...base,
40120
+ outcome: "reembedded",
40121
+ modelId: outcome.modelId
40122
+ };
40123
+ }
40124
+ async function reembedFaceSamples(deps, options) {
40125
+ const pinned = options.modelId;
40126
+ const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
40127
+ const pacingMs = options.pacingMs ?? 25;
40128
+ const seen = /* @__PURE__ */ new Set();
40129
+ const rows = [];
40130
+ let noNode = null;
40131
+ deps.logger.info("face sample re-embed starting", { meta: {
40132
+ pinnedModelId: pinned,
40133
+ requested: options.sampleIds.length
40134
+ } });
40135
+ for (const sampleId of options.sampleIds) {
40136
+ if (seen.has(sampleId)) {
40137
+ rows.push({
40138
+ sampleId,
40139
+ outcome: "duplicate"
40140
+ });
40141
+ continue;
40142
+ }
40143
+ seen.add(sampleId);
40144
+ const sample = byId.get(sampleId);
40145
+ if (sample === void 0) {
40146
+ rows.push({
40147
+ sampleId,
40148
+ outcome: "not-found"
40149
+ });
40150
+ continue;
40151
+ }
40152
+ if (noNode !== null) {
40153
+ rows.push({
40154
+ sampleId,
40155
+ previousModelId: sample.modelId,
40156
+ outcome: "no-capable-node",
40157
+ detail: noNode
40158
+ });
40159
+ continue;
40160
+ }
40161
+ const row = await reembedOne(deps, sample, pinned);
40162
+ if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
40163
+ if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
40164
+ sampleId,
40165
+ outcome: row.outcome,
40166
+ detail: row.detail ?? null,
40167
+ pinnedModelId: pinned
40168
+ } });
40169
+ rows.push(row);
40170
+ await deps.pause(pacingMs);
40171
+ }
40172
+ const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
40173
+ deps.logger.info("face sample re-embed finished", { meta: {
40174
+ pinnedModelId: pinned,
40175
+ requested: options.sampleIds.length,
40176
+ reembedded,
40177
+ failed: rows.length - reembedded
40178
+ } });
40179
+ return {
40180
+ pinnedModelId: pinned,
40181
+ requested: options.sampleIds.length,
40182
+ reembedded,
40183
+ failed: rows.length - reembedded,
40184
+ rows
40185
+ };
40186
+ }
40187
+ //#endregion
40188
+ //#region src/pipeline-analytics/face-embedding-audit.ts
40189
+ /** At or above: the stored vector is this model's. */
40190
+ var AUDIT_OK_MIN_COSINE = .98;
40191
+ /** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
40192
+ var AUDIT_MISMATCH_MAX_COSINE = .9;
40193
+ /** Cosine of two vectors of equal length; `null` when either has no length. */
40194
+ function cosine(a, b) {
40195
+ let dot = 0;
40196
+ let na = 0;
40197
+ let nb = 0;
40198
+ for (let i = 0; i < a.length; i++) {
40199
+ const x = a[i] ?? 0;
40200
+ const y = b[i] ?? 0;
40201
+ dot += x * y;
40202
+ na += x * x;
40203
+ nb += y * y;
40204
+ }
40205
+ if (na === 0 || nb === 0) return null;
40206
+ return dot / Math.sqrt(na * nb);
40207
+ }
40208
+ /** The verdict a measured cosine earns. Pure. */
40209
+ function verdictOf(c) {
40210
+ if (c >= .98) return "ok";
40211
+ if (c < .9) return "mismatch";
40212
+ return "inconclusive";
40213
+ }
40214
+ function rowBase(s) {
40215
+ return {
40216
+ sampleId: s.sampleId,
40217
+ identityId: s.identityId,
40218
+ identityName: s.identityName,
40219
+ stampedModelId: s.modelId
40220
+ };
40221
+ }
40222
+ async function measure$2(deps, sample, stored) {
40223
+ const base = rowBase(sample);
40224
+ if (stored === void 0) return {
40225
+ ...base,
40226
+ verdict: "unmeasurable",
40227
+ reason: "no-stored-vector"
40228
+ };
40229
+ const crop = await deps.loadAlignedCrop(sample);
40230
+ if (crop === null) return {
40231
+ ...base,
40232
+ verdict: "unmeasurable",
40233
+ reason: "no-pixels"
40234
+ };
40235
+ const outcome = await deps.embed(sample, crop);
40236
+ if (outcome.kind === "failed") return {
40237
+ ...base,
40238
+ verdict: "unmeasurable",
40239
+ reason: "embed-failed",
40240
+ detail: outcome.reason
40241
+ };
40242
+ if (outcome.kind === "no-capable-node") return {
40243
+ ...base,
40244
+ verdict: "unmeasurable",
40245
+ reason: "no-capable-node",
40246
+ detail: outcome.reason
40247
+ };
40248
+ if (outcome.embedding.length !== stored.length) return {
40249
+ ...base,
40250
+ verdict: "unmeasurable",
40251
+ reason: "dimension-differs",
40252
+ detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
40253
+ };
40254
+ const c = cosine(outcome.embedding, stored);
40255
+ if (c === null) return {
40256
+ ...base,
40257
+ verdict: "unmeasurable",
40258
+ reason: "no-stored-vector",
40259
+ detail: "zero vector"
40260
+ };
40261
+ return {
40262
+ ...base,
40263
+ verdict: verdictOf(c),
40264
+ cosine: Math.round(c * 1e4) / 1e4
40265
+ };
40266
+ }
40267
+ /**
40268
+ * Audit every sample stamped with `auditedModelId`. Read-only; bounded by
40269
+ * `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
40270
+ * the rest as unmeasurable rather than asking again per sample.
40271
+ */
40272
+ async function auditFaceEmbeddings(deps, options) {
40273
+ const all = await deps.listSamples();
40274
+ const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
40275
+ const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
40276
+ const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
40277
+ const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
40278
+ const rows = [];
40279
+ let measured = 0;
40280
+ let complete = true;
40281
+ let noNode = null;
40282
+ for (const sample of samples) {
40283
+ if (sample.modelId !== options.auditedModelId) {
40284
+ rows.push({
40285
+ ...rowBase(sample),
40286
+ verdict: "unmeasurable",
40287
+ reason: "stamped-other-model"
40288
+ });
40289
+ continue;
40290
+ }
40291
+ if (measured >= budget) {
40292
+ complete = false;
40293
+ continue;
40294
+ }
40295
+ if (noNode !== null) {
40296
+ rows.push({
40297
+ ...rowBase(sample),
40298
+ verdict: "unmeasurable",
40299
+ reason: "no-capable-node",
40300
+ detail: noNode
40301
+ });
40302
+ continue;
40303
+ }
40304
+ const row = await measure$2(deps, sample, stored.get(sample.sampleId));
40305
+ if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
40306
+ if (row.cosine !== void 0) measured += 1;
40307
+ rows.push(row);
40308
+ }
40309
+ const count = (v) => rows.filter((r) => r.verdict === v).length;
40310
+ return {
40311
+ auditedModelId: options.auditedModelId,
40312
+ total: samples.length,
40313
+ ok: count("ok"),
40314
+ mismatch: count("mismatch"),
40315
+ inconclusive: count("inconclusive"),
40316
+ unmeasurable: count("unmeasurable"),
40317
+ complete,
40318
+ rows
40319
+ };
40320
+ }
39967
40321
  //#endregion
39968
40322
  //#region src/pipeline-analytics/face-alignment-version.ts
39969
40323
  /**
@@ -66007,7 +66361,8 @@ var FaceReembedEmbedder = class {
66007
66361
  return {
66008
66362
  kind: "embedded",
66009
66363
  embedding: vector,
66010
- dim: vector.length
66364
+ dim: vector.length,
66365
+ modelId: result.modelId
66011
66366
  };
66012
66367
  }
66013
66368
  }
@@ -84298,6 +84653,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
84298
84653
  frameHeight: dims.h
84299
84654
  };
84300
84655
  }
84656
+ var MESSAGES = {
84657
+ "dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
84658
+ "no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
84659
+ "no-model-id": "object embeddings refused — the result names no model"
84660
+ };
84661
+ function keyOf(r) {
84662
+ return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
84663
+ }
84664
+ var EmbeddingRefusalReport = class {
84665
+ logger;
84666
+ intervalMs;
84667
+ now;
84668
+ keys = /* @__PURE__ */ new Map();
84669
+ constructor(options) {
84670
+ this.logger = options.logger;
84671
+ this.intervalMs = options.intervalMs ?? 3e5;
84672
+ this.now = options.now ?? (() => Date.now());
84673
+ }
84674
+ /** Count one refusal; write a line if this key is new or its interval passed. */
84675
+ note(refusal) {
84676
+ const key = keyOf(refusal);
84677
+ const at = this.now();
84678
+ const held = this.keys.get(key);
84679
+ const pending = (held?.pending ?? 0) + 1;
84680
+ if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
84681
+ this.write(refusal, pending, held === void 0 ? "first" : "interval");
84682
+ this.keys = new Map(this.keys).set(key, {
84683
+ last: refusal,
84684
+ pending: 0,
84685
+ lastLineAtMs: at
84686
+ });
84687
+ return;
84688
+ }
84689
+ this.keys = new Map(this.keys).set(key, {
84690
+ ...held,
84691
+ last: refusal,
84692
+ pending
84693
+ });
84694
+ }
84695
+ /** Report every key whose interval passed with refusals still pending. */
84696
+ sweep() {
84697
+ const at = this.now();
84698
+ for (const [key, held] of this.keys) {
84699
+ if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
84700
+ this.write(held.last, held.pending, "interval");
84701
+ this.keys = new Map(this.keys).set(key, {
84702
+ ...held,
84703
+ pending: 0,
84704
+ lastLineAtMs: at
84705
+ });
84706
+ }
84707
+ }
84708
+ /** Report every pending count now, whatever its interval (shutdown). */
84709
+ flush() {
84710
+ for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
84711
+ this.keys = /* @__PURE__ */ new Map();
84712
+ }
84713
+ write(r, refused, closing) {
84714
+ this.logger.warn(MESSAGES[r.reason], {
84715
+ tags: { deviceId: r.deviceId },
84716
+ meta: {
84717
+ reason: r.reason,
84718
+ modelId: r.modelId,
84719
+ refused,
84720
+ windowMs: this.intervalMs,
84721
+ closing,
84722
+ lastTrackId: r.trackId,
84723
+ ...r.dim !== void 0 ? { dim: r.dim } : {},
84724
+ ...r.expected !== void 0 ? { expected: r.expected } : {}
84725
+ }
84726
+ });
84727
+ }
84728
+ };
84301
84729
  //#endregion
84302
84730
  //#region src/pipeline-analytics/store/object-embedding-store.ts
84303
84731
  /**
@@ -84360,10 +84788,33 @@ var ObjectEmbeddingStore = class {
84360
84788
  clipModels;
84361
84789
  /** index → the dim this process declared it at. Per index, per process. */
84362
84790
  declaredDims = /* @__PURE__ */ new Map();
84791
+ /**
84792
+ * Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
84793
+ * — a runner stamping the wrong model refuses every vector it sends — so a
84794
+ * per-track WARN was a flood that hid the one fact that mattered (D658).
84795
+ */
84796
+ refusals;
84363
84797
  constructor(deps) {
84364
84798
  this.vectorStore = deps.vectorStore;
84365
84799
  this.logger = deps.logger;
84366
84800
  this.clipModels = deps.clipModels;
84801
+ this.refusals = new EmbeddingRefusalReport({
84802
+ logger: deps.logger,
84803
+ ...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
84804
+ ...deps.now !== void 0 ? { now: deps.now } : {}
84805
+ });
84806
+ }
84807
+ /**
84808
+ * Count a write refused BEFORE it reached this store — a detail whose result
84809
+ * named no model (D658). Same report, same rate limit, as the refusals the
84810
+ * store makes itself.
84811
+ */
84812
+ noteRefused(refusal) {
84813
+ this.refusals.note(refusal);
84814
+ }
84815
+ /** Report every refusal count still pending (shutdown). */
84816
+ flushRefusals() {
84817
+ this.refusals.flush();
84367
84818
  }
84368
84819
  /** Every index a delete, scan or wipe must reach. */
84369
84820
  indexes() {
@@ -84461,19 +84912,28 @@ var ObjectEmbeddingStore = class {
84461
84912
  * and dropped", because a counter that cannot reads as verification.
84462
84913
  */
84463
84914
  async upsertIfBetter(input) {
84915
+ this.refusals.sweep();
84464
84916
  const vector = resolveVectorPayload(input);
84465
84917
  if (vector === null) return "failed";
84466
- const space = this.spaceOrWarn(input.modelId, "upsert", input.deviceId);
84467
- if (space === null) return "failed";
84918
+ const space = this.clipModels.spaceFor(input.modelId);
84919
+ if (space === null) {
84920
+ this.refusals.note({
84921
+ deviceId: input.deviceId,
84922
+ modelId: input.modelId,
84923
+ reason: "no-clip-metadata",
84924
+ trackId: input.trackId,
84925
+ dim: vector.dim
84926
+ });
84927
+ return "failed";
84928
+ }
84468
84929
  if (vector.dim !== space.dim) {
84469
- this.logger.warn("object embedding refused — its length is not its model's dimension", {
84470
- tags: { deviceId: input.deviceId },
84471
- meta: {
84472
- trackId: input.trackId,
84473
- modelId: input.modelId,
84474
- dim: vector.dim,
84475
- expected: space.dim
84476
- }
84930
+ this.refusals.note({
84931
+ deviceId: input.deviceId,
84932
+ modelId: input.modelId,
84933
+ reason: "dimension-mismatch",
84934
+ trackId: input.trackId,
84935
+ dim: vector.dim,
84936
+ expected: space.dim
84477
84937
  });
84478
84938
  return "failed";
84479
84939
  }
@@ -84947,6 +85407,30 @@ function rowFromMetadata(id, metadata) {
84947
85407
  };
84948
85408
  }
84949
85409
  //#endregion
85410
+ //#region src/pipeline-analytics/clip-detail-route.ts
85411
+ async function routeClipDetailToStore(store, input) {
85412
+ const modelId = input.embeddingModelId;
85413
+ if (modelId === void 0 || modelId === "") {
85414
+ store.noteRefused({
85415
+ deviceId: input.deviceId,
85416
+ modelId: null,
85417
+ reason: "no-model-id",
85418
+ trackId: input.trackId
85419
+ });
85420
+ return "failed";
85421
+ }
85422
+ return store.upsertIfBetter({
85423
+ trackId: input.trackId,
85424
+ deviceId: input.deviceId,
85425
+ timestamp: input.timestamp,
85426
+ className: input.className,
85427
+ embeddingBase64: input.embeddingBase64,
85428
+ modelId,
85429
+ confidence: input.score,
85430
+ ...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
85431
+ });
85432
+ }
85433
+ //#endregion
84950
85434
  //#region src/pipeline-analytics/clip-space-plan.ts
84951
85435
  /**
84952
85436
  * Which CLIP feature space a search reads and a rebuild fills — decided in one
@@ -87158,9 +87642,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
87158
87642
  * covers a healthy trail many times over; past it the honest answer is "no
87159
87643
  * evidence" and the temporal bound takes over. */
87160
87644
  var KEYFRAME_CROP_MAX_SAMPLE_GAP_MS = 1e3;
87161
- /** How long the active CLIP model id (from the embedding-encoder) is cached
87162
- * before re-reading. */
87163
- var CLIP_MODEL_ID_CACHE_TTL_MS = 6e4;
87164
87645
  var SETTINGS_CACHE_TTL_MS = 5e3;
87165
87646
  /** TTL for the per-node confirmation-gate detector-step cache (the node's
87166
87647
  * default object-detector step rarely changes). */
@@ -87451,10 +87932,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
87451
87932
  overlayState = new OverlayDetailStateStore();
87452
87933
  /** Throttle state for the "overlay synthesis failed" warn, one per device. */
87453
87934
  overlaySynthesisWarnAt = /* @__PURE__ */ new Map();
87454
- /** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
87455
- * Stamped on object-embedding rows from the detail plane so semantic search's
87456
- * same-model gate keeps matching. */
87457
- clipModelIdCache = null;
87458
87935
  /** Frame-based event/track media (crop + boxed full-frame) from the
87459
87936
  * detection-pipeline DECODED frame — the ONLY image source (never the
87460
87937
  * snapshot cap). Null when shm frame access is unavailable. */
@@ -91330,6 +91807,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
91330
91807
  await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
91331
91808
  await this.embeddingDispatcher?.stop();
91332
91809
  this.embeddingDispatcher = null;
91810
+ this.objectEmbeddingStore?.flushRefusals();
91333
91811
  await this.notificationCenter?.stop();
91334
91812
  this.notificationCenter = null;
91335
91813
  for (const id of this.proxies.keys()) this.releaseProxy(id);
@@ -93012,31 +93490,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
93012
93490
  });
93013
93491
  }
93014
93492
  /** CLIP object-embedding detail → the object-embedding store (semantic
93015
- * search). Stamps the active encoder's model id so the same-model search
93016
- * gate keeps matching. */
93493
+ * search), under the model the RUNNER stamped — never a guess (D658). */
93017
93494
  async routeClipDetail(deviceId, trackId, detail, timestamp) {
93018
93495
  const store = this.objectEmbeddingStore;
93019
93496
  if (!store || detail.embedding === void 0) return;
93020
- const modelId = detail.embeddingModelId ?? await this.resolveClipModelId();
93021
- if (modelId === null) {
93022
- this.ctx.logger.debug("clip detail dropped — no active embedding model id", {
93023
- tags: { deviceId },
93024
- meta: {
93025
- trackId,
93026
- stepId: detail.stepId
93027
- }
93028
- });
93029
- return;
93030
- }
93031
93497
  const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
93032
- await store.upsertIfBetter({
93033
- trackId,
93498
+ await routeClipDetailToStore(store, {
93034
93499
  deviceId,
93500
+ trackId,
93035
93501
  timestamp,
93036
93502
  className: detail.className,
93037
- embedding: decodeEmbeddingBase64(detail.embedding),
93038
- modelId,
93039
- confidence: detail.score,
93503
+ score: detail.score,
93504
+ embeddingBase64: detail.embedding,
93505
+ embeddingModelId: detail.embeddingModelId,
93040
93506
  ...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
93041
93507
  });
93042
93508
  }
@@ -93065,23 +93531,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
93065
93531
  ...identityId !== void 0 ? { identityId } : {}
93066
93532
  });
93067
93533
  }
93068
- /** Active CLIP image-embedding model id (== the text encoder's), TTL-cached.
93069
- * Returns null when the embedding-encoder cap is unavailable. */
93070
- async resolveClipModelId() {
93071
- const now = Date.now();
93072
- if (this.clipModelIdCache && now < this.clipModelIdCache.expiresAt) return this.clipModelIdCache.value;
93073
- let value = null;
93074
- try {
93075
- value = (await this.ctx.api.embeddingEncoder.getInfo.query())?.modelId ?? null;
93076
- } catch (err) {
93077
- this.ctx.logger.debug("resolveClipModelId: getInfo failed", { meta: { error: require_clip_model_registry.errMsg(err) } });
93078
- }
93079
- this.clipModelIdCache = {
93080
- value,
93081
- expiresAt: now + CLIP_MODEL_ID_CACHE_TTL_MS
93082
- };
93083
- return value;
93084
- }
93085
93534
  /**
93086
93535
  * §5 — decide which active tracks need periodic media THIS frame. Pure over
93087
93536
  * TrackStore.lastSnapshotAt + the per-track best-confidence map:
@@ -93977,6 +94426,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
93977
94426
  kind: "audio",
93978
94427
  rms: input.rms,
93979
94428
  dbfs: input.dbfs,
94429
+ classifyOutcome: { state: "classified" },
93980
94430
  classification: {
93981
94431
  className: input.className,
93982
94432
  ...input.originalClass !== void 0 ? { originalClass: input.originalClass } : {},
@@ -94124,11 +94574,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
94124
94574
  score: topClassification.score
94125
94575
  } : {}
94126
94576
  }, this.audioMarkerConfig(settings));
94127
- this.notificationCenter?.observeAudio(deviceId, {
94128
- timestampMs: timestamp,
94129
- ...level?.dbfs !== void 0 ? { dbfs: level.dbfs } : {},
94130
- labels: (frame.detections ?? []).filter((d) => d.score >= settings.classificationMinScore).map((d) => d.macroClass).filter((macroClass) => settings.audioAllowedClasses.length === 0 || settings.audioAllowedClasses.includes(macroClass))
94131
- });
94577
+ this.notificationCenter?.observeAudio(deviceId, ncAudioSampleFromFrame(frame, timestamp, settings));
94132
94578
  const route = classifyAudioFrame(topClassification ? {
94133
94579
  className: topClassification.macroClass,
94134
94580
  score: topClassification.score
@@ -94203,13 +94649,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
94203
94649
  });
94204
94650
  this.levelStateByDevice.set(deviceId, res.nextState);
94205
94651
  if (!res.emit) return;
94206
- const draft = {
94207
- deviceId,
94208
- timestamp,
94209
- kind: "audio",
94210
- rms: level?.rms ?? 0,
94211
- dbfs: level?.dbfs ?? 0
94212
- };
94652
+ const draft = levelAudioEventDraft(deviceId, timestamp, frame);
94213
94653
  this.lastAudioInsertByDevice.set(deviceId, {
94214
94654
  className: void 0,
94215
94655
  atMs: timestamp
@@ -97007,6 +97447,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97007
97447
  }
97008
97448
  }
97009
97449
  /**
97450
+ * D658: re-embed EXACTLY the named samples with ONE pinned model, forced
97451
+ * (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
97452
+ * the gallery-job exclusion with the re-embed pass, the photo pass and the
97453
+ * reset, and reloads the matcher's gallery when anything landed.
97454
+ */
97455
+ async reembedFaceGallerySamples(input) {
97456
+ const api = this.ctx.api;
97457
+ if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
97458
+ const identityStore = this.identityStore;
97459
+ if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
97460
+ const mediaStore = this.mediaStore;
97461
+ if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
97462
+ if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
97463
+ if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
97464
+ if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
97465
+ this.faceReembedRunning = true;
97466
+ try {
97467
+ const embedder = new FaceReembedEmbedder({
97468
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97469
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97470
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
97471
+ const result = await reembedFaceSamples({
97472
+ listSamples: () => identityStore.listSamplesForReembed(),
97473
+ loadAlignedCrop: async (sample) => {
97474
+ if (sample.mediaId === void 0) return null;
97475
+ try {
97476
+ const file = await mediaStore.getById(sample.mediaId);
97477
+ return file === null ? null : Buffer.from(file.base64, "base64");
97478
+ } catch (err) {
97479
+ this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
97480
+ sampleId: sample.sampleId,
97481
+ error: String(err)
97482
+ } });
97483
+ return null;
97484
+ }
97485
+ },
97486
+ embed: (sample, crop) => embedder.embed(sample, crop),
97487
+ store: (sample, embedding, modelId, dim) => identityStore.restampSample({
97488
+ sampleId: sample.sampleId,
97489
+ identityId: sample.identityId,
97490
+ embedding,
97491
+ modelId,
97492
+ dim
97493
+ }),
97494
+ pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
97495
+ logger: {
97496
+ info: (m, e) => this.ctx.logger.info(m, e),
97497
+ warn: (m, e) => this.ctx.logger.warn(m, e)
97498
+ }
97499
+ }, {
97500
+ sampleIds: input.sampleIds,
97501
+ modelId: input.modelId
97502
+ });
97503
+ if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
97504
+ return {
97505
+ ...result,
97506
+ rows: [...result.rows]
97507
+ };
97508
+ } finally {
97509
+ this.faceReembedRunning = false;
97510
+ }
97511
+ }
97512
+ /**
97513
+ * D658: re-embed each sample's stored template with ONE pinned model and
97514
+ * compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
97515
+ * nothing is restamped, re-embedded or removed. The pass pins the model the
97516
+ * caller names (default `auraface-r100`), not the cluster row: the question
97517
+ * is whether a STAMP is true, whatever the cluster runs today.
97518
+ */
97519
+ async auditFaceGalleryEmbeddings(input) {
97520
+ const api = this.ctx.api;
97521
+ if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
97522
+ const identityStore = this.identityStore;
97523
+ if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
97524
+ const mediaStore = this.mediaStore;
97525
+ if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
97526
+ const auditedModelId = input.modelId ?? "auraface-r100";
97527
+ const embedder = new FaceReembedEmbedder({
97528
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97529
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97530
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
97531
+ const report = await auditFaceEmbeddings({
97532
+ listSamples: () => identityStore.listSamplesForReembed(),
97533
+ loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
97534
+ loadAlignedCrop: async (sample) => {
97535
+ if (sample.mediaId === void 0) return null;
97536
+ try {
97537
+ const file = await mediaStore.getById(sample.mediaId);
97538
+ return file === null ? null : Buffer.from(file.base64, "base64");
97539
+ } catch (err) {
97540
+ this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
97541
+ sampleId: sample.sampleId,
97542
+ error: String(err)
97543
+ } });
97544
+ return null;
97545
+ }
97546
+ },
97547
+ embed: (sample, crop) => embedder.embed(sample, crop)
97548
+ }, {
97549
+ auditedModelId,
97550
+ ...input.identityId !== void 0 ? { identityId: input.identityId } : {},
97551
+ ...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
97552
+ });
97553
+ this.ctx.logger.info("face embedding audit finished", { meta: {
97554
+ auditedModelId,
97555
+ total: report.total,
97556
+ ok: report.ok,
97557
+ mismatch: report.mismatch,
97558
+ inconclusive: report.inconclusive,
97559
+ unmeasurable: report.unmeasurable,
97560
+ complete: report.complete
97561
+ } });
97562
+ return {
97563
+ ...report,
97564
+ okMinCosine: AUDIT_OK_MIN_COSINE,
97565
+ mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
97566
+ rows: [...report.rows]
97567
+ };
97568
+ }
97569
+ /**
97010
97570
  * The server-side half of "change the model from any UI and the gallery
97011
97571
  * follows": if a sample sits outside the cluster space and a pass could
97012
97572
  * still bring it in, start one. Runs on a timer; converges and goes quiet.
@@ -97529,6 +98089,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97529
98089
  samples: [...result.samples]
97530
98090
  };
97531
98091
  },
98092
+ "faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
98093
+ "faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
97532
98094
  "faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
97533
98095
  ...d,
97534
98096
  dropped: { ...d.dropped }