@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
@@ -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-Cq1X8kTo.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) {
@@ -12780,14 +12780,14 @@ var NcPruneRecordSchema = require_clip_model_registry.object({
12780
12780
  });
12781
12781
  /** Rules × deleted subjects — small; a generous ceiling. */
12782
12782
  var LOAD_LIMIT$4 = 1e4;
12783
- function keyOf(row) {
12783
+ function keyOf$1(row) {
12784
12784
  return `${row.kind}:${row.ruleId}:${row.field}:${row.subject}`;
12785
12785
  }
12786
12786
  var NC_DEVICE_PRUNES_SPEC = {
12787
12787
  collection: NC_DEVICE_PRUNES_COLLECTION,
12788
12788
  columns: [...NC_DEVICE_PRUNES_COLUMNS],
12789
12789
  writeMode: "write-through",
12790
- keyOf,
12790
+ keyOf: keyOf$1,
12791
12791
  toValue: (row) => ({
12792
12792
  ruleId: row.ruleId,
12793
12793
  at: row.at,
@@ -12827,7 +12827,7 @@ var NcPruneLedger = class {
12827
12827
  * {@link isLoaded} as it was — see the module docblock.
12828
12828
  */
12829
12829
  async load() {
12830
- const heldBefore = new Set(this.ledger.snapshot().map(keyOf));
12830
+ const heldBefore = new Set(this.ledger.snapshot().map(keyOf$1));
12831
12831
  const startedAt = this.writes;
12832
12832
  const writtenSince = (key) => (this.writtenAt.get(key) ?? 0) > startedAt;
12833
12833
  try {
@@ -12838,7 +12838,7 @@ var NcPruneLedger = class {
12838
12838
  const next = /* @__PURE__ */ new Map();
12839
12839
  for (const record of records) {
12840
12840
  const row = NC_DEVICE_PRUNES_SPEC.fromRecord(record.id, record.data);
12841
- if (row !== null) next.set(keyOf(row), row);
12841
+ if (row !== null) next.set(keyOf$1(row), row);
12842
12842
  }
12843
12843
  for (const key of heldBefore) if (!next.has(key) && !writtenSince(key)) this.ledger.evict(key);
12844
12844
  for (const [key, row] of next) if (!writtenSince(key)) this.ledger.stage(row);
@@ -12868,7 +12868,7 @@ var NcPruneLedger = class {
12868
12868
  */
12869
12869
  async record(notes, source, at) {
12870
12870
  for (const note of notes) {
12871
- const held = this.ledger.get(keyOf(note));
12871
+ const held = this.ledger.get(keyOf$1(note));
12872
12872
  if (held !== void 0 && KEPT_OUTCOMES.has(held.outcome) && !KEPT_OUTCOMES.has(note.outcome)) continue;
12873
12873
  const row = NcPruneRecordSchema.parse({
12874
12874
  ...note,
@@ -12876,7 +12876,7 @@ var NcPruneLedger = class {
12876
12876
  source
12877
12877
  });
12878
12878
  this.writes += 1;
12879
- this.writtenAt.set(keyOf(row), this.writes);
12879
+ this.writtenAt.set(keyOf$1(row), this.writes);
12880
12880
  await this.ledger.put(row);
12881
12881
  }
12882
12882
  }
@@ -39708,6 +39708,87 @@ var ReembedFacesInputSchema = require_clip_model_registry.object({
39708
39708
  /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39709
39709
  executeOnNodeId: require_clip_model_registry.string().optional()
39710
39710
  });
39711
+ /**
39712
+ * D658: re-embed each sample's stored template with ONE pinned model and
39713
+ * compare with the stored vector — does the stamp say what produced it?
39714
+ * Read-only: it reports and writes nothing.
39715
+ */
39716
+ var AuditFaceEmbeddingsInputSchema = require_clip_model_registry.object({
39717
+ /** The stamp to verify. Omit for `auraface-r100` (the D651 default). */
39718
+ modelId: require_clip_model_registry.string().min(1).optional(),
39719
+ /** Only this identity's samples. */
39720
+ identityId: require_clip_model_registry.string().min(1).optional(),
39721
+ /** Stop after this many measured samples. */
39722
+ maxSamples: require_clip_model_registry.number().int().positive().optional(),
39723
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39724
+ executeOnNodeId: require_clip_model_registry.string().optional()
39725
+ });
39726
+ var AuditFaceEmbeddingsResultSchema = require_clip_model_registry.object({
39727
+ auditedModelId: require_clip_model_registry.string(),
39728
+ total: require_clip_model_registry.number(),
39729
+ ok: require_clip_model_registry.number(),
39730
+ mismatch: require_clip_model_registry.number(),
39731
+ inconclusive: require_clip_model_registry.number(),
39732
+ unmeasurable: require_clip_model_registry.number(),
39733
+ complete: require_clip_model_registry.boolean(),
39734
+ okMinCosine: require_clip_model_registry.number(),
39735
+ mismatchMaxCosine: require_clip_model_registry.number(),
39736
+ rows: require_clip_model_registry.array(require_clip_model_registry.object({
39737
+ sampleId: require_clip_model_registry.string(),
39738
+ identityId: require_clip_model_registry.string(),
39739
+ identityName: require_clip_model_registry.string(),
39740
+ stampedModelId: require_clip_model_registry.string(),
39741
+ verdict: require_clip_model_registry._enum([
39742
+ "ok",
39743
+ "mismatch",
39744
+ "inconclusive",
39745
+ "unmeasurable"
39746
+ ]),
39747
+ cosine: require_clip_model_registry.number().optional(),
39748
+ reason: require_clip_model_registry._enum([
39749
+ "stamped-other-model",
39750
+ "no-pixels",
39751
+ "no-stored-vector",
39752
+ "embed-failed",
39753
+ "no-capable-node",
39754
+ "dimension-differs"
39755
+ ]).optional(),
39756
+ detail: require_clip_model_registry.string().optional()
39757
+ }))
39758
+ });
39759
+ /**
39760
+ * D658: re-embed EXACTLY these samples with ONE pinned model, replacing their
39761
+ * vectors even when already stamped with it — the repair for what
39762
+ * `faceGallery.auditEmbeddings` accuses. See `face-sample-reembed.ts`.
39763
+ */
39764
+ var ReembedSamplesInputSchema = require_clip_model_registry.object({
39765
+ sampleIds: require_clip_model_registry.array(require_clip_model_registry.string().min(1)).min(1).max(200),
39766
+ modelId: require_clip_model_registry.string().min(1),
39767
+ /** Run every embed on ONE named node instead of rotating the fleet. Data, not a pin. */
39768
+ executeOnNodeId: require_clip_model_registry.string().optional()
39769
+ });
39770
+ var ReembedSamplesResultSchema = require_clip_model_registry.object({
39771
+ pinnedModelId: require_clip_model_registry.string(),
39772
+ requested: require_clip_model_registry.number(),
39773
+ reembedded: require_clip_model_registry.number(),
39774
+ failed: require_clip_model_registry.number(),
39775
+ rows: require_clip_model_registry.array(require_clip_model_registry.object({
39776
+ sampleId: require_clip_model_registry.string(),
39777
+ outcome: require_clip_model_registry._enum([
39778
+ "reembedded",
39779
+ "not-found",
39780
+ "duplicate",
39781
+ "no-pixels",
39782
+ "embed-failed",
39783
+ "no-capable-node",
39784
+ "no-model-stamp",
39785
+ "not-landed"
39786
+ ]),
39787
+ previousModelId: require_clip_model_registry.string().optional(),
39788
+ modelId: require_clip_model_registry.string().optional(),
39789
+ detail: require_clip_model_registry.string().optional()
39790
+ }))
39791
+ });
39711
39792
  /** Mirrors `FaceReembedResult` (`face-reembed-pass.ts`) minus the sample-id set. */
39712
39793
  var ReembedFacesResultSchema = require_clip_model_registry.object({
39713
39794
  targetModelId: require_clip_model_registry.string(),
@@ -39959,11 +40040,256 @@ var embeddingActions = require_clip_model_registry.defineCustomActions({
39959
40040
  "embedding.faceAlignmentStatus": require_clip_model_registry.customAction(NoInputSchema$1, FaceAlignmentStatusSchema),
39960
40041
  "embedding.backfillPhotoSources": require_clip_model_registry.customAction(BackfillPhotoSourcesInputSchema, BackfillPhotoSourcesResultSchema, { auth: "admin" }),
39961
40042
  "faceGallery.sourceFrameStats": require_clip_model_registry.customAction(NoInputSchema$1, SourceFrameStatsSchema),
40043
+ "faceGallery.reembedSamples": require_clip_model_registry.customAction(ReembedSamplesInputSchema, ReembedSamplesResultSchema, {
40044
+ kind: "mutation",
40045
+ auth: "admin"
40046
+ }),
40047
+ "faceGallery.auditEmbeddings": require_clip_model_registry.customAction(AuditFaceEmbeddingsInputSchema, AuditFaceEmbeddingsResultSchema, { auth: "admin" }),
39962
40048
  "faceGallery.resetToCore": require_clip_model_registry.customAction(ResetToCoreInputSchema, ResetToCoreResultSchema, {
39963
40049
  kind: "mutation",
39964
40050
  auth: "admin"
39965
40051
  })
39966
40052
  });
40053
+ async function reembedOne(deps, sample, pinned) {
40054
+ const base = {
40055
+ sampleId: sample.sampleId,
40056
+ previousModelId: sample.modelId
40057
+ };
40058
+ const crop = await deps.loadAlignedCrop(sample);
40059
+ if (crop === null) return {
40060
+ ...base,
40061
+ outcome: "no-pixels"
40062
+ };
40063
+ const outcome = await deps.embed(sample, crop);
40064
+ if (outcome.kind === "failed") return {
40065
+ ...base,
40066
+ outcome: "embed-failed",
40067
+ detail: outcome.reason
40068
+ };
40069
+ if (outcome.kind === "no-capable-node") return {
40070
+ ...base,
40071
+ outcome: "no-capable-node",
40072
+ detail: outcome.reason
40073
+ };
40074
+ if (outcome.modelId === void 0 || outcome.modelId === "") return {
40075
+ ...base,
40076
+ outcome: "no-model-stamp",
40077
+ detail: "the embed answered no model id"
40078
+ };
40079
+ if (outcome.modelId !== pinned) return {
40080
+ ...base,
40081
+ outcome: "embed-failed",
40082
+ detail: `ran "${outcome.modelId}" for a call pinned to "${pinned}"`
40083
+ };
40084
+ const write = await deps.store(sample, outcome.embedding, outcome.modelId, outcome.dim);
40085
+ if (!write.landed) return {
40086
+ ...base,
40087
+ outcome: "not-landed",
40088
+ detail: write.reason ?? "the write did not land"
40089
+ };
40090
+ return {
40091
+ ...base,
40092
+ outcome: "reembedded",
40093
+ modelId: outcome.modelId
40094
+ };
40095
+ }
40096
+ async function reembedFaceSamples(deps, options) {
40097
+ const pinned = options.modelId;
40098
+ const byId = new Map((await deps.listSamples()).map((s) => [s.sampleId, s]));
40099
+ const pacingMs = options.pacingMs ?? 25;
40100
+ const seen = /* @__PURE__ */ new Set();
40101
+ const rows = [];
40102
+ let noNode = null;
40103
+ deps.logger.info("face sample re-embed starting", { meta: {
40104
+ pinnedModelId: pinned,
40105
+ requested: options.sampleIds.length
40106
+ } });
40107
+ for (const sampleId of options.sampleIds) {
40108
+ if (seen.has(sampleId)) {
40109
+ rows.push({
40110
+ sampleId,
40111
+ outcome: "duplicate"
40112
+ });
40113
+ continue;
40114
+ }
40115
+ seen.add(sampleId);
40116
+ const sample = byId.get(sampleId);
40117
+ if (sample === void 0) {
40118
+ rows.push({
40119
+ sampleId,
40120
+ outcome: "not-found"
40121
+ });
40122
+ continue;
40123
+ }
40124
+ if (noNode !== null) {
40125
+ rows.push({
40126
+ sampleId,
40127
+ previousModelId: sample.modelId,
40128
+ outcome: "no-capable-node",
40129
+ detail: noNode
40130
+ });
40131
+ continue;
40132
+ }
40133
+ const row = await reembedOne(deps, sample, pinned);
40134
+ if (row.outcome === "no-capable-node") noNode = row.detail ?? "no capable node";
40135
+ if (row.outcome !== "reembedded") deps.logger.warn("face sample re-embed: sample NOT re-embedded", { meta: {
40136
+ sampleId,
40137
+ outcome: row.outcome,
40138
+ detail: row.detail ?? null,
40139
+ pinnedModelId: pinned
40140
+ } });
40141
+ rows.push(row);
40142
+ await deps.pause(pacingMs);
40143
+ }
40144
+ const reembedded = rows.filter((r) => r.outcome === "reembedded").length;
40145
+ deps.logger.info("face sample re-embed finished", { meta: {
40146
+ pinnedModelId: pinned,
40147
+ requested: options.sampleIds.length,
40148
+ reembedded,
40149
+ failed: rows.length - reembedded
40150
+ } });
40151
+ return {
40152
+ pinnedModelId: pinned,
40153
+ requested: options.sampleIds.length,
40154
+ reembedded,
40155
+ failed: rows.length - reembedded,
40156
+ rows
40157
+ };
40158
+ }
40159
+ //#endregion
40160
+ //#region src/pipeline-analytics/face-embedding-audit.ts
40161
+ /** At or above: the stored vector is this model's. */
40162
+ var AUDIT_OK_MIN_COSINE = .98;
40163
+ /** Below: the stored vector is NOT this model's. Between the two: inconclusive. */
40164
+ var AUDIT_MISMATCH_MAX_COSINE = .9;
40165
+ /** Cosine of two vectors of equal length; `null` when either has no length. */
40166
+ function cosine(a, b) {
40167
+ let dot = 0;
40168
+ let na = 0;
40169
+ let nb = 0;
40170
+ for (let i = 0; i < a.length; i++) {
40171
+ const x = a[i] ?? 0;
40172
+ const y = b[i] ?? 0;
40173
+ dot += x * y;
40174
+ na += x * x;
40175
+ nb += y * y;
40176
+ }
40177
+ if (na === 0 || nb === 0) return null;
40178
+ return dot / Math.sqrt(na * nb);
40179
+ }
40180
+ /** The verdict a measured cosine earns. Pure. */
40181
+ function verdictOf(c) {
40182
+ if (c >= .98) return "ok";
40183
+ if (c < .9) return "mismatch";
40184
+ return "inconclusive";
40185
+ }
40186
+ function rowBase(s) {
40187
+ return {
40188
+ sampleId: s.sampleId,
40189
+ identityId: s.identityId,
40190
+ identityName: s.identityName,
40191
+ stampedModelId: s.modelId
40192
+ };
40193
+ }
40194
+ async function measure$2(deps, sample, stored) {
40195
+ const base = rowBase(sample);
40196
+ if (stored === void 0) return {
40197
+ ...base,
40198
+ verdict: "unmeasurable",
40199
+ reason: "no-stored-vector"
40200
+ };
40201
+ const crop = await deps.loadAlignedCrop(sample);
40202
+ if (crop === null) return {
40203
+ ...base,
40204
+ verdict: "unmeasurable",
40205
+ reason: "no-pixels"
40206
+ };
40207
+ const outcome = await deps.embed(sample, crop);
40208
+ if (outcome.kind === "failed") return {
40209
+ ...base,
40210
+ verdict: "unmeasurable",
40211
+ reason: "embed-failed",
40212
+ detail: outcome.reason
40213
+ };
40214
+ if (outcome.kind === "no-capable-node") return {
40215
+ ...base,
40216
+ verdict: "unmeasurable",
40217
+ reason: "no-capable-node",
40218
+ detail: outcome.reason
40219
+ };
40220
+ if (outcome.embedding.length !== stored.length) return {
40221
+ ...base,
40222
+ verdict: "unmeasurable",
40223
+ reason: "dimension-differs",
40224
+ detail: `stored ${stored.length}-d, re-embedded ${outcome.embedding.length}-d`
40225
+ };
40226
+ const c = cosine(outcome.embedding, stored);
40227
+ if (c === null) return {
40228
+ ...base,
40229
+ verdict: "unmeasurable",
40230
+ reason: "no-stored-vector",
40231
+ detail: "zero vector"
40232
+ };
40233
+ return {
40234
+ ...base,
40235
+ verdict: verdictOf(c),
40236
+ cosine: Math.round(c * 1e4) / 1e4
40237
+ };
40238
+ }
40239
+ /**
40240
+ * Audit every sample stamped with `auditedModelId`. Read-only; bounded by
40241
+ * `maxSamples`; a pass-terminal `no-capable-node` stops measuring and reports
40242
+ * the rest as unmeasurable rather than asking again per sample.
40243
+ */
40244
+ async function auditFaceEmbeddings(deps, options) {
40245
+ const all = await deps.listSamples();
40246
+ const samples = options.identityId === void 0 ? all : all.filter((s) => s.identityId === options.identityId);
40247
+ const inSpace = samples.filter((s) => s.modelId === options.auditedModelId);
40248
+ const stored = await deps.loadStoredVectors(inSpace.map((s) => s.sampleId));
40249
+ const budget = options.maxSamples ?? Number.POSITIVE_INFINITY;
40250
+ const rows = [];
40251
+ let measured = 0;
40252
+ let complete = true;
40253
+ let noNode = null;
40254
+ for (const sample of samples) {
40255
+ if (sample.modelId !== options.auditedModelId) {
40256
+ rows.push({
40257
+ ...rowBase(sample),
40258
+ verdict: "unmeasurable",
40259
+ reason: "stamped-other-model"
40260
+ });
40261
+ continue;
40262
+ }
40263
+ if (measured >= budget) {
40264
+ complete = false;
40265
+ continue;
40266
+ }
40267
+ if (noNode !== null) {
40268
+ rows.push({
40269
+ ...rowBase(sample),
40270
+ verdict: "unmeasurable",
40271
+ reason: "no-capable-node",
40272
+ detail: noNode
40273
+ });
40274
+ continue;
40275
+ }
40276
+ const row = await measure$2(deps, sample, stored.get(sample.sampleId));
40277
+ if (row.reason === "no-capable-node") noNode = row.detail ?? "no capable node";
40278
+ if (row.cosine !== void 0) measured += 1;
40279
+ rows.push(row);
40280
+ }
40281
+ const count = (v) => rows.filter((r) => r.verdict === v).length;
40282
+ return {
40283
+ auditedModelId: options.auditedModelId,
40284
+ total: samples.length,
40285
+ ok: count("ok"),
40286
+ mismatch: count("mismatch"),
40287
+ inconclusive: count("inconclusive"),
40288
+ unmeasurable: count("unmeasurable"),
40289
+ complete,
40290
+ rows
40291
+ };
40292
+ }
39967
40293
  //#endregion
39968
40294
  //#region src/pipeline-analytics/face-alignment-version.ts
39969
40295
  /**
@@ -66007,7 +66333,8 @@ var FaceReembedEmbedder = class {
66007
66333
  return {
66008
66334
  kind: "embedded",
66009
66335
  embedding: vector,
66010
- dim: vector.length
66336
+ dim: vector.length,
66337
+ modelId: result.modelId
66011
66338
  };
66012
66339
  }
66013
66340
  }
@@ -84298,6 +84625,79 @@ async function resolveSubjectShot(ports, trackId, deviceId, aroundMs) {
84298
84625
  frameHeight: dims.h
84299
84626
  };
84300
84627
  }
84628
+ var MESSAGES = {
84629
+ "dimension-mismatch": "object embeddings refused — their length is not their model's dimension",
84630
+ "no-clip-metadata": "object embeddings refused — the model has no CLIP metadata",
84631
+ "no-model-id": "object embeddings refused — the result names no model"
84632
+ };
84633
+ function keyOf(r) {
84634
+ return `${String(r.deviceId)}|${r.modelId ?? ""}|${r.reason}`;
84635
+ }
84636
+ var EmbeddingRefusalReport = class {
84637
+ logger;
84638
+ intervalMs;
84639
+ now;
84640
+ keys = /* @__PURE__ */ new Map();
84641
+ constructor(options) {
84642
+ this.logger = options.logger;
84643
+ this.intervalMs = options.intervalMs ?? 3e5;
84644
+ this.now = options.now ?? (() => Date.now());
84645
+ }
84646
+ /** Count one refusal; write a line if this key is new or its interval passed. */
84647
+ note(refusal) {
84648
+ const key = keyOf(refusal);
84649
+ const at = this.now();
84650
+ const held = this.keys.get(key);
84651
+ const pending = (held?.pending ?? 0) + 1;
84652
+ if (held === void 0 || at - held.lastLineAtMs >= this.intervalMs) {
84653
+ this.write(refusal, pending, held === void 0 ? "first" : "interval");
84654
+ this.keys = new Map(this.keys).set(key, {
84655
+ last: refusal,
84656
+ pending: 0,
84657
+ lastLineAtMs: at
84658
+ });
84659
+ return;
84660
+ }
84661
+ this.keys = new Map(this.keys).set(key, {
84662
+ ...held,
84663
+ last: refusal,
84664
+ pending
84665
+ });
84666
+ }
84667
+ /** Report every key whose interval passed with refusals still pending. */
84668
+ sweep() {
84669
+ const at = this.now();
84670
+ for (const [key, held] of this.keys) {
84671
+ if (held.pending === 0 || at - held.lastLineAtMs < this.intervalMs) continue;
84672
+ this.write(held.last, held.pending, "interval");
84673
+ this.keys = new Map(this.keys).set(key, {
84674
+ ...held,
84675
+ pending: 0,
84676
+ lastLineAtMs: at
84677
+ });
84678
+ }
84679
+ }
84680
+ /** Report every pending count now, whatever its interval (shutdown). */
84681
+ flush() {
84682
+ for (const held of this.keys.values()) if (held.pending > 0) this.write(held.last, held.pending, "flush");
84683
+ this.keys = /* @__PURE__ */ new Map();
84684
+ }
84685
+ write(r, refused, closing) {
84686
+ this.logger.warn(MESSAGES[r.reason], {
84687
+ tags: { deviceId: r.deviceId },
84688
+ meta: {
84689
+ reason: r.reason,
84690
+ modelId: r.modelId,
84691
+ refused,
84692
+ windowMs: this.intervalMs,
84693
+ closing,
84694
+ lastTrackId: r.trackId,
84695
+ ...r.dim !== void 0 ? { dim: r.dim } : {},
84696
+ ...r.expected !== void 0 ? { expected: r.expected } : {}
84697
+ }
84698
+ });
84699
+ }
84700
+ };
84301
84701
  //#endregion
84302
84702
  //#region src/pipeline-analytics/store/object-embedding-store.ts
84303
84703
  /**
@@ -84360,10 +84760,33 @@ var ObjectEmbeddingStore = class {
84360
84760
  clipModels;
84361
84761
  /** index → the dim this process declared it at. Per index, per process. */
84362
84762
  declaredDims = /* @__PURE__ */ new Map();
84763
+ /**
84764
+ * Refused writes, COUNTED per (camera, model, reason). A refusal is systemic
84765
+ * — a runner stamping the wrong model refuses every vector it sends — so a
84766
+ * per-track WARN was a flood that hid the one fact that mattered (D658).
84767
+ */
84768
+ refusals;
84363
84769
  constructor(deps) {
84364
84770
  this.vectorStore = deps.vectorStore;
84365
84771
  this.logger = deps.logger;
84366
84772
  this.clipModels = deps.clipModels;
84773
+ this.refusals = new EmbeddingRefusalReport({
84774
+ logger: deps.logger,
84775
+ ...deps.refusalReportIntervalMs !== void 0 ? { intervalMs: deps.refusalReportIntervalMs } : {},
84776
+ ...deps.now !== void 0 ? { now: deps.now } : {}
84777
+ });
84778
+ }
84779
+ /**
84780
+ * Count a write refused BEFORE it reached this store — a detail whose result
84781
+ * named no model (D658). Same report, same rate limit, as the refusals the
84782
+ * store makes itself.
84783
+ */
84784
+ noteRefused(refusal) {
84785
+ this.refusals.note(refusal);
84786
+ }
84787
+ /** Report every refusal count still pending (shutdown). */
84788
+ flushRefusals() {
84789
+ this.refusals.flush();
84367
84790
  }
84368
84791
  /** Every index a delete, scan or wipe must reach. */
84369
84792
  indexes() {
@@ -84461,19 +84884,28 @@ var ObjectEmbeddingStore = class {
84461
84884
  * and dropped", because a counter that cannot reads as verification.
84462
84885
  */
84463
84886
  async upsertIfBetter(input) {
84887
+ this.refusals.sweep();
84464
84888
  const vector = resolveVectorPayload(input);
84465
84889
  if (vector === null) return "failed";
84466
- const space = this.spaceOrWarn(input.modelId, "upsert", input.deviceId);
84467
- if (space === null) return "failed";
84890
+ const space = this.clipModels.spaceFor(input.modelId);
84891
+ if (space === null) {
84892
+ this.refusals.note({
84893
+ deviceId: input.deviceId,
84894
+ modelId: input.modelId,
84895
+ reason: "no-clip-metadata",
84896
+ trackId: input.trackId,
84897
+ dim: vector.dim
84898
+ });
84899
+ return "failed";
84900
+ }
84468
84901
  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
- }
84902
+ this.refusals.note({
84903
+ deviceId: input.deviceId,
84904
+ modelId: input.modelId,
84905
+ reason: "dimension-mismatch",
84906
+ trackId: input.trackId,
84907
+ dim: vector.dim,
84908
+ expected: space.dim
84477
84909
  });
84478
84910
  return "failed";
84479
84911
  }
@@ -84947,6 +85379,30 @@ function rowFromMetadata(id, metadata) {
84947
85379
  };
84948
85380
  }
84949
85381
  //#endregion
85382
+ //#region src/pipeline-analytics/clip-detail-route.ts
85383
+ async function routeClipDetailToStore(store, input) {
85384
+ const modelId = input.embeddingModelId;
85385
+ if (modelId === void 0 || modelId === "") {
85386
+ store.noteRefused({
85387
+ deviceId: input.deviceId,
85388
+ modelId: null,
85389
+ reason: "no-model-id",
85390
+ trackId: input.trackId
85391
+ });
85392
+ return "failed";
85393
+ }
85394
+ return store.upsertIfBetter({
85395
+ trackId: input.trackId,
85396
+ deviceId: input.deviceId,
85397
+ timestamp: input.timestamp,
85398
+ className: input.className,
85399
+ embeddingBase64: input.embeddingBase64,
85400
+ modelId,
85401
+ confidence: input.score,
85402
+ ...input.keyFrameMediaId !== void 0 ? { keyFrameMediaId: input.keyFrameMediaId } : {}
85403
+ });
85404
+ }
85405
+ //#endregion
84950
85406
  //#region src/pipeline-analytics/clip-space-plan.ts
84951
85407
  /**
84952
85408
  * Which CLIP feature space a search reads and a rebuild fills — decided in one
@@ -87158,9 +87614,6 @@ var KEYFRAME_CROP_MIN_SUBJECT_IOU = .5;
87158
87614
  * covers a healthy trail many times over; past it the honest answer is "no
87159
87615
  * evidence" and the temporal bound takes over. */
87160
87616
  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
87617
  var SETTINGS_CACHE_TTL_MS = 5e3;
87165
87618
  /** TTL for the per-node confirmation-gate detector-step cache (the node's
87166
87619
  * default object-detector step rarely changes). */
@@ -87451,10 +87904,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
87451
87904
  overlayState = new OverlayDetailStateStore();
87452
87905
  /** Throttle state for the "overlay synthesis failed" warn, one per device. */
87453
87906
  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
87907
  /** Frame-based event/track media (crop + boxed full-frame) from the
87459
87908
  * detection-pipeline DECODED frame — the ONLY image source (never the
87460
87909
  * snapshot cap). Null when shm frame access is unavailable. */
@@ -91330,6 +91779,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
91330
91779
  await Promise.all([this.drainSensorHandlers(), this.motionEpisodes?.closeAll("addon-shutdown")]);
91331
91780
  await this.embeddingDispatcher?.stop();
91332
91781
  this.embeddingDispatcher = null;
91782
+ this.objectEmbeddingStore?.flushRefusals();
91333
91783
  await this.notificationCenter?.stop();
91334
91784
  this.notificationCenter = null;
91335
91785
  for (const id of this.proxies.keys()) this.releaseProxy(id);
@@ -93012,31 +93462,19 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
93012
93462
  });
93013
93463
  }
93014
93464
  /** 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. */
93465
+ * search), under the model the RUNNER stamped — never a guess (D658). */
93017
93466
  async routeClipDetail(deviceId, trackId, detail, timestamp) {
93018
93467
  const store = this.objectEmbeddingStore;
93019
93468
  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
93469
  const keyFrameMediaId = this.residents.keyFrameMediaId(trackId);
93032
- await store.upsertIfBetter({
93033
- trackId,
93470
+ await routeClipDetailToStore(store, {
93034
93471
  deviceId,
93472
+ trackId,
93035
93473
  timestamp,
93036
93474
  className: detail.className,
93037
- embedding: decodeEmbeddingBase64(detail.embedding),
93038
- modelId,
93039
- confidence: detail.score,
93475
+ score: detail.score,
93476
+ embeddingBase64: detail.embedding,
93477
+ embeddingModelId: detail.embeddingModelId,
93040
93478
  ...keyFrameMediaId !== void 0 ? { keyFrameMediaId } : {}
93041
93479
  });
93042
93480
  }
@@ -93065,23 +93503,6 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
93065
93503
  ...identityId !== void 0 ? { identityId } : {}
93066
93504
  });
93067
93505
  }
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
93506
  /**
93086
93507
  * §5 — decide which active tracks need periodic media THIS frame. Pure over
93087
93508
  * TrackStore.lastSnapshotAt + the per-track best-confidence map:
@@ -97007,6 +97428,126 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97007
97428
  }
97008
97429
  }
97009
97430
  /**
97431
+ * D658: re-embed EXACTLY the named samples with ONE pinned model, forced
97432
+ * (`face-sample-reembed.ts`) — the repair for what the audit accuses. Shares
97433
+ * the gallery-job exclusion with the re-embed pass, the photo pass and the
97434
+ * reset, and reloads the matcher's gallery when anything landed.
97435
+ */
97436
+ async reembedFaceGallerySamples(input) {
97437
+ const api = this.ctx.api;
97438
+ if (!api) throw new Error("faceGallery.reembedSamples: the addon API is not available");
97439
+ const identityStore = this.identityStore;
97440
+ if (!identityStore) throw new Error("faceGallery.reembedSamples: the identity store is not ready");
97441
+ const mediaStore = this.mediaStore;
97442
+ if (!mediaStore) throw new Error("faceGallery.reembedSamples: the media store is not ready");
97443
+ if (this.faceReembedRunning) throw new Error("faceGallery.reembedSamples: a face re-embed pass is already running");
97444
+ if (this.faceAlignmentJobs?.isPhotoPassRunning === true) throw new Error("faceGallery.reembedSamples: a photo re-enrol pass is running (D643)");
97445
+ if (this.faceAlignmentJobs?.isResetRunning === true) throw new Error("faceGallery.reembedSamples: a face gallery reset is running (D644)");
97446
+ this.faceReembedRunning = true;
97447
+ try {
97448
+ const embedder = new FaceReembedEmbedder({
97449
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97450
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97451
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), input.modelId);
97452
+ const result = await reembedFaceSamples({
97453
+ listSamples: () => identityStore.listSamplesForReembed(),
97454
+ loadAlignedCrop: async (sample) => {
97455
+ if (sample.mediaId === void 0) return null;
97456
+ try {
97457
+ const file = await mediaStore.getById(sample.mediaId);
97458
+ return file === null ? null : Buffer.from(file.base64, "base64");
97459
+ } catch (err) {
97460
+ this.ctx.logger.warn("face sample re-embed: aligned crop read threw", { meta: {
97461
+ sampleId: sample.sampleId,
97462
+ error: String(err)
97463
+ } });
97464
+ return null;
97465
+ }
97466
+ },
97467
+ embed: (sample, crop) => embedder.embed(sample, crop),
97468
+ store: (sample, embedding, modelId, dim) => identityStore.restampSample({
97469
+ sampleId: sample.sampleId,
97470
+ identityId: sample.identityId,
97471
+ embedding,
97472
+ modelId,
97473
+ dim
97474
+ }),
97475
+ pause: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
97476
+ logger: {
97477
+ info: (m, e) => this.ctx.logger.info(m, e),
97478
+ warn: (m, e) => this.ctx.logger.warn(m, e)
97479
+ }
97480
+ }, {
97481
+ sampleIds: input.sampleIds,
97482
+ modelId: input.modelId
97483
+ });
97484
+ if (result.reembedded > 0) await this.faceRecognizer?.refreshGallery();
97485
+ return {
97486
+ ...result,
97487
+ rows: [...result.rows]
97488
+ };
97489
+ } finally {
97490
+ this.faceReembedRunning = false;
97491
+ }
97492
+ }
97493
+ /**
97494
+ * D658: re-embed each sample's stored template with ONE pinned model and
97495
+ * compare with its stored vector (`face-embedding-audit.ts`). READ-ONLY —
97496
+ * nothing is restamped, re-embedded or removed. The pass pins the model the
97497
+ * caller names (default `auraface-r100`), not the cluster row: the question
97498
+ * is whether a STAMP is true, whatever the cluster runs today.
97499
+ */
97500
+ async auditFaceGalleryEmbeddings(input) {
97501
+ const api = this.ctx.api;
97502
+ if (!api) throw new Error("faceGallery.auditEmbeddings: the addon API is not available");
97503
+ const identityStore = this.identityStore;
97504
+ if (!identityStore) throw new Error("faceGallery.auditEmbeddings: the identity store is not ready");
97505
+ const mediaStore = this.mediaStore;
97506
+ if (!mediaStore) throw new Error("faceGallery.auditEmbeddings: the media store is not ready");
97507
+ const auditedModelId = input.modelId ?? "auraface-r100";
97508
+ const embedder = new FaceReembedEmbedder({
97509
+ runStatelessStep: (nodeId, stepInput) => this.runRebuildStepOn(nodeId, stepInput),
97510
+ logger: { warn: (m, e) => this.ctx.logger.warn(m, e) }
97511
+ }, await this.resolveRebuildNodes(api, input.executeOnNodeId), auditedModelId);
97512
+ const report = await auditFaceEmbeddings({
97513
+ listSamples: () => identityStore.listSamplesForReembed(),
97514
+ loadStoredVectors: (ids) => identityStore.loadSampleVectors(ids),
97515
+ loadAlignedCrop: async (sample) => {
97516
+ if (sample.mediaId === void 0) return null;
97517
+ try {
97518
+ const file = await mediaStore.getById(sample.mediaId);
97519
+ return file === null ? null : Buffer.from(file.base64, "base64");
97520
+ } catch (err) {
97521
+ this.ctx.logger.warn("face embedding audit: aligned crop read threw", { meta: {
97522
+ sampleId: sample.sampleId,
97523
+ error: String(err)
97524
+ } });
97525
+ return null;
97526
+ }
97527
+ },
97528
+ embed: (sample, crop) => embedder.embed(sample, crop)
97529
+ }, {
97530
+ auditedModelId,
97531
+ ...input.identityId !== void 0 ? { identityId: input.identityId } : {},
97532
+ ...input.maxSamples !== void 0 ? { maxSamples: input.maxSamples } : {}
97533
+ });
97534
+ this.ctx.logger.info("face embedding audit finished", { meta: {
97535
+ auditedModelId,
97536
+ total: report.total,
97537
+ ok: report.ok,
97538
+ mismatch: report.mismatch,
97539
+ inconclusive: report.inconclusive,
97540
+ unmeasurable: report.unmeasurable,
97541
+ complete: report.complete
97542
+ } });
97543
+ return {
97544
+ ...report,
97545
+ okMinCosine: AUDIT_OK_MIN_COSINE,
97546
+ mismatchMaxCosine: AUDIT_MISMATCH_MAX_COSINE,
97547
+ rows: [...report.rows]
97548
+ };
97549
+ }
97550
+ /**
97010
97551
  * The server-side half of "change the model from any UI and the gallery
97011
97552
  * follows": if a sample sits outside the cluster space and a pass could
97012
97553
  * still bring it in, start one. Runs on a timer; converges and goes quiet.
@@ -97529,6 +98070,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
97529
98070
  samples: [...result.samples]
97530
98071
  };
97531
98072
  },
98073
+ "faceGallery.reembedSamples": async (input) => this.reembedFaceGallerySamples(input),
98074
+ "faceGallery.auditEmbeddings": async (input) => this.auditFaceGalleryEmbeddings(input),
97532
98075
  "faceGallery.sourceFrameStats": async () => ({ devices: this.faceSourceStats.snapshot().map((d) => ({
97533
98076
  ...d,
97534
98077
  dropped: { ...d.dropped }