@camstack/addon-pipeline 1.2.332 → 1.2.334

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 (35) 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-Biwf5-ML.js → custom-model-catalog-CVeosmYm.js} +236 -165
  5. package/dist/{default-detection-model-CDffuH2m.mjs → custom-model-catalog-DoOVE7kz.mjs} +225 -166
  6. package/dist/detection-pipeline/index.js +70 -104
  7. package/dist/detection-pipeline/index.mjs +18 -52
  8. package/dist/{dist-DJV7Z6Bb.js → dist-C7v30DMP.js} +69 -22
  9. package/dist/{dist-BsT15i7i.mjs → dist-Dn3knu_K.mjs} +69 -22
  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-DWdekhW3.js} +1 -1
  13. package/dist/{node-Crsi_8ea.mjs → node-_BruMF4L.mjs} +1 -1
  14. package/dist/pipeline-runner/index.js +228 -42
  15. package/dist/pipeline-runner/index.mjs +206 -20
  16. package/dist/{process-memory-BJb7Ihdp.js → process-memory-CaDpfUdM.js} +1 -1
  17. package/dist/{process-memory-1mAUV8S1.mjs → process-memory-Dp-4bFOp.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-B_tboBcJ.mjs → segment-demux-js-B_-zBECg.mjs} +1 -1
  21. package/dist/{segment-demux-js-mK9IBGLf.js → segment-demux-js-BqPTLFZG.js} +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-CC0JSUlW.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BP8FFU2i.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-CpjKK9II.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-Bw8lUr35.mjs → hostInit-BEh-hm1x.mjs} +2 -2
  28. package/dist/stream-broker/index.js +33 -3
  29. package/dist/stream-broker/index.mjs +33 -3
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +1 -1
  32. package/python/postprocessors/test_yolo_seg_root.py +109 -0
  33. package/python/postprocessors/yolo.py +52 -13
  34. package/python/postprocessors/yolo_seg.py +79 -156
  35. 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-C7v30DMP.js");
7
+ const require_node = require("../node-DWdekhW3.js");
8
+ const require_custom_model_catalog = require("../custom-model-catalog-CVeosmYm.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).
@@ -2004,31 +2156,62 @@ var ROOT_SUPPLY_ROOT_STEP_ID = "object-detection";
2004
2156
  var PACKAGE_STEP_ID = "package-detection";
2005
2157
  var PACKAGE_THRESHOLD_KEY = "minConfidencePackage";
2006
2158
  var EMPTY_ROOT_SUPPLY_PLAN = { states: /* @__PURE__ */ new Map() };
2007
- function findStep(steps, addonId) {
2159
+ /** The step `addonId` when it is in the tree AND switched on — the per-camera gate's answer. */
2160
+ function findEnabledStep(steps, addonId) {
2008
2161
  for (const step of steps) {
2162
+ if (step.enabled === false) continue;
2009
2163
  if (step.addonId === addonId) return step;
2010
- const nested = step.children !== void 0 ? findStep(step.children, addonId) : void 0;
2164
+ const nested = step.children !== void 0 ? findEnabledStep(step.children, addonId) : void 0;
2011
2165
  if (nested !== void 0) return nested;
2012
2166
  }
2013
2167
  }
2014
2168
  /**
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.
2169
+ * The model an UNPINNED root runs on the elected device — the executor's own
2170
+ * rule (`resolveDefaultDetectionModel` on the device's backend, accelerator and
2171
+ * format), so the supplies are read off the model that really runs. Before
2172
+ * this, an unpinned root was judged as the step's flat `defaultModelId`
2173
+ * (`yolo26n`) on every accelerator, while an Intel GPU ran `yolov9m-320-int8`
2174
+ * and a Coral an SSD: `filledBy` named a model that was not running, and a
2175
+ * crop supply's input-size gate (D731) measured the wrong model. No elected
2176
+ * device (the runner's default pool) or an unknown backend ⇒ the flat default.
2177
+ */
2178
+ function unpinnedRootModelId(deviceKey, flatDefault) {
2179
+ if (deviceKey === void 0) return flatDefault;
2180
+ try {
2181
+ const engine = require_custom_model_catalog.resolveDeviceEngine(deviceKey);
2182
+ return require_custom_model_catalog.resolveDefaultDetectionModel(engine.backend, engine.device ?? null, engine.format);
2183
+ } catch {
2184
+ return flatDefault;
2185
+ }
2186
+ }
2187
+ /**
2188
+ * Plan a camera's root supplies from its configured tree.
2189
+ *
2190
+ * - The root's model is the one the executor runs: its pin, else the
2191
+ * hardware-aware default of the elected device (`deviceKey`).
2192
+ * - Only a step the camera RUNS is planned. A suppliable step that is absent
2193
+ * from the tree or switched off (the per-camera gate) has no state: the
2194
+ * operator switched that function off, so the root's labels are not made
2195
+ * authoritative for it, its root child boxes are removed, and nothing logs
2196
+ * that the root "supplies" it. Before this, a camera whose classifiers were
2197
+ * gated OFF logged `root model supplies a pipeline step` for both of them
2198
+ * and raised the root's labels to the detection score anyway (D736).
2018
2199
  */
2019
- function planRootSupplies(steps, resolveModel) {
2200
+ function planRootSupplies(steps, resolveModel, deviceKey) {
2020
2201
  if (steps === void 0) return EMPTY_ROOT_SUPPLY_PLAN;
2021
2202
  const root = steps.find((s) => s.addonId === "object-detection" && s.enabled !== false);
2022
2203
  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;
2204
+ const definition = require_custom_model_catalog.getStepDefinition(ROOT_SUPPLY_ROOT_STEP_ID);
2205
+ const rootModelId = root.modelId ?? unpinnedRootModelId(deviceKey, definition.defaultModelId);
2025
2206
  const supplies = require_dist.rootSuppliesOf(resolveModel(ROOT_SUPPLY_ROOT_STEP_ID, rootModelId), {
2026
2207
  labels: definition.labels ?? [],
2027
2208
  ...definition.classMap !== void 0 ? { classMap: definition.classMap } : {}
2028
2209
  });
2029
2210
  const states = /* @__PURE__ */ new Map();
2030
2211
  for (const stepId of require_dist.ROOT_SUPPLIABLE_STEP_IDS) {
2031
- const settings = findStep(steps, stepId)?.settings;
2212
+ const step = findEnabledStep(steps, stepId);
2213
+ if (step === void 0) continue;
2214
+ const settings = step.settings;
2032
2215
  states.set(stepId, {
2033
2216
  ...require_dist.resolveRootSupply(stepId, settings, supplies),
2034
2217
  ...settings !== void 0 ? { settings } : {}
@@ -2043,7 +2226,7 @@ function planRootSupplies(steps, resolveModel) {
2043
2226
  function suppliedSteps(plan) {
2044
2227
  const out = /* @__PURE__ */ new Map();
2045
2228
  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, {
2229
+ for (const [stepId, state] of plan.states) if (state.effective && state.supply !== void 0 && state.supply.kind !== "mask") out.set(stepId, {
2047
2230
  modelId: plan.rootModelId,
2048
2231
  kind: state.supply.kind
2049
2232
  });
@@ -2121,7 +2304,7 @@ function applyRootSupplyAuthority(result, plan) {
2121
2304
  }
2122
2305
  /** The root step's schema default for its class allow-list. */
2123
2306
  function rootDefaultMacroClasses() {
2124
- const field = require_default_detection_model.getStep(ROOT_SUPPLY_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
2307
+ const field = require_custom_model_catalog.getStep(ROOT_SUPPLY_ROOT_STEP_ID).getConfigSchema().find((f) => "key" in f && f.key === "enabledMacroClasses");
2125
2308
  const value = field !== void 0 && "default" in field ? field.default : void 0;
2126
2309
  return Array.isArray(value) ? value : [];
2127
2310
  }
@@ -3363,7 +3546,7 @@ var FaceOutsideParentLog = class {
3363
3546
  */
3364
3547
  /** The face detector's input side for the step as it will run. */
3365
3548
  function detectorInputPx(step) {
3366
- const def = require_default_detection_model.getStepDefinition(step.addonId);
3549
+ const def = require_custom_model_catalog.getStepDefinition(step.addonId);
3367
3550
  const modelId = step.modelId ?? def.defaultModelId;
3368
3551
  return def.models.find((m) => m.id === modelId)?.inputSize.width ?? 160;
3369
3552
  }
@@ -3474,7 +3657,7 @@ async function refineRootFace(deps, input, supplied, step, requested, deviceKey)
3474
3657
  }
3475
3658
  const chain = requested === void 0 ? step : pruneChildStepsToRequested(step, requested);
3476
3659
  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;
3660
+ const modelId = step.modelId ?? require_custom_model_catalog.getStepDefinition(step.addonId).defaultModelId;
3478
3661
  const embedded = await deps.runPipeline([chain], crop.jpeg, input.deviceId, deviceKey, nativeCropRef, refinedAsSupplied(face, step.addonId, modelId));
3479
3662
  if (embedded === null) return [toDetailResult(face, step.addonId, crop)];
3480
3663
  return embedded.detections.map((d) => toDetailResult(d, step.addonId, crop));
@@ -7438,7 +7621,7 @@ function writeRetainedDetectionFrame(store, deviceId, frame, retention) {
7438
7621
  if (retention) return retention.admitFrame(deviceId, frame);
7439
7622
  if (!store) return void 0;
7440
7623
  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;
7624
+ return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(require_custom_model_catalog.localFrameRegistry, frame.frameRef) : void 0) ?? void 0;
7442
7625
  }
7443
7626
  /**
7444
7627
  * Occupancy-burst safety timeout (both the dial path and the analyzer-tap
@@ -7555,7 +7738,7 @@ function collectEnabledStepIds(steps, acc) {
7555
7738
  */
7556
7739
  function tryGetStepDefinition(stepId) {
7557
7740
  try {
7558
- return require_default_detection_model.getStepDefinition(stepId);
7741
+ return require_custom_model_catalog.getStepDefinition(stepId);
7559
7742
  } catch {
7560
7743
  return null;
7561
7744
  }
@@ -8121,7 +8304,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8121
8304
  });
8122
8305
  this.retention = new OnDemandRetentionSession({
8123
8306
  store: this.retainedFrames,
8124
- registry: require_default_detection_model.localFrameRegistry,
8307
+ registry: require_custom_model_catalog.localFrameRegistry,
8125
8308
  telemetry: this.frameLazy,
8126
8309
  nodeId: this.nodeId,
8127
8310
  continuousRetain: isContinuousRetainEnabled()
@@ -8139,7 +8322,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8139
8322
  minThrottledFps: this.config.minThrottledFps,
8140
8323
  processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
8141
8324
  analyzeMotion: (deviceId, frame, handle) => this.runMotionAnalysis(deviceId, frame, handle),
8142
- releaseFrameRef: (ref, reason) => require_default_detection_model.localFrameRegistry.release(ref, reason),
8325
+ releaseFrameRef: (ref, reason) => require_custom_model_catalog.localFrameRegistry.release(ref, reason),
8143
8326
  releaseRetention: (handle, reason) => {
8144
8327
  this.retention?.release(handle, reason);
8145
8328
  this.frameCutters.drop(handle);
@@ -9085,7 +9268,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9085
9268
  detectionMode: config.detectionMode,
9086
9269
  ...config.detectionSources !== void 0 ? { detectionSources: config.detectionSources } : {},
9087
9270
  ...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) } : {},
9271
+ ...require_custom_model_catalog.rootObjectGate(config.steps) !== void 0 ? { objectGate: require_custom_model_catalog.rootObjectGate(config.steps) } : {},
9089
9272
  fps: config.detectionFps,
9090
9273
  motionCooldownMs: config.motionCooldownMs,
9091
9274
  occupancyRecheckEnabled: config.occupancyRecheckEnabled,
@@ -9484,7 +9667,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9484
9667
  }
9485
9668
  /** The evidence crop from the process-local lease, JPEG-encoded by the view. */
9486
9669
  async encodeEvidenceFromLease(ref, entry) {
9487
- const lease = require_default_detection_model.localFrameRegistry.acquire(ref);
9670
+ const lease = require_custom_model_catalog.localFrameRegistry.acquire(ref);
9488
9671
  if (!lease) return null;
9489
9672
  let reason = "error";
9490
9673
  try {
@@ -9574,7 +9757,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9574
9757
  /** A 16×32 RGB view of `crop`: the lease when there is one, else the inline buffer. */
9575
9758
  async readAppearanceView(frame, result, crop) {
9576
9759
  if (frame.frameRef !== void 0) {
9577
- const lease = require_default_detection_model.localFrameRegistry.acquire(frame.frameRef);
9760
+ const lease = require_custom_model_catalog.localFrameRegistry.acquire(frame.frameRef);
9578
9761
  if (lease) {
9579
9762
  let reason = "error";
9580
9763
  try {
@@ -9920,12 +10103,13 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9920
10103
  * Refreshed on the async boundaries (`runInference`, `runDetailSubtree`),
9921
10104
  * read synchronously by the placement.
9922
10105
  */
9923
- customModels = new require_default_detection_model.CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
10106
+ customModels = new require_custom_model_catalog.CustomModelCatalog({ warn: (message, extras) => this.ctxIfReady?.logger.warn(message, extras) });
9924
10107
  /**
9925
10108
  * Per camera, the root-supply plan (D730) and the announce derived from it,
9926
10109
  * 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.
10110
+ * snapshot (a Frigate+ root is a custom model), plus the elected device
10111
+ * whose default an unpinned root runs. Recomputed when any changes — at
10112
+ * most once per catalog refresh.
9929
10113
  */
9930
10114
  rootSupplyCache = /* @__PURE__ */ new Map();
9931
10115
  /**
@@ -9934,14 +10118,15 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9934
10118
  * animal and vehicle classifiers by default, and that must be readable per
9935
10119
  * camera rather than inferred from a missing dispatch (D391).
9936
10120
  */
9937
- rootSupplyPlanFor(deviceId, steps) {
10121
+ rootSupplyPlanFor(deviceId, steps, deviceKey) {
9938
10122
  const byStep = this.customModels.current();
9939
10123
  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));
10124
+ if (cached !== void 0 && cached.steps === steps && cached.deviceKey === deviceKey && cached.byStep === byStep) return cached.plan;
10125
+ const plan = planRootSupplies(steps, (stepId, modelId) => this.customModels.stepDefinition(stepId)?.models.find((m) => m.id === modelId), deviceKey);
9942
10126
  const announces = deriveDetailSteps(steps, plan);
9943
10127
  this.rootSupplyCache.set(deviceId, {
9944
10128
  steps,
10129
+ deviceKey,
9945
10130
  byStep,
9946
10131
  plan,
9947
10132
  announces
@@ -10084,7 +10269,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10084
10269
  * the elected pool pre-warms, D657) read. Pure: nothing is logged here.
10085
10270
  */
10086
10271
  placementOf(config, step, attachedByKey) {
10087
- return require_default_detection_model.placeDetailStep({
10272
+ return require_custom_model_catalog.placeDetailStep({
10088
10273
  step,
10089
10274
  effectiveDeviceKey: config?.deviceKey,
10090
10275
  roster: config?.inferenceDevices,
@@ -10106,7 +10291,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10106
10291
  return steps;
10107
10292
  }
10108
10293
  const attachedByKey = this.perDeviceAttachedCounts();
10109
- const result = require_default_detection_model.stepsForElectedPool(steps, isDetailPlaneStepId, (step) => this.placementOf(config, step, attachedByKey));
10294
+ const result = require_custom_model_catalog.stepsForElectedPool(steps, isDetailPlaneStepId, (step) => this.placementOf(config, step, attachedByKey));
10110
10295
  this.noteElectedPoolDelegation(deviceId, config.deviceKey, result.delegated);
10111
10296
  return result.delegated.length === 0 ? steps : result.steps;
10112
10297
  }
@@ -10797,7 +10982,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10797
10982
  return materialize;
10798
10983
  },
10799
10984
  ...useLocalFrameRef ? {
10800
- frameRegistry: require_default_detection_model.localFrameRegistry,
10985
+ frameRegistry: require_custom_model_catalog.localFrameRegistry,
10801
10986
  inlineWhen: () => runner.detectionBackpressure(config.deviceId),
10802
10987
  inlineViewSpec: SPLIT_DETECTION_VIEW,
10803
10988
  onFrameRef: async (frame, nativeCrop) => {
@@ -10817,7 +11002,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10817
11002
  const admitted = current.detection && (frame.preRoll === true ? runner.enqueuePreRollFrame(config.deviceId, frame, handle) : runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle));
10818
11003
  if (admitted) nativeCrop?.markInferred();
10819
11004
  if (current.motion) {
10820
- const motion = await resolveMotionFrameView(require_default_detection_model.localFrameRegistry, frame.frameRef);
11005
+ const motion = await resolveMotionFrameView(require_custom_model_catalog.localFrameRegistry, frame.frameRef);
10821
11006
  if (motion.kind === "view") runner.enqueueMotionFrame(config.deviceId, {
10822
11007
  data: Buffer.from(motion.data),
10823
11008
  width: motion.width,
@@ -11072,9 +11257,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11072
11257
  return null;
11073
11258
  }
11074
11259
  if (steps.length === 0) return null;
11075
- const rootSupplyPlan = this.rootSupplyPlanFor(deviceId, steps);
11260
+ const rootSupplyPlan = this.rootSupplyPlanFor(deviceId, steps, camConfig?.deviceKey);
11076
11261
  const packageFill = applyPackageFill(steps, rootSupplyPlan);
11077
- let dispatchSteps = packageFill.steps;
11262
+ const maskFill = applyMaskFill(packageFill.steps, rootSupplyPlan);
11263
+ let dispatchSteps = maskFill.steps;
11078
11264
  if (dispatchSteps.some((s) => s.addonId === "package-detection" && s.enabled !== false)) {
11079
11265
  this.refreshPackageZoneUnion(deviceId);
11080
11266
  const motionInZone = this.lastMotionNearPackageZone.get(deviceId) ?? this.lastAnalyzerDetected.get(deviceId) ?? false;
@@ -11144,7 +11330,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11144
11330
  });
11145
11331
  } catch (err) {
11146
11332
  const msg = require_dist.errMsg(err);
11147
- if (require_default_detection_model.isSessionDecodeReadGone(err)) {
11333
+ if (require_custom_model_catalog.isSessionDecodeReadGone(err)) {
11148
11334
  log.warn("runInference skipped — session decode frame gone", { meta: {
11149
11335
  error: msg,
11150
11336
  nodeId: this.nodeId,
@@ -11162,7 +11348,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11162
11348
  return null;
11163
11349
  }
11164
11350
  if (result === null) return null;
11165
- const looked = withPackageLook(applyRootSupplyAuthority(result, rootSupplyPlan), dispatchSteps, packageFill.filledByRoot);
11351
+ const looked = withPackageLook(applyRootMasks(applyRootSupplyAuthority(result, rootSupplyPlan), maskFill), dispatchSteps, packageFill.filledByRoot);
11166
11352
  return this.recoverSmallSubjects(deviceId, frame, looked, dispatchSteps, camConfig?.deviceKey);
11167
11353
  }
11168
11354
  /**
@@ -11297,7 +11483,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11297
11483
  * on a miss — the frame is gone, and the full-frame result stands.
11298
11484
  */
11299
11485
  async resolveRecoveryCropView(ref, crop) {
11300
- const lease = require_default_detection_model.localFrameRegistry.acquire(ref);
11486
+ const lease = require_custom_model_catalog.localFrameRegistry.acquire(ref);
11301
11487
  if (!lease) return null;
11302
11488
  let reason = "error";
11303
11489
  try {
@@ -11822,7 +12008,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
11822
12008
  },
11823
12009
  shouldMaterializeFrame: () => !admissionClosed,
11824
12010
  ...useLocalFrameRef ? {
11825
- frameRegistry: require_default_detection_model.localFrameRegistry,
12011
+ frameRegistry: require_custom_model_catalog.localFrameRegistry,
11826
12012
  inlineWhen: () => runner.detectionBackpressure(config.deviceId),
11827
12013
  inlineViewSpec: SPLIT_DETECTION_VIEW,
11828
12014
  onFrameRef: (frame, nativeCrop) => {