@camstack/addon-pipeline 1.2.333 → 1.2.335

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 (37) hide show
  1. package/THIRD_PARTY_MODELS.md +3 -8
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/{default-detection-model-CDffuH2m.mjs → custom-model-catalog-Buehc666.mjs} +518 -179
  5. package/dist/{default-detection-model-Biwf5-ML.js → custom-model-catalog-CL-xJa8w.js} +529 -178
  6. package/dist/detection-pipeline/index.js +186 -120
  7. package/dist/detection-pipeline/index.mjs +135 -69
  8. package/dist/{dist-DJV7Z6Bb.js → dist-C5DlS_lW.js} +214 -22
  9. package/dist/{dist-BsT15i7i.mjs → dist-CPhDwlSN.mjs} +203 -23
  10. package/dist/motion-wasm/index.js +1 -1
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-CkmucLYG.js → node-BUafKArW.js} +1 -1
  13. package/dist/{node-Crsi_8ea.mjs → node-DYfEOwjW.mjs} +1 -1
  14. package/dist/pipeline-runner/index.js +248 -44
  15. package/dist/pipeline-runner/index.mjs +226 -22
  16. package/dist/{process-memory-BJb7Ihdp.js → process-memory-DWOQCbg5.js} +1 -1
  17. package/dist/{process-memory-1mAUV8S1.mjs → process-memory-WPRYnN8c.mjs} +1 -1
  18. package/dist/recorder/index.js +2 -2
  19. package/dist/recorder/index.mjs +2 -2
  20. package/dist/{segment-demux-js-mK9IBGLf.js → segment-demux-js-DWIgUPW1.js} +1 -1
  21. package/dist/{segment-demux-js-B_tboBcJ.mjs → segment-demux-js-Kz4EnmOr.mjs} +1 -1
  22. package/dist/stream-broker/_stub.js +1 -1
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-a3HMFtd5.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-RPk9F8CY.mjs} +2 -2
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-1Y-WK-Zy.mjs +26 -0
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-DChPnZ5C.mjs → hostInit-DXXdQdLd.mjs} +2 -2
  28. package/dist/stream-broker/index.js +3 -3
  29. package/dist/stream-broker/index.mjs +3 -3
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +1 -1
  32. package/python/inference_pool.py +52 -2
  33. package/python/postprocessors/test_yolo_seg_root.py +109 -0
  34. package/python/postprocessors/yolo.py +52 -13
  35. package/python/postprocessors/yolo_seg.py +79 -156
  36. package/python/test_inference_pool_ov_precision.py +98 -0
  37. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DPbj3ML4.mjs +0 -26
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-DJV7Z6Bb.js");
7
- const require_node = require("../node-CkmucLYG.js");
8
- const require_default_detection_model = require("../default-detection-model-Biwf5-ML.js");
6
+ const require_dist = require("../dist-C5DlS_lW.js");
7
+ const require_node = require("../node-BUafKArW.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-CL-xJa8w.js");
9
9
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
10
10
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
11
11
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
@@ -1723,7 +1723,7 @@ function cropRgb(frame, bbox, maxWidth) {
1723
1723
  * pass exists to fix.
1724
1724
  */
1725
1725
  async function toDecodedFrame(frame, format, viewSpec) {
1726
- const geometry = viewSpec ? require_default_detection_model.resolveFrameViewGeometry(frame.image.width, frame.image.height, viewSpec) : void 0;
1726
+ const geometry = viewSpec ? require_custom_model_catalog.resolveFrameViewGeometry(frame.image.width, frame.image.height, viewSpec) : void 0;
1727
1727
  const bytes = await frame.image.toBuffer(geometry ? {
1728
1728
  crop: geometry.sourceCrop,
1729
1729
  resize: geometry.output,
@@ -1762,7 +1762,7 @@ function startSessionDecodePump(deps) {
1762
1762
  workerGone = true;
1763
1763
  deps.onWorkerGone?.();
1764
1764
  };
1765
- const iterator = require_default_detection_model.createSessionDecodeCoordinator({
1765
+ const iterator = require_custom_model_catalog.createSessionDecodeCoordinator({
1766
1766
  fork: (source) => {
1767
1767
  const child = deps.fork(source);
1768
1768
  forked = true;
@@ -1964,7 +1964,7 @@ function onboardRootDetections(slot, frame, gate) {
1964
1964
  }
1965
1965
  };
1966
1966
  });
1967
- const admitted = detections.filter((d) => require_default_detection_model.macroGateVerdict(d.macroClass, d.score, gate) === "pass");
1967
+ const admitted = detections.filter((d) => require_custom_model_catalog.macroGateVerdict(d.macroClass, d.score, gate) === "pass");
1968
1968
  if (admitted.length === 0) return {
1969
1969
  kind: "gated",
1970
1970
  refused: detections.length
@@ -1976,6 +1976,158 @@ function onboardRootDetections(slot, frame, gate) {
1976
1976
  };
1977
1977
  }
1978
1978
  //#endregion
1979
+ //#region src/pipeline-runner/root-mask-supply.ts
1980
+ /** The frame-plane step a seg root supplies. */
1981
+ var SEGMENTATION_STEP_ID = "segmentation";
1982
+ /** Whether an ENABLED `stepId` is reachable through enabled ancestors. */
1983
+ function dispatchesStep(steps, stepId) {
1984
+ return steps.some((s) => s.enabled !== false && (s.addonId === stepId || s.children !== void 0 && dispatchesStep(s.children, stepId)));
1985
+ }
1986
+ function withoutStep(steps, stepId) {
1987
+ return steps.filter((s) => s.addonId !== stepId).map((s) => s.children !== void 0 ? {
1988
+ ...s,
1989
+ children: withoutStep(s.children, stepId)
1990
+ } : s);
1991
+ }
1992
+ /**
1993
+ * When the camera's mask supply is effective and the step would run, drop it
1994
+ * from the dispatch: the root's masks replace one inference per detection.
1995
+ * Otherwise the tree is returned untouched — a root that emits no masks (or a
1996
+ * flag the operator unchecked) leaves the step running its own model.
1997
+ */
1998
+ function applyMaskFill(steps, plan) {
1999
+ const dispatched = dispatchesStep(steps, SEGMENTATION_STEP_ID);
2000
+ if (!dispatched || plan.states.get("segmentation")?.effective !== true) return {
2001
+ steps,
2002
+ filledByRoot: false,
2003
+ segmentationDispatched: dispatched
2004
+ };
2005
+ return {
2006
+ steps: withoutStep(steps, SEGMENTATION_STEP_ID),
2007
+ filledByRoot: true,
2008
+ segmentationDispatched: false
2009
+ };
2010
+ }
2011
+ /**
2012
+ * The largest 4-connected non-zero component of a `width × height` byte mask —
2013
+ * the same reduction the `segmentation` step's decode applies to its own mask
2014
+ * (`saliency.py`). `null` when the mask has no foreground (or a size that does
2015
+ * not match its bytes).
2016
+ */
2017
+ function largestMaskComponent(mask, width, height) {
2018
+ const size = width * height;
2019
+ if (!(size > 0) || mask.length < size) return null;
2020
+ const visited = new Uint8Array(size);
2021
+ const stack = new Int32Array(size);
2022
+ let best = null;
2023
+ for (let start = 0; start < size; start++) {
2024
+ if (mask[start] === 0 || visited[start] === 1) continue;
2025
+ visited[start] = 1;
2026
+ let top = 0;
2027
+ stack[top++] = start;
2028
+ let area = 0;
2029
+ let minX = width;
2030
+ let minY = height;
2031
+ let maxX = -1;
2032
+ let maxY = -1;
2033
+ while (top > 0) {
2034
+ const p = stack[--top];
2035
+ area += 1;
2036
+ const x = p % width;
2037
+ const y = (p - x) / width;
2038
+ if (x < minX) minX = x;
2039
+ if (x > maxX) maxX = x;
2040
+ if (y < minY) minY = y;
2041
+ if (y > maxY) maxY = y;
2042
+ if (x > 0 && visited[p - 1] === 0 && mask[p - 1] !== 0) {
2043
+ visited[p - 1] = 1;
2044
+ stack[top++] = p - 1;
2045
+ }
2046
+ if (x < width - 1 && visited[p + 1] === 0 && mask[p + 1] !== 0) {
2047
+ visited[p + 1] = 1;
2048
+ stack[top++] = p + 1;
2049
+ }
2050
+ if (y > 0 && visited[p - width] === 0 && mask[p - width] !== 0) {
2051
+ visited[p - width] = 1;
2052
+ stack[top++] = p - width;
2053
+ }
2054
+ if (y < height - 1 && visited[p + width] === 0 && mask[p + width] !== 0) {
2055
+ visited[p + width] = 1;
2056
+ stack[top++] = p + width;
2057
+ }
2058
+ }
2059
+ if (best === null || area > best.area) best = {
2060
+ area,
2061
+ box: [
2062
+ minX,
2063
+ minY,
2064
+ maxX - minX + 1,
2065
+ maxY - minY + 1
2066
+ ]
2067
+ };
2068
+ }
2069
+ return best?.box ?? null;
2070
+ }
2071
+ /**
2072
+ * The detection's `refinedBbox` from its own mask — the mapping the result
2073
+ * assembler applies to the `segmentation` step's `maskBbox`: the mask spans
2074
+ * the detection's box, so mask pixels scale linearly onto it.
2075
+ */
2076
+ function refinedBboxFromMask(detection) {
2077
+ const { mask, maskWidth, maskHeight, bbox } = detection;
2078
+ if (mask === void 0 || maskWidth === void 0 || maskHeight === void 0) return void 0;
2079
+ const component = largestMaskComponent(new Uint8Array(Buffer.from(mask, "base64")), maskWidth, maskHeight);
2080
+ if (component === null) return void 0;
2081
+ const [mx, my, mw, mh] = component;
2082
+ const sx = bbox.width / maskWidth;
2083
+ const sy = bbox.height / maskHeight;
2084
+ return {
2085
+ x: bbox.x + mx * sx,
2086
+ y: bbox.y + my * sy,
2087
+ width: mw * sx,
2088
+ height: mh * sy
2089
+ };
2090
+ }
2091
+ function withoutMask(detection) {
2092
+ const { mask: _mask, maskWidth: _w, maskHeight: _h, ...rest } = detection;
2093
+ if (rest.debug === void 0) return rest;
2094
+ const { mask: _dm, maskWidth: _dw, maskHeight: _dh, ...debug } = rest.debug;
2095
+ return {
2096
+ ...rest,
2097
+ debug
2098
+ };
2099
+ }
2100
+ /**
2101
+ * Apply the mask decision to a frame. Filled by the root ⇒ each root
2102
+ * detection's mask is completed with its `refinedBbox`, exactly what the
2103
+ * step would have written. The step ran ⇒ untouched (its own masks). Neither
2104
+ * ⇒ a seg root's masks are removed from the first-level detections. Never
2105
+ * mutates; returns the input when nothing changes.
2106
+ */
2107
+ function applyRootMasks(result, fill) {
2108
+ if (fill.segmentationDispatched) return result;
2109
+ let changed = false;
2110
+ const detections = result.detections.map((d) => {
2111
+ if (d.kind !== "first-level" || d.mask === void 0) return d;
2112
+ if (!fill.filledByRoot) {
2113
+ changed = true;
2114
+ return withoutMask(d);
2115
+ }
2116
+ if (d.refinedBbox !== void 0) return d;
2117
+ const refinedBbox = refinedBboxFromMask(d);
2118
+ if (refinedBbox === void 0) return d;
2119
+ changed = true;
2120
+ return {
2121
+ ...d,
2122
+ refinedBbox
2123
+ };
2124
+ });
2125
+ return changed ? {
2126
+ ...result,
2127
+ detections
2128
+ } : result;
2129
+ }
2130
+ //#endregion
1979
2131
  //#region src/pipeline-runner/root-supply.ts
1980
2132
  /**
1981
2133
  * The runner's half of root-model supply (D715, D730).
@@ -2003,35 +2155,80 @@ function onboardRootDetections(slot, frame, gate) {
2003
2155
  var ROOT_SUPPLY_ROOT_STEP_ID = "object-detection";
2004
2156
  var PACKAGE_STEP_ID = "package-detection";
2005
2157
  var PACKAGE_THRESHOLD_KEY = "minConfidencePackage";
2158
+ /** The step slider that governs a supplied label — one knob per step (D741). */
2159
+ var SUPPLIED_LABEL_FLOOR_KEY = "minConfidence";
2160
+ /**
2161
+ * The floor a supplied label must clear for one step, under the D738 binding
2162
+ * rule against the ROOT model: an operator value made for the root applies; one
2163
+ * made for the step's own classifier does not (another scale); the root's
2164
+ * measured floor is the default, and no floor at all when it declares none.
2165
+ */
2166
+ function suppliedLabelFloor(stepId, settings, rootModelId, rootEntry) {
2167
+ const state = require_dist.resolveModelBoundSetting(settings, SUPPLIED_LABEL_FLOOR_KEY, rootModelId, rootEntry?.suppliedLabelFloors?.[stepId] ?? 0, 0);
2168
+ return typeof state.value === "number" ? state.value : 0;
2169
+ }
2006
2170
  var EMPTY_ROOT_SUPPLY_PLAN = { states: /* @__PURE__ */ new Map() };
2007
- function findStep(steps, addonId) {
2171
+ /** The step `addonId` when it is in the tree AND switched on — the per-camera gate's answer. */
2172
+ function findEnabledStep(steps, addonId) {
2008
2173
  for (const step of steps) {
2174
+ if (step.enabled === false) continue;
2009
2175
  if (step.addonId === addonId) return step;
2010
- const nested = step.children !== void 0 ? findStep(step.children, addonId) : void 0;
2176
+ const nested = step.children !== void 0 ? findEnabledStep(step.children, addonId) : void 0;
2011
2177
  if (nested !== void 0) return nested;
2012
2178
  }
2013
2179
  }
2014
2180
  /**
2015
- * Plan a camera's root supplies from its configured tree. A root with no
2016
- * pinned model runs the step's default — a COCO model, which supplies the
2017
- * animal and vehicle classifiers like any other root.
2181
+ * The model an UNPINNED root runs on the elected device — the executor's own
2182
+ * rule (`resolveDefaultDetectionModel` on the device's backend, accelerator and
2183
+ * format), so the supplies are read off the model that really runs. Before
2184
+ * this, an unpinned root was judged as the step's flat `defaultModelId`
2185
+ * (`yolo26n`) on every accelerator, while an Intel GPU ran `yolov9m-320-int8`
2186
+ * and a Coral an SSD: `filledBy` named a model that was not running, and a
2187
+ * crop supply's input-size gate (D731) measured the wrong model. No elected
2188
+ * device (the runner's default pool) or an unknown backend ⇒ the flat default.
2018
2189
  */
2019
- function planRootSupplies(steps, resolveModel) {
2190
+ function unpinnedRootModelId(deviceKey, flatDefault) {
2191
+ if (deviceKey === void 0) return flatDefault;
2192
+ try {
2193
+ const engine = require_custom_model_catalog.resolveDeviceEngine(deviceKey);
2194
+ return require_custom_model_catalog.resolveDefaultDetectionModel(engine.backend, engine.device ?? null, engine.format);
2195
+ } catch {
2196
+ return flatDefault;
2197
+ }
2198
+ }
2199
+ /**
2200
+ * Plan a camera's root supplies from its configured tree.
2201
+ *
2202
+ * - The root's model is the one the executor runs: its pin, else the
2203
+ * hardware-aware default of the elected device (`deviceKey`).
2204
+ * - Only a step the camera RUNS is planned. A suppliable step that is absent
2205
+ * from the tree or switched off (the per-camera gate) has no state: the
2206
+ * operator switched that function off, so the root's labels are not made
2207
+ * authoritative for it, its root child boxes are removed, and nothing logs
2208
+ * that the root "supplies" it. Before this, a camera whose classifiers were
2209
+ * gated OFF logged `root model supplies a pipeline step` for both of them
2210
+ * and raised the root's labels to the detection score anyway (D736).
2211
+ */
2212
+ function planRootSupplies(steps, resolveModel, deviceKey) {
2020
2213
  if (steps === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
2021
2214
  const root = steps.find((s) => s.addonId === "object-detection" && s.enabled !== false);
2022
2215
  if (root === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
2023
- const definition = require_default_detection_model.getStepDefinition(ROOT_SUPPLY_ROOT_STEP_ID);
2024
- const rootModelId = root.modelId ?? definition.defaultModelId;
2025
- const supplies = require_dist.rootSuppliesOf(resolveModel(ROOT_SUPPLY_ROOT_STEP_ID, rootModelId), {
2216
+ const definition = require_custom_model_catalog.getStepDefinition(ROOT_SUPPLY_ROOT_STEP_ID);
2217
+ const rootModelId = root.modelId ?? unpinnedRootModelId(deviceKey, definition.defaultModelId);
2218
+ const rootEntry = resolveModel(ROOT_SUPPLY_ROOT_STEP_ID, rootModelId);
2219
+ const supplies = require_dist.rootSuppliesOf(rootEntry, {
2026
2220
  labels: definition.labels ?? [],
2027
2221
  ...definition.classMap !== void 0 ? { classMap: definition.classMap } : {}
2028
2222
  });
2029
2223
  const states = /* @__PURE__ */ new Map();
2030
2224
  for (const stepId of require_dist.ROOT_SUPPLIABLE_STEP_IDS) {
2031
- const settings = findStep(steps, stepId)?.settings;
2225
+ const step = findEnabledStep(steps, stepId);
2226
+ if (step === void 0) continue;
2227
+ const settings = step.settings;
2032
2228
  states.set(stepId, {
2033
2229
  ...require_dist.resolveRootSupply(stepId, settings, supplies),
2034
- ...settings !== void 0 ? { settings } : {}
2230
+ ...settings !== void 0 ? { settings } : {},
2231
+ labelFloor: suppliedLabelFloor(stepId, settings, rootModelId, rootEntry)
2035
2232
  });
2036
2233
  }
2037
2234
  return {
@@ -2043,7 +2240,7 @@ function planRootSupplies(steps, resolveModel) {
2043
2240
  function suppliedSteps(plan) {
2044
2241
  const out = /* @__PURE__ */ new Map();
2045
2242
  if (plan.rootModelId === void 0) return out;
2046
- for (const [stepId, state] of plan.states) if (state.effective && state.supply !== void 0) out.set(stepId, {
2243
+ for (const [stepId, state] of plan.states) if (state.effective && state.supply !== void 0 && state.supply.kind !== "mask") out.set(stepId, {
2047
2244
  modelId: plan.rootModelId,
2048
2245
  kind: state.supply.kind
2049
2246
  });
@@ -2069,6 +2266,10 @@ function authoritativeLabels(detection, plan) {
2069
2266
  changed = true;
2070
2267
  continue;
2071
2268
  }
2269
+ if (state.labelFloor >= 1 || detection.score < state.labelFloor) {
2270
+ changed = true;
2271
+ continue;
2272
+ }
2072
2273
  if (label.score === detection.score) {
2073
2274
  labels.push(label);
2074
2275
  continue;
@@ -2121,7 +2322,7 @@ function applyRootSupplyAuthority(result, plan) {
2121
2322
  }
2122
2323
  /** The root step's schema default for its class allow-list. */
2123
2324
  function rootDefaultMacroClasses() {
2124
- const field = require_default_detection_model.getStep(ROOT_SUPPLY_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
2325
+ const field = require_custom_model_catalog.getStep(ROOT_SUPPLY_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
2125
2326
  const value = field !== void 0 && "default" in field ? field.default : void 0;
2126
2327
  return Array.isArray(value) ? value : [];
2127
2328
  }
@@ -3363,7 +3564,7 @@ var FaceOutsideParentLog = class {
3363
3564
  */
3364
3565
  /** The face detector's input side for the step as it will run. */
3365
3566
  function detectorInputPx(step) {
3366
- const def = require_default_detection_model.getStepDefinition(step.addonId);
3567
+ const def = require_custom_model_catalog.getStepDefinition(step.addonId);
3367
3568
  const modelId = step.modelId ?? def.defaultModelId;
3368
3569
  return def.models.find((m) => m.id === modelId)?.inputSize.width ?? 160;
3369
3570
  }
@@ -3474,7 +3675,7 @@ async function refineRootFace(deps, input, supplied, step, requested, deviceKey)
3474
3675
  }
3475
3676
  const chain = requested === void 0 ? step : pruneChildStepsToRequested(step, requested);
3476
3677
  if (chain.children === void 0 || chain.children.length === 0) return [toDetailResult(face, step.addonId, crop)];
3477
- const modelId = step.modelId ?? require_default_detection_model.getStepDefinition(step.addonId).defaultModelId;
3678
+ const modelId = step.modelId ?? require_custom_model_catalog.getStepDefinition(step.addonId).defaultModelId;
3478
3679
  const embedded = await deps.runPipeline([chain], crop.jpeg, input.deviceId, deviceKey, nativeCropRef, refinedAsSupplied(face, step.addonId, modelId));
3479
3680
  if (embedded === null) return [toDetailResult(face, step.addonId, crop)];
3480
3681
  return embedded.detections.map((d) => toDetailResult(d, step.addonId, crop));
@@ -7438,7 +7639,7 @@ function writeRetainedDetectionFrame(store, deviceId, frame, retention) {
7438
7639
  if (retention) return retention.admitFrame(deviceId, frame);
7439
7640
  if (!store) return void 0;
7440
7641
  if (!isContinuousRetainEnabled()) return store.allocateHandle(deviceId, frame) ?? void 0;
7441
- return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(require_default_detection_model.localFrameRegistry, frame.frameRef) : void 0) ?? void 0;
7642
+ return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(require_custom_model_catalog.localFrameRegistry, frame.frameRef) : void 0) ?? void 0;
7442
7643
  }
7443
7644
  /**
7444
7645
  * Occupancy-burst safety timeout (both the dial path and the analyzer-tap
@@ -7555,7 +7756,7 @@ function collectEnabledStepIds(steps, acc) {
7555
7756
  */
7556
7757
  function tryGetStepDefinition(stepId) {
7557
7758
  try {
7558
- return require_default_detection_model.getStepDefinition(stepId);
7759
+ return require_custom_model_catalog.getStepDefinition(stepId);
7559
7760
  } catch {
7560
7761
  return null;
7561
7762
  }
@@ -8121,7 +8322,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8121
8322
  });
8122
8323
  this.retention = new OnDemandRetentionSession({
8123
8324
  store: this.retainedFrames,
8124
- registry: require_default_detection_model.localFrameRegistry,
8325
+ registry: require_custom_model_catalog.localFrameRegistry,
8125
8326
  telemetry: this.frameLazy,
8126
8327
  nodeId: this.nodeId,
8127
8328
  continuousRetain: isContinuousRetainEnabled()
@@ -8139,7 +8340,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8139
8340
  minThrottledFps: this.config.minThrottledFps,
8140
8341
  processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
8141
8342
  analyzeMotion: (deviceId, frame, handle) => this.runMotionAnalysis(deviceId, frame, handle),
8142
- releaseFrameRef: (ref, reason) => require_default_detection_model.localFrameRegistry.release(ref, reason),
8343
+ releaseFrameRef: (ref, reason) => require_custom_model_catalog.localFrameRegistry.release(ref, reason),
8143
8344
  releaseRetention: (handle, reason) => {
8144
8345
  this.retention?.release(handle, reason);
8145
8346
  this.frameCutters.drop(handle);
@@ -9085,7 +9286,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9085
9286
  detectionMode: config.detectionMode,
9086
9287
  ...config.detectionSources !== void 0 ? { detectionSources: config.detectionSources } : {},
9087
9288
  ...config.sessionTrigger !== void 0 ? { sessionTrigger: config.sessionTrigger } : {},
9088
- ...require_default_detection_model.rootObjectGate(config.steps) !== void 0 ? { objectGate: require_default_detection_model.rootObjectGate(config.steps) } : {},
9289
+ ...require_custom_model_catalog.rootObjectGate(config.steps) !== void 0 ? { objectGate: require_custom_model_catalog.rootObjectGate(config.steps) } : {},
9089
9290
  fps: config.detectionFps,
9090
9291
  motionCooldownMs: config.motionCooldownMs,
9091
9292
  occupancyRecheckEnabled: config.occupancyRecheckEnabled,
@@ -9484,7 +9685,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9484
9685
  }
9485
9686
  /** The evidence crop from the process-local lease, JPEG-encoded by the view. */
9486
9687
  async encodeEvidenceFromLease(ref, entry) {
9487
- const lease = require_default_detection_model.localFrameRegistry.acquire(ref);
9688
+ const lease = require_custom_model_catalog.localFrameRegistry.acquire(ref);
9488
9689
  if (!lease) return null;
9489
9690
  let reason = "error";
9490
9691
  try {
@@ -9574,7 +9775,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9574
9775
  /** A 16×32 RGB view of `crop`: the lease when there is one, else the inline buffer. */
9575
9776
  async readAppearanceView(frame, result, crop) {
9576
9777
  if (frame.frameRef !== void 0) {
9577
- const lease = require_default_detection_model.localFrameRegistry.acquire(frame.frameRef);
9778
+ const lease = require_custom_model_catalog.localFrameRegistry.acquire(frame.frameRef);
9578
9779
  if (lease) {
9579
9780
  let reason = "error";
9580
9781
  try {
@@ -9920,12 +10121,13 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9920
10121
  * Refreshed on the async boundaries (`runInference`, `runDetailSubtree`),
9921
10122
  * read synchronously by the placement.
9922
10123
  */
9923
- customModels = new require_default_detection_model.CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
10124
+ customModels = new require_custom_model_catalog.CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
9924
10125
  /**
9925
10126
  * Per camera, the root-supply plan (D730) and the announce derived from it,
9926
10127
  * keyed on the two inputs it reads: the attach tree and the custom-model
9927
- * snapshot (a Frigate+ root is a custom model). Recomputed when either
9928
- * changes identity — at most once per catalog refresh.
10128
+ * snapshot (a Frigate+ root is a custom model), plus the elected device
10129
+ * whose default an unpinned root runs. Recomputed when any changes — at
10130
+ * most once per catalog refresh.
9929
10131
  */
9930
10132
  rootSupplyCache = /* @__PURE__ */ new Map();
9931
10133
  /**
@@ -9934,14 +10136,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9934
10136
  * animal and vehicle classifiers by default, and that must be readable per
9935
10137
  * camera rather than inferred from a missing dispatch (D391).
9936
10138
  */
9937
- rootSupplyPlanFor(deviceId, steps) {
10139
+ rootSupplyPlanFor(deviceId, steps, deviceKey) {
9938
10140
  const byStep = this.customModels.current();
9939
10141
  const cached = this.rootSupplyCache.get(deviceId);
9940
- if (cached !== void 0 && cached.steps === steps && cached.byStep === byStep) return cached.plan;
9941
- const plan = planRootSupplies(steps, (stepId, modelId) => this.customModels.stepDefinition(stepId)?.models.find((m) => m.id === modelId));
10142
+ if (cached !== void 0 && cached.steps === steps && cached.deviceKey === deviceKey && cached.byStep === byStep) return cached.plan;
10143
+ const plan = planRootSupplies(steps, (stepId, modelId) => this.customModels.stepDefinition(stepId)?.models.find((m) => m.id === modelId), deviceKey);
9942
10144
  const announces = deriveDetailSteps(steps, plan);
9943
10145
  this.rootSupplyCache.set(deviceId, {
9944
10146
  steps,
10147
+ deviceKey,
9945
10148
  byStep,
9946
10149
  plan,
9947
10150
  announces
@@ -10084,7 +10287,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10084
10287
  * the elected pool pre-warms, D657) read. Pure: nothing is logged here.
10085
10288
  */
10086
10289
  placementOf(config, step, attachedByKey) {
10087
- return require_default_detection_model.placeDetailStep({
10290
+ return require_custom_model_catalog.placeDetailStep({
10088
10291
  step,
10089
10292
  effectiveDeviceKey: config?.deviceKey,
10090
10293
  roster: config?.inferenceDevices,
@@ -10106,7 +10309,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10106
10309
  return steps;
10107
10310
  }
10108
10311
  const attachedByKey = this.perDeviceAttachedCounts();
10109
- const result = require_default_detection_model.stepsForElectedPool(steps, isDetailPlaneStepId, (step) => this.placementOf(config, step, attachedByKey));
10312
+ const result = require_custom_model_catalog.stepsForElectedPool(steps, isDetailPlaneStepId, (step) => this.placementOf(config, step, attachedByKey));
10110
10313
  this.noteElectedPoolDelegation(deviceId, config.deviceKey, result.delegated);
10111
10314
  return result.delegated.length === 0 ? steps : result.steps;
10112
10315
  }
@@ -10797,7 +11000,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10797
11000
  return materialize;
10798
11001
  },
10799
11002
  ...useLocalFrameRef ? {
10800
- frameRegistry: require_default_detection_model.localFrameRegistry,
11003
+ frameRegistry: require_custom_model_catalog.localFrameRegistry,
10801
11004
  inlineWhen: () => runner.detectionBackpressure(config.deviceId),
10802
11005
  inlineViewSpec: SPLIT_DETECTION_VIEW,
10803
11006
  onFrameRef: async (frame, nativeCrop) => {
@@ -10817,7 +11020,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10817
11020
  const admitted = current.detection && (frame.preRoll === true ? runner.enqueuePreRollFrame(config.deviceId, frame, handle) : runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle));
10818
11021
  if (admitted) nativeCrop?.markInferred();
10819
11022
  if (current.motion) {
10820
- const motion = await resolveMotionFrameView(require_default_detection_model.localFrameRegistry, frame.frameRef);
11023
+ const motion = await resolveMotionFrameView(require_custom_model_catalog.localFrameRegistry, frame.frameRef);
10821
11024
  if (motion.kind === "view") runner.enqueueMotionFrame(config.deviceId, {
10822
11025
  data: Buffer.from(motion.data),
10823
11026
  width: motion.width,
@@ -11072,9 +11275,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11072
11275
  return null;
11073
11276
  }
11074
11277
  if (steps.length === 0) return null;
11075
- const rootSupplyPlan = this.rootSupplyPlanFor(deviceId, steps);
11278
+ const rootSupplyPlan = this.rootSupplyPlanFor(deviceId, steps, camConfig?.deviceKey);
11076
11279
  const packageFill = applyPackageFill(steps, rootSupplyPlan);
11077
- let dispatchSteps = packageFill.steps;
11280
+ const maskFill = applyMaskFill(packageFill.steps, rootSupplyPlan);
11281
+ let dispatchSteps = maskFill.steps;
11078
11282
  if (dispatchSteps.some((s) => s.addonId === "package-detection" && s.enabled !== false)) {
11079
11283
  this.refreshPackageZoneUnion(deviceId);
11080
11284
  const motionInZone = this.lastMotionNearPackageZone.get(deviceId) ?? this.lastAnalyzerDetected.get(deviceId) ?? false;
@@ -11144,7 +11348,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11144
11348
  });
11145
11349
  } catch (err) {
11146
11350
  const msg = require_dist.errMsg(err);
11147
- if (require_default_detection_model.isSessionDecodeReadGone(err)) {
11351
+ if (require_custom_model_catalog.isSessionDecodeReadGone(err)) {
11148
11352
  log.warn("runInference skipped — session decode frame gone", { meta: {
11149
11353
  error: msg,
11150
11354
  nodeId: this.nodeId,
@@ -11162,7 +11366,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11162
11366
  return null;
11163
11367
  }
11164
11368
  if (result === null) return null;
11165
- const looked = withPackageLook(applyRootSupplyAuthority(result, rootSupplyPlan), dispatchSteps, packageFill.filledByRoot);
11369
+ const looked = withPackageLook(applyRootMasks(applyRootSupplyAuthority(result, rootSupplyPlan), maskFill), dispatchSteps, packageFill.filledByRoot);
11166
11370
  return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
11167
11371
  }
11168
11372
  /**
@@ -11297,7 +11501,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11297
11501
  * on a miss — the frame is gone, and the full-frame result stands.
11298
11502
  */
11299
11503
  async resolveRecoveryCropView(ref, crop) {
11300
- const lease = require_default_detection_model.localFrameRegistry.acquire(ref);
11504
+ const lease = require_custom_model_catalog.localFrameRegistry.acquire(ref);
11301
11505
  if (!lease) return null;
11302
11506
  let reason = "error";
11303
11507
  try {
@@ -11822,7 +12026,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11822
12026
  },
11823
12027
  shouldMaterializeFrame: () => !admissionClosed,
11824
12028
  ...useLocalFrameRef ? {
11825
- frameRegistry: require_default_detection_model.localFrameRegistry,
12029
+ frameRegistry: require_custom_model_catalog.localFrameRegistry,
11826
12030
  inlineWhen: () => runner.detectionBackpressure(config.deviceId),
11827
12031
  inlineViewSpec: SPLIT_DETECTION_VIEW,
11828
12032
  onFrameRef: (frame, nativeCrop) => {