@camstack/addon-pipeline 1.2.145 → 1.2.146

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,8 +4,9 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  const require_dist = require("../dist-DML3mpn9.js");
6
6
  const require_node = require("../node-OvVqe9eh.js");
7
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaV4Zdsz.js");
7
+ const require_local_frame_registry = require("../local-frame-registry-DO8-LuSo.js");
8
8
  const require_lazy_sharp = require("../lazy-sharp-BzXIjfN8.js");
9
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
9
10
  const require_process_memory = require("../process-memory-NWKrKfmi.js");
10
11
  let node_fs = require("node:fs");
11
12
  node_fs = require_dist.__toESM(node_fs);
@@ -519,7 +520,7 @@ function classifyAccelerator(backend, device) {
519
520
  * missing-build fallback branch is unit-testable without a live catalog —
520
521
  * mirrors `collectZeroBuildIssues`' injection seam.
521
522
  */
522
- function resolveDefaultDetectionModel(backend, device, format, getStepDef = require_event_loop_stall_monitor.getStepDefinition) {
523
+ function resolveDefaultDetectionModel(backend, device, format, getStepDef = require_local_frame_registry.getStepDefinition) {
523
524
  let def;
524
525
  try {
525
526
  def = getStepDef(OBJECT_DETECTION_STEP_ID);
@@ -531,7 +532,7 @@ function resolveDefaultDetectionModel(backend, device, format, getStepDef = requ
531
532
  const entry = def.models.find((m) => m.id === candidate);
532
533
  if (entry && entry.formats[format] !== void 0) return candidate;
533
534
  }
534
- return require_event_loop_stall_monitor.getDefaultModelForFormatFromDef(def, format);
535
+ return require_local_frame_registry.getDefaultModelForFormatFromDef(def, format);
535
536
  }
536
537
  //#endregion
537
538
  //#region src/detection-pipeline/device-override-mirror.ts
@@ -2524,7 +2525,7 @@ var EngineFactory = class {
2524
2525
  * to `buildConfig`, which throws the message naming the step and the model.
2525
2526
  */
2526
2527
  resolveDecode(stepId, modelId, settings) {
2527
- const def = require_event_loop_stall_monitor.getStepDefinition(stepId);
2528
+ const def = require_local_frame_registry.getStepDefinition(stepId);
2528
2529
  const modelEntry = def.models.find((m) => m.id === modelId) ?? this.opts.resolveCustomModel?.(stepId, modelId);
2529
2530
  if (!modelEntry) return null;
2530
2531
  return resolvePoolDecodeSettings({
@@ -2568,7 +2569,7 @@ var EngineFactory = class {
2568
2569
  */
2569
2570
  function buildPoolModelConfigForStep(inputs) {
2570
2571
  const { stepId, modelId, poolRuntime, modelsDir } = inputs;
2571
- const def = require_event_loop_stall_monitor.getStepDefinition(stepId);
2572
+ const def = require_local_frame_registry.getStepDefinition(stepId);
2572
2573
  const modelEntry = def.models.find((m) => m.id === modelId) ?? inputs.resolveCustomModel?.(stepId, modelId);
2573
2574
  if (!modelEntry) throw new Error(`Model "${modelId}" not found in step "${stepId}" catalog (static or custom registry)`);
2574
2575
  const format = RUNTIME_TO_FORMAT[poolRuntime];
@@ -5427,7 +5428,7 @@ var PipelineExecutor = class {
5427
5428
  this.warnFaceTooSmall(child, detShortSide, minFaceSize, void 0, deviceId);
5428
5429
  continue;
5429
5430
  }
5430
- const precision = require_event_loop_stall_monitor.landmarkPrecisionVerdict({
5431
+ const precision = require_local_frame_registry.landmarkPrecisionVerdict({
5431
5432
  detectionShortSidePx: detShortSide,
5432
5433
  floorPx: numericSetting(child.settings, "minLandmarkFaceSize", 24)
5433
5434
  });
@@ -5690,7 +5691,7 @@ function buildExecutableTree(steps, getEngine, resolveModel) {
5690
5691
  return { roots: steps.filter((s) => s.enabled).filter((s) => s.slot !== "audio-classifier").map((s) => buildNode(s, getEngine, resolveModel)) };
5691
5692
  }
5692
5693
  function buildNode(step, getEngine, resolveModel) {
5693
- const definition = require_event_loop_stall_monitor.getStepDefinition(step.addonId);
5694
+ const definition = require_local_frame_registry.getStepDefinition(step.addonId);
5694
5695
  const engine = getEngine(step.addonId);
5695
5696
  const children = (step.children ?? []).filter((c) => c.enabled).map((c) => buildNode(c, getEngine, resolveModel));
5696
5697
  const mergedSettings = {
@@ -5717,7 +5718,7 @@ function buildNode(step, getEngine, resolveModel) {
5717
5718
  * entered recursively. Fields without a declared default are skipped.
5718
5719
  */
5719
5720
  function collectSchemaDefaults(stepId) {
5720
- const schema = require_event_loop_stall_monitor.getStep(stepId).getConfigSchema();
5721
+ const schema = require_local_frame_registry.getStep(stepId).getConfigSchema();
5721
5722
  const out = {};
5722
5723
  walkFieldsForDefaults(schema, out);
5723
5724
  return out;
@@ -5811,7 +5812,7 @@ function mergeCustomModels(staticModels, customModels) {
5811
5812
  */
5812
5813
  var defaultLookup = (stepId) => {
5813
5814
  try {
5814
- return require_event_loop_stall_monitor.getStepDefinition(stepId);
5815
+ return require_local_frame_registry.getStepDefinition(stepId);
5815
5816
  } catch {
5816
5817
  return null;
5817
5818
  }
@@ -5860,7 +5861,7 @@ function collectSubstitutions(steps, format) {
5860
5861
  for (const step of steps) {
5861
5862
  const chosen = step.modelId;
5862
5863
  if (chosen != null) {
5863
- const running = require_event_loop_stall_monitor.resolveModelForFormat(step.addonId, chosen, format);
5864
+ const running = require_local_frame_registry.resolveModelForFormat(step.addonId, chosen, format);
5864
5865
  if (running !== chosen) result.push({
5865
5866
  addonId: step.addonId,
5866
5867
  chosen,
@@ -5886,7 +5887,7 @@ function collectSubstitutions(steps, format) {
5886
5887
  * black-box test. Unknown/custom addonIds (no registered `StepDefinition`)
5887
5888
  * are skipped, never thrown.
5888
5889
  */
5889
- function collectZeroBuildIssues(steps, format, getStepDef = require_event_loop_stall_monitor.getStepDefinition) {
5890
+ function collectZeroBuildIssues(steps, format, getStepDef = require_local_frame_registry.getStepDefinition) {
5890
5891
  const result = [];
5891
5892
  for (const step of steps) {
5892
5893
  let def;
@@ -5912,7 +5913,7 @@ function collectZeroBuildIssues(steps, format, getStepDef = require_event_loop_s
5912
5913
  * tests can exercise the unknown-addon branch with a fabricated resolver
5913
5914
  * without `vi.mock`.
5914
5915
  */
5915
- function collectUnknownAddonIssues(steps, getStepDef = require_event_loop_stall_monitor.getStepDefinition) {
5916
+ function collectUnknownAddonIssues(steps, getStepDef = require_local_frame_registry.getStepDefinition) {
5916
5917
  const result = [];
5917
5918
  for (const step of steps) {
5918
5919
  try {
@@ -5953,7 +5954,7 @@ function resolveInputSteps(steps, format, engine, clusterModels, clusterSettings
5953
5954
  for (const s of steps) {
5954
5955
  let def;
5955
5956
  try {
5956
- def = require_event_loop_stall_monitor.getStepDefinition(s.addonId);
5957
+ def = require_local_frame_registry.getStepDefinition(s.addonId);
5957
5958
  } catch {
5958
5959
  unknownAddonIds.push(s.addonId);
5959
5960
  continue;
@@ -5989,7 +5990,7 @@ function resolveInputSteps(steps, format, engine, clusterModels, clusterSettings
5989
5990
  }
5990
5991
  }
5991
5992
  const chosenModelId = s.modelId ?? null;
5992
- const runningModelId = chosenModelId === null && engine !== void 0 && s.addonId === "object-detection" ? resolveDefaultDetectionModel(engine.backend, engine.device, format) : require_event_loop_stall_monitor.resolveModelForFormat(s.addonId, chosenModelId, format);
5993
+ const runningModelId = chosenModelId === null && engine !== void 0 && s.addonId === "object-detection" ? resolveDefaultDetectionModel(engine.backend, engine.device, format) : require_local_frame_registry.resolveModelForFormat(s.addonId, chosenModelId, format);
5993
5994
  if (chosenModelId !== null && runningModelId !== chosenModelId) substitutions.push({
5994
5995
  addonId: s.addonId,
5995
5996
  chosen: chosenModelId,
@@ -7004,7 +7005,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7004
7005
  * first, then the custom registry. Returns undefined if neither has it.
7005
7006
  */
7006
7007
  async resolveModelEntry(addonId, modelId) {
7007
- const fromCatalog = require_event_loop_stall_monitor.getStepDefinition(addonId).models.find((m) => m.id === modelId);
7008
+ const fromCatalog = require_local_frame_registry.getStepDefinition(addonId).models.find((m) => m.id === modelId);
7008
7009
  if (fromCatalog) return fromCatalog;
7009
7010
  return (await this.getCustomModels()).get(addonId)?.find((m) => m.id === modelId);
7010
7011
  }
@@ -7100,7 +7101,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7100
7101
  /** Pure recursive resolver — builds the resolved step tree without touching `modelSubstitutions`. */
7101
7102
  resolveStepModelTree(steps, format) {
7102
7103
  return steps.map((step) => {
7103
- const resolved = require_event_loop_stall_monitor.resolveModelForFormat(step.addonId, step.modelId ?? null, format);
7104
+ const resolved = require_local_frame_registry.resolveModelForFormat(step.addonId, step.modelId ?? null, format);
7104
7105
  const children = step.children?.length ? this.resolveStepModelTree([...step.children], format) : step.children;
7105
7106
  return resolved === step.modelId && children === step.children ? step : {
7106
7107
  ...step,
@@ -7248,7 +7249,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7248
7249
  for (const step of nodes) {
7249
7250
  if (!step.enabled) continue;
7250
7251
  try {
7251
- if (require_event_loop_stall_monitor.getStepDefinition(step.addonId).slot === "audio-classifier") continue;
7252
+ if (require_local_frame_registry.getStepDefinition(step.addonId).slot === "audio-classifier") continue;
7252
7253
  } catch {
7253
7254
  continue;
7254
7255
  }
@@ -7268,7 +7269,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7268
7269
  async getCapabilities(_forceRefresh) {
7269
7270
  const agentId = (this.addonCtx?.kernel?.cluster?.broker)?.nodeID ?? node_os.hostname();
7270
7271
  const agentName = agentId;
7271
- const addons = require_event_loop_stall_monitor.ALL_PIPELINE_STEPS.map((s) => ({
7272
+ const addons = require_local_frame_registry.ALL_PIPELINE_STEPS.map((s) => ({
7272
7273
  id: s.definition.id,
7273
7274
  name: s.definition.name,
7274
7275
  models: s.definition.models.filter((m) => m.legacy !== true).map((m) => {
@@ -7766,7 +7767,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
7766
7767
  let frameViewResolver;
7767
7768
  let rootInputViewProvider;
7768
7769
  if (runtimeFrameRef) {
7769
- frameViewResolver = createRootFrameViewResolver(require_event_loop_stall_monitor.localFrameRegistry, runtimeFrameRef);
7770
+ frameViewResolver = createRootFrameViewResolver(require_local_frame_registry.localFrameRegistry, runtimeFrameRef);
7770
7771
  rootInputViewProvider = async (root, crop) => {
7771
7772
  const view = await frameViewResolver(root, crop ? {
7772
7773
  left: crop[0],
@@ -9213,7 +9214,7 @@ var DetectionPipelineProvider = class DetectionPipelineProvider {
9213
9214
  function buildSchemaSlots(formats, modelsDir, customByStep) {
9214
9215
  const slotMap = /* @__PURE__ */ new Map();
9215
9216
  const formatList = [...formats];
9216
- for (const pipelineStep of require_event_loop_stall_monitor.ALL_PIPELINE_STEPS) {
9217
+ for (const pipelineStep of require_local_frame_registry.ALL_PIPELINE_STEPS) {
9217
9218
  const step = pipelineStep.definition;
9218
9219
  const availableModels = mergeCustomModels(step.models, customByStep?.get(step.id) ?? []).filter((m) => m.legacy !== true && formatList.some((f) => m.formats[f]));
9219
9220
  if (availableModels.length === 0) continue;
@@ -9307,7 +9308,7 @@ function resolveChildSlots(slot) {
9307
9308
  */
9308
9309
  function buildDefaultStepTree(format, objectDetectionModelId) {
9309
9310
  const makeStep = (stepId, children, opts) => {
9310
- const def = require_event_loop_stall_monitor.getStepDefinition(stepId);
9311
+ const def = require_local_frame_registry.getStepDefinition(stepId);
9311
9312
  if (!def.models.some((m) => m.formats[format])) return null;
9312
9313
  return {
9313
9314
  addonId: def.id,
@@ -9316,7 +9317,7 @@ function buildDefaultStepTree(format, objectDetectionModelId) {
9316
9317
  inputClasses: def.inputClasses ?? [],
9317
9318
  outputClasses: def.outputClasses,
9318
9319
  enabled: opts?.enabled ?? true,
9319
- modelId: opts?.modelId ?? require_event_loop_stall_monitor.getDefaultModelForFormat(stepId, format),
9320
+ modelId: opts?.modelId ?? require_local_frame_registry.getDefaultModelForFormat(stepId, format),
9320
9321
  children,
9321
9322
  ...def.group ? { group: def.group } : {}
9322
9323
  };
@@ -9373,7 +9374,7 @@ function flattenSteps(steps) {
9373
9374
  */
9374
9375
  function isDetailPlaneStep(addonId) {
9375
9376
  try {
9376
- return require_event_loop_stall_monitor.getStepDefinition(addonId).inputClasses !== null;
9377
+ return require_local_frame_registry.getStepDefinition(addonId).inputClasses !== null;
9377
9378
  } catch {
9378
9379
  return false;
9379
9380
  }
@@ -9394,7 +9395,7 @@ function stepHasZeroBuildsFor(addonId, format) {
9394
9395
  */
9395
9396
  function availableFormatsForStep(addonId) {
9396
9397
  try {
9397
- const def = require_event_loop_stall_monitor.getStepDefinition(addonId);
9398
+ const def = require_local_frame_registry.getStepDefinition(addonId);
9398
9399
  const formats = /* @__PURE__ */ new Set();
9399
9400
  for (const model of def.models) for (const format of Object.keys(model.formats)) formats.add(format);
9400
9401
  return Array.from(formats).toSorted();
@@ -9963,7 +9964,7 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
9963
9964
  }).catch(() => {});
9964
9965
  }
9965
9966
  getModelCatalog() {
9966
- return require_event_loop_stall_monitor.ALL_STEPS.flatMap((s) => s.models.filter((m) => m.legacy !== true));
9967
+ return require_local_frame_registry.ALL_STEPS.flatMap((s) => s.models.filter((m) => m.legacy !== true));
9967
9968
  }
9968
9969
  /**
9969
9970
  * Proactively install the OpenVINO Python package when Intel hardware
@@ -10049,9 +10050,9 @@ var DetectionPipelineAddon = class extends require_dist.BaseAddon {
10049
10050
  }
10050
10051
  };
10051
10052
  //#endregion
10052
- exports.ALL_STEPS = require_event_loop_stall_monitor.ALL_STEPS;
10053
+ exports.ALL_STEPS = require_local_frame_registry.ALL_STEPS;
10053
10054
  exports.DetectionPipelineProvider = DetectionPipelineProvider;
10054
10055
  exports.default = DetectionPipelineAddon;
10055
- exports.getDefaultModelForFormat = require_event_loop_stall_monitor.getDefaultModelForFormat;
10056
- exports.getStepDefinition = require_event_loop_stall_monitor.getStepDefinition;
10056
+ exports.getDefaultModelForFormat = require_local_frame_registry.getDefaultModelForFormat;
10057
+ exports.getStepDefinition = require_local_frame_registry.getStepDefinition;
10057
10058
  exports.shouldInstallOpenvino = shouldInstallOpenvino;
@@ -1,8 +1,9 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
2
  import { Bt as nodePin, Ft as createEvent, It as hydrateSchema, Kt as array, O as YAMNET_TO_MACRO, Q as inferModelProvider, Qt as object, S as PoolMemoryWatchdog, St as supportedRuntimes$1, Vt as parseJsonUnknown, W as detectionPipelineCapability, Wt as sleep, Y as evaluateZoneRules, _t as resolvePoolMemoryPolicy, at as overlayClusterStepSettings, c as DEFAULT_CLUSTER_STEP_MODELS, ct as pickClusterStepSettings, dt as pipelineExecutorCapability, en as string, et as loadContributionCapability, ht as resolveClusterStepModelId, jt as BaseAddon, l as DEFAULT_CLUSTER_STEP_SETTINGS, m as DEVICE_BACKEND_TO_FORMAT, nn as EventCategory, q as enumerateInferenceDevices, st as pickClusterStepModels, t as APPLE_SA_TO_MACRO, tn as union, wt as errMsg, yt as runtimeDevices$1, z as defaultDeviceFor$1 } from "../dist-cZopp8Xm.mjs";
3
3
  import { a as readProcessCost } from "../node-Br6p9KvD.mjs";
4
- import { a as ALL_PIPELINE_STEPS, c as getDefaultModelForFormatFromDef, d as resolveModelForFormat, f as landmarkPrecisionVerdict, l as getStep, n as startEventLoopStallMonitor, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as getStepDefinition } from "../event-loop-stall-monitor-DH_vFYWu.mjs";
4
+ import { a as getDefaultModelForFormat, c as getStepDefinition, i as ALL_STEPS, l as resolveModelForFormat, o as getDefaultModelForFormatFromDef, r as ALL_PIPELINE_STEPS, s as getStep, t as localFrameRegistry, u as landmarkPrecisionVerdict } from "../local-frame-registry-Ctv_TzmF.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
+ import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
6
7
  import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-DX25Z60Q.mjs";
7
8
  import * as fs from "node:fs";
8
9
  import * as path$1 from "node:path";
@@ -0,0 +1,138 @@
1
+ import { PerformanceObserver } from "node:perf_hooks";
2
+ //#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
3
+ /**
4
+ * event-loop-stall-monitor — names the thing that freezes the detection process.
5
+ *
6
+ * ## What this exists to settle
7
+ *
8
+ * On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
9
+ * calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
10
+ * and EVERY one of them eventually resolved. Six explanations were killed with
11
+ * telemetry alone:
12
+ *
13
+ * - not a slow GPU or a wedged pool — the work completes, just enormously late;
14
+ * - not CPU starvation — hosts sat at 8-48% during the freezes;
15
+ * - not swap — `pswpin/pswpout` are flat zero on every host;
16
+ * - not a logging artifact — Loki's timestamp trails the in-process one by
17
+ * 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
18
+ * - not the v1.2.30 release — the stalls predate it by a day;
19
+ * - not only the idle-pool recompile — that is real and hub-side, but the
20
+ * agent stalls with no eviction and no compile at all.
21
+ *
22
+ * What IS established: the 10s stats timer — which touches no inference code —
23
+ * stops firing for 22-88s and resumes at the same instant the abandoned calls
24
+ * settle. The event loop itself freezes. From outside the process, GC and a
25
+ * synchronous block are indistinguishable, and guessing between them is how
26
+ * this investigation would go wrong. So measure it.
27
+ *
28
+ * ## How
29
+ *
30
+ * A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
31
+ * the loop was blocked ~39s, whatever blocked it. In the same window the GC
32
+ * observer accumulates pause time. The two numbers together answer the question
33
+ * outright:
34
+ *
35
+ * - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
36
+ * - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
37
+ *
38
+ * Heap and RSS ride along because if it is GC, the next question is always
39
+ * "growing since when?".
40
+ *
41
+ * Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
42
+ * distribution, and the question here is about ONE outlier and what coincided
43
+ * with it.
44
+ */
45
+ /** Tick period. Small enough to bound attribution error, large enough to cost
46
+ * nothing on a process already doing frame work. */
47
+ var STALL_TICK_MS = 1e3;
48
+ /**
49
+ * Report a freeze only past this. The runner's own inference deadline is 20s
50
+ * and the observed freezes run 22-88s, so this is far below the signal while
51
+ * staying clear of ordinary scheduling jitter (a busy frame path routinely
52
+ * overruns a 1s timer by tens of ms).
53
+ */
54
+ var STALL_REPORT_MS = 2e3;
55
+ /**
56
+ * Attribute a freeze. Pure, because this is the ONE judgement the whole module
57
+ * exists to make and it must be testable without freezing a real event loop.
58
+ *
59
+ * `gc` when garbage collection accounts for most of the stall; a
60
+ * `synchronous-block` when it accounts for almost none — that verdict points
61
+ * away from the heap entirely and at whatever ran without yielding.
62
+ */
63
+ function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
64
+ const share = stallMs > 0 ? gcMs / stallMs : 0;
65
+ return {
66
+ stallMs,
67
+ gcMs,
68
+ gcCount,
69
+ longestGcMs,
70
+ cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
71
+ };
72
+ }
73
+ /**
74
+ * Start watching. Returns a stop function; safe to call twice.
75
+ *
76
+ * Never throws into the caller: a diagnostic that can take the detection
77
+ * process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
78
+ * unavailable in some embedded runtimes, and that must degrade to
79
+ * stall-detection-without-attribution rather than a boot failure.
80
+ */
81
+ function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
82
+ let gc = {
83
+ totalMs: 0,
84
+ count: 0,
85
+ longestMs: 0
86
+ };
87
+ let observer = null;
88
+ try {
89
+ observer = new PerformanceObserver((list) => {
90
+ for (const entry of list.getEntries()) {
91
+ gc.totalMs += entry.duration;
92
+ gc.count += 1;
93
+ if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
94
+ }
95
+ });
96
+ observer.observe({ entryTypes: ["gc"] });
97
+ } catch {
98
+ observer = null;
99
+ }
100
+ let expected = Date.now() + STALL_TICK_MS;
101
+ const timer = setInterval(() => {
102
+ const now = Date.now();
103
+ const lateBy = now - expected;
104
+ const window = gc;
105
+ gc = {
106
+ totalMs: 0,
107
+ count: 0,
108
+ longestMs: 0
109
+ };
110
+ expected = now + STALL_TICK_MS;
111
+ if (lateBy < reportThresholdMs) return;
112
+ const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
113
+ const mem = process.memoryUsage();
114
+ logger.warn("event loop stalled", { meta: {
115
+ stallMs: verdict.stallMs,
116
+ cause: verdict.cause,
117
+ gcMs: verdict.gcMs,
118
+ gcCount: verdict.gcCount,
119
+ longestGcMs: verdict.longestGcMs,
120
+ gcObserved: observer !== null,
121
+ heapUsedMb: Math.round(mem.heapUsed / 1e6),
122
+ heapTotalMb: Math.round(mem.heapTotal / 1e6),
123
+ rssMb: Math.round(mem.rss / 1e6),
124
+ externalMb: Math.round(mem.external / 1e6),
125
+ arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
126
+ } });
127
+ }, STALL_TICK_MS);
128
+ timer.unref?.();
129
+ let stopped = false;
130
+ return () => {
131
+ if (stopped) return;
132
+ stopped = true;
133
+ clearInterval(timer);
134
+ observer?.disconnect();
135
+ };
136
+ }
137
+ //#endregion
138
+ export { startEventLoopStallMonitor as n, attributeStall as t };
@@ -0,0 +1,149 @@
1
+ let node_perf_hooks = require("node:perf_hooks");
2
+ //#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
3
+ /**
4
+ * event-loop-stall-monitor — names the thing that freezes the detection process.
5
+ *
6
+ * ## What this exists to settle
7
+ *
8
+ * On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
9
+ * calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
10
+ * and EVERY one of them eventually resolved. Six explanations were killed with
11
+ * telemetry alone:
12
+ *
13
+ * - not a slow GPU or a wedged pool — the work completes, just enormously late;
14
+ * - not CPU starvation — hosts sat at 8-48% during the freezes;
15
+ * - not swap — `pswpin/pswpout` are flat zero on every host;
16
+ * - not a logging artifact — Loki's timestamp trails the in-process one by
17
+ * 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
18
+ * - not the v1.2.30 release — the stalls predate it by a day;
19
+ * - not only the idle-pool recompile — that is real and hub-side, but the
20
+ * agent stalls with no eviction and no compile at all.
21
+ *
22
+ * What IS established: the 10s stats timer — which touches no inference code —
23
+ * stops firing for 22-88s and resumes at the same instant the abandoned calls
24
+ * settle. The event loop itself freezes. From outside the process, GC and a
25
+ * synchronous block are indistinguishable, and guessing between them is how
26
+ * this investigation would go wrong. So measure it.
27
+ *
28
+ * ## How
29
+ *
30
+ * A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
31
+ * the loop was blocked ~39s, whatever blocked it. In the same window the GC
32
+ * observer accumulates pause time. The two numbers together answer the question
33
+ * outright:
34
+ *
35
+ * - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
36
+ * - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
37
+ *
38
+ * Heap and RSS ride along because if it is GC, the next question is always
39
+ * "growing since when?".
40
+ *
41
+ * Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
42
+ * distribution, and the question here is about ONE outlier and what coincided
43
+ * with it.
44
+ */
45
+ /** Tick period. Small enough to bound attribution error, large enough to cost
46
+ * nothing on a process already doing frame work. */
47
+ var STALL_TICK_MS = 1e3;
48
+ /**
49
+ * Report a freeze only past this. The runner's own inference deadline is 20s
50
+ * and the observed freezes run 22-88s, so this is far below the signal while
51
+ * staying clear of ordinary scheduling jitter (a busy frame path routinely
52
+ * overruns a 1s timer by tens of ms).
53
+ */
54
+ var STALL_REPORT_MS = 2e3;
55
+ /**
56
+ * Attribute a freeze. Pure, because this is the ONE judgement the whole module
57
+ * exists to make and it must be testable without freezing a real event loop.
58
+ *
59
+ * `gc` when garbage collection accounts for most of the stall; a
60
+ * `synchronous-block` when it accounts for almost none — that verdict points
61
+ * away from the heap entirely and at whatever ran without yielding.
62
+ */
63
+ function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
64
+ const share = stallMs > 0 ? gcMs / stallMs : 0;
65
+ return {
66
+ stallMs,
67
+ gcMs,
68
+ gcCount,
69
+ longestGcMs,
70
+ cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
71
+ };
72
+ }
73
+ /**
74
+ * Start watching. Returns a stop function; safe to call twice.
75
+ *
76
+ * Never throws into the caller: a diagnostic that can take the detection
77
+ * process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
78
+ * unavailable in some embedded runtimes, and that must degrade to
79
+ * stall-detection-without-attribution rather than a boot failure.
80
+ */
81
+ function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
82
+ let gc = {
83
+ totalMs: 0,
84
+ count: 0,
85
+ longestMs: 0
86
+ };
87
+ let observer = null;
88
+ try {
89
+ observer = new node_perf_hooks.PerformanceObserver((list) => {
90
+ for (const entry of list.getEntries()) {
91
+ gc.totalMs += entry.duration;
92
+ gc.count += 1;
93
+ if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
94
+ }
95
+ });
96
+ observer.observe({ entryTypes: ["gc"] });
97
+ } catch {
98
+ observer = null;
99
+ }
100
+ let expected = Date.now() + STALL_TICK_MS;
101
+ const timer = setInterval(() => {
102
+ const now = Date.now();
103
+ const lateBy = now - expected;
104
+ const window = gc;
105
+ gc = {
106
+ totalMs: 0,
107
+ count: 0,
108
+ longestMs: 0
109
+ };
110
+ expected = now + STALL_TICK_MS;
111
+ if (lateBy < reportThresholdMs) return;
112
+ const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
113
+ const mem = process.memoryUsage();
114
+ logger.warn("event loop stalled", { meta: {
115
+ stallMs: verdict.stallMs,
116
+ cause: verdict.cause,
117
+ gcMs: verdict.gcMs,
118
+ gcCount: verdict.gcCount,
119
+ longestGcMs: verdict.longestGcMs,
120
+ gcObserved: observer !== null,
121
+ heapUsedMb: Math.round(mem.heapUsed / 1e6),
122
+ heapTotalMb: Math.round(mem.heapTotal / 1e6),
123
+ rssMb: Math.round(mem.rss / 1e6),
124
+ externalMb: Math.round(mem.external / 1e6),
125
+ arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
126
+ } });
127
+ }, STALL_TICK_MS);
128
+ timer.unref?.();
129
+ let stopped = false;
130
+ return () => {
131
+ if (stopped) return;
132
+ stopped = true;
133
+ clearInterval(timer);
134
+ observer?.disconnect();
135
+ };
136
+ }
137
+ //#endregion
138
+ Object.defineProperty(exports, "attributeStall", {
139
+ enumerable: true,
140
+ get: function() {
141
+ return attributeStall;
142
+ }
143
+ });
144
+ Object.defineProperty(exports, "startEventLoopStallMonitor", {
145
+ enumerable: true,
146
+ get: function() {
147
+ return startEventLoopStallMonitor;
148
+ }
149
+ });
@@ -1,6 +1,5 @@
1
1
  import { M as audioKindId, Z as hfModelUrl, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS } from "./dist-cZopp8Xm.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
- import { PerformanceObserver } from "node:perf_hooks";
4
3
  //#region src/detection-pipeline/registry/model-catalogs.ts
5
4
  var HF_REPO = "camstack/camstack-models";
6
5
  var HF_SCRYPTED = "scrypted/plugin-models";
@@ -2111,140 +2110,4 @@ var localFrameRegistry = new LocalFrameRegistry({
2111
2110
  maxBytes: envPositiveInt("CAMSTACK_LOCAL_FRAME_BUDGET_MB", DEFAULT_MAX_BYTES / 1048576) * 1048576
2112
2111
  });
2113
2112
  //#endregion
2114
- //#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
2115
- /**
2116
- * event-loop-stall-monitor — names the thing that freezes the detection process.
2117
- *
2118
- * ## What this exists to settle
2119
- *
2120
- * On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
2121
- * calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
2122
- * and EVERY one of them eventually resolved. Six explanations were killed with
2123
- * telemetry alone:
2124
- *
2125
- * - not a slow GPU or a wedged pool — the work completes, just enormously late;
2126
- * - not CPU starvation — hosts sat at 8-48% during the freezes;
2127
- * - not swap — `pswpin/pswpout` are flat zero on every host;
2128
- * - not a logging artifact — Loki's timestamp trails the in-process one by
2129
- * 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
2130
- * - not the v1.2.30 release — the stalls predate it by a day;
2131
- * - not only the idle-pool recompile — that is real and hub-side, but the
2132
- * agent stalls with no eviction and no compile at all.
2133
- *
2134
- * What IS established: the 10s stats timer — which touches no inference code —
2135
- * stops firing for 22-88s and resumes at the same instant the abandoned calls
2136
- * settle. The event loop itself freezes. From outside the process, GC and a
2137
- * synchronous block are indistinguishable, and guessing between them is how
2138
- * this investigation would go wrong. So measure it.
2139
- *
2140
- * ## How
2141
- *
2142
- * A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
2143
- * the loop was blocked ~39s, whatever blocked it. In the same window the GC
2144
- * observer accumulates pause time. The two numbers together answer the question
2145
- * outright:
2146
- *
2147
- * - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
2148
- * - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
2149
- *
2150
- * Heap and RSS ride along because if it is GC, the next question is always
2151
- * "growing since when?".
2152
- *
2153
- * Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
2154
- * distribution, and the question here is about ONE outlier and what coincided
2155
- * with it.
2156
- */
2157
- /** Tick period. Small enough to bound attribution error, large enough to cost
2158
- * nothing on a process already doing frame work. */
2159
- var STALL_TICK_MS = 1e3;
2160
- /**
2161
- * Report a freeze only past this. The runner's own inference deadline is 20s
2162
- * and the observed freezes run 22-88s, so this is far below the signal while
2163
- * staying clear of ordinary scheduling jitter (a busy frame path routinely
2164
- * overruns a 1s timer by tens of ms).
2165
- */
2166
- var STALL_REPORT_MS = 2e3;
2167
- /**
2168
- * Attribute a freeze. Pure, because this is the ONE judgement the whole module
2169
- * exists to make and it must be testable without freezing a real event loop.
2170
- *
2171
- * `gc` when garbage collection accounts for most of the stall; a
2172
- * `synchronous-block` when it accounts for almost none — that verdict points
2173
- * away from the heap entirely and at whatever ran without yielding.
2174
- */
2175
- function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
2176
- const share = stallMs > 0 ? gcMs / stallMs : 0;
2177
- return {
2178
- stallMs,
2179
- gcMs,
2180
- gcCount,
2181
- longestGcMs,
2182
- cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
2183
- };
2184
- }
2185
- /**
2186
- * Start watching. Returns a stop function; safe to call twice.
2187
- *
2188
- * Never throws into the caller: a diagnostic that can take the detection
2189
- * process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
2190
- * unavailable in some embedded runtimes, and that must degrade to
2191
- * stall-detection-without-attribution rather than a boot failure.
2192
- */
2193
- function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
2194
- let gc = {
2195
- totalMs: 0,
2196
- count: 0,
2197
- longestMs: 0
2198
- };
2199
- let observer = null;
2200
- try {
2201
- observer = new PerformanceObserver((list) => {
2202
- for (const entry of list.getEntries()) {
2203
- gc.totalMs += entry.duration;
2204
- gc.count += 1;
2205
- if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
2206
- }
2207
- });
2208
- observer.observe({ entryTypes: ["gc"] });
2209
- } catch {
2210
- observer = null;
2211
- }
2212
- let expected = Date.now() + STALL_TICK_MS;
2213
- const timer = setInterval(() => {
2214
- const now = Date.now();
2215
- const lateBy = now - expected;
2216
- const window = gc;
2217
- gc = {
2218
- totalMs: 0,
2219
- count: 0,
2220
- longestMs: 0
2221
- };
2222
- expected = now + STALL_TICK_MS;
2223
- if (lateBy < reportThresholdMs) return;
2224
- const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
2225
- const mem = process.memoryUsage();
2226
- logger.warn("event loop stalled", { meta: {
2227
- stallMs: verdict.stallMs,
2228
- cause: verdict.cause,
2229
- gcMs: verdict.gcMs,
2230
- gcCount: verdict.gcCount,
2231
- longestGcMs: verdict.longestGcMs,
2232
- gcObserved: observer !== null,
2233
- heapUsedMb: Math.round(mem.heapUsed / 1e6),
2234
- heapTotalMb: Math.round(mem.heapTotal / 1e6),
2235
- rssMb: Math.round(mem.rss / 1e6),
2236
- externalMb: Math.round(mem.external / 1e6),
2237
- arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
2238
- } });
2239
- }, STALL_TICK_MS);
2240
- timer.unref?.();
2241
- let stopped = false;
2242
- return () => {
2243
- if (stopped) return;
2244
- stopped = true;
2245
- clearInterval(timer);
2246
- observer?.disconnect();
2247
- };
2248
- }
2249
- //#endregion
2250
- export { ALL_PIPELINE_STEPS as a, getDefaultModelForFormatFromDef as c, resolveModelForFormat as d, landmarkPrecisionVerdict as f, resolveFrameViewGeometry as i, getStep as l, startEventLoopStallMonitor as n, ALL_STEPS as o, localFrameRegistry as r, getDefaultModelForFormat as s, attributeStall as t, getStepDefinition as u };
2113
+ export { getDefaultModelForFormat as a, getStepDefinition as c, ALL_STEPS as i, resolveModelForFormat as l, resolveFrameViewGeometry as n, getDefaultModelForFormatFromDef as o, ALL_PIPELINE_STEPS as r, getStep as s, localFrameRegistry as t, landmarkPrecisionVerdict as u };
@@ -1,6 +1,5 @@
1
1
  const require_dist = require("./dist-DML3mpn9.js");
2
2
  let node_crypto = require("node:crypto");
3
- let node_perf_hooks = require("node:perf_hooks");
4
3
  //#region src/detection-pipeline/registry/model-catalogs.ts
5
4
  var HF_REPO = "camstack/camstack-models";
6
5
  var HF_SCRYPTED = "scrypted/plugin-models";
@@ -2111,142 +2110,6 @@ var localFrameRegistry = new LocalFrameRegistry({
2111
2110
  maxBytes: envPositiveInt("CAMSTACK_LOCAL_FRAME_BUDGET_MB", DEFAULT_MAX_BYTES / 1048576) * 1048576
2112
2111
  });
2113
2112
  //#endregion
2114
- //#region src/detection-pipeline/diagnostics/event-loop-stall-monitor.ts
2115
- /**
2116
- * event-loop-stall-monitor — names the thing that freezes the detection process.
2117
- *
2118
- * ## What this exists to settle
2119
- *
2120
- * On 2026-08-01/02 inference across the whole fleet stalled in bursts: 759
2121
- * calls in 90 minutes crossed the runner's 20s deadline, p50 41s and max 80s,
2122
- * and EVERY one of them eventually resolved. Six explanations were killed with
2123
- * telemetry alone:
2124
- *
2125
- * - not a slow GPU or a wedged pool — the work completes, just enormously late;
2126
- * - not CPU starvation — hosts sat at 8-48% during the freezes;
2127
- * - not swap — `pswpin/pswpout` are flat zero on every host;
2128
- * - not a logging artifact — Loki's timestamp trails the in-process one by
2129
- * 0.1-1.0s at p95, so the gaps are real, not buffered delivery;
2130
- * - not the v1.2.30 release — the stalls predate it by a day;
2131
- * - not only the idle-pool recompile — that is real and hub-side, but the
2132
- * agent stalls with no eviction and no compile at all.
2133
- *
2134
- * What IS established: the 10s stats timer — which touches no inference code —
2135
- * stops firing for 22-88s and resumes at the same instant the abandoned calls
2136
- * settle. The event loop itself freezes. From outside the process, GC and a
2137
- * synchronous block are indistinguishable, and guessing between them is how
2138
- * this investigation would go wrong. So measure it.
2139
- *
2140
- * ## How
2141
- *
2142
- * A 1s interval whose LATENESS is the freeze: a tick that arrives 40s late means
2143
- * the loop was blocked ~39s, whatever blocked it. In the same window the GC
2144
- * observer accumulates pause time. The two numbers together answer the question
2145
- * outright:
2146
- *
2147
- * - `gcMs` ≈ `stallMs` → garbage collection. Look at heap growth.
2148
- * - `gcMs` ≈ 0 → something SYNCHRONOUS blocked the loop, and GC is innocent.
2149
- *
2150
- * Heap and RSS ride along because if it is GC, the next question is always
2151
- * "growing since when?".
2152
- *
2153
- * Deliberately not a histogram (`monitorEventLoopDelay`): that reports a
2154
- * distribution, and the question here is about ONE outlier and what coincided
2155
- * with it.
2156
- */
2157
- /** Tick period. Small enough to bound attribution error, large enough to cost
2158
- * nothing on a process already doing frame work. */
2159
- var STALL_TICK_MS = 1e3;
2160
- /**
2161
- * Report a freeze only past this. The runner's own inference deadline is 20s
2162
- * and the observed freezes run 22-88s, so this is far below the signal while
2163
- * staying clear of ordinary scheduling jitter (a busy frame path routinely
2164
- * overruns a 1s timer by tens of ms).
2165
- */
2166
- var STALL_REPORT_MS = 2e3;
2167
- /**
2168
- * Attribute a freeze. Pure, because this is the ONE judgement the whole module
2169
- * exists to make and it must be testable without freezing a real event loop.
2170
- *
2171
- * `gc` when garbage collection accounts for most of the stall; a
2172
- * `synchronous-block` when it accounts for almost none — that verdict points
2173
- * away from the heap entirely and at whatever ran without yielding.
2174
- */
2175
- function attributeStall(stallMs, gcMs, gcCount, longestGcMs) {
2176
- const share = stallMs > 0 ? gcMs / stallMs : 0;
2177
- return {
2178
- stallMs,
2179
- gcMs,
2180
- gcCount,
2181
- longestGcMs,
2182
- cause: share >= .7 ? "gc" : share <= .2 ? "synchronous-block" : "mixed"
2183
- };
2184
- }
2185
- /**
2186
- * Start watching. Returns a stop function; safe to call twice.
2187
- *
2188
- * Never throws into the caller: a diagnostic that can take the detection
2189
- * process down is worse than no diagnostic. `PerformanceObserver` for `gc` is
2190
- * unavailable in some embedded runtimes, and that must degrade to
2191
- * stall-detection-without-attribution rather than a boot failure.
2192
- */
2193
- function startEventLoopStallMonitor(logger, reportThresholdMs = STALL_REPORT_MS) {
2194
- let gc = {
2195
- totalMs: 0,
2196
- count: 0,
2197
- longestMs: 0
2198
- };
2199
- let observer = null;
2200
- try {
2201
- observer = new node_perf_hooks.PerformanceObserver((list) => {
2202
- for (const entry of list.getEntries()) {
2203
- gc.totalMs += entry.duration;
2204
- gc.count += 1;
2205
- if (entry.duration > gc.longestMs) gc.longestMs = entry.duration;
2206
- }
2207
- });
2208
- observer.observe({ entryTypes: ["gc"] });
2209
- } catch {
2210
- observer = null;
2211
- }
2212
- let expected = Date.now() + STALL_TICK_MS;
2213
- const timer = setInterval(() => {
2214
- const now = Date.now();
2215
- const lateBy = now - expected;
2216
- const window = gc;
2217
- gc = {
2218
- totalMs: 0,
2219
- count: 0,
2220
- longestMs: 0
2221
- };
2222
- expected = now + STALL_TICK_MS;
2223
- if (lateBy < reportThresholdMs) return;
2224
- const verdict = attributeStall(lateBy, Math.round(window.totalMs), window.count, Math.round(window.longestMs));
2225
- const mem = process.memoryUsage();
2226
- logger.warn("event loop stalled", { meta: {
2227
- stallMs: verdict.stallMs,
2228
- cause: verdict.cause,
2229
- gcMs: verdict.gcMs,
2230
- gcCount: verdict.gcCount,
2231
- longestGcMs: verdict.longestGcMs,
2232
- gcObserved: observer !== null,
2233
- heapUsedMb: Math.round(mem.heapUsed / 1e6),
2234
- heapTotalMb: Math.round(mem.heapTotal / 1e6),
2235
- rssMb: Math.round(mem.rss / 1e6),
2236
- externalMb: Math.round(mem.external / 1e6),
2237
- arrayBuffersMb: Math.round(mem.arrayBuffers / 1e6)
2238
- } });
2239
- }, STALL_TICK_MS);
2240
- timer.unref?.();
2241
- let stopped = false;
2242
- return () => {
2243
- if (stopped) return;
2244
- stopped = true;
2245
- clearInterval(timer);
2246
- observer?.disconnect();
2247
- };
2248
- }
2249
- //#endregion
2250
2113
  Object.defineProperty(exports, "ALL_PIPELINE_STEPS", {
2251
2114
  enumerable: true,
2252
2115
  get: function() {
@@ -2259,12 +2122,6 @@ Object.defineProperty(exports, "ALL_STEPS", {
2259
2122
  return ALL_STEPS;
2260
2123
  }
2261
2124
  });
2262
- Object.defineProperty(exports, "attributeStall", {
2263
- enumerable: true,
2264
- get: function() {
2265
- return attributeStall;
2266
- }
2267
- });
2268
2125
  Object.defineProperty(exports, "getDefaultModelForFormat", {
2269
2126
  enumerable: true,
2270
2127
  get: function() {
@@ -2313,9 +2170,3 @@ Object.defineProperty(exports, "resolveModelForFormat", {
2313
2170
  return resolveModelForFormat;
2314
2171
  }
2315
2172
  });
2316
- Object.defineProperty(exports, "startEventLoopStallMonitor", {
2317
- enumerable: true,
2318
- get: function() {
2319
- return startEventLoopStallMonitor;
2320
- }
2321
- });
@@ -4,11 +4,12 @@ Object.defineProperties(exports, {
4
4
  });
5
5
  const require_dist = require("../dist-DML3mpn9.js");
6
6
  const require_node = require("../node-OvVqe9eh.js");
7
- const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaV4Zdsz.js");
7
+ const require_local_frame_registry = require("../local-frame-registry-DO8-LuSo.js");
8
8
  const require_worker_protocol = require("../worker-protocol-Dt3zWCQY.js");
9
9
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
10
10
  const require_lazy_sharp = require("../lazy-sharp-BzXIjfN8.js");
11
11
  const require_restream_intent = require("../restream-intent-Cv9x3jmu.js");
12
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
12
13
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
13
14
  let node_child_process = require("node:child_process");
14
15
  let sharp = require("sharp");
@@ -1408,7 +1409,7 @@ function createSessionDecodeCoordinator(deps) {
1408
1409
  * shm-ring cameras.
1409
1410
  */
1410
1411
  async function toDecodedFrame(frame, format, viewSpec) {
1411
- const geometry = viewSpec ? require_event_loop_stall_monitor.resolveFrameViewGeometry(frame.image.width, frame.image.height, viewSpec) : void 0;
1412
+ const geometry = viewSpec ? require_local_frame_registry.resolveFrameViewGeometry(frame.image.width, frame.image.height, viewSpec) : void 0;
1412
1413
  const bytes = await frame.image.toBuffer(geometry ? {
1413
1414
  crop: geometry.sourceCrop,
1414
1415
  resize: geometry.output,
@@ -5056,7 +5057,7 @@ function writeRetainedDetectionFrame(store, deviceId, frame, retention) {
5056
5057
  if (retention) return retention.admitFrame(deviceId, frame);
5057
5058
  if (!store) return void 0;
5058
5059
  if (!isContinuousRetainEnabled()) return store.allocateHandle(deviceId, frame) ?? void 0;
5059
- return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(require_event_loop_stall_monitor.localFrameRegistry, frame.frameRef) : void 0) ?? void 0;
5060
+ return store.write(deviceId, frame, frame.frameRef ? createRetainedContentFallback(require_local_frame_registry.localFrameRegistry, frame.frameRef) : void 0) ?? void 0;
5060
5061
  }
5061
5062
  var DETECTION_KEEPWARM_MS = 15e3;
5062
5063
  /**
@@ -5117,7 +5118,7 @@ function collectEnabledStepIds(steps, acc) {
5117
5118
  */
5118
5119
  function tryGetStepDefinition(stepId) {
5119
5120
  try {
5120
- return require_event_loop_stall_monitor.getStepDefinition(stepId);
5121
+ return require_local_frame_registry.getStepDefinition(stepId);
5121
5122
  } catch {
5122
5123
  return null;
5123
5124
  }
@@ -5576,7 +5577,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
5576
5577
  });
5577
5578
  this.retention = new OnDemandRetentionSession({
5578
5579
  store: this.retainedFrames,
5579
- registry: require_event_loop_stall_monitor.localFrameRegistry,
5580
+ registry: require_local_frame_registry.localFrameRegistry,
5580
5581
  telemetry: this.frameLazy,
5581
5582
  nodeId: this.nodeId,
5582
5583
  continuousRetain: isContinuousRetainEnabled()
@@ -5588,7 +5589,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
5588
5589
  minThrottledFps: this.config.minThrottledFps,
5589
5590
  processFrame: (deviceId, frame, handle) => this.runInference(deviceId, frame, handle),
5590
5591
  analyzeMotion: (deviceId, frame, handle) => this.runMotionAnalysis(deviceId, frame, handle),
5591
- releaseFrameRef: (ref, reason) => require_event_loop_stall_monitor.localFrameRegistry.release(ref, reason),
5592
+ releaseFrameRef: (ref, reason) => require_local_frame_registry.localFrameRegistry.release(ref, reason),
5592
5593
  releaseRetention: (handle, reason) => this.retention?.release(handle, reason),
5593
5594
  hydrateRetainedFrame: (handle) => this.retainedFrames?.ensureContent(handle) ?? Promise.resolve(null),
5594
5595
  onPhaseChanged: (deviceId, phase, meta) => this.handlePhaseChanged(deviceId, phase, meta),
@@ -7424,7 +7425,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7424
7425
  return materialize;
7425
7426
  },
7426
7427
  ...useLocalFrameRef ? {
7427
- frameRegistry: require_event_loop_stall_monitor.localFrameRegistry,
7428
+ frameRegistry: require_local_frame_registry.localFrameRegistry,
7428
7429
  onFrameRef: async (frame, nativeCrop) => {
7429
7430
  const current = reservation;
7430
7431
  reservation = null;
@@ -7441,7 +7442,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
7441
7442
  });
7442
7443
  const admitted = current.detection && runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle);
7443
7444
  if (current.motion) {
7444
- const motion = await resolveMotionFrameView(require_event_loop_stall_monitor.localFrameRegistry, frame.frameRef);
7445
+ const motion = await resolveMotionFrameView(require_local_frame_registry.localFrameRegistry, frame.frameRef);
7445
7446
  if (motion.kind === "view") runner.enqueueMotionFrame(config.deviceId, {
7446
7447
  data: Buffer.from(motion.data),
7447
7448
  width: motion.width,
@@ -8106,7 +8107,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8106
8107
  format: "rgb"
8107
8108
  },
8108
8109
  ...useLocalFrameRef ? {
8109
- frameRegistry: require_event_loop_stall_monitor.localFrameRegistry,
8110
+ frameRegistry: require_local_frame_registry.localFrameRegistry,
8110
8111
  onFrameRef: (frame, nativeCrop) => {
8111
8112
  if (settled) return false;
8112
8113
  const handle = writeRetainedDetectionFrame(retainedFrames, config.deviceId, frame, this.retention);
@@ -1,10 +1,11 @@
1
1
  import { B as defineCustomActions, Bt as nodePin, Ft as createEvent, Gt as _enum, H as deriveDetailCropRect, Kt as array, L as customAction, Qt as object, X as failureContributionCapability, Xt as literal, Yt as lazy, Zt as number, _ as FailureCounters, en as string, et as loadContributionCapability, ft as pipelineRunnerCapability, jt as BaseAddon, lt as pickDetailCropConvention, m as DEVICE_BACKEND_TO_FORMAT, nn as EventCategory, p as DEFAULT_NATIVE_LEASE_SETTINGS, qt as boolean, u as DEFAULT_DETAIL_CROP_CONVENTION, ut as pickNativeLeaseOverride, wt as errMsg } from "../dist-cZopp8Xm.mjs";
2
2
  import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-Br6p9KvD.mjs";
3
- import { i as resolveFrameViewGeometry, r as localFrameRegistry, t as attributeStall, u as getStepDefinition } from "../event-loop-stall-monitor-DH_vFYWu.mjs";
3
+ import { c as getStepDefinition, n as resolveFrameViewGeometry, t as localFrameRegistry } from "../local-frame-registry-Ctv_TzmF.mjs";
4
4
  import { a as nativeLeaseSettingEnv, t as isWorkerReply } from "../worker-protocol-CUCQ1IPR.mjs";
5
5
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
6
6
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
7
7
  import { n as withDetectionIntent } from "../restream-intent-B4BXZra7.mjs";
8
+ import { t as attributeStall } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
8
9
  import { n as profileForStreamId, t as isRemoteRestream } from "../remote-restream-BeHi78PZ.mjs";
9
10
  import { fork } from "node:child_process";
10
11
  import sharp from "sharp";
@@ -2,6 +2,7 @@ const require_dist = require("../dist-DML3mpn9.js");
2
2
  const require_node = require("../node-OvVqe9eh.js");
3
3
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
4
4
  const require_restream_intent = require("../restream-intent-Cv9x3jmu.js");
5
+ const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
5
6
  const require_retire_root_keys = require("../retire-root-keys-KE6D6Xh_.js");
6
7
  let node_crypto = require("node:crypto");
7
8
  let node_fs = require("node:fs");
@@ -13365,6 +13366,16 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
13365
13366
  isRecordingNode = true;
13366
13367
  index = new RecordingIndex();
13367
13368
  /**
13369
+ * Stops the GC-attributing stall monitor armed in {@link onInitialize}.
13370
+ *
13371
+ * REUSED, not re-implemented, and not promoted: `startEventLoopStallMonitor`
13372
+ * already ships inside `@camstack/addon-pipeline` — the same bundle as this
13373
+ * addon — and moving it into `@camstack/system` would duplicate a mechanism
13374
+ * that already exists (forbidden) or relocate it across a package boundary,
13375
+ * which is a refactor with a slower ship chain, not a measurement.
13376
+ */
13377
+ stopStallMonitor = null;
13378
+ /**
13368
13379
  * Durable hour-directory copy of {@link index}. Null on an inert
13369
13380
  * (non-recording) node. Boot reseeds the RAM index from this; the
13370
13381
  * deferred archive walk reconciles it against disk.
@@ -13532,6 +13543,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
13532
13543
  } });
13533
13544
  }
13534
13545
  async onInitialize() {
13546
+ this.stopStallMonitor = require_event_loop_stall_monitor.startEventLoopStallMonitor(this.ctx.logger);
13535
13547
  const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
13536
13548
  this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
13537
13549
  const configuredNode = this.config.recordingNodeId;
@@ -13971,6 +13983,8 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
13971
13983
  };
13972
13984
  }
13973
13985
  async onShutdown() {
13986
+ this.stopStallMonitor?.();
13987
+ this.stopStallMonitor = null;
13974
13988
  this.eventUnsub?.();
13975
13989
  this.eventUnsub = null;
13976
13990
  this.audioTriggerUnsub?.();
@@ -2,6 +2,7 @@ import { $ as isFirstLevelMacroClass, $t as record, B as defineCustomActions, Bt
2
2
  import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-Br6p9KvD.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { r as withRecordingIntent } from "../restream-intent-B4BXZra7.mjs";
5
+ import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
5
6
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DMolfhsP.mjs";
6
7
  import { randomUUID } from "node:crypto";
7
8
  import { createReadStream, promises, realpathSync } from "node:fs";
@@ -13363,6 +13364,16 @@ var RecorderV2Addon = class extends BaseAddon {
13363
13364
  isRecordingNode = true;
13364
13365
  index = new RecordingIndex();
13365
13366
  /**
13367
+ * Stops the GC-attributing stall monitor armed in {@link onInitialize}.
13368
+ *
13369
+ * REUSED, not re-implemented, and not promoted: `startEventLoopStallMonitor`
13370
+ * already ships inside `@camstack/addon-pipeline` — the same bundle as this
13371
+ * addon — and moving it into `@camstack/system` would duplicate a mechanism
13372
+ * that already exists (forbidden) or relocate it across a package boundary,
13373
+ * which is a refactor with a slower ship chain, not a measurement.
13374
+ */
13375
+ stopStallMonitor = null;
13376
+ /**
13366
13377
  * Durable hour-directory copy of {@link index}. Null on an inert
13367
13378
  * (non-recording) node. Boot reseeds the RAM index from this; the
13368
13379
  * deferred archive walk reconciles it against disk.
@@ -13530,6 +13541,7 @@ var RecorderV2Addon = class extends BaseAddon {
13530
13541
  } });
13531
13542
  }
13532
13543
  async onInitialize() {
13544
+ this.stopStallMonitor = startEventLoopStallMonitor(this.ctx.logger);
13533
13545
  const raw = this.ctx.kernel.localNodeId ?? this.ctx.id;
13534
13546
  this.nodeId = raw.includes("/") ? raw.split("/")[0] : raw;
13535
13547
  const configuredNode = this.config.recordingNodeId;
@@ -13969,6 +13981,8 @@ var RecorderV2Addon = class extends BaseAddon {
13969
13981
  };
13970
13982
  }
13971
13983
  async onShutdown() {
13984
+ this.stopStallMonitor?.();
13985
+ this.stopStallMonitor = null;
13972
13986
  this.eventUnsub?.();
13973
13987
  this.eventUnsub = null;
13974
13988
  this.audioTriggerUnsub?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.145",
3
+ "version": "1.2.146",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",