@camstack/addon-post-analysis 1.2.192 → 1.2.193
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.
- package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-Gc-d7G7c.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BmbhUxPs.mjs} +3 -3
- package/dist/pipeline-analytics/{hostInit-Bprfn5kJ.mjs → hostInit-qRtMpIyP.mjs} +3 -3
- package/dist/pipeline-analytics/index.js +266 -16
- package/dist/pipeline-analytics/index.mjs +266 -16
- package/dist/pipeline-analytics/remoteEntry.js +1 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import "./dist-CYZr2fwk.mjs";
|
|
|
3
3
|
var e = {
|
|
4
4
|
"@camstack/sdk": {
|
|
5
5
|
name: "@camstack/sdk",
|
|
6
|
-
version: "1.2.
|
|
6
|
+
version: "1.2.78",
|
|
7
7
|
scope: ["default"],
|
|
8
8
|
loaded: !1,
|
|
9
9
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -18,7 +18,7 @@ var e = {
|
|
|
18
18
|
},
|
|
19
19
|
"@camstack/types": {
|
|
20
20
|
name: "@camstack/types",
|
|
21
|
-
version: "1.2.
|
|
21
|
+
version: "1.2.158",
|
|
22
22
|
scope: ["default"],
|
|
23
23
|
loaded: !1,
|
|
24
24
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -33,7 +33,7 @@ var e = {
|
|
|
33
33
|
},
|
|
34
34
|
"@camstack/ui-library": {
|
|
35
35
|
name: "@camstack/ui-library",
|
|
36
|
-
version: "1.2.
|
|
36
|
+
version: "1.2.126",
|
|
37
37
|
scope: ["default"],
|
|
38
38
|
loaded: !1,
|
|
39
39
|
from: "addon_pipeline_analytics_widgets",
|
|
@@ -36,7 +36,7 @@ async function r() {
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"@camstack/types": {
|
|
39
|
-
version: "1.2.
|
|
39
|
+
version: "1.2.158",
|
|
40
40
|
scope: "default",
|
|
41
41
|
shareConfig: {
|
|
42
42
|
singleton: !0,
|
|
@@ -45,7 +45,7 @@ async function r() {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
"@camstack/sdk": {
|
|
48
|
-
version: "1.2.
|
|
48
|
+
version: "1.2.78",
|
|
49
49
|
scope: "default",
|
|
50
50
|
shareConfig: {
|
|
51
51
|
singleton: !0,
|
|
@@ -81,7 +81,7 @@ async function r() {
|
|
|
81
81
|
}
|
|
82
82
|
},
|
|
83
83
|
"@camstack/ui-library": {
|
|
84
|
-
version: "1.2.
|
|
84
|
+
version: "1.2.126",
|
|
85
85
|
scope: "default",
|
|
86
86
|
shareConfig: {
|
|
87
87
|
singleton: !0,
|
|
@@ -34017,6 +34017,18 @@ var LooseBindSampler = class {
|
|
|
34017
34017
|
this.counts.clear();
|
|
34018
34018
|
}
|
|
34019
34019
|
};
|
|
34020
|
+
/** Per-reason counter + sampling decision. One instance per tracker, so the
|
|
34021
|
+
* counts are per camera and per detection source. */
|
|
34022
|
+
var SpawnRefusalSampler = class {
|
|
34023
|
+
counts = /* @__PURE__ */ new Map();
|
|
34024
|
+
/** The running count when this refusal should be reported, else `undefined`. */
|
|
34025
|
+
next(reason) {
|
|
34026
|
+
const count = (this.counts.get(reason) ?? 0) + 1;
|
|
34027
|
+
this.counts.set(reason, count);
|
|
34028
|
+
if (count <= 10) return count;
|
|
34029
|
+
return count % 10 === 0 ? count : void 0;
|
|
34030
|
+
}
|
|
34031
|
+
};
|
|
34020
34032
|
//#endregion
|
|
34021
34033
|
//#region src/pipeline-analytics/pipeline/tracker/sort-tracker.ts
|
|
34022
34034
|
var DEFAULT_TRACKER_CONFIG = {
|
|
@@ -34101,6 +34113,10 @@ var SortTracker = class SortTracker {
|
|
|
34101
34113
|
/** Sink for salvage-bind diagnostics. Null until an owner that knows the
|
|
34102
34114
|
* `deviceId` wires one — see {@link setBindObserver}. */
|
|
34103
34115
|
bindObserver = null;
|
|
34116
|
+
/** Per-reason counters behind the sampled dropped-detection diagnostics. */
|
|
34117
|
+
refusalSampler = new SpawnRefusalSampler();
|
|
34118
|
+
/** Sink for dropped-detection diagnostics — see {@link setSpawnRefusalObserver}. */
|
|
34119
|
+
spawnRefusalObserver = null;
|
|
34104
34120
|
constructor(config = {}) {
|
|
34105
34121
|
this.config = {
|
|
34106
34122
|
...DEFAULT_TRACKER_CONFIG,
|
|
@@ -34116,6 +34132,37 @@ var SortTracker = class SortTracker {
|
|
|
34116
34132
|
setBindObserver(observer) {
|
|
34117
34133
|
this.bindObserver = observer;
|
|
34118
34134
|
}
|
|
34135
|
+
/**
|
|
34136
|
+
* Wire the diagnostics sink for detections the tracker DROPS — a spawn a
|
|
34137
|
+
* gate refused, or a box a track of another class absorbed. Same contract as
|
|
34138
|
+
* {@link setBindObserver}: the tracker has no `deviceId`, the caller stamps
|
|
34139
|
+
* it. Nothing is computed when no observer is wired.
|
|
34140
|
+
*/
|
|
34141
|
+
setSpawnRefusalObserver(observer) {
|
|
34142
|
+
this.spawnRefusalObserver = observer;
|
|
34143
|
+
}
|
|
34144
|
+
/** Emit one dropped-detection record, subject to the refusal sampler. */
|
|
34145
|
+
reportRefusal(reason, det, extra = {}) {
|
|
34146
|
+
const observer = this.spawnRefusalObserver;
|
|
34147
|
+
if (!observer) return;
|
|
34148
|
+
const refusalCount = this.refusalSampler.next(reason);
|
|
34149
|
+
if (refusalCount === void 0) return;
|
|
34150
|
+
observer({
|
|
34151
|
+
reason,
|
|
34152
|
+
detClass: det.class,
|
|
34153
|
+
detScore: det.score,
|
|
34154
|
+
bbox: det.bbox,
|
|
34155
|
+
...det.nativeRecovery !== void 0 ? { nativeRecoveryScore: det.nativeRecovery.score } : {},
|
|
34156
|
+
...extra.threshold !== void 0 ? { threshold: extra.threshold } : {},
|
|
34157
|
+
...extra.measured !== void 0 ? { measured: extra.measured } : {},
|
|
34158
|
+
...extra.against !== void 0 ? {
|
|
34159
|
+
againstTrackId: extra.against.id,
|
|
34160
|
+
againstTrackClass: extra.against.class
|
|
34161
|
+
} : {},
|
|
34162
|
+
refusalCount,
|
|
34163
|
+
sampledEvery: 10
|
|
34164
|
+
});
|
|
34165
|
+
}
|
|
34119
34166
|
/** Emit one salvage decision, subject to {@link LOOSE_BIND_LOG_SAMPLE_EVERY}.
|
|
34120
34167
|
* Nothing is computed when no observer is wired. */
|
|
34121
34168
|
reportBind(stage, gate, track, det, timestamp) {
|
|
@@ -34175,9 +34222,49 @@ var SortTracker = class SortTracker {
|
|
|
34175
34222
|
* exact class when `classGating` is off (no gate at all) — matching the legacy
|
|
34176
34223
|
* contract — otherwise same association group (group == exact class unless
|
|
34177
34224
|
* `classGroupAssoc` widened it to {person,animal}). */
|
|
34178
|
-
associable(trackClass,
|
|
34225
|
+
associable(trackClass, det) {
|
|
34226
|
+
if (!this.sameGroupAsMeasured(trackClass, det)) return false;
|
|
34179
34227
|
if (!this.config.classGating) return true;
|
|
34180
|
-
return this.assocGroup(trackClass) === this.assocGroup(
|
|
34228
|
+
return this.assocGroup(trackClass) === this.assocGroup(det.class);
|
|
34229
|
+
}
|
|
34230
|
+
/**
|
|
34231
|
+
* A NATIVE-MEASURED BOX IS ITS OWN SUBJECT ACROSS CLASSES (D378).
|
|
34232
|
+
*
|
|
34233
|
+
* `classGroupAssoc` collapses {person, animal} into one association group for
|
|
34234
|
+
* one failure of the COARSE full-frame pass: a bending person re-labelled
|
|
34235
|
+
* `animal` for a frame, over the person's own box. Letting that box re-match
|
|
34236
|
+
* the person track is strictly better than minting a concurrent id.
|
|
34237
|
+
*
|
|
34238
|
+
* The runner's second pass makes the opposite measurement — the same detector
|
|
34239
|
+
* on a 640-800 px native square around a motion region the full-frame boxes
|
|
34240
|
+
* do NOT account for. Such a box is, by construction, not the subject the
|
|
34241
|
+
* full pass already has; absorbing it into a person track means the animal is
|
|
34242
|
+
* never seen at all, which on 2026-09-07 07:51:33 is exactly what happened to
|
|
34243
|
+
* an `animal` at `nativeRecoveryScore=0.833`.
|
|
34244
|
+
*
|
|
34245
|
+
* So the widening does not apply to a detection carrying `nativeRecovery`:
|
|
34246
|
+
* it associates to its OWN class or it spawns. Every other guard on that
|
|
34247
|
+
* spawn still runs — the per-class score floor, the area floor, the
|
|
34248
|
+
* `dedupSpawnIou` overlap, and `resolveDuplicates` afterwards, where a person
|
|
34249
|
+
* still beats a cross-class animal.
|
|
34250
|
+
*/
|
|
34251
|
+
sameGroupAsMeasured(trackClass, det) {
|
|
34252
|
+
if (det.nativeRecovery === void 0) return true;
|
|
34253
|
+
return trackClass === det.class;
|
|
34254
|
+
}
|
|
34255
|
+
/**
|
|
34256
|
+
* Whether a matched track's box refuses `det` a birth. The gate exists for a
|
|
34257
|
+
* detector double-firing INSIDE a subject it already has, so it is a
|
|
34258
|
+
* same-subject claim: for a box the native pass measured (D378), a
|
|
34259
|
+
* cross-class occluder is not that claim — a dog at its owner's knee is 100%
|
|
34260
|
+
* contained in the person's box and is not the person. 2026-09-07 07:56:37,
|
|
34261
|
+
* an `animal` at `nativeRecoveryScore=0.5143` refused on `threshold=0.6`.
|
|
34262
|
+
* A SAME-class occluder still refuses it: that is the torso the crop cut out
|
|
34263
|
+
* of a person the full pass already has.
|
|
34264
|
+
*/
|
|
34265
|
+
occludesSpawn(det, occluder) {
|
|
34266
|
+
if (!this.sameGroupAsMeasured(occluder.class, det)) return false;
|
|
34267
|
+
return containment(det.bbox, occluder.bbox) >= this.config.occlusionContainment;
|
|
34181
34268
|
}
|
|
34182
34269
|
/** Whether a spawn candidate clears its per-class minimum bbox-area floor
|
|
34183
34270
|
* (fraction of frame area). Ungated when disabled, when the class has no floor,
|
|
@@ -34199,14 +34286,30 @@ var SortTracker = class SortTracker {
|
|
|
34199
34286
|
isDuplicateSpawn(det, live, timestamp) {
|
|
34200
34287
|
for (const t of live) {
|
|
34201
34288
|
if (!this.dedupCompatible(t.class, det.class)) continue;
|
|
34202
|
-
|
|
34289
|
+
const overlap = iou$3(det.bbox, t.bbox);
|
|
34290
|
+
if (overlap >= this.config.dedupSpawnIou) {
|
|
34291
|
+
this.reportRefusal("dedup-iou", det, {
|
|
34292
|
+
threshold: this.config.dedupSpawnIou,
|
|
34293
|
+
measured: overlap,
|
|
34294
|
+
against: t
|
|
34295
|
+
});
|
|
34296
|
+
return true;
|
|
34297
|
+
}
|
|
34203
34298
|
if (t.class === det.class && growthAdjacentMatch(t.bbox, det.bbox, SortTracker.growthContext(t, timestamp))) {
|
|
34204
34299
|
this.reportBind("spawn-dedup", "growth", t, det, timestamp);
|
|
34205
34300
|
return true;
|
|
34206
34301
|
}
|
|
34207
34302
|
}
|
|
34208
|
-
if (this.config.personAnimalDedup && det.class === ANIMAL_CLASS && det.score < this.config.animalOverPersonMaxScore) {
|
|
34209
|
-
|
|
34303
|
+
if (this.config.personAnimalDedup && det.class === ANIMAL_CLASS && det.nativeRecovery === void 0 && det.score < this.config.animalOverPersonMaxScore) for (const t of live) {
|
|
34304
|
+
const overlap = iou$3(det.bbox, t.bbox);
|
|
34305
|
+
if (t.class === PERSON_CLASS && overlap >= this.config.animalOverPersonIou) {
|
|
34306
|
+
this.reportRefusal("animal-over-person", det, {
|
|
34307
|
+
threshold: this.config.animalOverPersonMaxScore,
|
|
34308
|
+
measured: overlap,
|
|
34309
|
+
against: t
|
|
34310
|
+
});
|
|
34311
|
+
return true;
|
|
34312
|
+
}
|
|
34210
34313
|
}
|
|
34211
34314
|
return false;
|
|
34212
34315
|
}
|
|
@@ -34321,6 +34424,7 @@ var SortTracker = class SortTracker {
|
|
|
34321
34424
|
* class.
|
|
34322
34425
|
*/
|
|
34323
34426
|
looseMatch(track, det, timestamp) {
|
|
34427
|
+
if (!this.sameGroupAsMeasured(track.class, det)) return null;
|
|
34324
34428
|
if (this.assocGroup(track.class) !== this.assocGroup(det.class)) return null;
|
|
34325
34429
|
if (iou$3(track.bbox, det.bbox) >= this.config.rescueIouThreshold) return "iou";
|
|
34326
34430
|
const tc = bboxCentroid(track.bbox);
|
|
@@ -34407,7 +34511,7 @@ var SortTracker = class SortTracker {
|
|
|
34407
34511
|
for (const di of candidateIdxs) {
|
|
34408
34512
|
if (used.has(di)) continue;
|
|
34409
34513
|
const det = detections[di];
|
|
34410
|
-
if (!this.associable(track.class, det
|
|
34514
|
+
if (!this.associable(track.class, det)) continue;
|
|
34411
34515
|
const score = iou$3(pbox, det.bbox);
|
|
34412
34516
|
if (score >= iouThreshold) pairs.push({
|
|
34413
34517
|
track,
|
|
@@ -34486,6 +34590,7 @@ var SortTracker = class SortTracker {
|
|
|
34486
34590
|
}
|
|
34487
34591
|
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, used, matchedTracks, matched);
|
|
34488
34592
|
for (const [track, det] of matched) {
|
|
34593
|
+
if (track.class !== det.class) this.reportRefusal("cross-class-absorbed", det, { against: track });
|
|
34489
34594
|
const prevCenter = bboxCentroid({
|
|
34490
34595
|
x: track.bbox.x,
|
|
34491
34596
|
y: track.bbox.y,
|
|
@@ -34514,15 +34619,18 @@ var SortTracker = class SortTracker {
|
|
|
34514
34619
|
if (track.path.length > MAX_PATH_LENGTH) track.path.shift();
|
|
34515
34620
|
this.addClassVote(track, det);
|
|
34516
34621
|
}
|
|
34517
|
-
const
|
|
34518
|
-
for (const track of matchedTracks)
|
|
34622
|
+
const occluders = [];
|
|
34623
|
+
for (const track of matchedTracks) occluders.push({
|
|
34624
|
+
bbox: track.bbox,
|
|
34625
|
+
class: track.class
|
|
34626
|
+
});
|
|
34519
34627
|
const surviving = [];
|
|
34520
34628
|
for (const track of this.tracks) {
|
|
34521
34629
|
if (matchedTracks.has(track)) {
|
|
34522
34630
|
surviving.push(track);
|
|
34523
34631
|
continue;
|
|
34524
34632
|
}
|
|
34525
|
-
const occluded = this.config.occlusionEnabled &&
|
|
34633
|
+
const occluded = this.config.occlusionEnabled && occluders.some((ob) => containment(this.predicted(track), ob.bbox) >= this.config.occlusionContainment);
|
|
34526
34634
|
track.age++;
|
|
34527
34635
|
if (timestamp - track.lastSeen > (occluded ? this.config.occlusionMaxMissedMs : this.config.maxMissedMs)) {
|
|
34528
34636
|
track.lost = true;
|
|
@@ -34601,14 +34709,25 @@ var SortTracker = class SortTracker {
|
|
|
34601
34709
|
for (const di of unmatchedIdx) {
|
|
34602
34710
|
const det = detections[di];
|
|
34603
34711
|
if (!this.meetsClassScoreFloor(det)) {
|
|
34712
|
+
this.reportRefusal("score-floor", det, {
|
|
34713
|
+
threshold: this.config.classMinScores[det.class] ?? 0,
|
|
34714
|
+
measured: det.score
|
|
34715
|
+
});
|
|
34604
34716
|
used.add(di);
|
|
34605
34717
|
continue;
|
|
34606
34718
|
}
|
|
34607
34719
|
if (!this.meetsSpawnAreaFloor(det, frameArea)) {
|
|
34720
|
+
this.reportRefusal("area-floor", det, {
|
|
34721
|
+
threshold: this.config.classSpawnAreaFracs[det.class] ?? 0,
|
|
34722
|
+
measured: frameArea > 0 ? det.bbox.w * det.bbox.h / frameArea : 0
|
|
34723
|
+
});
|
|
34608
34724
|
used.add(di);
|
|
34609
34725
|
continue;
|
|
34610
34726
|
}
|
|
34611
|
-
if (this.config.occlusionEnabled &&
|
|
34727
|
+
if (this.config.occlusionEnabled && occluders.some((ob) => this.occludesSpawn(det, ob))) {
|
|
34728
|
+
this.reportRefusal("occlusion", det, { threshold: this.config.occlusionContainment });
|
|
34729
|
+
continue;
|
|
34730
|
+
}
|
|
34612
34731
|
if (this.config.dedupEnabled && this.isDuplicateSpawn(det, surviving, timestamp)) {
|
|
34613
34732
|
used.add(di);
|
|
34614
34733
|
continue;
|
|
@@ -35096,6 +35215,9 @@ var FrameProcessor = class {
|
|
|
35096
35215
|
* Only the rescue / resurrection / growth branches report, and they report
|
|
35097
35216
|
* sampled: stage 1 runs per track per frame at frame-rate.
|
|
35098
35217
|
*/
|
|
35218
|
+
setSpawnRefusalObserver(observer) {
|
|
35219
|
+
this.tracker.setSpawnRefusalObserver(observer);
|
|
35220
|
+
}
|
|
35099
35221
|
setBindObserver(observer) {
|
|
35100
35222
|
this.tracker.setBindObserver(observer);
|
|
35101
35223
|
}
|
|
@@ -35118,6 +35240,7 @@ var FrameProcessor = class {
|
|
|
35118
35240
|
const faceAlignedCropByBbox = /* @__PURE__ */ new Map();
|
|
35119
35241
|
const plateByBbox = /* @__PURE__ */ new Map();
|
|
35120
35242
|
const maskByBbox = /* @__PURE__ */ new Map();
|
|
35243
|
+
const nativeRecoveryByBbox = /* @__PURE__ */ new Map();
|
|
35121
35244
|
const flatDetections = frame.detections.filter((d) => d.kind === "first-level").map((det) => {
|
|
35122
35245
|
const bbox = {
|
|
35123
35246
|
x: det.bbox.x,
|
|
@@ -35129,8 +35252,10 @@ var FrameProcessor = class {
|
|
|
35129
35252
|
class: det.macroClass,
|
|
35130
35253
|
originalClass: det.debug?.originalClass ?? det.macroClass,
|
|
35131
35254
|
score: det.score,
|
|
35132
|
-
bbox
|
|
35255
|
+
bbox,
|
|
35256
|
+
...det.nativeRecovery !== void 0 ? { nativeRecovery: det.nativeRecovery } : {}
|
|
35133
35257
|
};
|
|
35258
|
+
if (det.nativeRecovery !== void 0) nativeRecoveryByBbox.set(bbox, det.nativeRecovery);
|
|
35134
35259
|
if (det.labels.length > 0) labelsByBbox.set(bbox, det.labels);
|
|
35135
35260
|
if (det.debug?.originalClassTier !== void 0) originalClassTierByBbox.set(bbox, det.debug.originalClassTier);
|
|
35136
35261
|
if (det.embedding !== void 0) embeddingByBbox.set(bbox, {
|
|
@@ -35355,6 +35480,7 @@ var FrameProcessor = class {
|
|
|
35355
35480
|
const faceAlignedCrop = faceAlignedCropByBbox.get(td.bbox);
|
|
35356
35481
|
const plate = plateByBbox.get(td.bbox);
|
|
35357
35482
|
const sourceDetectionId = sourceIdByBbox.get(td.bbox);
|
|
35483
|
+
const nativeRecovery = td.matchedThisFrame !== false ? nativeRecoveryByBbox.get(td.bbox) : void 0;
|
|
35358
35484
|
return {
|
|
35359
35485
|
trackId: td.trackId,
|
|
35360
35486
|
...sourceDetectionId !== void 0 ? { sourceDetectionId } : {},
|
|
@@ -35377,7 +35503,8 @@ var FrameProcessor = class {
|
|
|
35377
35503
|
plateText: plate.text,
|
|
35378
35504
|
plateScore: plate.score,
|
|
35379
35505
|
plateBbox: plate.bbox
|
|
35380
|
-
} : {}
|
|
35506
|
+
} : {},
|
|
35507
|
+
...nativeRecovery !== void 0 ? { nativeRecovery } : {}
|
|
35381
35508
|
};
|
|
35382
35509
|
});
|
|
35383
35510
|
this.lastLabelWriteRefusals = refusals;
|
|
@@ -45238,7 +45365,7 @@ async function runConfirmation(candidate, config, deps, exhausted) {
|
|
|
45238
45365
|
for (const d of detections) if (isConfirmationCompatible(candidate.className, d.macroClass)) {
|
|
45239
45366
|
if (!best || d.score > best.score) best = d;
|
|
45240
45367
|
} else if (!bestIncompatible || d.score > bestIncompatible.score) bestIncompatible = d;
|
|
45241
|
-
const bar =
|
|
45368
|
+
const bar = barFor(candidate, config, deps);
|
|
45242
45369
|
const confirmed = best !== void 0 && best.score >= bar;
|
|
45243
45370
|
const secondary = bestIncompatible ? promoteSecondary(candidate, bestIncompatible, bar, deps) : null;
|
|
45244
45371
|
return {
|
|
@@ -45277,7 +45404,27 @@ function promoteSecondary(candidate, detection, bar, deps) {
|
|
|
45277
45404
|
}
|
|
45278
45405
|
};
|
|
45279
45406
|
}
|
|
45407
|
+
/**
|
|
45408
|
+
* The bar this candidate faces. Never below the configured one: the
|
|
45409
|
+
* `minConfidenceFor` hook is monotonic by construction, and clamping here says
|
|
45410
|
+
* so at the point of use rather than trusting the caller to have been careful.
|
|
45411
|
+
*/
|
|
45412
|
+
function barFor(candidate, config, deps) {
|
|
45413
|
+
return Math.max(config.minConfidence, deps.minConfidenceFor?.(candidate) ?? 0);
|
|
45414
|
+
}
|
|
45280
45415
|
async function confirmOne(candidate, config, deps, exhausted) {
|
|
45416
|
+
const native = candidate.nativeRecovery;
|
|
45417
|
+
if (native !== void 0) {
|
|
45418
|
+
const bar = barFor(candidate, config, deps);
|
|
45419
|
+
if (native.score >= bar) return {
|
|
45420
|
+
trackId: candidate.trackId,
|
|
45421
|
+
verdict: "confirmed",
|
|
45422
|
+
reason: "native-pass",
|
|
45423
|
+
className: candidate.className,
|
|
45424
|
+
bestScore: native.score,
|
|
45425
|
+
appliedMinConfidence: bar
|
|
45426
|
+
};
|
|
45427
|
+
}
|
|
45281
45428
|
const cropPx = Math.max(candidate.bbox.w, candidate.bbox.h);
|
|
45282
45429
|
if (config.minCropPx > 0 && cropPx < config.minCropPx) return undecided(candidate, "below-min-crop");
|
|
45283
45430
|
try {
|
|
@@ -55167,6 +55314,77 @@ function rowToEntry(id, data) {
|
|
|
55167
55314
|
};
|
|
55168
55315
|
}
|
|
55169
55316
|
//#endregion
|
|
55317
|
+
//#region src/pipeline-analytics/pipeline/started-births.ts
|
|
55318
|
+
/**
|
|
55319
|
+
* Memory of births whose ritual has already RUN, per device+source.
|
|
55320
|
+
*
|
|
55321
|
+
* A birth is a decision, and a decision is taken once. Until 2026-09-07 it
|
|
55322
|
+
* could be taken twice, on camera 615 it routinely was, and the second vote
|
|
55323
|
+
* won:
|
|
55324
|
+
*
|
|
55325
|
+
* 04:59:38.335Z confirmation gate: birth confirmed {trackId=f585b2ee…
|
|
55326
|
+
* className=animal bestScore=0.7068}
|
|
55327
|
+
* 04:59:38.335Z track started {trackId=f585b2ee…}
|
|
55328
|
+
* 04:59:38.821Z confirmation gate: birth suppressed (false positive)
|
|
55329
|
+
* {trackId=f585b2ee… className=animal} ← no bestScore:
|
|
55330
|
+
* the second crop held no compatible detection AT ALL
|
|
55331
|
+
* 04:59:38.821Z birth suppressed — track record retracted
|
|
55332
|
+
*
|
|
55333
|
+
* TWO conditions have to meet, and both are structural:
|
|
55334
|
+
*
|
|
55335
|
+
* 1. `processFrame` reads `lastActiveTrackIds` at the top and writes it at the
|
|
55336
|
+
* bottom, and AWAITS the confirmation gate in between (a crop round trip to
|
|
55337
|
+
* the frame's node plus a re-detection: 130–490 ms measured on this camera,
|
|
55338
|
+
* against 100 ms frame spacing). Several frames are therefore in flight at
|
|
55339
|
+
* once and each of them sees the same fresh id as unjudged.
|
|
55340
|
+
* 2. The other birth discriminator, `positionsCount > 1`, does not close the
|
|
55341
|
+
* gap either: a COASTED frame deliberately appends no position
|
|
55342
|
+
* (`TrackStore.upsert`, `matched: false`), so a track the full-frame pass
|
|
55343
|
+
* cannot see — which is the entire premise of the small-subject recovery —
|
|
55344
|
+
* keeps exactly one position for as long as it coasts, and
|
|
55345
|
+
* `classifyTrackAppearance` keeps answering `birth`.
|
|
55346
|
+
*
|
|
55347
|
+
* So the id that has already been born is remembered HERE, and both halves of
|
|
55348
|
+
* the birth path consult it: it is not offered to the gate again, and a verdict
|
|
55349
|
+
* that arrives for it anyway cannot retract it. Same lifetime discipline as
|
|
55350
|
+
* {@link SuppressedBirthRegistry} — held while the tracker still carries the
|
|
55351
|
+
* id, forgotten the frame it stops.
|
|
55352
|
+
*/
|
|
55353
|
+
var StartedBirthRegistry = class {
|
|
55354
|
+
byDevice = /* @__PURE__ */ new Map();
|
|
55355
|
+
/** Record that this id's birth ritual ran — `start`, firstFrame, the lot. */
|
|
55356
|
+
markStarted(deviceKey, trackId) {
|
|
55357
|
+
let ids = this.byDevice.get(deviceKey);
|
|
55358
|
+
if (!ids) {
|
|
55359
|
+
ids = /* @__PURE__ */ new Set();
|
|
55360
|
+
this.byDevice.set(deviceKey, ids);
|
|
55361
|
+
}
|
|
55362
|
+
ids.add(trackId);
|
|
55363
|
+
}
|
|
55364
|
+
hasStarted(deviceKey, trackId) {
|
|
55365
|
+
return this.byDevice.get(deviceKey)?.has(trackId) === true;
|
|
55366
|
+
}
|
|
55367
|
+
/**
|
|
55368
|
+
* Forget every started id the tracker is no longer carrying. Called once per
|
|
55369
|
+
* frame with the ids present THIS frame — an id the tracker has dropped can
|
|
55370
|
+
* never be re-offered as a birth, so holding it serves nothing.
|
|
55371
|
+
*/
|
|
55372
|
+
retain(deviceKey, currentTrackIds) {
|
|
55373
|
+
const ids = this.byDevice.get(deviceKey);
|
|
55374
|
+
if (!ids) return;
|
|
55375
|
+
for (const id of ids) if (!currentTrackIds.has(id)) ids.delete(id);
|
|
55376
|
+
if (ids.size === 0) this.byDevice.delete(deviceKey);
|
|
55377
|
+
}
|
|
55378
|
+
/** Started ids currently held for a device — diagnostics and tests. */
|
|
55379
|
+
size(deviceKey) {
|
|
55380
|
+
return this.byDevice.get(deviceKey)?.size ?? 0;
|
|
55381
|
+
}
|
|
55382
|
+
/** Drop a device's memory wholesale (device removed / pipeline reset). */
|
|
55383
|
+
clearDevice(deviceKey) {
|
|
55384
|
+
this.byDevice.delete(deviceKey);
|
|
55385
|
+
}
|
|
55386
|
+
};
|
|
55387
|
+
//#endregion
|
|
55170
55388
|
//#region src/pipeline-analytics/pipeline/suppressed-births.ts
|
|
55171
55389
|
/**
|
|
55172
55390
|
* Memory of births the confirmation gate rejected, per device.
|
|
@@ -67527,6 +67745,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
67527
67745
|
/** See `pipeline/suppressed-births.ts` — rejected births must not be
|
|
67528
67746
|
* re-upserted, and must be forgotten when the tracker drops the id. */
|
|
67529
67747
|
suppressedBirths = new SuppressedBirthRegistry();
|
|
67748
|
+
/** Ids whose birth ritual has already run — a birth is decided ONCE (D377). */
|
|
67749
|
+
startedBirths = new StartedBirthRegistry();
|
|
67530
67750
|
/** Births the gate could not MEASURE, awaiting another look on a later frame. */
|
|
67531
67751
|
deferredBirths = new DeferredBirthRegistry();
|
|
67532
67752
|
/**
|
|
@@ -70745,10 +70965,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70745
70965
|
if (prevIds.has(id)) continue;
|
|
70746
70966
|
const t = result.tracked.find((x) => x.trackId === id);
|
|
70747
70967
|
if (!t) continue;
|
|
70748
|
-
|
|
70968
|
+
const appearance = classifyTrackAppearance({
|
|
70749
70969
|
inPrevActive: false,
|
|
70750
70970
|
positionsCount: positionsCountById.get(id) ?? 1
|
|
70751
|
-
})
|
|
70971
|
+
});
|
|
70972
|
+
if (this.startedBirths.hasStarted(key, id)) continue;
|
|
70973
|
+
if (appearance === "resurrection") {
|
|
70752
70974
|
log.info("track resumed", {
|
|
70753
70975
|
tags: { deviceId },
|
|
70754
70976
|
meta: {
|
|
@@ -70784,7 +71006,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70784
71006
|
const outcome = await this.confirmTrackBirths(bornCandidates.map(({ id, t }) => ({
|
|
70785
71007
|
trackId: id,
|
|
70786
71008
|
className: t.className,
|
|
70787
|
-
bbox: t.bbox
|
|
71009
|
+
bbox: t.bbox,
|
|
71010
|
+
...t.nativeRecovery !== void 0 ? { nativeRecovery: t.nativeRecovery } : {}
|
|
70788
71011
|
})), {
|
|
70789
71012
|
deviceId,
|
|
70790
71013
|
source,
|
|
@@ -70809,6 +71032,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70809
71032
|
const deferredForMs = this.deferredBirths.elapsedMs(key, id, gateNowMs);
|
|
70810
71033
|
const deferredAttempts = this.deferredBirths.attemptsFor(key, id);
|
|
70811
71034
|
this.deferredBirths.resolve(key, id);
|
|
71035
|
+
if (this.startedBirths.hasStarted(key, id)) {
|
|
71036
|
+
this.deferredBirths.resolve(key, id);
|
|
71037
|
+
log.info("late birth verdict ignored — the track was already born", { meta: {
|
|
71038
|
+
trackId: id,
|
|
71039
|
+
className: t.className,
|
|
71040
|
+
source,
|
|
71041
|
+
verdict: outcome.confirmed.has(id) ? "confirmed" : "suppressed"
|
|
71042
|
+
} });
|
|
71043
|
+
continue;
|
|
71044
|
+
}
|
|
70812
71045
|
if (!outcome.confirmed.has(id)) {
|
|
70813
71046
|
this.suppressedBirths.reject(key, id);
|
|
70814
71047
|
this.trackStore?.dropActive(id);
|
|
@@ -70846,6 +71079,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70846
71079
|
})));
|
|
70847
71080
|
this.failureReport.notePersonBirth(deviceId, overlap === null ? void 0 : REASON_PERSON_OVER_VEHICLE);
|
|
70848
71081
|
}
|
|
71082
|
+
this.startedBirths.markStarted(key, id);
|
|
70849
71083
|
log.info("track started", { meta: {
|
|
70850
71084
|
trackId: id,
|
|
70851
71085
|
className: t.className,
|
|
@@ -70913,6 +71147,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
70913
71147
|
});
|
|
70914
71148
|
}
|
|
70915
71149
|
this.suppressedBirths.retain(key, currentTrackIds);
|
|
71150
|
+
this.startedBirths.retain(key, currentTrackIds);
|
|
70916
71151
|
this.deferredBirths.retain(key, currentTrackIds);
|
|
70917
71152
|
this.lastActiveTrackIds.set(key, currentTrackIds);
|
|
70918
71153
|
if (currentTrackIds.size > 0) this.lastTrackActivityMs.set(deviceId, Date.now());
|
|
@@ -71492,6 +71727,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
71492
71727
|
return allConfirmed();
|
|
71493
71728
|
}
|
|
71494
71729
|
const nodeId = frameHandle.nodeId;
|
|
71730
|
+
const candidateNativeRecovery = /* @__PURE__ */ new Map();
|
|
71731
|
+
for (const c of candidates) if (c.nativeRecovery !== void 0) candidateNativeRecovery.set(c.trackId, c.nativeRecovery);
|
|
71495
71732
|
return confirmBirths(candidates, config, {
|
|
71496
71733
|
fetchCrop: (candidate) => this.captureScheduler.request({
|
|
71497
71734
|
deviceId,
|
|
@@ -71541,6 +71778,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
71541
71778
|
bestIncompatibleScore: decision.bestIncompatibleScore
|
|
71542
71779
|
} : {},
|
|
71543
71780
|
minConfidence: config.minConfidence,
|
|
71781
|
+
...decision.reason === "native-pass" ? {
|
|
71782
|
+
nativeRecoveryCropSidePx: candidateNativeRecovery.get(decision.trackId)?.cropSidePx,
|
|
71783
|
+
nativeRecoveryViewPx: candidateNativeRecovery.get(decision.trackId)?.viewPx
|
|
71784
|
+
} : {},
|
|
71544
71785
|
...decision.appliedMinConfidence !== void 0 && decision.appliedMinConfidence > config.minConfidence ? {
|
|
71545
71786
|
appliedMinConfidence: decision.appliedMinConfidence,
|
|
71546
71787
|
barRaisedByPhantomCell: true
|
|
@@ -73802,6 +74043,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
|
|
|
73802
74043
|
}
|
|
73803
74044
|
});
|
|
73804
74045
|
});
|
|
74046
|
+
p.setSpawnRefusalObserver((record) => {
|
|
74047
|
+
this.ctx.logger.info("tracker: detection dropped — no track was born from it", {
|
|
74048
|
+
tags: { deviceId },
|
|
74049
|
+
meta: {
|
|
74050
|
+
source,
|
|
74051
|
+
...record
|
|
74052
|
+
}
|
|
74053
|
+
});
|
|
74054
|
+
});
|
|
73805
74055
|
p.setTrackLabelState((trackId) => {
|
|
73806
74056
|
const active = this.trackStore?.getActiveByTrack(trackId);
|
|
73807
74057
|
return active !== null && active !== void 0 ? {
|
|
@@ -33992,6 +33992,18 @@ var LooseBindSampler = class {
|
|
|
33992
33992
|
this.counts.clear();
|
|
33993
33993
|
}
|
|
33994
33994
|
};
|
|
33995
|
+
/** Per-reason counter + sampling decision. One instance per tracker, so the
|
|
33996
|
+
* counts are per camera and per detection source. */
|
|
33997
|
+
var SpawnRefusalSampler = class {
|
|
33998
|
+
counts = /* @__PURE__ */ new Map();
|
|
33999
|
+
/** The running count when this refusal should be reported, else `undefined`. */
|
|
34000
|
+
next(reason) {
|
|
34001
|
+
const count = (this.counts.get(reason) ?? 0) + 1;
|
|
34002
|
+
this.counts.set(reason, count);
|
|
34003
|
+
if (count <= 10) return count;
|
|
34004
|
+
return count % 10 === 0 ? count : void 0;
|
|
34005
|
+
}
|
|
34006
|
+
};
|
|
33995
34007
|
//#endregion
|
|
33996
34008
|
//#region src/pipeline-analytics/pipeline/tracker/sort-tracker.ts
|
|
33997
34009
|
var DEFAULT_TRACKER_CONFIG = {
|
|
@@ -34076,6 +34088,10 @@ var SortTracker = class SortTracker {
|
|
|
34076
34088
|
/** Sink for salvage-bind diagnostics. Null until an owner that knows the
|
|
34077
34089
|
* `deviceId` wires one — see {@link setBindObserver}. */
|
|
34078
34090
|
bindObserver = null;
|
|
34091
|
+
/** Per-reason counters behind the sampled dropped-detection diagnostics. */
|
|
34092
|
+
refusalSampler = new SpawnRefusalSampler();
|
|
34093
|
+
/** Sink for dropped-detection diagnostics — see {@link setSpawnRefusalObserver}. */
|
|
34094
|
+
spawnRefusalObserver = null;
|
|
34079
34095
|
constructor(config = {}) {
|
|
34080
34096
|
this.config = {
|
|
34081
34097
|
...DEFAULT_TRACKER_CONFIG,
|
|
@@ -34091,6 +34107,37 @@ var SortTracker = class SortTracker {
|
|
|
34091
34107
|
setBindObserver(observer) {
|
|
34092
34108
|
this.bindObserver = observer;
|
|
34093
34109
|
}
|
|
34110
|
+
/**
|
|
34111
|
+
* Wire the diagnostics sink for detections the tracker DROPS — a spawn a
|
|
34112
|
+
* gate refused, or a box a track of another class absorbed. Same contract as
|
|
34113
|
+
* {@link setBindObserver}: the tracker has no `deviceId`, the caller stamps
|
|
34114
|
+
* it. Nothing is computed when no observer is wired.
|
|
34115
|
+
*/
|
|
34116
|
+
setSpawnRefusalObserver(observer) {
|
|
34117
|
+
this.spawnRefusalObserver = observer;
|
|
34118
|
+
}
|
|
34119
|
+
/** Emit one dropped-detection record, subject to the refusal sampler. */
|
|
34120
|
+
reportRefusal(reason, det, extra = {}) {
|
|
34121
|
+
const observer = this.spawnRefusalObserver;
|
|
34122
|
+
if (!observer) return;
|
|
34123
|
+
const refusalCount = this.refusalSampler.next(reason);
|
|
34124
|
+
if (refusalCount === void 0) return;
|
|
34125
|
+
observer({
|
|
34126
|
+
reason,
|
|
34127
|
+
detClass: det.class,
|
|
34128
|
+
detScore: det.score,
|
|
34129
|
+
bbox: det.bbox,
|
|
34130
|
+
...det.nativeRecovery !== void 0 ? { nativeRecoveryScore: det.nativeRecovery.score } : {},
|
|
34131
|
+
...extra.threshold !== void 0 ? { threshold: extra.threshold } : {},
|
|
34132
|
+
...extra.measured !== void 0 ? { measured: extra.measured } : {},
|
|
34133
|
+
...extra.against !== void 0 ? {
|
|
34134
|
+
againstTrackId: extra.against.id,
|
|
34135
|
+
againstTrackClass: extra.against.class
|
|
34136
|
+
} : {},
|
|
34137
|
+
refusalCount,
|
|
34138
|
+
sampledEvery: 10
|
|
34139
|
+
});
|
|
34140
|
+
}
|
|
34094
34141
|
/** Emit one salvage decision, subject to {@link LOOSE_BIND_LOG_SAMPLE_EVERY}.
|
|
34095
34142
|
* Nothing is computed when no observer is wired. */
|
|
34096
34143
|
reportBind(stage, gate, track, det, timestamp) {
|
|
@@ -34150,9 +34197,49 @@ var SortTracker = class SortTracker {
|
|
|
34150
34197
|
* exact class when `classGating` is off (no gate at all) — matching the legacy
|
|
34151
34198
|
* contract — otherwise same association group (group == exact class unless
|
|
34152
34199
|
* `classGroupAssoc` widened it to {person,animal}). */
|
|
34153
|
-
associable(trackClass,
|
|
34200
|
+
associable(trackClass, det) {
|
|
34201
|
+
if (!this.sameGroupAsMeasured(trackClass, det)) return false;
|
|
34154
34202
|
if (!this.config.classGating) return true;
|
|
34155
|
-
return this.assocGroup(trackClass) === this.assocGroup(
|
|
34203
|
+
return this.assocGroup(trackClass) === this.assocGroup(det.class);
|
|
34204
|
+
}
|
|
34205
|
+
/**
|
|
34206
|
+
* A NATIVE-MEASURED BOX IS ITS OWN SUBJECT ACROSS CLASSES (D378).
|
|
34207
|
+
*
|
|
34208
|
+
* `classGroupAssoc` collapses {person, animal} into one association group for
|
|
34209
|
+
* one failure of the COARSE full-frame pass: a bending person re-labelled
|
|
34210
|
+
* `animal` for a frame, over the person's own box. Letting that box re-match
|
|
34211
|
+
* the person track is strictly better than minting a concurrent id.
|
|
34212
|
+
*
|
|
34213
|
+
* The runner's second pass makes the opposite measurement — the same detector
|
|
34214
|
+
* on a 640-800 px native square around a motion region the full-frame boxes
|
|
34215
|
+
* do NOT account for. Such a box is, by construction, not the subject the
|
|
34216
|
+
* full pass already has; absorbing it into a person track means the animal is
|
|
34217
|
+
* never seen at all, which on 2026-09-07 07:51:33 is exactly what happened to
|
|
34218
|
+
* an `animal` at `nativeRecoveryScore=0.833`.
|
|
34219
|
+
*
|
|
34220
|
+
* So the widening does not apply to a detection carrying `nativeRecovery`:
|
|
34221
|
+
* it associates to its OWN class or it spawns. Every other guard on that
|
|
34222
|
+
* spawn still runs — the per-class score floor, the area floor, the
|
|
34223
|
+
* `dedupSpawnIou` overlap, and `resolveDuplicates` afterwards, where a person
|
|
34224
|
+
* still beats a cross-class animal.
|
|
34225
|
+
*/
|
|
34226
|
+
sameGroupAsMeasured(trackClass, det) {
|
|
34227
|
+
if (det.nativeRecovery === void 0) return true;
|
|
34228
|
+
return trackClass === det.class;
|
|
34229
|
+
}
|
|
34230
|
+
/**
|
|
34231
|
+
* Whether a matched track's box refuses `det` a birth. The gate exists for a
|
|
34232
|
+
* detector double-firing INSIDE a subject it already has, so it is a
|
|
34233
|
+
* same-subject claim: for a box the native pass measured (D378), a
|
|
34234
|
+
* cross-class occluder is not that claim — a dog at its owner's knee is 100%
|
|
34235
|
+
* contained in the person's box and is not the person. 2026-09-07 07:56:37,
|
|
34236
|
+
* an `animal` at `nativeRecoveryScore=0.5143` refused on `threshold=0.6`.
|
|
34237
|
+
* A SAME-class occluder still refuses it: that is the torso the crop cut out
|
|
34238
|
+
* of a person the full pass already has.
|
|
34239
|
+
*/
|
|
34240
|
+
occludesSpawn(det, occluder) {
|
|
34241
|
+
if (!this.sameGroupAsMeasured(occluder.class, det)) return false;
|
|
34242
|
+
return containment(det.bbox, occluder.bbox) >= this.config.occlusionContainment;
|
|
34156
34243
|
}
|
|
34157
34244
|
/** Whether a spawn candidate clears its per-class minimum bbox-area floor
|
|
34158
34245
|
* (fraction of frame area). Ungated when disabled, when the class has no floor,
|
|
@@ -34174,14 +34261,30 @@ var SortTracker = class SortTracker {
|
|
|
34174
34261
|
isDuplicateSpawn(det, live, timestamp) {
|
|
34175
34262
|
for (const t of live) {
|
|
34176
34263
|
if (!this.dedupCompatible(t.class, det.class)) continue;
|
|
34177
|
-
|
|
34264
|
+
const overlap = iou$3(det.bbox, t.bbox);
|
|
34265
|
+
if (overlap >= this.config.dedupSpawnIou) {
|
|
34266
|
+
this.reportRefusal("dedup-iou", det, {
|
|
34267
|
+
threshold: this.config.dedupSpawnIou,
|
|
34268
|
+
measured: overlap,
|
|
34269
|
+
against: t
|
|
34270
|
+
});
|
|
34271
|
+
return true;
|
|
34272
|
+
}
|
|
34178
34273
|
if (t.class === det.class && growthAdjacentMatch(t.bbox, det.bbox, SortTracker.growthContext(t, timestamp))) {
|
|
34179
34274
|
this.reportBind("spawn-dedup", "growth", t, det, timestamp);
|
|
34180
34275
|
return true;
|
|
34181
34276
|
}
|
|
34182
34277
|
}
|
|
34183
|
-
if (this.config.personAnimalDedup && det.class === ANIMAL_CLASS && det.score < this.config.animalOverPersonMaxScore) {
|
|
34184
|
-
|
|
34278
|
+
if (this.config.personAnimalDedup && det.class === ANIMAL_CLASS && det.nativeRecovery === void 0 && det.score < this.config.animalOverPersonMaxScore) for (const t of live) {
|
|
34279
|
+
const overlap = iou$3(det.bbox, t.bbox);
|
|
34280
|
+
if (t.class === PERSON_CLASS && overlap >= this.config.animalOverPersonIou) {
|
|
34281
|
+
this.reportRefusal("animal-over-person", det, {
|
|
34282
|
+
threshold: this.config.animalOverPersonMaxScore,
|
|
34283
|
+
measured: overlap,
|
|
34284
|
+
against: t
|
|
34285
|
+
});
|
|
34286
|
+
return true;
|
|
34287
|
+
}
|
|
34185
34288
|
}
|
|
34186
34289
|
return false;
|
|
34187
34290
|
}
|
|
@@ -34296,6 +34399,7 @@ var SortTracker = class SortTracker {
|
|
|
34296
34399
|
* class.
|
|
34297
34400
|
*/
|
|
34298
34401
|
looseMatch(track, det, timestamp) {
|
|
34402
|
+
if (!this.sameGroupAsMeasured(track.class, det)) return null;
|
|
34299
34403
|
if (this.assocGroup(track.class) !== this.assocGroup(det.class)) return null;
|
|
34300
34404
|
if (iou$3(track.bbox, det.bbox) >= this.config.rescueIouThreshold) return "iou";
|
|
34301
34405
|
const tc = bboxCentroid(track.bbox);
|
|
@@ -34382,7 +34486,7 @@ var SortTracker = class SortTracker {
|
|
|
34382
34486
|
for (const di of candidateIdxs) {
|
|
34383
34487
|
if (used.has(di)) continue;
|
|
34384
34488
|
const det = detections[di];
|
|
34385
|
-
if (!this.associable(track.class, det
|
|
34489
|
+
if (!this.associable(track.class, det)) continue;
|
|
34386
34490
|
const score = iou$3(pbox, det.bbox);
|
|
34387
34491
|
if (score >= iouThreshold) pairs.push({
|
|
34388
34492
|
track,
|
|
@@ -34461,6 +34565,7 @@ var SortTracker = class SortTracker {
|
|
|
34461
34565
|
}
|
|
34462
34566
|
if (this.config.byteTrackEnabled && lowIdxs.length > 0) this.greedyAssociate(detections, lowIdxs, this.config.byteTrackLowIouThreshold, used, matchedTracks, matched);
|
|
34463
34567
|
for (const [track, det] of matched) {
|
|
34568
|
+
if (track.class !== det.class) this.reportRefusal("cross-class-absorbed", det, { against: track });
|
|
34464
34569
|
const prevCenter = bboxCentroid({
|
|
34465
34570
|
x: track.bbox.x,
|
|
34466
34571
|
y: track.bbox.y,
|
|
@@ -34489,15 +34594,18 @@ var SortTracker = class SortTracker {
|
|
|
34489
34594
|
if (track.path.length > MAX_PATH_LENGTH) track.path.shift();
|
|
34490
34595
|
this.addClassVote(track, det);
|
|
34491
34596
|
}
|
|
34492
|
-
const
|
|
34493
|
-
for (const track of matchedTracks)
|
|
34597
|
+
const occluders = [];
|
|
34598
|
+
for (const track of matchedTracks) occluders.push({
|
|
34599
|
+
bbox: track.bbox,
|
|
34600
|
+
class: track.class
|
|
34601
|
+
});
|
|
34494
34602
|
const surviving = [];
|
|
34495
34603
|
for (const track of this.tracks) {
|
|
34496
34604
|
if (matchedTracks.has(track)) {
|
|
34497
34605
|
surviving.push(track);
|
|
34498
34606
|
continue;
|
|
34499
34607
|
}
|
|
34500
|
-
const occluded = this.config.occlusionEnabled &&
|
|
34608
|
+
const occluded = this.config.occlusionEnabled && occluders.some((ob) => containment(this.predicted(track), ob.bbox) >= this.config.occlusionContainment);
|
|
34501
34609
|
track.age++;
|
|
34502
34610
|
if (timestamp - track.lastSeen > (occluded ? this.config.occlusionMaxMissedMs : this.config.maxMissedMs)) {
|
|
34503
34611
|
track.lost = true;
|
|
@@ -34576,14 +34684,25 @@ var SortTracker = class SortTracker {
|
|
|
34576
34684
|
for (const di of unmatchedIdx) {
|
|
34577
34685
|
const det = detections[di];
|
|
34578
34686
|
if (!this.meetsClassScoreFloor(det)) {
|
|
34687
|
+
this.reportRefusal("score-floor", det, {
|
|
34688
|
+
threshold: this.config.classMinScores[det.class] ?? 0,
|
|
34689
|
+
measured: det.score
|
|
34690
|
+
});
|
|
34579
34691
|
used.add(di);
|
|
34580
34692
|
continue;
|
|
34581
34693
|
}
|
|
34582
34694
|
if (!this.meetsSpawnAreaFloor(det, frameArea)) {
|
|
34695
|
+
this.reportRefusal("area-floor", det, {
|
|
34696
|
+
threshold: this.config.classSpawnAreaFracs[det.class] ?? 0,
|
|
34697
|
+
measured: frameArea > 0 ? det.bbox.w * det.bbox.h / frameArea : 0
|
|
34698
|
+
});
|
|
34583
34699
|
used.add(di);
|
|
34584
34700
|
continue;
|
|
34585
34701
|
}
|
|
34586
|
-
if (this.config.occlusionEnabled &&
|
|
34702
|
+
if (this.config.occlusionEnabled && occluders.some((ob) => this.occludesSpawn(det, ob))) {
|
|
34703
|
+
this.reportRefusal("occlusion", det, { threshold: this.config.occlusionContainment });
|
|
34704
|
+
continue;
|
|
34705
|
+
}
|
|
34587
34706
|
if (this.config.dedupEnabled && this.isDuplicateSpawn(det, surviving, timestamp)) {
|
|
34588
34707
|
used.add(di);
|
|
34589
34708
|
continue;
|
|
@@ -35071,6 +35190,9 @@ var FrameProcessor = class {
|
|
|
35071
35190
|
* Only the rescue / resurrection / growth branches report, and they report
|
|
35072
35191
|
* sampled: stage 1 runs per track per frame at frame-rate.
|
|
35073
35192
|
*/
|
|
35193
|
+
setSpawnRefusalObserver(observer) {
|
|
35194
|
+
this.tracker.setSpawnRefusalObserver(observer);
|
|
35195
|
+
}
|
|
35074
35196
|
setBindObserver(observer) {
|
|
35075
35197
|
this.tracker.setBindObserver(observer);
|
|
35076
35198
|
}
|
|
@@ -35093,6 +35215,7 @@ var FrameProcessor = class {
|
|
|
35093
35215
|
const faceAlignedCropByBbox = /* @__PURE__ */ new Map();
|
|
35094
35216
|
const plateByBbox = /* @__PURE__ */ new Map();
|
|
35095
35217
|
const maskByBbox = /* @__PURE__ */ new Map();
|
|
35218
|
+
const nativeRecoveryByBbox = /* @__PURE__ */ new Map();
|
|
35096
35219
|
const flatDetections = frame.detections.filter((d) => d.kind === "first-level").map((det) => {
|
|
35097
35220
|
const bbox = {
|
|
35098
35221
|
x: det.bbox.x,
|
|
@@ -35104,8 +35227,10 @@ var FrameProcessor = class {
|
|
|
35104
35227
|
class: det.macroClass,
|
|
35105
35228
|
originalClass: det.debug?.originalClass ?? det.macroClass,
|
|
35106
35229
|
score: det.score,
|
|
35107
|
-
bbox
|
|
35230
|
+
bbox,
|
|
35231
|
+
...det.nativeRecovery !== void 0 ? { nativeRecovery: det.nativeRecovery } : {}
|
|
35108
35232
|
};
|
|
35233
|
+
if (det.nativeRecovery !== void 0) nativeRecoveryByBbox.set(bbox, det.nativeRecovery);
|
|
35109
35234
|
if (det.labels.length > 0) labelsByBbox.set(bbox, det.labels);
|
|
35110
35235
|
if (det.debug?.originalClassTier !== void 0) originalClassTierByBbox.set(bbox, det.debug.originalClassTier);
|
|
35111
35236
|
if (det.embedding !== void 0) embeddingByBbox.set(bbox, {
|
|
@@ -35330,6 +35455,7 @@ var FrameProcessor = class {
|
|
|
35330
35455
|
const faceAlignedCrop = faceAlignedCropByBbox.get(td.bbox);
|
|
35331
35456
|
const plate = plateByBbox.get(td.bbox);
|
|
35332
35457
|
const sourceDetectionId = sourceIdByBbox.get(td.bbox);
|
|
35458
|
+
const nativeRecovery = td.matchedThisFrame !== false ? nativeRecoveryByBbox.get(td.bbox) : void 0;
|
|
35333
35459
|
return {
|
|
35334
35460
|
trackId: td.trackId,
|
|
35335
35461
|
...sourceDetectionId !== void 0 ? { sourceDetectionId } : {},
|
|
@@ -35352,7 +35478,8 @@ var FrameProcessor = class {
|
|
|
35352
35478
|
plateText: plate.text,
|
|
35353
35479
|
plateScore: plate.score,
|
|
35354
35480
|
plateBbox: plate.bbox
|
|
35355
|
-
} : {}
|
|
35481
|
+
} : {},
|
|
35482
|
+
...nativeRecovery !== void 0 ? { nativeRecovery } : {}
|
|
35356
35483
|
};
|
|
35357
35484
|
});
|
|
35358
35485
|
this.lastLabelWriteRefusals = refusals;
|
|
@@ -45164,7 +45291,7 @@ async function runConfirmation(candidate, config, deps, exhausted) {
|
|
|
45164
45291
|
for (const d of detections) if (isConfirmationCompatible(candidate.className, d.macroClass)) {
|
|
45165
45292
|
if (!best || d.score > best.score) best = d;
|
|
45166
45293
|
} else if (!bestIncompatible || d.score > bestIncompatible.score) bestIncompatible = d;
|
|
45167
|
-
const bar =
|
|
45294
|
+
const bar = barFor(candidate, config, deps);
|
|
45168
45295
|
const confirmed = best !== void 0 && best.score >= bar;
|
|
45169
45296
|
const secondary = bestIncompatible ? promoteSecondary(candidate, bestIncompatible, bar, deps) : null;
|
|
45170
45297
|
return {
|
|
@@ -45203,7 +45330,27 @@ function promoteSecondary(candidate, detection, bar, deps) {
|
|
|
45203
45330
|
}
|
|
45204
45331
|
};
|
|
45205
45332
|
}
|
|
45333
|
+
/**
|
|
45334
|
+
* The bar this candidate faces. Never below the configured one: the
|
|
45335
|
+
* `minConfidenceFor` hook is monotonic by construction, and clamping here says
|
|
45336
|
+
* so at the point of use rather than trusting the caller to have been careful.
|
|
45337
|
+
*/
|
|
45338
|
+
function barFor(candidate, config, deps) {
|
|
45339
|
+
return Math.max(config.minConfidence, deps.minConfidenceFor?.(candidate) ?? 0);
|
|
45340
|
+
}
|
|
45206
45341
|
async function confirmOne(candidate, config, deps, exhausted) {
|
|
45342
|
+
const native = candidate.nativeRecovery;
|
|
45343
|
+
if (native !== void 0) {
|
|
45344
|
+
const bar = barFor(candidate, config, deps);
|
|
45345
|
+
if (native.score >= bar) return {
|
|
45346
|
+
trackId: candidate.trackId,
|
|
45347
|
+
verdict: "confirmed",
|
|
45348
|
+
reason: "native-pass",
|
|
45349
|
+
className: candidate.className,
|
|
45350
|
+
bestScore: native.score,
|
|
45351
|
+
appliedMinConfidence: bar
|
|
45352
|
+
};
|
|
45353
|
+
}
|
|
45207
45354
|
const cropPx = Math.max(candidate.bbox.w, candidate.bbox.h);
|
|
45208
45355
|
if (config.minCropPx > 0 && cropPx < config.minCropPx) return undecided(candidate, "below-min-crop");
|
|
45209
45356
|
try {
|
|
@@ -55093,6 +55240,77 @@ function rowToEntry(id, data) {
|
|
|
55093
55240
|
};
|
|
55094
55241
|
}
|
|
55095
55242
|
//#endregion
|
|
55243
|
+
//#region src/pipeline-analytics/pipeline/started-births.ts
|
|
55244
|
+
/**
|
|
55245
|
+
* Memory of births whose ritual has already RUN, per device+source.
|
|
55246
|
+
*
|
|
55247
|
+
* A birth is a decision, and a decision is taken once. Until 2026-09-07 it
|
|
55248
|
+
* could be taken twice, on camera 615 it routinely was, and the second vote
|
|
55249
|
+
* won:
|
|
55250
|
+
*
|
|
55251
|
+
* 04:59:38.335Z confirmation gate: birth confirmed {trackId=f585b2ee…
|
|
55252
|
+
* className=animal bestScore=0.7068}
|
|
55253
|
+
* 04:59:38.335Z track started {trackId=f585b2ee…}
|
|
55254
|
+
* 04:59:38.821Z confirmation gate: birth suppressed (false positive)
|
|
55255
|
+
* {trackId=f585b2ee… className=animal} ← no bestScore:
|
|
55256
|
+
* the second crop held no compatible detection AT ALL
|
|
55257
|
+
* 04:59:38.821Z birth suppressed — track record retracted
|
|
55258
|
+
*
|
|
55259
|
+
* TWO conditions have to meet, and both are structural:
|
|
55260
|
+
*
|
|
55261
|
+
* 1. `processFrame` reads `lastActiveTrackIds` at the top and writes it at the
|
|
55262
|
+
* bottom, and AWAITS the confirmation gate in between (a crop round trip to
|
|
55263
|
+
* the frame's node plus a re-detection: 130–490 ms measured on this camera,
|
|
55264
|
+
* against 100 ms frame spacing). Several frames are therefore in flight at
|
|
55265
|
+
* once and each of them sees the same fresh id as unjudged.
|
|
55266
|
+
* 2. The other birth discriminator, `positionsCount > 1`, does not close the
|
|
55267
|
+
* gap either: a COASTED frame deliberately appends no position
|
|
55268
|
+
* (`TrackStore.upsert`, `matched: false`), so a track the full-frame pass
|
|
55269
|
+
* cannot see — which is the entire premise of the small-subject recovery —
|
|
55270
|
+
* keeps exactly one position for as long as it coasts, and
|
|
55271
|
+
* `classifyTrackAppearance` keeps answering `birth`.
|
|
55272
|
+
*
|
|
55273
|
+
* So the id that has already been born is remembered HERE, and both halves of
|
|
55274
|
+
* the birth path consult it: it is not offered to the gate again, and a verdict
|
|
55275
|
+
* that arrives for it anyway cannot retract it. Same lifetime discipline as
|
|
55276
|
+
* {@link SuppressedBirthRegistry} — held while the tracker still carries the
|
|
55277
|
+
* id, forgotten the frame it stops.
|
|
55278
|
+
*/
|
|
55279
|
+
var StartedBirthRegistry = class {
|
|
55280
|
+
byDevice = /* @__PURE__ */ new Map();
|
|
55281
|
+
/** Record that this id's birth ritual ran — `start`, firstFrame, the lot. */
|
|
55282
|
+
markStarted(deviceKey, trackId) {
|
|
55283
|
+
let ids = this.byDevice.get(deviceKey);
|
|
55284
|
+
if (!ids) {
|
|
55285
|
+
ids = /* @__PURE__ */ new Set();
|
|
55286
|
+
this.byDevice.set(deviceKey, ids);
|
|
55287
|
+
}
|
|
55288
|
+
ids.add(trackId);
|
|
55289
|
+
}
|
|
55290
|
+
hasStarted(deviceKey, trackId) {
|
|
55291
|
+
return this.byDevice.get(deviceKey)?.has(trackId) === true;
|
|
55292
|
+
}
|
|
55293
|
+
/**
|
|
55294
|
+
* Forget every started id the tracker is no longer carrying. Called once per
|
|
55295
|
+
* frame with the ids present THIS frame — an id the tracker has dropped can
|
|
55296
|
+
* never be re-offered as a birth, so holding it serves nothing.
|
|
55297
|
+
*/
|
|
55298
|
+
retain(deviceKey, currentTrackIds) {
|
|
55299
|
+
const ids = this.byDevice.get(deviceKey);
|
|
55300
|
+
if (!ids) return;
|
|
55301
|
+
for (const id of ids) if (!currentTrackIds.has(id)) ids.delete(id);
|
|
55302
|
+
if (ids.size === 0) this.byDevice.delete(deviceKey);
|
|
55303
|
+
}
|
|
55304
|
+
/** Started ids currently held for a device — diagnostics and tests. */
|
|
55305
|
+
size(deviceKey) {
|
|
55306
|
+
return this.byDevice.get(deviceKey)?.size ?? 0;
|
|
55307
|
+
}
|
|
55308
|
+
/** Drop a device's memory wholesale (device removed / pipeline reset). */
|
|
55309
|
+
clearDevice(deviceKey) {
|
|
55310
|
+
this.byDevice.delete(deviceKey);
|
|
55311
|
+
}
|
|
55312
|
+
};
|
|
55313
|
+
//#endregion
|
|
55096
55314
|
//#region src/pipeline-analytics/pipeline/suppressed-births.ts
|
|
55097
55315
|
/**
|
|
55098
55316
|
* Memory of births the confirmation gate rejected, per device.
|
|
@@ -67453,6 +67671,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
67453
67671
|
/** See `pipeline/suppressed-births.ts` — rejected births must not be
|
|
67454
67672
|
* re-upserted, and must be forgotten when the tracker drops the id. */
|
|
67455
67673
|
suppressedBirths = new SuppressedBirthRegistry();
|
|
67674
|
+
/** Ids whose birth ritual has already run — a birth is decided ONCE (D377). */
|
|
67675
|
+
startedBirths = new StartedBirthRegistry();
|
|
67456
67676
|
/** Births the gate could not MEASURE, awaiting another look on a later frame. */
|
|
67457
67677
|
deferredBirths = new DeferredBirthRegistry();
|
|
67458
67678
|
/**
|
|
@@ -70671,10 +70891,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70671
70891
|
if (prevIds.has(id)) continue;
|
|
70672
70892
|
const t = result.tracked.find((x) => x.trackId === id);
|
|
70673
70893
|
if (!t) continue;
|
|
70674
|
-
|
|
70894
|
+
const appearance = classifyTrackAppearance({
|
|
70675
70895
|
inPrevActive: false,
|
|
70676
70896
|
positionsCount: positionsCountById.get(id) ?? 1
|
|
70677
|
-
})
|
|
70897
|
+
});
|
|
70898
|
+
if (this.startedBirths.hasStarted(key, id)) continue;
|
|
70899
|
+
if (appearance === "resurrection") {
|
|
70678
70900
|
log.info("track resumed", {
|
|
70679
70901
|
tags: { deviceId },
|
|
70680
70902
|
meta: {
|
|
@@ -70710,7 +70932,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70710
70932
|
const outcome = await this.confirmTrackBirths(bornCandidates.map(({ id, t }) => ({
|
|
70711
70933
|
trackId: id,
|
|
70712
70934
|
className: t.className,
|
|
70713
|
-
bbox: t.bbox
|
|
70935
|
+
bbox: t.bbox,
|
|
70936
|
+
...t.nativeRecovery !== void 0 ? { nativeRecovery: t.nativeRecovery } : {}
|
|
70714
70937
|
})), {
|
|
70715
70938
|
deviceId,
|
|
70716
70939
|
source,
|
|
@@ -70735,6 +70958,16 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70735
70958
|
const deferredForMs = this.deferredBirths.elapsedMs(key, id, gateNowMs);
|
|
70736
70959
|
const deferredAttempts = this.deferredBirths.attemptsFor(key, id);
|
|
70737
70960
|
this.deferredBirths.resolve(key, id);
|
|
70961
|
+
if (this.startedBirths.hasStarted(key, id)) {
|
|
70962
|
+
this.deferredBirths.resolve(key, id);
|
|
70963
|
+
log.info("late birth verdict ignored — the track was already born", { meta: {
|
|
70964
|
+
trackId: id,
|
|
70965
|
+
className: t.className,
|
|
70966
|
+
source,
|
|
70967
|
+
verdict: outcome.confirmed.has(id) ? "confirmed" : "suppressed"
|
|
70968
|
+
} });
|
|
70969
|
+
continue;
|
|
70970
|
+
}
|
|
70738
70971
|
if (!outcome.confirmed.has(id)) {
|
|
70739
70972
|
this.suppressedBirths.reject(key, id);
|
|
70740
70973
|
this.trackStore?.dropActive(id);
|
|
@@ -70772,6 +71005,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70772
71005
|
})));
|
|
70773
71006
|
this.failureReport.notePersonBirth(deviceId, overlap === null ? void 0 : REASON_PERSON_OVER_VEHICLE);
|
|
70774
71007
|
}
|
|
71008
|
+
this.startedBirths.markStarted(key, id);
|
|
70775
71009
|
log.info("track started", { meta: {
|
|
70776
71010
|
trackId: id,
|
|
70777
71011
|
className: t.className,
|
|
@@ -70839,6 +71073,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
70839
71073
|
});
|
|
70840
71074
|
}
|
|
70841
71075
|
this.suppressedBirths.retain(key, currentTrackIds);
|
|
71076
|
+
this.startedBirths.retain(key, currentTrackIds);
|
|
70842
71077
|
this.deferredBirths.retain(key, currentTrackIds);
|
|
70843
71078
|
this.lastActiveTrackIds.set(key, currentTrackIds);
|
|
70844
71079
|
if (currentTrackIds.size > 0) this.lastTrackActivityMs.set(deviceId, Date.now());
|
|
@@ -71418,6 +71653,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
71418
71653
|
return allConfirmed();
|
|
71419
71654
|
}
|
|
71420
71655
|
const nodeId = frameHandle.nodeId;
|
|
71656
|
+
const candidateNativeRecovery = /* @__PURE__ */ new Map();
|
|
71657
|
+
for (const c of candidates) if (c.nativeRecovery !== void 0) candidateNativeRecovery.set(c.trackId, c.nativeRecovery);
|
|
71421
71658
|
return confirmBirths(candidates, config, {
|
|
71422
71659
|
fetchCrop: (candidate) => this.captureScheduler.request({
|
|
71423
71660
|
deviceId,
|
|
@@ -71467,6 +71704,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
71467
71704
|
bestIncompatibleScore: decision.bestIncompatibleScore
|
|
71468
71705
|
} : {},
|
|
71469
71706
|
minConfidence: config.minConfidence,
|
|
71707
|
+
...decision.reason === "native-pass" ? {
|
|
71708
|
+
nativeRecoveryCropSidePx: candidateNativeRecovery.get(decision.trackId)?.cropSidePx,
|
|
71709
|
+
nativeRecoveryViewPx: candidateNativeRecovery.get(decision.trackId)?.viewPx
|
|
71710
|
+
} : {},
|
|
71470
71711
|
...decision.appliedMinConfidence !== void 0 && decision.appliedMinConfidence > config.minConfidence ? {
|
|
71471
71712
|
appliedMinConfidence: decision.appliedMinConfidence,
|
|
71472
71713
|
barRaisedByPhantomCell: true
|
|
@@ -73728,6 +73969,15 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
|
|
|
73728
73969
|
}
|
|
73729
73970
|
});
|
|
73730
73971
|
});
|
|
73972
|
+
p.setSpawnRefusalObserver((record) => {
|
|
73973
|
+
this.ctx.logger.info("tracker: detection dropped — no track was born from it", {
|
|
73974
|
+
tags: { deviceId },
|
|
73975
|
+
meta: {
|
|
73976
|
+
source,
|
|
73977
|
+
...record
|
|
73978
|
+
}
|
|
73979
|
+
});
|
|
73980
|
+
});
|
|
73731
73981
|
p.setTrackLabelState((trackId) => {
|
|
73732
73982
|
const active = this.trackStore?.getActiveByTrack(trackId);
|
|
73733
73983
|
return active !== null && active !== void 0 ? {
|
|
@@ -30,7 +30,7 @@ async function d(e) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
async function f() {
|
|
33
|
-
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-
|
|
33
|
+
return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BmbhUxPs.mjs")).catch((e) => {
|
|
34
34
|
throw l = void 0, e;
|
|
35
35
|
}), l;
|
|
36
36
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-post-analysis",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.193",
|
|
4
4
|
"description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|