@camstack/addon-pipeline 1.2.28 → 1.2.29

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.
@@ -4,7 +4,7 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
6
  const require_dist = require("../dist-DUHlZcWd.js");
7
- const require_step_definitions = require("../step-definitions-Bo1hJQQw.js");
7
+ const require_step_definitions = require("../step-definitions-Drb9cWbB.js");
8
8
  const require_node_topology_platform = require("../node-topology-platform-CFZ7F4xW.js");
9
9
  const require_model_download_service_Cp9f4dk6 = require("../model-download-service-Cp9f4dk6-fsdDExML.js");
10
10
  let node_child_process = require("node:child_process");
@@ -2704,19 +2704,25 @@ async function nativeChildCrop(provider, bbox, imageWidth, imageHeight, maxWidth
2704
2704
  * DROPS detections outright, so it sits between that and a hard cut).
2705
2705
  */
2706
2706
  var FULL_FRAME_MAX_CONFIDENCE = .7;
2707
+ /** A cut only makes sense in (0, 1); anything else is treated as OFF rather
2708
+ * than dropping every detection on a typo. */
2709
+ function isUsableHardAreaCut(ratio) {
2710
+ return Number.isFinite(ratio) && ratio > 0 && ratio < 1;
2711
+ }
2707
2712
  /**
2708
2713
  * True when `bbox` is a near-full-frame LOW-confidence phantom that should be
2709
2714
  * DROPPED from the frame's detections (overlay / tracker / events / motion-wake).
2710
2715
  *
2711
2716
  * Rule (drop ⇔ returns true):
2712
- * (areaRatio ≥ {@link FULL_FRAME_AREA_RATIO}
2713
- * OR width ≥ {@link FULL_FRAME_DIMENSION_RATIO} × frameWidth
2714
- * OR height ≥ {@link FULL_FRAME_DIMENSION_RATIO} × frameHeight)
2715
- * AND score < `maxConfidence`
2717
+ * areaRatio ≥ `hardAreaRatio` ← score-independent, OFF by default
2718
+ * OR ( (areaRatio ≥ {@link FULL_FRAME_AREA_RATIO}
2719
+ * OR width ≥ {@link FULL_FRAME_DIMENSION_RATIO} × frameWidth
2720
+ * OR height {@link FULL_FRAME_DIMENSION_RATIO} × frameHeight)
2721
+ * AND score < `maxConfidence` )
2716
2722
  *
2717
- * A high-confidence detection (`score ≥ maxConfidence`) is trusted as a real
2718
- * subject and always survives. Degenerate frame dimensions (≤0) carry no info
2719
- * to reject on → not a phantom.
2723
+ * Below the hard cut a high-confidence detection (`score ≥ maxConfidence`) is
2724
+ * trusted as a real subject and survives. Degenerate frame dimensions (≤0)
2725
+ * carry no info to reject on → not a phantom.
2720
2726
  *
2721
2727
  * @param bbox absolute-pixel `[x1, y1, x2, y2]` box in source-frame coords.
2722
2728
  * @param frameWidth source frame width in px.
@@ -2725,10 +2731,26 @@ var FULL_FRAME_MAX_CONFIDENCE = .7;
2725
2731
  * @param maxConfidence confidence bar below which a full-frame box is a phantom
2726
2732
  * (defaults to {@link FULL_FRAME_MAX_CONFIDENCE}; overridable by the caller so
2727
2733
  * the bar can be threaded from detection settings without new cap methods).
2734
+ * @param hardAreaRatio area fraction at/above which the box is dropped NO
2735
+ * MATTER the score. Defaults to {@link HARD_AREA_CUT_DISABLED} (off) — it is
2736
+ * armed per camera, because a doorbell close-up legitimately fills the frame
2737
+ * while an overhead courtyard box never does. Threaded from step settings as
2738
+ * `fullFrameGuardHardAreaRatio`.
2728
2739
  */
2729
- function isFullFramePhantomDetection(bbox, frameWidth, frameHeight, score, maxConfidence = FULL_FRAME_MAX_CONFIDENCE) {
2740
+ /**
2741
+ * True when a box trips the guard's GEOMETRY test (near-full-frame, or
2742
+ * edge-to-edge in one axis) but SURVIVES because its score cleared the
2743
+ * confidence bar.
2744
+ *
2745
+ * These are the ones worth counting: on 2026-07-30 device 615 produced six
2746
+ * such boxes in a day, at 0.710-0.787, and they were only found by an offline
2747
+ * sweep of stored events — nothing in production reported them. Arming the
2748
+ * hard-area cut on a camera should be a decision made from this count, not
2749
+ * from one incident.
2750
+ */
2751
+ function isFullFrameSurvivor(bbox, frameWidth, frameHeight, score, maxConfidence = FULL_FRAME_MAX_CONFIDENCE) {
2730
2752
  if (frameWidth <= 0 || frameHeight <= 0) return false;
2731
- if (score >= maxConfidence) return false;
2753
+ if (score < maxConfidence) return false;
2732
2754
  const [x1, y1, x2, y2] = bbox;
2733
2755
  const w = x2 - x1;
2734
2756
  const h = y2 - y1;
@@ -2737,6 +2759,19 @@ function isFullFramePhantomDetection(bbox, frameWidth, frameHeight, score, maxCo
2737
2759
  if (h >= frameHeight * .98) return true;
2738
2760
  return false;
2739
2761
  }
2762
+ function isFullFramePhantomDetection(bbox, frameWidth, frameHeight, score, maxConfidence = FULL_FRAME_MAX_CONFIDENCE, hardAreaRatio = 1) {
2763
+ if (frameWidth <= 0 || frameHeight <= 0) return false;
2764
+ const [x1, y1, x2, y2] = bbox;
2765
+ const w = x2 - x1;
2766
+ const h = y2 - y1;
2767
+ const areaRatio = w * h / (frameWidth * frameHeight);
2768
+ if (isUsableHardAreaCut(hardAreaRatio) && areaRatio >= hardAreaRatio) return true;
2769
+ if (score >= maxConfidence) return false;
2770
+ if (areaRatio >= .9) return true;
2771
+ if (w >= frameWidth * .98) return true;
2772
+ if (h >= frameHeight * .98) return true;
2773
+ return false;
2774
+ }
2740
2775
  //#endregion
2741
2776
  //#region src/detection-pipeline/pipeline/plate-deskew.ts
2742
2777
  /**
@@ -3584,10 +3619,34 @@ var PipelineExecutor = class {
3584
3619
  continue;
3585
3620
  }
3586
3621
  const fullFrameBar = rootStep.settings?.["fullFrameGuardMaxConfidence"];
3587
- if (isFullFramePhantomDetection(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0)) {
3588
- if (debug) console.log(`[executor] drop det macro=${mutable.macroClass} score=${mutable.score} bbox=${JSON.stringify(mutable.bbox)} (full-frame phantom guard)`);
3622
+ const fullFrameHardArea = rootStep.settings?.["fullFrameGuardHardAreaRatio"];
3623
+ if (isFullFramePhantomDetection(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0, typeof fullFrameHardArea === "number" ? fullFrameHardArea : void 0)) {
3624
+ this.opts.logger?.info("full-frame phantom guard: detection dropped", {
3625
+ tags: { deviceId },
3626
+ meta: {
3627
+ macroClass: mutable.macroClass,
3628
+ score: mutable.score,
3629
+ bbox: mutable.bbox,
3630
+ frameWidth: imageWidth,
3631
+ frameHeight: imageHeight,
3632
+ areaRatio: imageWidth > 0 && imageHeight > 0 ? Math.round((mutable.bbox[2] - mutable.bbox[0]) * (mutable.bbox[3] - mutable.bbox[1]) / (imageWidth * imageHeight) * 1e3) / 1e3 : void 0,
3633
+ bar: typeof fullFrameBar === "number" ? fullFrameBar : void 0,
3634
+ hardAreaRatio: typeof fullFrameHardArea === "number" ? fullFrameHardArea : void 0
3635
+ }
3636
+ });
3589
3637
  continue;
3590
3638
  }
3639
+ if (isFullFrameSurvivor(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0)) this.opts.logger?.warn("full-frame box SURVIVED the guard on score", {
3640
+ tags: { deviceId },
3641
+ meta: {
3642
+ macroClass: mutable.macroClass,
3643
+ score: mutable.score,
3644
+ bbox: mutable.bbox,
3645
+ areaRatio: imageWidth > 0 && imageHeight > 0 ? Math.round((mutable.bbox[2] - mutable.bbox[0]) * (mutable.bbox[3] - mutable.bbox[1]) / (imageWidth * imageHeight) * 1e3) / 1e3 : void 0,
3646
+ bar: typeof fullFrameBar === "number" ? fullFrameBar : void 0,
3647
+ hint: "arm fullFrameGuardHardAreaRatio on this camera if these are phantoms"
3648
+ }
3649
+ });
3591
3650
  try {
3592
3651
  await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeCropProvider);
3593
3652
  } catch (err) {
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
2
  import { B as errMsg, E as evaluateZoneRules, F as runtimeDevices$1, G as createEvent, K as hydrateSchema, R as supportedRuntimes$1, T as enumerateInferenceDevices, V as BaseAddon, X as nodePin, Z as parseJsonUnknown, ct as object, dt as union, et as sleep, ft as EventCategory, h as YAMNET_TO_MACRO, j as pipelineExecutorCapability, nt as array, s as DEVICE_BACKEND_TO_FORMAT, t as APPLE_SA_TO_MACRO, ut as string, w as detectionPipelineCapability, x as defaultDeviceFor$1 } from "../dist-CsKaXmP8.mjs";
3
- import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-C8A78dBi.mjs";
3
+ import { a as getStepDefinition, i as getStep, n as ALL_STEPS, o as resolveModelForFormat, r as getDefaultModelForFormat, t as ALL_PIPELINE_STEPS } from "../step-definitions-1GIwU6R8.mjs";
4
4
  import { t as pickNodePlatformArch } from "../node-topology-platform-BkR_k6WT.mjs";
5
5
  import { a as ensureModel, o as isModelDownloaded, r as deleteModelFromDisk } from "../model-download-service-Cp9f4dk6-CwUb5v3Y.mjs";
6
6
  import { spawn } from "node:child_process";
@@ -2696,19 +2696,25 @@ async function nativeChildCrop(provider, bbox, imageWidth, imageHeight, maxWidth
2696
2696
  * DROPS detections outright, so it sits between that and a hard cut).
2697
2697
  */
2698
2698
  var FULL_FRAME_MAX_CONFIDENCE = .7;
2699
+ /** A cut only makes sense in (0, 1); anything else is treated as OFF rather
2700
+ * than dropping every detection on a typo. */
2701
+ function isUsableHardAreaCut(ratio) {
2702
+ return Number.isFinite(ratio) && ratio > 0 && ratio < 1;
2703
+ }
2699
2704
  /**
2700
2705
  * True when `bbox` is a near-full-frame LOW-confidence phantom that should be
2701
2706
  * DROPPED from the frame's detections (overlay / tracker / events / motion-wake).
2702
2707
  *
2703
2708
  * Rule (drop ⇔ returns true):
2704
- * (areaRatio ≥ {@link FULL_FRAME_AREA_RATIO}
2705
- * OR width ≥ {@link FULL_FRAME_DIMENSION_RATIO} × frameWidth
2706
- * OR height ≥ {@link FULL_FRAME_DIMENSION_RATIO} × frameHeight)
2707
- * AND score < `maxConfidence`
2709
+ * areaRatio ≥ `hardAreaRatio` ← score-independent, OFF by default
2710
+ * OR ( (areaRatio ≥ {@link FULL_FRAME_AREA_RATIO}
2711
+ * OR width ≥ {@link FULL_FRAME_DIMENSION_RATIO} × frameWidth
2712
+ * OR height {@link FULL_FRAME_DIMENSION_RATIO} × frameHeight)
2713
+ * AND score < `maxConfidence` )
2708
2714
  *
2709
- * A high-confidence detection (`score ≥ maxConfidence`) is trusted as a real
2710
- * subject and always survives. Degenerate frame dimensions (≤0) carry no info
2711
- * to reject on → not a phantom.
2715
+ * Below the hard cut a high-confidence detection (`score ≥ maxConfidence`) is
2716
+ * trusted as a real subject and survives. Degenerate frame dimensions (≤0)
2717
+ * carry no info to reject on → not a phantom.
2712
2718
  *
2713
2719
  * @param bbox absolute-pixel `[x1, y1, x2, y2]` box in source-frame coords.
2714
2720
  * @param frameWidth source frame width in px.
@@ -2717,10 +2723,26 @@ var FULL_FRAME_MAX_CONFIDENCE = .7;
2717
2723
  * @param maxConfidence confidence bar below which a full-frame box is a phantom
2718
2724
  * (defaults to {@link FULL_FRAME_MAX_CONFIDENCE}; overridable by the caller so
2719
2725
  * the bar can be threaded from detection settings without new cap methods).
2726
+ * @param hardAreaRatio area fraction at/above which the box is dropped NO
2727
+ * MATTER the score. Defaults to {@link HARD_AREA_CUT_DISABLED} (off) — it is
2728
+ * armed per camera, because a doorbell close-up legitimately fills the frame
2729
+ * while an overhead courtyard box never does. Threaded from step settings as
2730
+ * `fullFrameGuardHardAreaRatio`.
2720
2731
  */
2721
- function isFullFramePhantomDetection(bbox, frameWidth, frameHeight, score, maxConfidence = FULL_FRAME_MAX_CONFIDENCE) {
2732
+ /**
2733
+ * True when a box trips the guard's GEOMETRY test (near-full-frame, or
2734
+ * edge-to-edge in one axis) but SURVIVES because its score cleared the
2735
+ * confidence bar.
2736
+ *
2737
+ * These are the ones worth counting: on 2026-07-30 device 615 produced six
2738
+ * such boxes in a day, at 0.710-0.787, and they were only found by an offline
2739
+ * sweep of stored events — nothing in production reported them. Arming the
2740
+ * hard-area cut on a camera should be a decision made from this count, not
2741
+ * from one incident.
2742
+ */
2743
+ function isFullFrameSurvivor(bbox, frameWidth, frameHeight, score, maxConfidence = FULL_FRAME_MAX_CONFIDENCE) {
2722
2744
  if (frameWidth <= 0 || frameHeight <= 0) return false;
2723
- if (score >= maxConfidence) return false;
2745
+ if (score < maxConfidence) return false;
2724
2746
  const [x1, y1, x2, y2] = bbox;
2725
2747
  const w = x2 - x1;
2726
2748
  const h = y2 - y1;
@@ -2729,6 +2751,19 @@ function isFullFramePhantomDetection(bbox, frameWidth, frameHeight, score, maxCo
2729
2751
  if (h >= frameHeight * .98) return true;
2730
2752
  return false;
2731
2753
  }
2754
+ function isFullFramePhantomDetection(bbox, frameWidth, frameHeight, score, maxConfidence = FULL_FRAME_MAX_CONFIDENCE, hardAreaRatio = 1) {
2755
+ if (frameWidth <= 0 || frameHeight <= 0) return false;
2756
+ const [x1, y1, x2, y2] = bbox;
2757
+ const w = x2 - x1;
2758
+ const h = y2 - y1;
2759
+ const areaRatio = w * h / (frameWidth * frameHeight);
2760
+ if (isUsableHardAreaCut(hardAreaRatio) && areaRatio >= hardAreaRatio) return true;
2761
+ if (score >= maxConfidence) return false;
2762
+ if (areaRatio >= .9) return true;
2763
+ if (w >= frameWidth * .98) return true;
2764
+ if (h >= frameHeight * .98) return true;
2765
+ return false;
2766
+ }
2732
2767
  //#endregion
2733
2768
  //#region src/detection-pipeline/pipeline/plate-deskew.ts
2734
2769
  /**
@@ -3576,10 +3611,34 @@ var PipelineExecutor = class {
3576
3611
  continue;
3577
3612
  }
3578
3613
  const fullFrameBar = rootStep.settings?.["fullFrameGuardMaxConfidence"];
3579
- if (isFullFramePhantomDetection(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0)) {
3580
- if (debug) console.log(`[executor] drop det macro=${mutable.macroClass} score=${mutable.score} bbox=${JSON.stringify(mutable.bbox)} (full-frame phantom guard)`);
3614
+ const fullFrameHardArea = rootStep.settings?.["fullFrameGuardHardAreaRatio"];
3615
+ if (isFullFramePhantomDetection(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0, typeof fullFrameHardArea === "number" ? fullFrameHardArea : void 0)) {
3616
+ this.opts.logger?.info("full-frame phantom guard: detection dropped", {
3617
+ tags: { deviceId },
3618
+ meta: {
3619
+ macroClass: mutable.macroClass,
3620
+ score: mutable.score,
3621
+ bbox: mutable.bbox,
3622
+ frameWidth: imageWidth,
3623
+ frameHeight: imageHeight,
3624
+ areaRatio: imageWidth > 0 && imageHeight > 0 ? Math.round((mutable.bbox[2] - mutable.bbox[0]) * (mutable.bbox[3] - mutable.bbox[1]) / (imageWidth * imageHeight) * 1e3) / 1e3 : void 0,
3625
+ bar: typeof fullFrameBar === "number" ? fullFrameBar : void 0,
3626
+ hardAreaRatio: typeof fullFrameHardArea === "number" ? fullFrameHardArea : void 0
3627
+ }
3628
+ });
3581
3629
  continue;
3582
3630
  }
3631
+ if (isFullFrameSurvivor(mutable.bbox, imageWidth, imageHeight, mutable.score, typeof fullFrameBar === "number" ? fullFrameBar : void 0)) this.opts.logger?.warn("full-frame box SURVIVED the guard on score", {
3632
+ tags: { deviceId },
3633
+ meta: {
3634
+ macroClass: mutable.macroClass,
3635
+ score: mutable.score,
3636
+ bbox: mutable.bbox,
3637
+ areaRatio: imageWidth > 0 && imageHeight > 0 ? Math.round((mutable.bbox[2] - mutable.bbox[0]) * (mutable.bbox[3] - mutable.bbox[1]) / (imageWidth * imageHeight) * 1e3) / 1e3 : void 0,
3638
+ bar: typeof fullFrameBar === "number" ? fullFrameBar : void 0,
3639
+ hint: "arm fullFrameGuardHardAreaRatio on this camera if these are phantoms"
3640
+ }
3641
+ });
3583
3642
  try {
3584
3643
  await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeCropProvider);
3585
3644
  } catch (err) {
@@ -7,7 +7,7 @@ const require_dist = require("../dist-DUHlZcWd.js");
7
7
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_worker_protocol = require("../worker-protocol-DextwlTX.js");
10
- const require_step_definitions = require("../step-definitions-Bo1hJQQw.js");
10
+ const require_step_definitions = require("../step-definitions-Drb9cWbB.js");
11
11
  let node_child_process = require("node:child_process");
12
12
  let node_url = require("node:url");
13
13
  let sharp = require("sharp");
@@ -658,6 +658,9 @@ function toFrameInput$1(frame) {
658
658
  * without spinning up Moleculer or the addon framework.
659
659
  */
660
660
  var PipelineRunner = class {
661
+ /** Abandoned inference calls that have not yet settled. A BURST reads as
662
+ * one rising number instead of N unrelated lines. */
663
+ inflightTimeouts = 0;
661
664
  config;
662
665
  cameras = /* @__PURE__ */ new Map();
663
666
  semaphore;
@@ -1144,16 +1147,33 @@ var PipelineRunner = class {
1144
1147
  timer.unref?.();
1145
1148
  });
1146
1149
  try {
1147
- const outcome = await Promise.race([this.config.processFrame(deviceId, frameInput, executorHandle), expiry]);
1150
+ const inflight = this.config.processFrame(deviceId, frameInput, executorHandle);
1151
+ const outcome = await Promise.race([inflight, expiry]);
1148
1152
  if (outcome === TIMED_OUT) {
1149
1153
  release();
1154
+ this.inflightTimeouts += 1;
1150
1155
  this.logger?.warn("inference timed out — permit released, result discarded", {
1151
1156
  tags: { deviceId },
1152
1157
  meta: {
1153
1158
  timeoutMs,
1154
- availablePermits: this.semaphore.available
1159
+ availablePermits: this.semaphore.available,
1160
+ outstandingTimeouts: this.inflightTimeouts
1155
1161
  }
1156
1162
  });
1163
+ const deadlineAt = Date.now();
1164
+ const noteSettled = (fate) => {
1165
+ this.inflightTimeouts = Math.max(0, this.inflightTimeouts - 1);
1166
+ this.logger?.info("inference: abandoned call settled after the deadline", {
1167
+ tags: { deviceId },
1168
+ meta: {
1169
+ lateByMs: Date.now() - deadlineAt,
1170
+ totalMs: timeoutMs + (Date.now() - deadlineAt),
1171
+ fate,
1172
+ outstandingTimeouts: this.inflightTimeouts
1173
+ }
1174
+ });
1175
+ };
1176
+ inflight.then(() => noteSettled("resolved"), () => noteSettled("rejected"));
1157
1177
  }
1158
1178
  return outcome;
1159
1179
  } finally {
@@ -2,7 +2,7 @@ import { B as errMsg, G as createEvent, M as pipelineRunnerCapability, S as defi
2
2
  import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-D7RzZIla.mjs";
5
- import { a as getStepDefinition } from "../step-definitions-C8A78dBi.mjs";
5
+ import { a as getStepDefinition } from "../step-definitions-1GIwU6R8.mjs";
6
6
  import { fork } from "node:child_process";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import sharp from "sharp";
@@ -652,6 +652,9 @@ function toFrameInput$1(frame) {
652
652
  * without spinning up Moleculer or the addon framework.
653
653
  */
654
654
  var PipelineRunner = class {
655
+ /** Abandoned inference calls that have not yet settled. A BURST reads as
656
+ * one rising number instead of N unrelated lines. */
657
+ inflightTimeouts = 0;
655
658
  config;
656
659
  cameras = /* @__PURE__ */ new Map();
657
660
  semaphore;
@@ -1138,16 +1141,33 @@ var PipelineRunner = class {
1138
1141
  timer.unref?.();
1139
1142
  });
1140
1143
  try {
1141
- const outcome = await Promise.race([this.config.processFrame(deviceId, frameInput, executorHandle), expiry]);
1144
+ const inflight = this.config.processFrame(deviceId, frameInput, executorHandle);
1145
+ const outcome = await Promise.race([inflight, expiry]);
1142
1146
  if (outcome === TIMED_OUT) {
1143
1147
  release();
1148
+ this.inflightTimeouts += 1;
1144
1149
  this.logger?.warn("inference timed out — permit released, result discarded", {
1145
1150
  tags: { deviceId },
1146
1151
  meta: {
1147
1152
  timeoutMs,
1148
- availablePermits: this.semaphore.available
1153
+ availablePermits: this.semaphore.available,
1154
+ outstandingTimeouts: this.inflightTimeouts
1149
1155
  }
1150
1156
  });
1157
+ const deadlineAt = Date.now();
1158
+ const noteSettled = (fate) => {
1159
+ this.inflightTimeouts = Math.max(0, this.inflightTimeouts - 1);
1160
+ this.logger?.info("inference: abandoned call settled after the deadline", {
1161
+ tags: { deviceId },
1162
+ meta: {
1163
+ lateByMs: Date.now() - deadlineAt,
1164
+ totalMs: timeoutMs + (Date.now() - deadlineAt),
1165
+ fate,
1166
+ outstandingTimeouts: this.inflightTimeouts
1167
+ }
1168
+ });
1169
+ };
1170
+ inflight.then(() => noteSettled("resolved"), () => noteSettled("rejected"));
1151
1171
  }
1152
1172
  return outcome;
1153
1173
  } finally {
@@ -1296,7 +1296,18 @@ var ObjectDetectionStep = class {
1296
1296
  min: 0,
1297
1297
  max: 1,
1298
1298
  step: .05,
1299
- default: .5,
1299
+ default: .65,
1300
+ showValue: true
1301
+ },
1302
+ {
1303
+ type: "slider",
1304
+ key: "fullFrameGuardHardAreaRatio",
1305
+ label: "Drop full-frame boxes above",
1306
+ description: "Fraction of the frame at or above which a detection is dropped WHATEVER its score. 1 = off. Use on cameras where a subject can never fill the frame; leave off for doorbells and close-up lenses.",
1307
+ min: .5,
1308
+ max: 1,
1309
+ step: .01,
1310
+ default: 1,
1300
1311
  showValue: true
1301
1312
  },
1302
1313
  {
@@ -1296,7 +1296,18 @@ var ObjectDetectionStep = class {
1296
1296
  min: 0,
1297
1297
  max: 1,
1298
1298
  step: .05,
1299
- default: .5,
1299
+ default: .65,
1300
+ showValue: true
1301
+ },
1302
+ {
1303
+ type: "slider",
1304
+ key: "fullFrameGuardHardAreaRatio",
1305
+ label: "Drop full-frame boxes above",
1306
+ description: "Fraction of the frame at or above which a detection is dropped WHATEVER its score. 1 = off. Use on cameras where a subject can never fill the frame; leave off for doorbells and close-up lenses.",
1307
+ min: .5,
1308
+ max: 1,
1309
+ step: .01,
1310
+ default: 1,
1300
1311
  showValue: true
1301
1312
  },
1302
1313
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.28",
3
+ "version": "1.2.29",
4
4
  "description": "CamStack Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",