@camstack/addon-pipeline 1.2.106 → 1.2.115

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/dist/{addon-utils-BEzVJ0xg.js → addon-utils-C0ht2KPO.js} +51 -15
  2. package/dist/{addon-utils-A2S9D7pu.mjs → addon-utils-CgBCF-mE.mjs} +50 -14
  3. package/dist/audio-analyzer/index.js +3 -3
  4. package/dist/audio-analyzer/index.mjs +3 -3
  5. package/dist/detection-pipeline/index.js +198 -38
  6. package/dist/detection-pipeline/index.mjs +197 -37
  7. package/dist/{dist-BSeR_hVv.js → dist-BqIbYW1A.js} +290 -17
  8. package/dist/{dist-lITI1Bn5.mjs → dist-DGQkkWc6.mjs} +273 -18
  9. package/dist/{event-loop-stall-monitor-0VvjTRDP.mjs → event-loop-stall-monitor-D5jK5v4Z.mjs} +223 -2
  10. package/dist/{event-loop-stall-monitor-BNsp9k8r.js → event-loop-stall-monitor-ylMGpz48.js} +234 -1
  11. package/dist/{lazy-sharp-CBohM8D8.js → lazy-sharp-cOsy7l_P.js} +1 -1
  12. package/dist/motion-wasm/index.js +2 -2
  13. package/dist/motion-wasm/index.mjs +1 -1
  14. package/dist/pipeline-runner/index.js +915 -107
  15. package/dist/pipeline-runner/index.mjs +914 -106
  16. package/dist/{process-memory-C33u3tQz.js → process-memory-45juuHpN.js} +1 -1
  17. package/dist/{process-memory-CEKVKfwg.mjs → process-memory-BtT7WfbJ.mjs} +1 -1
  18. package/dist/recorder/index.js +187 -47
  19. package/dist/recorder/index.mjs +187 -47
  20. package/dist/session-decode/decode-worker-child.js +30 -10
  21. package/dist/session-decode/decode-worker-child.mjs +29 -9
  22. package/dist/stream-broker/_stub.js +2 -2
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-w2XLD3uI.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-C_SFFNGT.mjs} +3 -3
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B1LDFHY9.mjs +26 -0
  25. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DsIi4G5Q.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BAMyzCWw.mjs} +1 -1
  26. package/dist/stream-broker/{hostInit-B-h24cDy.mjs → hostInit-CG28NvlV.mjs} +3 -3
  27. package/dist/stream-broker/index.js +279 -198
  28. package/dist/stream-broker/index.mjs +279 -198
  29. package/dist/stream-broker/remoteEntry.js +1 -1
  30. package/dist/{worker-protocol-CiKfal2P.js → worker-protocol-CIrqGbpG.js} +6 -3
  31. package/dist/{worker-protocol-BDiL8TBm.mjs → worker-protocol-DyocTIx_.mjs} +6 -3
  32. package/package.json +1 -1
  33. package/python/inference_pool.py +4 -1
  34. package/python/test_inference_pool_preprocess.py +87 -16
  35. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DvZWLWk1.mjs +0 -26
@@ -2,11 +2,11 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-BSeR_hVv.js");
6
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-BNsp9k8r.js");
7
- const require_lazy_sharp = require("../lazy-sharp-CBohM8D8.js");
8
- const require_process_memory = require("../process-memory-C33u3tQz.js");
9
- const require_addon_utils = require("../addon-utils-BEzVJ0xg.js");
5
+ const require_dist = require("../dist-BqIbYW1A.js");
6
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-ylMGpz48.js");
7
+ const require_lazy_sharp = require("../lazy-sharp-cOsy7l_P.js");
8
+ const require_process_memory = require("../process-memory-45juuHpN.js");
9
+ const require_addon_utils = require("../addon-utils-C0ht2KPO.js");
10
10
  let sharp = require("sharp");
11
11
  sharp = require_dist.__toESM(sharp);
12
12
  let node_child_process = require("node:child_process");
@@ -264,7 +264,13 @@ var OBJECT_DETECTION_STEP_ID = "object-detection";
264
264
  * `null` → the caller substitutes the step's own `defaultModelId` (`yolo26n`).
265
265
  * INTEL NPU + iGPU default to OUR YOLOv9 (int8 @320): yolo26 does NOT compile on
266
266
  * the Intel NPU (arch incompat → 0 frames), while yolov9 runs on it (~298 fps
267
- * measured). Apple ANE stays on yolo26 until the CoreML yolov9 build lands.
267
+ * measured). Apple ANE uses the CoreML yolov9 @320 build.
268
+ *
269
+ * Do **not** promote these ids to `yolov9m-640` / `*-640`. The controlled
270
+ * evaluation recovered 0/3 misses at the current 0.50 threshold and failed the
271
+ * false-positive and capacity gates — see
272
+ * `docs/benchmarks/pipeline-frame-model-eval.md`. 640 remains an explicit
273
+ * operator pick, not a hardware-aware default.
268
274
  */
269
275
  var MODEL_BY_CLASS = {
270
276
  "apple-ane": "yolov9m-320",
@@ -1374,7 +1380,7 @@ function macroOfMinConfidenceKey(key) {
1374
1380
  */
1375
1381
  function reachableMacros(def, entry) {
1376
1382
  if (entry.labels.length === 0) return void 0;
1377
- const map = def.classMap;
1383
+ const map = entry.classMap ?? def.classMap;
1378
1384
  if (!map) return new Set(entry.labels.map((l) => l.id));
1379
1385
  if (map.preserveOriginal) return void 0;
1380
1386
  const macros = /* @__PURE__ */ new Set();
@@ -1394,7 +1400,7 @@ function reachableMacros(def, entry) {
1394
1400
  */
1395
1401
  function collectClassMapDropWarning(def, entry) {
1396
1402
  if (entry.labels.length === 0) return null;
1397
- const map = def.classMap;
1403
+ const map = entry.classMap ?? def.classMap;
1398
1404
  if (!map || map.preserveOriginal) return null;
1399
1405
  const unmapped = entry.labels.filter((l) => map.mapping[l.id] === void 0).map((l) => l.id);
1400
1406
  if (unmapped.length === 0) return null;
@@ -4170,7 +4176,7 @@ function applyChildOutput(parent, childStep, output, stepLatencyMs, ctx) {
4170
4176
  switch (output.kind) {
4171
4177
  case "detections":
4172
4178
  if (childStep.definition.slot === "refiner") {
4173
- const classMap = childStep.definition.classMap?.mapping;
4179
+ const classMap = childStep.effectiveClassMap?.mapping;
4174
4180
  const parentMacro = parent.macroClass;
4175
4181
  const best = output.detections.filter((d) => {
4176
4182
  if (d.mask === void 0) return false;
@@ -4510,6 +4516,29 @@ function reprojectCropZoneDetections(output, cropOrigin) {
4510
4516
  }))
4511
4517
  };
4512
4518
  }
4519
+ function reprojectViewDetections(output, geometry) {
4520
+ const mapPoint = (x, y) => ({
4521
+ x: geometry.sourceCrop.left + x / geometry.scale.x,
4522
+ y: geometry.sourceCrop.top + y / geometry.scale.y
4523
+ });
4524
+ return {
4525
+ kind: "detections",
4526
+ detections: output.detections.map((det) => {
4527
+ const origin = mapPoint(det.bbox[0], det.bbox[1]);
4528
+ const end = mapPoint(det.bbox[2], det.bbox[3]);
4529
+ return {
4530
+ ...det,
4531
+ bbox: [
4532
+ origin.x,
4533
+ origin.y,
4534
+ end.x,
4535
+ end.y
4536
+ ],
4537
+ ...det.landmarks ? { landmarks: det.landmarks.map((landmark) => mapPoint(landmark.x, landmark.y)) } : {}
4538
+ };
4539
+ })
4540
+ };
4541
+ }
4513
4542
  var PipelineExecutor = class {
4514
4543
  opts;
4515
4544
  /** Bounds the full-frame-guard drop log: first example per (device,
@@ -4571,7 +4600,7 @@ var PipelineExecutor = class {
4571
4600
  *
4572
4601
  * @returns FrameResult + optional trace
4573
4602
  */
4574
- async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts, nativeCropProvider, cropZoneBbox) {
4603
+ async run(tree, rootInput, fullFrameJpegProvider, imageWidth, imageHeight, deviceId, runOpts, nativeCropProvider, cropZoneBbox, rootInputViewProvider) {
4575
4604
  const startMs = Date.now();
4576
4605
  const verbosity = runOpts?.traceVerbosity ?? "off";
4577
4606
  const traceBuilder = new ExecutionTraceBuilder(verbosity, deviceId, imageWidth, imageHeight, this.opts.engineRuntime);
@@ -4619,18 +4648,20 @@ var PipelineExecutor = class {
4619
4648
  };
4620
4649
  for (const rootStep of tree.roots) {
4621
4650
  if (debug) console.log(`[executor] rootStep=${rootStep.stepId} settings=${JSON.stringify(rootStep.settings ?? {})}`);
4622
- const cropPrep = rootStep.definition.extractMode === "crop-zone" && cropZoneBbox !== void 0 ? await this.prepareCropZoneInput(cropZoneBbox, fullFrameJpegProvider, imageWidth, imageHeight, deviceId) : null;
4651
+ const rootCrop = rootStep.definition.extractMode === "crop-zone" ? cropZoneBbox : void 0;
4652
+ const rootView = rootInputViewProvider ? await rootInputViewProvider(rootStep, rootCrop) : null;
4653
+ const cropPrep = !rootView && rootCrop !== void 0 ? await this.prepareCropZoneInput(rootCrop, fullFrameJpegProvider, imageWidth, imageHeight, deviceId) : null;
4623
4654
  const rootStart = Date.now();
4624
- const rawRootOutput = await this.executeStep(rootStep, cropPrep?.input ?? withInferenceDeviceId(rootInput, deviceId), cropPrep?.width ?? imageWidth, cropPrep?.height ?? imageHeight, "full-frame", void 0, void 0, traceBuilder, stepTimings, poolAgg, rootShed);
4655
+ const rawRootOutput = await this.executeStep(rootStep, rootView?.input ?? cropPrep?.input ?? withInferenceDeviceId(rootInput, deviceId), rootView?.width ?? cropPrep?.width ?? imageWidth, rootView?.height ?? cropPrep?.height ?? imageHeight, "full-frame", void 0, void 0, traceBuilder, stepTimings, poolAgg, rootShed);
4625
4656
  const rootMs = Date.now() - rootStart;
4626
4657
  if (rawRootOutput === null) continue;
4627
- const rootOutput = cropPrep && rawRootOutput.kind === "detections" ? reprojectCropZoneDetections(rawRootOutput, cropPrep.origin) : rawRootOutput;
4658
+ const rootOutput = rootView && rawRootOutput.kind === "detections" ? reprojectViewDetections(rawRootOutput, rootView.geometry) : cropPrep && rawRootOutput.kind === "detections" ? reprojectCropZoneDetections(rawRootOutput, cropPrep.origin) : rawRootOutput;
4628
4659
  if (rootOutput.kind !== "detections") {
4629
4660
  if (isEnrichmentOutput(rootOutput)) {
4630
4661
  const mutable = this.synthesizeRootDetection(rootOutput, rootStep, idGen, rootMs, imageWidth, imageHeight);
4631
4662
  applyChildOutput(mutable, rootStep, rootOutput, rootMs, ctx);
4632
4663
  try {
4633
- await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeCropProvider);
4664
+ await this.executeChildren(rootStep.children, mutable, rootView?.jpegProvider ?? fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeCropProvider);
4634
4665
  } catch (err) {
4635
4666
  this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
4636
4667
  tags: { deviceId },
@@ -4648,12 +4679,12 @@ var PipelineExecutor = class {
4648
4679
  }
4649
4680
  for (const det of rootOutput.detections) {
4650
4681
  const mutable = toMutableRootDetection(det, rootStep, idGen, rootMs);
4651
- if (rootStep.definition.classMap) {
4652
- const mapped = rootStep.definition.classMap.mapping[det.class];
4682
+ if (rootStep.effectiveClassMap) {
4683
+ const mapped = rootStep.effectiveClassMap.mapping[det.class];
4653
4684
  if (mapped) {
4654
4685
  mutable.originalClass = det.class;
4655
4686
  mutable.macroClass = mapped;
4656
- } else if (!rootStep.definition.classMap.preserveOriginal) {
4687
+ } else if (!rootStep.effectiveClassMap.preserveOriginal) {
4657
4688
  (discarded ??= []).push({
4658
4689
  bbox: [
4659
4690
  mutable.bbox[0],
@@ -4735,7 +4766,7 @@ var PipelineExecutor = class {
4735
4766
  }
4736
4767
  });
4737
4768
  try {
4738
- await this.executeChildren(rootStep.children, mutable, fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeCropProvider);
4769
+ await this.executeChildren(rootStep.children, mutable, rootView?.jpegProvider ?? fullFrameJpegProvider, imageWidth, imageHeight, traceBuilder, stepTimings, ctx, poolAgg, runOpts?.plane, deviceId, nativeCropProvider);
4739
4770
  } catch (err) {
4740
4771
  this.opts.logger?.warn("Pipeline child execution failed — keeping parent detection", {
4741
4772
  tags: { deviceId },
@@ -5155,6 +5186,59 @@ function capitalize(s) {
5155
5186
  return s.charAt(0).toUpperCase() + s.slice(1);
5156
5187
  }
5157
5188
  //#endregion
5189
+ //#region src/session-decode/frame-view-payload.ts
5190
+ var FRAME_VIEW_PAYLOAD_POLICY = "jpeg";
5191
+ //#endregion
5192
+ //#region src/detection-pipeline/root-frame-view.ts
5193
+ /** Resolve the pre-padding content requested from the decode worker for one root. */
5194
+ function resolveRootFrameViewSpec(model, source, crop, format = FRAME_VIEW_PAYLOAD_POLICY) {
5195
+ const sourceWidth = crop?.width ?? source.width;
5196
+ const sourceHeight = crop?.height ?? source.height;
5197
+ if (model.preprocessMode !== "letterbox") return {
5198
+ ...crop ? { crop } : {},
5199
+ content: {
5200
+ width: model.inputSize.width,
5201
+ height: model.inputSize.height
5202
+ },
5203
+ fit: "stretch",
5204
+ format
5205
+ };
5206
+ const scale = Math.min(model.inputSize.width / sourceWidth, model.inputSize.height / sourceHeight);
5207
+ return {
5208
+ ...crop ? { crop } : {},
5209
+ content: {
5210
+ width: Math.max(1, Math.round(sourceWidth * scale)),
5211
+ height: Math.max(1, Math.round(sourceHeight * scale))
5212
+ },
5213
+ fit: "contain",
5214
+ format
5215
+ };
5216
+ }
5217
+ //#endregion
5218
+ //#region src/detection-pipeline/frame-ref-view-provider.ts
5219
+ var LocalFrameRefMissError = class extends Error {
5220
+ reason;
5221
+ constructor(reason) {
5222
+ super(`local frameRef miss: ${reason}`);
5223
+ this.reason = reason;
5224
+ this.name = "LocalFrameRefMissError";
5225
+ }
5226
+ };
5227
+ /** Bind one registry lease for all heterogeneous roots of a pipeline invocation. */
5228
+ function createRootFrameViewResolver(registry, ref) {
5229
+ const lease = registry.acquire(ref);
5230
+ if (!lease) throw new LocalFrameRefMissError(ref.registryId === registry.registryId ? "stale-ref" : "foreign-registry");
5231
+ const resolve = async (root, crop) => {
5232
+ const model = root.definition.models.find((candidate) => candidate.id === root.modelId);
5233
+ if (!model) throw new Error(`frame view model not found: ${root.modelId}`);
5234
+ const spec = resolveRootFrameViewSpec(model, ref, crop);
5235
+ const result = await lease.resolve(spec);
5236
+ if (result.kind === "miss") throw new LocalFrameRefMissError(result.reason);
5237
+ return result;
5238
+ };
5239
+ return Object.assign(resolve, { release: (reason) => lease.release(reason) });
5240
+ }
5241
+ //#endregion
5158
5242
  //#region src/detection-pipeline/pipeline/native-crop-compose.ts
5159
5243
  /** Clamp `v` into `[lo, hi]`. */
5160
5244
  function clamp(v, lo, hi) {
@@ -5194,23 +5278,32 @@ function composeCropRoiToFrameNorm(roi, cropFrameSpace, frameWidth, frameHeight)
5194
5278
  //#endregion
5195
5279
  //#region src/detection-pipeline/pipeline/tree-builder.ts
5196
5280
  /**
5281
+ * Effective class map for `(definition, modelId)` without writing back to the
5282
+ * shared StepDefinition. Custom models missing from `definition.models` are
5283
+ * resolved through the optional registry callback.
5284
+ */
5285
+ function resolveEffectiveClassMap(definition, modelId, resolveModel) {
5286
+ return (definition.models.find((model) => model.id === modelId) ?? resolveModel?.(definition.id, modelId))?.classMap ?? definition.classMap;
5287
+ }
5288
+ /**
5197
5289
  * Build an executable tree from user config.
5198
5290
  *
5199
5291
  * @param steps - User-configured pipeline steps (from PipelineDefaultStep[])
5200
5292
  * @param getEngine - Function that returns an IInferenceEngine for a step ID.
5201
5293
  * Throws if step not loaded.
5202
5294
  */
5203
- function buildExecutableTree(steps, getEngine) {
5204
- return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine)) };
5295
+ function buildExecutableTree(steps, getEngine, resolveModel) {
5296
+ return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine, resolveModel)) };
5205
5297
  }
5206
- function buildNode(step, getEngine) {
5298
+ function buildNode(step, getEngine, resolveModel) {
5207
5299
  const definition = require_event_loop_stall_monitor.getStepDefinition(step.addonId);
5208
5300
  const engine = getEngine(step.addonId);
5209
- const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine));
5301
+ const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine, resolveModel));
5210
5302
  const mergedSettings = {
5211
5303
  ...collectSchemaDefaults(step.addonId),
5212
5304
  ...step.settings
5213
5305
  };
5306
+ const effectiveClassMap = resolveEffectiveClassMap(definition, step.modelId, resolveModel);
5214
5307
  return {
5215
5308
  stepId: step.addonId,
5216
5309
  definition,
@@ -5219,7 +5312,8 @@ function buildNode(step, getEngine) {
5219
5312
  inputClasses: definition.inputClasses ?? [],
5220
5313
  enabled: step.enabled,
5221
5314
  children,
5222
- ...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {}
5315
+ ...Object.keys(mergedSettings).length > 0 ? { settings: mergedSettings } : {},
5316
+ ...effectiveClassMap !== void 0 ? { effectiveClassMap } : {}
5223
5317
  };
5224
5318
  }
5225
5319
  /**
@@ -5454,7 +5548,7 @@ function collectUnknownAddonIssues(steps, getStepDef = require_event_loop_stall_
5454
5548
  * `diagnostics.substitutions` instead of happening silently. The caller
5455
5549
  * (the provider) owns logging + dedup for both.
5456
5550
  */
5457
- function resolveInputSteps(steps, format, engine, clusterModels) {
5551
+ function resolveInputSteps(steps, format, engine, clusterModels, clusterSettings) {
5458
5552
  const resolvedSteps = [];
5459
5553
  const unknownAddonIds = [];
5460
5554
  const substitutions = [];
@@ -5476,12 +5570,13 @@ function resolveInputSteps(steps, format, engine, clusterModels) {
5476
5570
  format: verdict.format,
5477
5571
  formatsShipped: verdict.formatsShipped
5478
5572
  });
5479
- const childResult = s.children ? resolveInputSteps(s.children, format, engine, clusterModels) : null;
5573
+ const childResult = s.children ? resolveInputSteps(s.children, format, engine, clusterModels, clusterSettings) : null;
5480
5574
  if (childResult) {
5481
5575
  unknownAddonIds.push(...childResult.diagnostics.unknownAddonIds);
5482
5576
  substitutions.push(...childResult.diagnostics.substitutions);
5483
5577
  clusterRefusals.push(...childResult.diagnostics.clusterRefusals);
5484
5578
  }
5579
+ const settings = require_dist.overlayClusterStepSettings(s.addonId, s.settings, clusterSettings ?? {});
5485
5580
  resolvedSteps.push({
5486
5581
  addonId: s.addonId,
5487
5582
  addonName: def.name,
@@ -5491,7 +5586,7 @@ function resolveInputSteps(steps, format, engine, clusterModels) {
5491
5586
  enabled: s.enabled,
5492
5587
  modelId: verdict.modelId,
5493
5588
  children: childResult ? childResult.steps : [],
5494
- ...s.settings ? { settings: { ...s.settings } } : {}
5589
+ ...settings ? { settings } : {}
5495
5590
  });
5496
5591
  continue;
5497
5592
  }
@@ -5504,12 +5599,13 @@ function resolveInputSteps(steps, format, engine, clusterModels) {
5504
5599
  running: runningModelId,
5505
5600
  format
5506
5601
  });
5507
- const childResult = s.children ? resolveInputSteps(s.children, format, engine, clusterModels) : null;
5602
+ const childResult = s.children ? resolveInputSteps(s.children, format, engine, clusterModels, clusterSettings) : null;
5508
5603
  if (childResult) {
5509
5604
  unknownAddonIds.push(...childResult.diagnostics.unknownAddonIds);
5510
5605
  substitutions.push(...childResult.diagnostics.substitutions);
5511
5606
  clusterRefusals.push(...childResult.diagnostics.clusterRefusals);
5512
5607
  }
5608
+ const settings = require_dist.overlayClusterStepSettings(s.addonId, s.settings, clusterSettings ?? {});
5513
5609
  resolvedSteps.push({
5514
5610
  addonId: s.addonId,
5515
5611
  addonName: def.name,
@@ -5519,7 +5615,7 @@ function resolveInputSteps(steps, format, engine, clusterModels) {
5519
5615
  enabled: s.enabled,
5520
5616
  modelId: runningModelId,
5521
5617
  children: childResult ? childResult.steps : [],
5522
- ...s.settings ? { settings: { ...s.settings } } : {}
5618
+ ...settings ? { settings } : {}
5523
5619
  });
5524
5620
  }
5525
5621
  return {
@@ -5608,6 +5704,7 @@ var CLUSTER_MODEL_OWNER_ADDON_ID = "pipeline-orchestrator";
5608
5704
  /** TTL-cached read of the cluster model row. */
5609
5705
  var ClusterModelSource = class {
5610
5706
  models = require_dist.DEFAULT_CLUSTER_STEP_MODELS;
5707
+ settings = require_dist.DEFAULT_CLUSTER_STEP_SETTINGS;
5611
5708
  lastReadAtMs = Number.NEGATIVE_INFINITY;
5612
5709
  inFlight = null;
5613
5710
  logger;
@@ -5622,6 +5719,10 @@ var ClusterModelSource = class {
5622
5719
  current() {
5623
5720
  return this.models;
5624
5721
  }
5722
+ /** Cluster-scoped step knobs in force right now (admission floors, …). */
5723
+ currentSettings() {
5724
+ return this.settings;
5725
+ }
5625
5726
  /**
5626
5727
  * Re-read if the cached row has expired. Concurrent callers share one
5627
5728
  * in-flight read; a failure keeps the LAST KNOWN row.
@@ -5637,9 +5738,16 @@ var ClusterModelSource = class {
5637
5738
  }
5638
5739
  async read(api) {
5639
5740
  try {
5640
- const next = require_dist.pickClusterStepModels(await api.addonSettings.getGlobalSettings.query({ addonId: CLUSTER_MODEL_OWNER_ADDON_ID }));
5741
+ const view = await api.addonSettings.getGlobalSettings.query({ addonId: CLUSTER_MODEL_OWNER_ADDON_ID });
5742
+ const next = require_dist.pickClusterStepModels(view);
5743
+ const nextSettings = require_dist.pickClusterStepSettings(view);
5641
5744
  this.lastReadAtMs = this.now();
5642
5745
  const changed = Object.keys(next).filter((stepId) => next[stepId] !== this.models[stepId]);
5746
+ const settingsChanged = Object.keys(nextSettings).some((stepId) => {
5747
+ const from = this.settings[stepId] ?? {};
5748
+ const to = nextSettings[stepId] ?? {};
5749
+ return [...new Set([...Object.keys(from), ...Object.keys(to)])].some((key) => from[key] !== to[key]);
5750
+ });
5643
5751
  if (changed.length > 0) {
5644
5752
  this.logger.info("cluster step model changed — vectors already stored by the previous model need a re-embed pass", { meta: { changed: changed.map((stepId) => ({
5645
5753
  stepId,
@@ -5648,6 +5756,13 @@ var ClusterModelSource = class {
5648
5756
  })) } });
5649
5757
  this.models = next;
5650
5758
  }
5759
+ if (settingsChanged) {
5760
+ this.logger.info("cluster step settings changed", { meta: {
5761
+ from: { ...this.settings },
5762
+ to: { ...nextSettings }
5763
+ } });
5764
+ this.settings = nextSettings;
5765
+ }
5651
5766
  } catch (err) {
5652
5767
  this.logger.warn("cluster step model read failed — keeping the last known row", { meta: {
5653
5768
  owner: CLUSTER_MODEL_OWNER_ADDON_ID,
@@ -7077,17 +7192,30 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7077
7192
  let jpegProvider;
7078
7193
  const sources = [
7079
7194
  input.frame ? "frame" : null,
7195
+ input.frameRef ? "frameRef" : null,
7080
7196
  input.frameHandle ? "frameHandle" : null,
7081
7197
  input.image ? "image" : null,
7082
7198
  input.imageBase64 ? "imageBase64" : null,
7083
7199
  input.referenceImage ? "referenceImage" : null
7084
7200
  ].filter((s) => s !== null);
7085
- if (sources.length === 0) throw new Error("runPipeline requires exactly one of: frame, frameHandle, image, imageBase64, referenceImage");
7201
+ if (sources.length === 0) throw new Error("runPipeline requires exactly one of: frame, frameRef, frameHandle, image, imageBase64, referenceImage");
7086
7202
  if (sources.length > 1) throw new Error(`runPipeline received conflicting image sources: ${sources.join(", ")}`);
7087
7203
  if (input.frameHandle) return this.emptyFrameHandleResult(input);
7088
7204
  const runtimeFrame = input.frame ?? void 0;
7205
+ const runtimeFrameRef = input.frameRef;
7089
7206
  const decodeT0 = performance.now();
7090
- if (runtimeFrame) {
7207
+ if (runtimeFrameRef) {
7208
+ imageWidth = runtimeFrameRef.width;
7209
+ imageHeight = runtimeFrameRef.height;
7210
+ rootInput = {
7211
+ kind: "jpeg",
7212
+ data: Buffer.alloc(0)
7213
+ };
7214
+ jpegProvider = async () => {
7215
+ throw new Error("frameRef root view was requested before model resolution");
7216
+ };
7217
+ emit(`FrameRef: ${imageWidth}×${imageHeight} (${runtimeFrameRef.format})`);
7218
+ } else if (runtimeFrame) {
7091
7219
  const frame = runtimeFrame;
7092
7220
  imageWidth = frame.width;
7093
7221
  imageHeight = frame.height;
@@ -7287,9 +7415,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7287
7415
  emit(`All models loaded`);
7288
7416
  }
7289
7417
  emit("Running inference...");
7290
- const tree = buildExecutableTree(benchmarkSteps, (stepId) => dispatchFactory.getEngine(stepId));
7418
+ const tree = buildExecutableTree(benchmarkSteps, (stepId) => dispatchFactory.getEngine(stepId), this.customModelResolver);
7291
7419
  setupMs = performance.now() - wallT0 - decodeMs;
7292
- const isRuntime = Boolean(runtimeFrame);
7420
+ const isRuntime = Boolean(runtimeFrame || runtimeFrameRef);
7293
7421
  const effectiveDeviceId = input.deviceId ?? 0;
7294
7422
  const deviceOverrides = effectiveDeviceId > 0 ? await this.readDeviceStore(effectiveDeviceId) : {};
7295
7423
  const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
@@ -7300,10 +7428,42 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7300
7428
  const proxy = this.deviceProxies.get(effectiveDeviceId);
7301
7429
  if (proxy) cropZoneBbox = resolvePackageCropBbox(proxy.state.zones.value?.zones ?? [], proxy.state.zoneRules.value?.package ?? [], imageWidth, imageHeight) ?? void 0;
7302
7430
  }
7303
- const { result, trace } = await executor.run(effectiveTree, rootInput, jpegProvider, imageWidth, imageHeight, effectiveDeviceId, {
7304
- traceVerbosity: isRuntime ? this.eventBus ? "summary" : "off" : "full",
7305
- plane: input.plane
7306
- }, nativeCropProvider, cropZoneBbox);
7431
+ let frameViewResolver;
7432
+ let rootInputViewProvider;
7433
+ if (runtimeFrameRef) {
7434
+ frameViewResolver = createRootFrameViewResolver(require_event_loop_stall_monitor.localFrameRegistry, runtimeFrameRef);
7435
+ rootInputViewProvider = async (root, crop) => {
7436
+ const view = await frameViewResolver(root, crop ? {
7437
+ left: crop[0],
7438
+ top: crop[1],
7439
+ width: crop[2] - crop[0],
7440
+ height: crop[3] - crop[1]
7441
+ } : void 0);
7442
+ const data = Buffer.from(view.data.buffer, view.data.byteOffset, view.data.byteLength);
7443
+ return {
7444
+ input: {
7445
+ kind: "jpeg",
7446
+ data
7447
+ },
7448
+ jpegProvider: async () => data,
7449
+ width: view.width,
7450
+ height: view.height,
7451
+ geometry: view.geometry
7452
+ };
7453
+ };
7454
+ }
7455
+ let executionSucceeded = false;
7456
+ let execution;
7457
+ try {
7458
+ execution = await executor.run(effectiveTree, rootInput, jpegProvider, imageWidth, imageHeight, effectiveDeviceId, {
7459
+ traceVerbosity: isRuntime ? this.eventBus ? "summary" : "off" : "full",
7460
+ plane: input.plane
7461
+ }, nativeCropProvider, cropZoneBbox, rootInputViewProvider);
7462
+ executionSucceeded = true;
7463
+ } finally {
7464
+ frameViewResolver?.release(executionSucceeded ? "success" : "error");
7465
+ }
7466
+ const { result, trace } = execution;
7307
7467
  if (isRuntime) {
7308
7468
  if (trace && this.eventBus) this.eventBus.emit(require_dist.createEvent(require_dist.EventCategory.PipelineTrace, {
7309
7469
  type: "device",
@@ -7504,7 +7664,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7504
7664
  backend: this.currentEngine.backend,
7505
7665
  device: this.currentEngine.device ?? null
7506
7666
  }, clusterRefusalsOut) {
7507
- const { steps: resolvedSteps, diagnostics } = resolveInputSteps(steps, format, engine, this.clusterModels.current());
7667
+ const { steps: resolvedSteps, diagnostics } = resolveInputSteps(steps, format, engine, this.clusterModels.current(), this.clusterModels.currentSettings());
7508
7668
  if (clusterRefusalsOut) clusterRefusalsOut.push(...diagnostics.clusterRefusals);
7509
7669
  for (const addonId of diagnostics.unknownAddonIds) this.logLiveDispatchIssueOnce(`unknown:${addonId}`, () => this.log.warn("Live pipeline step references an unknown addon — dropping step", { meta: { addonId } }));
7510
7670
  for (const sub of diagnostics.substitutions) this.logLiveDispatchIssueOnce(`${sub.addonId}|${sub.chosen}|${sub.running}|${sub.format}`, () => this.log.info("Live pipeline step model substituted for engine format (node-local)", { meta: {