@camstack/addon-pipeline 1.2.621 → 1.2.623

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 (49) hide show
  1. package/dist/audio-analyzer/index.js +3 -3
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/{custom-model-catalog-Il8UY4D4.js → custom-model-catalog-BTBdgN2p.js} +29 -16
  4. package/dist/{custom-model-catalog-0_SnSXzN.mjs → custom-model-catalog-D5zTw1LU.mjs} +29 -10
  5. package/dist/detection-pipeline/index.js +67 -416
  6. package/dist/detection-pipeline/index.mjs +91 -440
  7. package/dist/{dist-ClkumZgv.js → dist-BVo3wa7s.js} +711 -302
  8. package/dist/{dist-DHqkQhTQ.mjs → dist-opSx6v5i.mjs} +651 -296
  9. package/dist/{model-catalogs-BagalaYp.js → model-catalogs-CQytR2AN.js} +1 -1
  10. package/dist/{model-catalogs-Z1GZAhSb.mjs → model-catalogs-rIvLUVlw.mjs} +1 -1
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-CkJLxlWI.mjs → node-CGIYuJMN.mjs} +1 -1
  14. package/dist/{node-0O_61WVW.js → node-DqLsuN_A.js} +1 -1
  15. package/dist/pipeline-runner/index.js +115 -460
  16. package/dist/pipeline-runner/index.mjs +108 -453
  17. package/dist/{process-memory-Dj3UgL1e.mjs → process-memory-Bd-TuWfM.mjs} +1 -1
  18. package/dist/{process-memory-DdIOhjYB.js → process-memory-DyxG-0mG.js} +1 -1
  19. package/dist/recorder/index.js +2 -2
  20. package/dist/recorder/index.mjs +2 -2
  21. package/dist/{segment-demux-js-BePzacki.js → segment-demux-js-B4bfTQpK.js} +1 -1
  22. package/dist/{segment-demux-js-Bv0t0-tQ.mjs → segment-demux-js-CdVRzwSd.mjs} +1 -1
  23. package/dist/session-decode/decode-worker-main.js +12 -21
  24. package/dist/session-decode/decode-worker-main.mjs +12 -21
  25. package/dist/stream-broker/_stub.js +2 -2
  26. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-dNk74ahz.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DF3FGCR8.mjs} +2 -2
  27. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-oUZNn2Kr.mjs +26 -0
  28. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Bb-ETNGC.mjs +26 -0
  29. package/dist/stream-broker/demux-worker-child.js +1 -1
  30. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  31. package/dist/stream-broker/{hostInit-CH5g78CJ.mjs → hostInit-BiyFzSqq.mjs} +2 -2
  32. package/dist/stream-broker/index.js +3 -3
  33. package/dist/stream-broker/index.mjs +3 -3
  34. package/dist/stream-broker/remoteEntry.js +1 -1
  35. package/dist/{worker-protocol-D_NzPcnh.mjs → worker-protocol-DngGEAT2.js} +102 -2
  36. package/dist/{worker-protocol-C-G8qmye.js → worker-protocol-_avq7y8O.mjs} +67 -25
  37. package/package.json +1 -1
  38. package/python/accelerator_facts.py +275 -0
  39. package/python/coreml_load_probe.py +105 -15
  40. package/python/inference_pool.py +19 -2
  41. package/python/openvino_compile_probe.py +72 -18
  42. package/python/postprocessors/ssd.py +12 -4
  43. package/python/postprocessors/test_ssd.py +64 -0
  44. package/python/test_accelerator_facts.py +105 -0
  45. package/python/test_coreml_load_probe.py +83 -0
  46. package/python/test_openvino_compile_probe.py +114 -0
  47. package/python/webgpu_isolated.py +19 -0
  48. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-2KkGoQ55.mjs +0 -26
  49. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Dx-WrMx0.mjs +0 -26
@@ -1,9 +1,9 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $t as pipelineExecutorCapability, Ct as enumerateInferenceDevices, F as PoolMemoryWatchdog, Hn as createEvent, In as BaseAddon, It as linkedLabelStepId, Jn as hydrateSchema, Jt as overlayClusterStepSettings, Kt as modelBindingKey, O as LABEL_SUPPLY_MACRO, Pn as errMsg$1, Rt as loadContributionCapability, Sn as supportedRuntimes$1, X as applyModelBoundSettings, Xt as pickClusterStepSettings, _n as stepModelRunsOnFormat, _r as EventCategory, _t as detectionPipelineCapability, bt as edgetpuDelegateDevice, ct as containingParentIndex, d as CLUSTER_MODEL_SCOPED_STEPS, dn as resolvePoolMemoryPolicy, er as nodePin, fr as object, ft as defaultDeviceFor$1, g as DEFAULT_CLUSTER_STEP_SETTINGS, gr as union, hn as runtimeDevices$1, hr as string, mn as rootSuppliesOf, n as APPLE_SA_TO_MACRO, on as resolveClusterStepModelId, or as array, q as YAMNET_TO_MACRO, qn as hfModelUrl, rr as sleep, st as collectModelBoundKeys, wt as evaluateZoneRules } from "../dist-DHqkQhTQ.mjs";
3
- import { c as readProcessCost } from "../node-CkJLxlWI.mjs";
4
- import { C as resolveModelDefaultConfidence, S as landmarkPrecisionVerdict, _ as ALL_STEPS, b as getStepDefinition, f as localFrameRegistry, g as ALL_PIPELINE_STEPS, l as resolveDefaultDetectionModel, n as mergeCustomModels, o as deviceKeyOf, r as buildSubtreeCanRun, s as resolveDeviceEngine, t as CustomModelCatalog, u as macroGateVerdict, v as getDefaultModelForFormat, x as resolveModelForFormat, y as getStep } from "../custom-model-catalog-0_SnSXzN.mjs";
2
+ import { $ as applyModelBoundSettings, Cn as runtimeDevices$1, Dt as enumerateInferenceDevices, En as stepModelRunsOnFormat, Er as EventCategory, Gn as BaseAddon, I as PoolMemoryWatchdog, O as LABEL_SUPPLY_MACRO, Ot as evaluateZoneRules, Qt as overlayClusterStepSettings, Sn as rootSuppliesOf, Tr as union, Un as errMsg, Ut as loadContributionCapability, Vt as linkedLabelStepId, Y as YAMNET_TO_MACRO, Zn as createEvent, d as CLUSTER_MODEL_SCOPED_STEPS, dt as containingParentIndex, en as pickClusterStepSettings, fr as sleep, g as DEFAULT_CLUSTER_STEP_SETTINGS, gt as defaultDeviceFor$1, hr as array, jn as supportedRuntimes$1, lr as nodePin, mn as resolveClusterStepModelId, n as APPLE_SA_TO_MACRO, nr as hfModelUrl, rn as pipelineExecutorCapability, rr as hydrateSchema, ut as collectModelBoundKeys, wr as string, wt as edgetpuDelegateDevice, xr as object, xt as detectionPipelineCapability, yn as resolvePoolMemoryPolicy } from "../dist-opSx6v5i.mjs";
3
+ import { c as readProcessCost } from "../node-CGIYuJMN.mjs";
4
+ import { S as resolveModelDefaultConfidence, _ as getDefaultModelForFormat, b as resolveModelForFormat, c as resolveDefaultDetectionModel, d as localFrameRegistry, g as ALL_STEPS, h as ALL_PIPELINE_STEPS, l as macroGateVerdict, n as mergeCustomModels, o as deviceKeyOf, r as buildSubtreeCanRun, s as resolveDeviceEngine, t as CustomModelCatalog, v as getStep, x as landmarkPrecisionVerdict, y as getStepDefinition } from "../custom-model-catalog-D5zTw1LU.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-Dj3UgL1e.mjs";
6
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-Bd-TuWfM.mjs";
7
7
  import * as os$1 from "node:os";
8
8
  import * as fs from "node:fs";
9
9
  import * as path$1 from "node:path";
@@ -350,84 +350,6 @@ function normalizeEngineNodeId(rawNodeId) {
350
350
  return raw.includes("/") ? raw.split("/")[0] ?? "hub" : raw;
351
351
  }
352
352
  //#endregion
353
- //#region src/detection-pipeline/model-bound-device-overrides.ts
354
- /**
355
- * Auto tune (D745), the executor leg: list — and optionally remove — the
356
- * MODEL-BOUND thresholds stored in this addon's per-camera settings (the
357
- * object-detection root's `bindsToModel` keys and their binding keys, D738).
358
- * Every other key of a camera's store is kept.
359
- *
360
- * The device store only MERGES on write, so a removal is clear + write of the
361
- * kept keys — the idiom `migrate-hardware-state.ts` uses. Both run in this
362
- * process, serialized per camera; a write that fails after the clear restores
363
- * the original row, and the camera is reported `refused` rather than clean. A
364
- * store that cannot be read is `refused` too: a failed read is never "nothing
365
- * to clear" (D393).
366
- */
367
- function errMsg(err) {
368
- return err instanceof Error ? err.message : String(err);
369
- }
370
- /** The stored keys among `boundKeys` (values and bindings), sorted. */
371
- function presentBoundKeys(row, boundKeys) {
372
- return boundKeys.flatMap((k) => [k, modelBindingKey(k)]).filter((k) => k in row).toSorted();
373
- }
374
- async function clearModelBoundDeviceOverrides(deps, input) {
375
- const ids = input.deviceIds ?? await deps.listDeviceIds();
376
- const entries = [];
377
- const refused = [];
378
- for (const deviceId of [...ids].toSorted((a, b) => a - b)) {
379
- let row;
380
- try {
381
- row = await deps.readDeviceStore(deviceId);
382
- } catch (err) {
383
- refused.push({
384
- deviceId,
385
- reason: `read failed: ${errMsg(err)}`
386
- });
387
- continue;
388
- }
389
- const keys = presentBoundKeys(row, deps.boundKeys);
390
- if (keys.length === 0) continue;
391
- if (input.dryRun) {
392
- entries.push({
393
- deviceId,
394
- keys
395
- });
396
- continue;
397
- }
398
- const kept = { ...row };
399
- for (const k of keys) delete kept[k];
400
- try {
401
- await deps.clearDeviceStore(deviceId);
402
- } catch (err) {
403
- refused.push({
404
- deviceId,
405
- reason: `clear failed: ${errMsg(err)}`
406
- });
407
- continue;
408
- }
409
- try {
410
- if (Object.keys(kept).length > 0) await deps.writeDeviceStore(deviceId, kept);
411
- entries.push({
412
- deviceId,
413
- keys
414
- });
415
- } catch (err) {
416
- await deps.writeDeviceStore(deviceId, row).catch(() => void 0);
417
- refused.push({
418
- deviceId,
419
- reason: `write failed, row restored: ${errMsg(err)}`
420
- });
421
- } finally {
422
- deps.invalidate(deviceId);
423
- }
424
- }
425
- return {
426
- entries,
427
- refused
428
- };
429
- }
430
- //#endregion
431
353
  //#region src/detection-pipeline/model-format-facts.ts
432
354
  /**
433
355
  * Two per-format facts `getSchema` reports beside `downloaded`, for Model
@@ -762,122 +684,6 @@ var ClusterUnknownRefusalLog = class {
762
684
  }
763
685
  };
764
686
  //#endregion
765
- //#region src/detection-pipeline/device-override-mirror.ts
766
- /**
767
- * The per-device detection overrides, kept fresh OFF the frame path.
768
- *
769
- * ## The read this replaces
770
- *
771
- * `runPipeline` resolved a device's overrides by calling
772
- * `settings.readDeviceStore(deviceId)` on **every inference frame**. Measured
773
- * on the live hub 2026-08-25: `get detection-pipeline:addon-devices`, 1 143
774
- * calls a minute at 0.44 rows per call — the disk asked, nineteen times a
775
- * second, whether an operator has ever changed a threshold.
776
- *
777
- * ## Why this is not "a cache in the consumer" (D224)
778
- *
779
- * D224 forbids a SECOND authority over a fact that belongs to somebody else:
780
- * the failure it records is a consumer caching a value whose write path does
781
- * not run through that consumer, so an operator's own change is the one read
782
- * the cache gets wrong.
783
- *
784
- * This addon OWNS the row. `<addonId>:addon-devices` is detection-pipeline's
785
- * own store, and the only writers are this addon's `updateDeviceSettings` and
786
- * its `clearDeviceOverrides` — both of which call {@link invalidate}. A write
787
- * is therefore its own invalidation, exactly as D224 requires, and the window
788
- * below governs only the case nobody writes through this process.
789
- *
790
- * ## The window, declared
791
- *
792
- * {@link DEVICE_OVERRIDE_STALE_MS} is what an operator's change costs on a node
793
- * that did NOT serve the write — this addon runs per node, and a settings write
794
- * lands on one of them. Thirty seconds is the trade: it removes 99.9% of the
795
- * reads (one per device per window instead of one per frame) and it is short
796
- * enough that "I moved the threshold and nothing happened" is not a support
797
- * question. Ten minutes — {@link OPERATOR_WRITTEN_STALE_MS}, the constant for a
798
- * cap whose value only an operator changes — is right for a POLLED reader that
799
- * costs a camera round trip; this read costs one local SQLite `get`, and the
800
- * cheap read buys a short window.
801
- *
802
- * A stale entry is SERVED and revalidated behind the caller, never awaited: a
803
- * frame that waits for the settings store is a frame the pipeline dropped, and
804
- * the reader is by definition about to ask again.
805
- */
806
- /**
807
- * How long a mirrored entry is served before a refresh is started behind the
808
- * next reader. See the module doc for why it is 30 s and not ten minutes.
809
- */
810
- var DEVICE_OVERRIDE_STALE_MS = 3e4;
811
- /**
812
- * How long a FAILED refresh is not retried.
813
- *
814
- * A refresh that throws leaves `fetchedAt` where it was, so without a cooldown
815
- * every subsequent frame would start another one — a broken settings store
816
- * would be hammered at frame rate by the very mechanism built to stop that.
817
- */
818
- var DEVICE_OVERRIDE_MISS_COOLDOWN_MS = 5e3;
819
- var DeviceOverrideMirror = class {
820
- deps;
821
- staleMs;
822
- missCooldownMs;
823
- entries = /* @__PURE__ */ new Map();
824
- /** In-flight reads, so a burst of frames on a cold device costs ONE read and
825
- * not one per frame — the exact failure this class exists to remove. */
826
- inFlight = /* @__PURE__ */ new Map();
827
- now;
828
- constructor(deps, staleMs = DEVICE_OVERRIDE_STALE_MS, missCooldownMs = DEVICE_OVERRIDE_MISS_COOLDOWN_MS) {
829
- this.deps = deps;
830
- this.staleMs = staleMs;
831
- this.missCooldownMs = missCooldownMs;
832
- this.now = deps.now ?? (() => Date.now());
833
- }
834
- /**
835
- * The device's overrides.
836
- *
837
- * COLD awaits — the first frame on a camera has to get the real answer, and a
838
- * default is exactly what "the operator lowered this threshold" looks like.
839
- * WARM returns immediately, stale or not, and starts the refresh behind the
840
- * caller.
841
- */
842
- async resolve(deviceId) {
843
- const entry = this.entries.get(deviceId);
844
- if (entry === void 0) return this.load(deviceId);
845
- if (this.now() - entry.fetchedAt >= this.staleMs) this.load(deviceId);
846
- return entry.value;
847
- }
848
- /** Drop a device's entry. Called by every writer in this process, so an
849
- * operator's own change is never served from the mirror. */
850
- invalidate(deviceId) {
851
- this.entries.delete(deviceId);
852
- }
853
- invalidateAll() {
854
- this.entries.clear();
855
- }
856
- async load(deviceId) {
857
- const pending = this.inFlight.get(deviceId);
858
- if (pending !== void 0) return pending;
859
- const run = this.deps.read(deviceId).then((value) => {
860
- this.entries.set(deviceId, {
861
- value,
862
- fetchedAt: this.now()
863
- });
864
- return value;
865
- }).catch((err) => {
866
- this.deps.onRefreshFailed?.(deviceId, err);
867
- const existing = this.entries.get(deviceId);
868
- if (existing !== void 0) {
869
- existing.fetchedAt = this.now() - this.staleMs + this.missCooldownMs;
870
- return existing.value;
871
- }
872
- return {};
873
- }).finally(() => {
874
- this.inFlight.delete(deviceId);
875
- });
876
- this.inFlight.set(deviceId, run);
877
- return run;
878
- }
879
- };
880
- //#endregion
881
687
  //#region src/detection-pipeline/engine/pool-worker-health.ts
882
688
  /**
883
689
  * The one table of load bounds (D653, fix round 1).
@@ -3262,6 +3068,14 @@ var PipelineModelManager = class {
3262
3068
  }
3263
3069
  };
3264
3070
  //#endregion
3071
+ //#region src/detection-pipeline/engine/labels-for-decoder.ts
3072
+ /** Labels forwarded to the Python pool — SSD stock Coral keeps COCO-90 in Python. */
3073
+ function labelsForDecoder(postprocessor, modelLabels, stepLabels) {
3074
+ if (modelLabels !== void 0 && modelLabels.length > 0) return modelLabels;
3075
+ if (postprocessor === "ssd") return;
3076
+ return stepLabels;
3077
+ }
3078
+ //#endregion
3265
3079
  //#region src/detection-pipeline/engine/coral-cocompiled-set.ts
3266
3080
  var ROOT_DETECTOR_STEP_ID = "object-detection";
3267
3081
  /** The models a dispatch places on the pool that runs it: every enabled video step. */
@@ -3946,7 +3760,9 @@ function buildPoolModelConfigForStep(inputs) {
3946
3760
  const inputSize = Math.max(inputWidth, inputHeight);
3947
3761
  const inputChannels = modelEntry.inputChannels ?? 3;
3948
3762
  const entryLabels = modelEntry.labels.length > 0 ? modelEntry.labels.map((l) => l.id) : void 0;
3949
- const labels = readModelLabels(modelsDir, modelEntry) ?? entryLabels ?? def.labels;
3763
+ const modelLabels = readModelLabels(modelsDir, modelEntry) ?? entryLabels;
3764
+ const effectivePostprocessor = resolveEffectivePostprocessorForEntry(def, modelEntry);
3765
+ const labels = labelsForDecoder(effectivePostprocessor, modelLabels, def.labels);
3950
3766
  return {
3951
3767
  path: modelPath,
3952
3768
  inputSize,
@@ -3957,7 +3773,7 @@ function buildPoolModelConfigForStep(inputs) {
3957
3773
  inputNormalization: modelEntry.inputNormalization,
3958
3774
  outputProbabilities: modelEntry.outputProbabilities,
3959
3775
  ...modelEntry.gpuInferencePrecision !== void 0 ? { gpuInferencePrecision: modelEntry.gpuInferencePrecision } : {},
3960
- postprocessor: resolveEffectivePostprocessorForEntry(def, modelEntry),
3776
+ postprocessor: effectivePostprocessor,
3961
3777
  confidence: resolvePoolConfidenceFloor(stepId, resolveModelDefaultConfidence(def, modelEntry)),
3962
3778
  nmsIouThreshold: resolvePoolDecodeSettings({
3963
3779
  def,
@@ -4222,6 +4038,29 @@ function benchmarkKeyOf(deviceKey) {
4222
4038
  function isBenchmarkRun(input) {
4223
4039
  return input.frame === void 0 && input.frameRef === void 0 && input.deviceId === void 0 && input.replay !== true;
4224
4040
  }
4041
+ /**
4042
+ * Backends whose device opens in ONE process at a time. libedgetpu holds an
4043
+ * Edge TPU exclusively for the process that opened it, so a second pool on a
4044
+ * Coral the cameras' pool already holds cannot open it at all — every
4045
+ * benchmark and auto-benchmark (D913) of an in-use Coral failed that way on
4046
+ * the dev hub (1.2.619). Every other backend this node builds (OpenVINO,
4047
+ * CoreML, CUDA, ONNX, CPU) shares its device between processes.
4048
+ */
4049
+ var EXCLUSIVE_BACKENDS = new Set(["edgetpu"]);
4050
+ /** True when the device behind `deviceKey` (`<backend>:<device>`) cannot be opened by a second pool. */
4051
+ function isExclusiveDevice(deviceKey) {
4052
+ const colon = deviceKey.indexOf(":");
4053
+ const backend = colon >= 0 ? deviceKey.slice(0, colon) : deviceKey;
4054
+ return EXCLUSIVE_BACKENDS.has(backend);
4055
+ }
4056
+ /**
4057
+ * A benchmark on an EXCLUSIVE device is the one benchmark that goes to the
4058
+ * live pool: the separate pool could not open the device, so the choice is
4059
+ * between measuring through the cameras' pool and not measuring at all.
4060
+ */
4061
+ function benchmarkPlacementOf(benchmark, deviceKey) {
4062
+ return benchmark && !isExclusiveDevice(deviceKey) ? "benchmark-pool" : "live-pool";
4063
+ }
4225
4064
  var BenchmarkPools = class {
4226
4065
  options;
4227
4066
  pools = /* @__PURE__ */ new Map();
@@ -4802,14 +4641,15 @@ var PRIMARY_ROOT_STEP = "object-detection";
4802
4641
  /**
4803
4642
  * The same rule over an EXECUTABLE tree's roots (D658): their model ids are the
4804
4643
  * pool variants that will run — after resolution, per-step loads and drops —
4805
- * so this is what a live `runPipeline` stamps. The tree holds only enabled
4806
- * video roots already.
4807
- */
4808
- function resolveTreeRootModelId(roots) {
4809
- return primaryRootModelId(roots);
4810
- }
4811
- function primaryRootModelId(roots) {
4812
- return (roots.find((s) => s.stepId === PRIMARY_ROOT_STEP) ?? roots[0])?.modelId;
4644
+ * so this is what a live `runPipeline` stamps, with the root's step id beside
4645
+ * the model (D925). The tree holds only enabled video roots already.
4646
+ */
4647
+ function resolveTreeRoot(roots) {
4648
+ const root = roots.find((s) => s.stepId === PRIMARY_ROOT_STEP) ?? roots[0];
4649
+ return root === void 0 ? void 0 : {
4650
+ stepId: root.stepId,
4651
+ modelId: root.modelId
4652
+ };
4813
4653
  }
4814
4654
  //#endregion
4815
4655
  //#region src/detection-pipeline/engine-provisioner.ts
@@ -7189,7 +7029,7 @@ var PipelineExecutor = class {
7189
7029
  rootStepId: rootStep.stepId,
7190
7030
  parentClass: mutable.macroClass,
7191
7031
  parentScore: mutable.score,
7192
- error: errMsg$1(err)
7032
+ error: errMsg(err)
7193
7033
  }
7194
7034
  });
7195
7035
  }
@@ -7320,7 +7160,7 @@ var PipelineExecutor = class {
7320
7160
  rootStepId: rootStep.stepId,
7321
7161
  parentClass: mutable.macroClass,
7322
7162
  parentScore: mutable.score,
7323
- error: errMsg$1(err)
7163
+ error: errMsg(err)
7324
7164
  }
7325
7165
  });
7326
7166
  }
@@ -7419,7 +7259,7 @@ var PipelineExecutor = class {
7419
7259
  ]
7420
7260
  };
7421
7261
  } catch (err) {
7422
- this.throttledWarn(`crop-zone-prep:${deviceId}`, 3e4, "crop-zone frame preparation failed — running package-detection full-frame", { error: errMsg$1(err) }, deviceId);
7262
+ this.throttledWarn(`crop-zone-prep:${deviceId}`, 3e4, "crop-zone frame preparation failed — running package-detection full-frame", { error: errMsg(err) }, deviceId);
7423
7263
  return null;
7424
7264
  }
7425
7265
  }
@@ -7431,7 +7271,7 @@ var PipelineExecutor = class {
7431
7271
  try {
7432
7272
  engineOutput = await step.engine.infer(input);
7433
7273
  } catch (err) {
7434
- const errorMsg = errMsg$1(err);
7274
+ const errorMsg = errMsg(err);
7435
7275
  const errorOutput = {
7436
7276
  kind: "detections",
7437
7277
  detections: []
@@ -7718,7 +7558,7 @@ var PipelineExecutor = class {
7718
7558
  this.throttledWarn(`child-fail:${child.stepId}:${deviceId}`, 15e3, "pipeline child step failed", {
7719
7559
  step: child.stepId,
7720
7560
  model: child.modelId,
7721
- error: errMsg$1(err)
7561
+ error: errMsg(err)
7722
7562
  }, deviceId);
7723
7563
  }
7724
7564
  }
@@ -7865,16 +7705,6 @@ function collectSchemaDefaults(schema) {
7865
7705
  walkFieldsForDefaults(schema, out);
7866
7706
  return out;
7867
7707
  }
7868
- /**
7869
- * The operator settings of `stepId` as the model that RUNS sees them (D738):
7870
- * a model-bound value made for another model is dropped and binding keys are
7871
- * stripped. For a settings layer merged AFTER the tree is built (the
7872
- * executor's per-device store, `applyDeviceOverridesToTree`), so it obeys the
7873
- * same binding as the layers `buildNode` merges.
7874
- */
7875
- function resolveOperatorSettingsForModel(stepId, settings, runningModelId) {
7876
- return applyModelBoundSettings(settings, collectModelBoundKeys(getStep(stepId).getConfigSchema()), runningModelId) ?? {};
7877
- }
7878
7708
  function walkFieldsForDefaults(fields, out) {
7879
7709
  for (const field of fields) {
7880
7710
  if ("type" in field && field.type === "group" && "fields" in field) {
@@ -8563,7 +8393,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8563
8393
  * node-default tree against `this.currentEngine.format`. Recomputed
8564
8394
  * EAGERLY by {@link recomputeConfigIssues} whenever the engine actually
8565
8395
  * changes (see `onEngineSelectionChanged`), rather than relying on a
8566
- * `getGlobalSteps()` call that may never happen on a camera-only node.
8396
+ * `nodeDefaultSteps()` call that may never happen on a camera-only node.
8567
8397
  * Merged into {@link getEngineProvisioning}'s snapshot; never consulted by
8568
8398
  * `assertEngineReady` — this gate SURFACES, it does not block inference.
8569
8399
  */
@@ -8665,20 +8495,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8665
8495
  readStore;
8666
8496
  /** Write a patch to the addon settings store. */
8667
8497
  writeStore;
8668
- /** Read per-device settings. */
8669
- readDeviceStore;
8670
- /** Delete all per-device settings for a device (used by `clearDeviceOverrides`). */
8671
- clearDeviceStore;
8672
- /** Merge a patch into one device's settings (Auto tune' keep-the-rest rewrite, D745). */
8673
- writeDeviceStore;
8674
- /**
8675
- * The per-device overrides the FRAME path reads, kept fresh off that path.
8676
- *
8677
- * Public so the addon class can drop a device on its own
8678
- * `updateDeviceSettings` — the write and the mirror have to be the same
8679
- * process's, or the mirror becomes the second authority D224 forbids.
8680
- */
8681
- deviceOverrides;
8682
8498
  constructor(settings, modelsDir, logger, eventBus = null, detectionConfigSchemaSource = null, executorOptions = {}) {
8683
8499
  this.modelsDir = modelsDir;
8684
8500
  this.eventBus = eventBus;
@@ -8687,18 +8503,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8687
8503
  this.log = logger;
8688
8504
  this.readStore = () => settings.readAddonStore();
8689
8505
  this.writeStore = (patch) => settings.writeAddonStore(patch);
8690
- this.readDeviceStore = settings.readDeviceStore ?? (async () => ({}));
8691
- const clearDeviceStore = settings.clearDeviceStore;
8692
- this.clearDeviceStore = clearDeviceStore ? (deviceId) => clearDeviceStore.call(settings, deviceId) : async () => void 0;
8693
- const writeDeviceStore = settings.writeDeviceStore;
8694
- this.writeDeviceStore = writeDeviceStore ? (deviceId, patch) => writeDeviceStore.call(settings, deviceId, patch) : async () => void 0;
8695
- this.deviceOverrides = new DeviceOverrideMirror({
8696
- read: (deviceId) => this.readDeviceStore(deviceId),
8697
- onRefreshFailed: (deviceId, err) => this.log.warn("per-device detection overrides refresh failed — serving the last value", {
8698
- tags: { deviceId },
8699
- meta: { error: errMsg$1(err) }
8700
- })
8701
- });
8702
8506
  this.currentEngine = ONNX_FLOOR;
8703
8507
  this.log.info("Engine pick pending (placeholder until probe / persisted selection)", { meta: {
8704
8508
  runtime: this.currentEngine.runtime,
@@ -8722,7 +8526,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8722
8526
  stale: report.stale
8723
8527
  } });
8724
8528
  }, (err) => {
8725
- this.log.warn("Model revision check failed — copies are proven on their next download", { meta: { error: errMsg$1(err) } });
8529
+ this.log.warn("Model revision check failed — copies are proven on their next download", { meta: { error: errMsg(err) } });
8726
8530
  });
8727
8531
  }
8728
8532
  /**
@@ -8832,14 +8636,14 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
8832
8636
  else this.log.warn("platform-probe readiness gate timed out — conservative engine pick (floor is not persisted)", { meta: {
8833
8637
  nodeId,
8834
8638
  timeoutMs: PROBE_READY_TIMEOUT_MS,
8835
- error: errMsg$1(err)
8639
+ error: errMsg(err)
8836
8640
  } });
8837
8641
  }
8838
8642
  await this.resolveAutoEngine();
8839
8643
  this.needsAutoPick = false;
8840
8644
  this.startProvisioningForCurrentEngine();
8841
8645
  await this.recomputeConfigIssues().catch((err) => {
8842
- this.log.warn("config-issue recompute failed after node-default derive", { meta: { error: errMsg$1(err) } });
8646
+ this.log.warn("config-issue recompute failed after node-default derive", { meta: { error: errMsg(err) } });
8843
8647
  });
8844
8648
  }
8845
8649
  /**
@@ -9001,7 +8805,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9001
8805
  * the node-default step tree for `this.currentEngine.format` and compute
9002
8806
  * human-readable config issues from the result:
9003
8807
  * - model substitutions (`getActiveModelSubstitutions()`, populated as a
9004
- * side effect of the `getGlobalSteps()` re-resolve below) — a step's
8808
+ * side effect of the `nodeDefaultSteps()` re-resolve below) — a step's
9005
8809
  * chosen model lacks a build for this format, so the format default
9006
8810
  * runs instead (degraded but loadable).
9007
8811
  * - zero-build steps (`collectZeroBuildIssues`) — a step whose FULL
@@ -9016,7 +8820,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9016
8820
  async recomputeConfigIssues() {
9017
8821
  this.configIssues = [];
9018
8822
  try {
9019
- const steps = await this.getGlobalSteps();
8823
+ const steps = await this.nodeDefaultSteps();
9020
8824
  if (!steps) return;
9021
8825
  const format = this.currentEngine.format;
9022
8826
  const substitutionIssues = this.getActiveModelSubstitutions().map((s) => `${s.addonId}: chose ${s.chosen}, running ${s.running} (${s.format})`);
@@ -9024,7 +8828,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9024
8828
  this.configIssues = [...substitutionIssues, ...zeroBuildIssues];
9025
8829
  } catch (err) {
9026
8830
  this.configIssues = [];
9027
- this.log.warn("engine-change config gate failed — no config issues surfaced", { meta: { error: errMsg$1(err) } });
8831
+ this.log.warn("engine-change config gate failed — no config issues surfaced", { meta: { error: errMsg(err) } });
9028
8832
  }
9029
8833
  }
9030
8834
  /**
@@ -9064,7 +8868,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9064
8868
  * `format` for the download fan-out.
9065
8869
  */
9066
8870
  async ensureModelsForCurrentSteps(format) {
9067
- const steps = await this.getGlobalSteps();
8871
+ const steps = await this.nodeDefaultSteps();
9068
8872
  if (!steps || steps.length === 0) return;
9069
8873
  for (const step of flattenSteps(steps)) {
9070
8874
  if (!step.enabled) continue;
@@ -9237,10 +9041,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9237
9041
  defaultDevice: e.engine.device
9238
9042
  };
9239
9043
  }
9240
- async getDefaultSteps(engine) {
9241
- const detectionModelId = resolveDefaultDetectionModel(engine.backend, engine.device ?? null, engine.format);
9242
- return buildDefaultStepTree(engine.format, detectionModelId);
9243
- }
9244
9044
  /**
9245
9045
  * Resolve every step's model for this node's engine format via the shared
9246
9046
  * `resolveModelForFormat`, and surface any substitution. `step.engine?.format`
@@ -9283,18 +9083,19 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9283
9083
  return [...this.modelSubstitutions.values()];
9284
9084
  }
9285
9085
  /**
9286
- * PURE node-default step tree — computed on demand, never persisted.
9086
+ * PURE node-default step tree — computed on demand, never persisted, and
9087
+ * PRIVATE (D927): it is not the model any camera ran, so no caller outside
9088
+ * this provider may read it. It serves Gate A's config-issue probe and the
9089
+ * engine provisioning's model download.
9287
9090
  *
9288
9091
  * The object-detection default is hardware-aware (this node's resolved
9289
9092
  * backend + device via `resolveDefaultDetectionModel`); every other step
9290
9093
  * keeps its flat per-format default. The result is cached in
9291
9094
  * `currentSteps` only as a memo — the cache is dropped whenever the
9292
9095
  * engine (hence model format) changes, so a re-read always reflects the
9293
- * live engine. Nothing in the live per-camera dispatch path reads this:
9294
- * it exists as the benchmark fallback (empty input steps), the Gate-A
9295
- * config-issue probe, and the "effective post-reset model" probe.
9096
+ * live engine.
9296
9097
  */
9297
- async getGlobalSteps() {
9098
+ async nodeDefaultSteps() {
9298
9099
  if (this.currentSteps) return this.currentSteps;
9299
9100
  const detectionModelId = resolveDefaultDetectionModel(this.currentEngine.backend, this.currentEngine.device ?? null, this.currentEngine.format);
9300
9101
  const defaults = buildDefaultStepTree(this.currentEngine.format, detectionModelId);
@@ -9302,10 +9103,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9302
9103
  this.currentSteps = this.resolveStepModels(defaults);
9303
9104
  return this.currentSteps;
9304
9105
  }
9305
- async getGlobalPipelineConfig() {
9306
- if (!this.currentSteps) return null;
9307
- return stepsToPipelineConfig(this.currentSteps, this.currentEngine);
9308
- }
9309
9106
  /**
9310
9107
  * Task G5 (Gate B) — pre-init config-correctness gate. PURE COMPUTE
9311
9108
  * against `this.currentEngine.format`: resolves `steps` the same way the
@@ -9349,7 +9146,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9349
9146
  issues: [{
9350
9147
  addonId: "*",
9351
9148
  kind: "unknown-device",
9352
- detail: errMsg$1(err)
9149
+ detail: errMsg(err)
9353
9150
  }],
9354
9151
  substitutions: [],
9355
9152
  format
@@ -9376,7 +9173,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9376
9173
  format
9377
9174
  };
9378
9175
  } catch (err) {
9379
- this.log.warn("validatePipeline failed — reporting a clean result", { meta: { error: errMsg$1(err) } });
9176
+ this.log.warn("validatePipeline failed — reporting a clean result", { meta: { error: errMsg(err) } });
9380
9177
  return {
9381
9178
  ok: true,
9382
9179
  issues: [],
@@ -9568,7 +9365,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9568
9365
  durationMs
9569
9366
  };
9570
9367
  } catch (err) {
9571
- const msg = errMsg$1(err);
9368
+ const msg = errMsg(err);
9572
9369
  this.log.error("Model download failed", { meta: {
9573
9370
  error: msg,
9574
9371
  modelId,
@@ -9888,7 +9685,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9888
9685
  this.executor = executor;
9889
9686
  const dispatchEngine = dispatchDeviceKey ? resolveDeviceEngine(dispatchDeviceKey) : runEngine;
9890
9687
  const benchmark = isBenchmarkRun(input);
9891
- const dispatchFactory = benchmark ? await this.benchmarkPools.acquire(deviceKeyOf(dispatchEngine), dispatchEngine) : dispatchDeviceKey ? await this.resolveDeviceFactory(dispatchDeviceKey) : this.engineFactory;
9688
+ const placement = benchmarkPlacementOf(benchmark, deviceKeyOf(dispatchEngine));
9689
+ if (benchmark && placement === "live-pool") this.log.info("Benchmark on an exclusive device — served by the live pool", { meta: { deviceKey: deviceKeyOf(dispatchEngine) } });
9690
+ const dispatchFactory = placement === "benchmark-pool" ? await this.benchmarkPools.acquire(deviceKeyOf(dispatchEngine), dispatchEngine) : dispatchDeviceKey ? await this.resolveDeviceFactory(dispatchDeviceKey) : this.engineFactory;
9892
9691
  const needed = enabledSteps.filter((s) => dispatchFactory.needsPoolUpdate(s));
9893
9692
  let runSteps = benchmarkSteps;
9894
9693
  const loadPolicy = dispatchLoadPolicyOf(input);
@@ -9926,24 +9725,24 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9926
9725
  ...input.deviceId !== void 0 ? { deviceId: input.deviceId } : {},
9927
9726
  deviceKey: dispatchDeviceKeyLabel,
9928
9727
  outcome: "step-dropped"
9929
- }, dispatchDeviceKeyLabel, [model], model, "not-warm", errMsg$1(failure.error));
9728
+ }, dispatchDeviceKeyLabel, [model], model, "not-warm", errMsg(failure.error));
9930
9729
  }
9931
9730
  }
9932
- const rootModelId = resolveTreeRootModelId(tree.roots);
9731
+ const treeRoot = resolveTreeRoot(tree.roots);
9933
9732
  const rootDeviceKey = deviceKeyOf(dispatchEngine);
9934
- const stampRoot = (r) => rootModelId === void 0 ? {
9733
+ const stampRoot = (r) => treeRoot === void 0 ? {
9935
9734
  ...r,
9936
9735
  rootDeviceKey
9937
9736
  } : {
9938
9737
  ...r,
9939
- rootModelId,
9738
+ rootModelId: treeRoot.modelId,
9739
+ rootStepId: treeRoot.stepId,
9940
9740
  rootDeviceKey
9941
9741
  };
9942
9742
  if (input.replay !== true) this.clusterModelSwitchLog.note(input.deviceId ?? 0, tree);
9943
9743
  setupMs = performance.now() - wallT0 - decodeMs;
9944
9744
  const effectiveDeviceId = input.deviceId ?? 0;
9945
- const deviceOverrides = effectiveDeviceId > 0 ? await this.deviceOverrides.resolve(effectiveDeviceId) : {};
9946
- const effectiveTree = Object.keys(deviceOverrides).length > 0 ? applyDeviceOverridesToTree(tree, "object-detection", deviceOverrides) : tree;
9745
+ const effectiveTree = tree;
9947
9746
  const nativeCropProvider = this.buildNativeCropProviderFromRef(input.nativeCropRef) ?? this.buildNativeFaceCropProvider(input.frameHandle);
9948
9747
  let cropZoneBbox;
9949
9748
  if (appliesCameraGates && effectiveDeviceId > 0 && effectiveTree.roots.some((r) => r.definition.extractMode === "crop-zone")) {
@@ -10339,7 +10138,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10339
10138
  ...tags,
10340
10139
  meta: {
10341
10140
  deviceKey,
10342
- error: errMsg$1(err)
10141
+ error: errMsg(err)
10343
10142
  }
10344
10143
  });
10345
10144
  });
@@ -10376,7 +10175,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10376
10175
  throw err;
10377
10176
  }
10378
10177
  const failed = failedModelOf(err) ?? models[0] ?? "";
10379
- this.reportDispatchLoadLoss(context, deviceKey, models, failed, `failed:${flight.generation ?? "unknown"}`, errMsg$1(err));
10178
+ this.reportDispatchLoadLoss(context, deviceKey, models, failed, `failed:${flight.generation ?? "unknown"}`, errMsg(err));
10380
10179
  throw err;
10381
10180
  }
10382
10181
  }
@@ -10473,13 +10272,13 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10473
10272
  const poolModel = err instanceof StepVariantLoadError ? err.poolModel : null;
10474
10273
  let generation = 0;
10475
10274
  for (const model of failedModels) {
10476
- const outcome = this.loadGovernor.recordFailure(pool, deviceKey, model, errMsg$1(err), compileTimeout, Date.now(), poolModel, err instanceof StepVariantLoadError ? err.reason : null);
10275
+ const outcome = this.loadGovernor.recordFailure(pool, deviceKey, model, errMsg(err), compileTimeout, Date.now(), poolModel, err instanceof StepVariantLoadError ? err.reason : null);
10477
10276
  generation = outcome.generation;
10478
10277
  if (outcome.refusedNow) this.log.error("model REFUSED on this device — its compile timed out repeatedly", { meta: {
10479
10278
  deviceKey,
10480
10279
  model,
10481
10280
  compileTimeouts: outcome.compileTimeouts,
10482
- error: errMsg$1(err),
10281
+ error: errMsg(err),
10483
10282
  device: "keeps serving every other model",
10484
10283
  rearm: "pipelineExecutor.rearmInferenceDevice"
10485
10284
  } });
@@ -10540,7 +10339,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10540
10339
  this.log.info("Model downloaded successfully", { meta: { modelId: entry.id } });
10541
10340
  return;
10542
10341
  } catch (err) {
10543
- const msg = errMsg$1(err);
10342
+ const msg = errMsg(err);
10544
10343
  if (attempt < maxRetries) {
10545
10344
  const delayMs = 2e3 * Math.pow(2, attempt - 1);
10546
10345
  this.log.warn("Model download failed — retrying", { meta: {
@@ -10616,7 +10415,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10616
10415
  try {
10617
10416
  return await this.runPipelineBatchImpl(input);
10618
10417
  } catch (err) {
10619
- const msg = errMsg$1(err);
10418
+ const msg = errMsg(err);
10620
10419
  const stack = err instanceof Error ? err.stack : void 0;
10621
10420
  this.log.error("runPipelineBatch failed", { meta: {
10622
10421
  phase: "batch",
@@ -10901,9 +10700,9 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
10901
10700
  deviceKey: defaultDeviceKey,
10902
10701
  backend: this.currentEngine.backend,
10903
10702
  device: this.currentEngine.device ?? null,
10904
- error: errMsg$1(err)
10703
+ error: errMsg(err)
10905
10704
  } });
10906
- this.noteDeviceDeath(defaultDeviceKey, errMsg$1(err));
10705
+ this.noteDeviceDeath(defaultDeviceKey, errMsg(err));
10907
10706
  throw err;
10908
10707
  }
10909
10708
  this.engineFactory = factory;
@@ -11165,7 +10964,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
11165
10964
  await factory.dispose().catch(() => void 0);
11166
10965
  this.log.error("Benchmark pool failed to initialize", { meta: {
11167
10966
  deviceKey,
11168
- error: errMsg$1(err)
10967
+ error: errMsg(err)
11169
10968
  } });
11170
10969
  throw err;
11171
10970
  }
@@ -11212,7 +11011,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
11212
11011
  error: err instanceof Error ? err.message : String(err)
11213
11012
  } });
11214
11013
  await factory.dispose().catch(() => void 0);
11215
- this.noteDeviceDeath(deviceKey, errMsg$1(err));
11014
+ this.noteDeviceDeath(deviceKey, errMsg(err));
11216
11015
  throw err;
11217
11016
  }
11218
11017
  this.factoriesByDevice.set(deviceKey, factory);
@@ -11278,102 +11077,6 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
11278
11077
  this.log.info("videoPipelineSteps updated", { meta: { addons: Object.keys(sanitized).length } });
11279
11078
  return { success: true };
11280
11079
  }
11281
- /**
11282
- * Clear THIS node's executor-side PER-DEVICE settings stores — the
11283
- * per-camera step overrides the object-detection root reads via
11284
- * `applyDeviceOverridesToTree`.
11285
- *
11286
- * Node-routed (the cap's default `nodeIdMode: 'routing'`): the generated
11287
- * cap-router strips `nodeId` and dispatches to the target node, so this
11288
- * method runs ON the node being cleared and takes no argument.
11289
- *
11290
- * This is the slimmed executor leg of the orchestrator's
11291
- * `resetNodePipelineDefaults` flow. The legacy `resetToDefault` — which
11292
- * also reset a persisted GLOBAL step-tree seed nothing in the live
11293
- * per-camera path read — was removed together with that seed
11294
- * (`getGlobalSteps` is pure now, so there is nothing node-global left to
11295
- * reset here).
11296
- *
11297
- * Idempotent: clearing already-empty per-device stores is a no-op.
11298
- */
11299
- async clearDeviceOverrides() {
11300
- const clearedDevices = await this.clearAllDeviceOverrides();
11301
- this.log.info("executor per-device overrides cleared", { meta: { clearedDevices } });
11302
- return {
11303
- success: true,
11304
- clearedDevices
11305
- };
11306
- }
11307
- /**
11308
- * Auto tune (D745): list — or, unless `dryRun`, remove — the model-bound
11309
- * thresholds of the per-camera detector store. See
11310
- * `model-bound-device-overrides.ts`.
11311
- */
11312
- async clearModelBoundDeviceOverrides(input) {
11313
- const result = await clearModelBoundDeviceOverrides({
11314
- listDeviceIds: async () => {
11315
- const api = this.addonCtx?.api;
11316
- if (!api) throw new Error("clearModelBoundDeviceOverrides: ctx.api unavailable");
11317
- return (await api.deviceManager.listAll.query({ projection: "slim" })).map((d) => d.id);
11318
- },
11319
- readDeviceStore: (deviceId) => this.readDeviceStore(deviceId),
11320
- clearDeviceStore: (deviceId) => this.clearDeviceStore(deviceId),
11321
- writeDeviceStore: (deviceId, patch) => this.writeDeviceStore(deviceId, patch),
11322
- invalidate: (deviceId) => this.deviceOverrides.invalidate(deviceId),
11323
- boundKeys: collectModelBoundKeys(getStep("object-detection").getConfigSchema())
11324
- }, input);
11325
- if (!input.dryRun) for (const entry of result.entries) this.log.info("auto tune: per-camera detector thresholds cleared", {
11326
- tags: { deviceId: entry.deviceId },
11327
- meta: {
11328
- deviceId: entry.deviceId,
11329
- keys: entry.keys
11330
- }
11331
- });
11332
- for (const r of result.refused) this.log.warn("auto tune: per-camera detector thresholds not cleared", {
11333
- tags: { deviceId: r.deviceId },
11334
- meta: {
11335
- deviceId: r.deviceId,
11336
- reason: r.reason,
11337
- dryRun: input.dryRun
11338
- }
11339
- });
11340
- return result;
11341
- }
11342
- /**
11343
- * Clear the per-device pipeline-step overrides for every known device.
11344
- * Returns the count of devices whose store was cleared. There is no
11345
- * bulk-list primitive over the per-device store, so devices are
11346
- * enumerated via the device-manager and cleared one by one
11347
- * (`clearDeviceStore` is idempotent — a device with no overrides is a
11348
- * no-op). Never throws: a device-manager outage degrades to "cleared
11349
- * nothing" so the reset flow still completes.
11350
- */
11351
- async clearAllDeviceOverrides() {
11352
- const api = this.addonCtx?.api;
11353
- if (!api) return 0;
11354
- let devices = [];
11355
- try {
11356
- devices = await api.deviceManager.listAll.query({ projection: "slim" });
11357
- } catch (err) {
11358
- this.log.warn("clearDeviceOverrides: device enumeration failed — skipping per-device clear", { meta: { error: errMsg$1(err) } });
11359
- return 0;
11360
- }
11361
- let cleared = 0;
11362
- for (const device of devices) try {
11363
- await this.clearDeviceStore(device.id);
11364
- this.deviceOverrides.invalidate(device.id);
11365
- cleared++;
11366
- } catch (err) {
11367
- this.log.warn("clearDeviceOverrides: failed to clear per-device overrides for device", {
11368
- tags: { deviceId: device.id },
11369
- meta: {
11370
- deviceId: device.id,
11371
- error: errMsg$1(err)
11372
- }
11373
- });
11374
- }
11375
- return cleared;
11376
- }
11377
11080
  async listLoadedEngines() {
11378
11081
  const out = [];
11379
11082
  if (this.engineFactory) {
@@ -11560,7 +11263,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
11560
11263
  frame
11561
11264
  };
11562
11265
  } catch (err) {
11563
- const msg = errMsg$1(err);
11266
+ const msg = errMsg(err);
11564
11267
  this.log.error("failed", { meta: {
11565
11268
  phase: "audio-test",
11566
11269
  error: msg
@@ -11620,7 +11323,6 @@ function buildSchemaSlots(formats, modelsDir, customByStep) {
11620
11323
  defaultModelId: step.defaultModelId,
11621
11324
  ...step.defaultModelIdByFormat ? { defaultModelIdByFormat: { ...step.defaultModelIdByFormat } } : {},
11622
11325
  ...step.enabledByDefault !== void 0 ? { enabledByDefault: step.enabledByDefault } : {},
11623
- ...step.backfillIntoExistingOverrides !== void 0 ? { backfillIntoExistingOverrides: step.backfillIntoExistingOverrides } : {},
11624
11326
  defaultConfidence: step.defaultConfidence,
11625
11327
  ...step.group ? { group: step.group } : {},
11626
11328
  ...configSchemaWithRoots.length > 0 ? { configSchema: configSchemaWithRoots } : {}
@@ -11668,7 +11370,7 @@ function resolveChildSlots(slot) {
11668
11370
  }
11669
11371
  /**
11670
11372
  * V2 default: the node-default step tree (never persisted — see
11671
- * `getGlobalSteps`). The core detection + recognition steps are enabled; every
11373
+ * `nodeDefaultSteps`). The core detection + recognition steps are enabled; every
11672
11374
  * optional step is present with its catalog `enabledByDefault` (the animal and
11673
11375
  * vehicle classifiers on — their answer comes from the root model when it
11674
11376
  * declares them, D736 — the bird classifier and segmentation off).
@@ -11710,27 +11412,6 @@ function buildDefaultStepTree(format, objectDetectionModelId) {
11710
11412
  if (audioStep) steps.push(audioStep);
11711
11413
  return steps;
11712
11414
  }
11713
- function stepsToPipelineConfig(steps, engine) {
11714
- const toNode = (step) => ({
11715
- step: step.addonId,
11716
- addon: step.addonId,
11717
- configOverride: {
11718
- modelId: step.modelId,
11719
- ...step.settings
11720
- },
11721
- children: step.children.map(toNode)
11722
- });
11723
- const videoSteps = steps.filter((s) => s.slot !== "audio-classifier");
11724
- const audioStep = steps.find((s) => s.slot === "audio-classifier" && s.enabled);
11725
- return {
11726
- engine,
11727
- video: videoSteps.map(toNode),
11728
- ...audioStep ? {
11729
- audio: toNode(audioStep),
11730
- audioEngine: engine
11731
- } : {}
11732
- };
11733
- }
11734
11415
  /** Flatten a step tree into a flat list (DFS) — delegates to shared utility. */
11735
11416
  function flattenSteps(steps) {
11736
11417
  return flattenEnabledVideoSteps(steps);
@@ -11791,22 +11472,6 @@ function availableFormatsForStep(addonId) {
11791
11472
  return [];
11792
11473
  }
11793
11474
  }
11794
- /**
11795
- * Return a shallow-cloned executable tree with `rootStepId`'s `settings`
11796
- * merged with `overrides` (per-device values win). Non-matching root steps
11797
- * pass through unchanged. Used by `runFrame` to apply per-device detection
11798
- * settings without rebuilding the tree per frame. A model-bound value (D738)
11799
- * applies only while the model it was made for is the one the root RUNS.
11800
- */
11801
- function applyDeviceOverridesToTree(tree, rootStepId, overrides) {
11802
- return { roots: tree.roots.map((root) => root.stepId === rootStepId ? {
11803
- ...root,
11804
- settings: {
11805
- ...root.settings,
11806
- ...resolveOperatorSettingsForModel(rootStepId, overrides, root.modelId)
11807
- }
11808
- } : root) };
11809
- }
11810
11475
  //#endregion
11811
11476
  //#region src/detection-pipeline/python-readiness.ts
11812
11477
  /**
@@ -11921,20 +11586,6 @@ function isProfileSliderKey(key) {
11921
11586
  }
11922
11587
  var DetectionPipelineAddon = class extends BaseAddon {
11923
11588
  provider = null;
11924
- /**
11925
- * An operator's own write drops the mirror the frame path reads.
11926
- *
11927
- * This override is the half of `device-override-mirror.ts` that keeps it from
11928
- * being the second authority D224 forbids: `BaseAddon.updateDeviceSettings`
11929
- * writes THIS addon's `<addonId>:addon-devices` row, so the write and the
11930
- * mirror are in one process and the write invalidates. Without it the
11931
- * operator's own change would be the one read the mirror got wrong, which is
11932
- * the exact failure D224 records.
11933
- */
11934
- async updateDeviceSettings(deviceId, patch) {
11935
- await super.updateDeviceSettings(deviceId, patch);
11936
- this.provider?.deviceOverrides.invalidate(deviceId);
11937
- }
11938
11589
  /** Last non-null probed hardware PER NODE — reused when the probe transiently
11939
11590
  * returns null so offered backends / device lists don't collapse. Keyed by
11940
11591
  * node because the hub addon probes other nodes when serving their config.