@camstack/addon-pipeline 1.2.213 → 1.2.214

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 (33) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-B-PccVsO.mjs → default-detection-model-C2vYSX3O.mjs} +1 -1
  4. package/dist/{default-detection-model-BZTjgdod.js → default-detection-model-f0PImZnh.js} +1 -1
  5. package/dist/detection-pipeline/index.js +5 -5
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-CyfP2nFh.js → dist-DoHcimdu.js} +42 -1
  8. package/dist/{dist-CsI90TVi.mjs → dist-DttqKvsQ.mjs} +42 -1
  9. package/dist/{lazy-sharp-BawPy_a9.js → lazy-sharp-BeeQGCUu.js} +1 -1
  10. package/dist/motion-wasm/index.js +2 -2
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-CkBqyM-0.js → node-6uF72wri.js} +1 -1
  13. package/dist/{node-6Pb_t1Fm.mjs → node-NtM7__8q.mjs} +1 -1
  14. package/dist/pipeline-runner/index.js +213 -24
  15. package/dist/pipeline-runner/index.mjs +212 -23
  16. package/dist/{process-memory-B8yoOaEl.js → process-memory-BRdyl5fE.js} +1 -1
  17. package/dist/{process-memory-B_-QzMHn.mjs → process-memory-CddpCDWL.mjs} +1 -1
  18. package/dist/recorder/index.js +4 -2
  19. package/dist/recorder/index.mjs +4 -2
  20. package/dist/{segment-demux-js-oOpXzk2J.js → segment-demux-js-CkJPdyDH.js} +1 -1
  21. package/dist/{segment-demux-js-CBvM6zIM.mjs → segment-demux-js-CsFtPVzH.mjs} +1 -1
  22. package/dist/session-decode/decode-worker-child.js +319 -8
  23. package/dist/session-decode/decode-worker-child.mjs +318 -7
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Cf0O5S13.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CbBa4iV-.mjs} +3 -3
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-3eT4CIOk.mjs → hostInit-CagSUJH7.mjs} +3 -3
  28. package/dist/stream-broker/index.js +3 -3
  29. package/dist/stream-broker/index.mjs +3 -3
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/dist/{worker-protocol-DNqdtZzZ.mjs → worker-protocol-BKwkLZWT.mjs} +5 -2
  32. package/dist/{worker-protocol-CYopQrkK.js → worker-protocol-BWO2WNjw.js} +5 -2
  33. package/package.json +1 -1
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CyfP2nFh.js");
6
- const require_process_memory = require("../process-memory-B8yoOaEl.js");
5
+ const require_dist = require("../dist-DoHcimdu.js");
6
+ const require_process_memory = require("../process-memory-BRdyl5fE.js");
7
7
  let node_fs = require("node:fs");
8
8
  node_fs = require_dist.__toESM(node_fs);
9
9
  let node_path = require("node:path");
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { Dt as resolvePoolMemoryPolicy, It as errMsg, Jt as hydrateSchema, M as audioAnalyzerCapability, S as PoolMemoryWatchdog, Ut as BaseAddon, j as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, ot as mapAudioLabelToMacro, s as DEFAULT_AUDIO_ANALYZER_CONFIG, v as HF_BASE_URL } from "../dist-CsI90TVi.mjs";
3
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-B_-QzMHn.mjs";
2
+ import { Dt as resolvePoolMemoryPolicy, It as errMsg, Jt as hydrateSchema, M as audioAnalyzerCapability, S as PoolMemoryWatchdog, Ut as BaseAddon, j as audioAnalysisCapability, n as AUDIO_BACKEND_CHOICES, ot as mapAudioLabelToMacro, s as DEFAULT_AUDIO_ANALYZER_CONFIG, v as HF_BASE_URL } from "../dist-DttqKvsQ.mjs";
3
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-CddpCDWL.mjs";
4
4
  import * as fs from "node:fs";
5
5
  import * as path$1 from "node:path";
6
6
  import { downloadFile } from "@camstack/system/addon-utils";
@@ -1,4 +1,4 @@
1
- import { N as audioKindId, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS, tt as hfModelUrl } from "./dist-CsI90TVi.mjs";
1
+ import { N as audioKindId, a as COCO_80_LABELS, o as COCO_TO_MACRO, r as AUDIO_MACRO_LABELS, tt as hfModelUrl } from "./dist-DttqKvsQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  //#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CyfP2nFh.js");
1
+ const require_dist = require("./dist-DoHcimdu.js");
2
2
  let node_crypto = require("node:crypto");
3
3
  //#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
4
4
  /**
@@ -2,12 +2,12 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CyfP2nFh.js");
6
- const require_node = require("../node-CkBqyM-0.js");
7
- const require_default_detection_model = require("../default-detection-model-BZTjgdod.js");
8
- const require_lazy_sharp = require("../lazy-sharp-BawPy_a9.js");
5
+ const require_dist = require("../dist-DoHcimdu.js");
6
+ const require_node = require("../node-6uF72wri.js");
7
+ const require_default_detection_model = require("../default-detection-model-f0PImZnh.js");
8
+ const require_lazy_sharp = require("../lazy-sharp-BeeQGCUu.js");
9
9
  const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
10
- const require_process_memory = require("../process-memory-B8yoOaEl.js");
10
+ const require_process_memory = require("../process-memory-BRdyl5fE.js");
11
11
  let node_os = require("node:os");
12
12
  node_os = require_dist.__toESM(node_os);
13
13
  let node_fs = require("node:fs");
@@ -1,10 +1,10 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { $t as parseJsonUnknown, B as defaultDeviceFor$1, Dt as resolvePoolMemoryPolicy, G as detectionPipelineCapability, It as errMsg, J as enumerateInferenceDevices, Jt as hydrateSchema, Pt as supportedRuntimes$1, Qt as nodePin, S as PoolMemoryWatchdog, Ut as BaseAddon, X as evaluateZoneRules, c as DEFAULT_CLUSTER_STEP_MODELS, gt as pickClusterStepSettings, hn as EventCategory, ht as pickClusterStepModels, in as array, it as loadContributionCapability, k as YAMNET_TO_MACRO, kt as runtimeDevices$1, l as DEFAULT_CLUSTER_STEP_SETTINGS, m as DEVICE_BACKEND_TO_FORMAT, mn as union, nn as sleep, nt as inferModelProvider, pn as string, pt as overlayClusterStepSettings, qt as createEvent, t as APPLE_SA_TO_MACRO, un as object, wt as resolveClusterStepModelId, yt as pipelineExecutorCapability } from "../dist-CsI90TVi.mjs";
3
- import { o as readProcessCost } from "../node-6Pb_t1Fm.mjs";
4
- import { a as ALL_PIPELINE_STEPS, c as getStep, d as landmarkPrecisionVerdict, l as getStepDefinition, n as resolveDefaultDetectionModel, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as resolveModelForFormat } from "../default-detection-model-B-PccVsO.mjs";
2
+ import { $t as parseJsonUnknown, B as defaultDeviceFor$1, Dt as resolvePoolMemoryPolicy, G as detectionPipelineCapability, It as errMsg, J as enumerateInferenceDevices, Jt as hydrateSchema, Pt as supportedRuntimes$1, Qt as nodePin, S as PoolMemoryWatchdog, Ut as BaseAddon, X as evaluateZoneRules, c as DEFAULT_CLUSTER_STEP_MODELS, gt as pickClusterStepSettings, hn as EventCategory, ht as pickClusterStepModels, in as array, it as loadContributionCapability, k as YAMNET_TO_MACRO, kt as runtimeDevices$1, l as DEFAULT_CLUSTER_STEP_SETTINGS, m as DEVICE_BACKEND_TO_FORMAT, mn as union, nn as sleep, nt as inferModelProvider, pn as string, pt as overlayClusterStepSettings, qt as createEvent, t as APPLE_SA_TO_MACRO, un as object, wt as resolveClusterStepModelId, yt as pipelineExecutorCapability } from "../dist-DttqKvsQ.mjs";
3
+ import { o as readProcessCost } from "../node-NtM7__8q.mjs";
4
+ import { a as ALL_PIPELINE_STEPS, c as getStep, d as landmarkPrecisionVerdict, l as getStepDefinition, n as resolveDefaultDetectionModel, o as ALL_STEPS, r as localFrameRegistry, s as getDefaultModelForFormat, u as resolveModelForFormat } from "../default-detection-model-C2vYSX3O.mjs";
5
5
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
6
6
  import { n as startEventLoopStallMonitor } from "../event-loop-stall-monitor-DXLNBMkY.mjs";
7
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-B_-QzMHn.mjs";
7
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-CddpCDWL.mjs";
8
8
  import * as os from "node:os";
9
9
  import * as fs from "node:fs";
10
10
  import * as path$1 from "node:path";
@@ -23081,6 +23081,22 @@ var occupancyRecheckFramesField = {
23081
23081
  step: 1
23082
23082
  };
23083
23083
  /**
23084
+ * Frames one PRE-ROLL catch-up burst may send to inference at session open.
23085
+ *
23086
+ * The default of 12 over a ~4 s retained window is one frame per ~333 ms of
23087
+ * footage, and costs ~480 ms of ONE inference permit at the 40 ms/frame this
23088
+ * fleet measures — one-shot, and only ever taken while a permit would still be
23089
+ * spare for live. `0` turns the burst off entirely (the history is still
23090
+ * decoded, because that is how the dial reaches a decodable GOP, and is then
23091
+ * discarded — the pre-D411 behaviour).
23092
+ */
23093
+ var preRollInferenceFramesField = {
23094
+ min: 0,
23095
+ max: 30,
23096
+ default: 12,
23097
+ step: 1
23098
+ };
23099
+ /**
23084
23100
  * Source enum for motion signals fed to the runner. Extensible — add
23085
23101
  * new variants here when new motion-trigger paths are wired in
23086
23102
  * (`wasm-cross-camera`, `event-bus-relay`, etc.). The runner uses
@@ -23321,6 +23337,31 @@ var RunnerCameraConfigSchema = object({
23321
23337
  * to every occupancy rule until something moved in front of it. The churn is
23322
23338
  * now paid on the interval instead — see `occupancyRecheckSecField`.
23323
23339
  */
23340
+ /**
23341
+ * Infer the session's PRE-ROLL — the retained history the restreamer replays
23342
+ * at session open — instead of decoding it and throwing it away.
23343
+ *
23344
+ * DEFAULT `true`, and the default is the argument. This is not a new
23345
+ * capability being offered cautiously: the pre-roll is ALREADY requested,
23346
+ * already served, already decoded and already paid for on every on-motion
23347
+ * detection session that asks for history. What shipped was a last-moment
23348
+ * discard — `FrameSlot`'s throttle compares wall clocks, and ~4 s of media
23349
+ * arriving inside a few hundred ms of wall clock looks to it like one frame's
23350
+ * worth. Device 617, 2026-09-08: a re-run of the SAME model over the stored
23351
+ * recording scored the subject `vehicle 0.5669` — above the 0.50 floor —
23352
+ * 1.68 s BEFORE the live track was born, on a frame that was in the pre-roll,
23353
+ * was decoded, and was freed.
23354
+ *
23355
+ * Shipping that as opt-in would ship the bug: an operator cannot discover a
23356
+ * setting whose absence looks exactly like a camera that noticed the cyclist
23357
+ * late. What makes ON safe is not a switch but the BOUND —
23358
+ * `preRollInferenceFrames`, a wall-clock ceiling, and a lane that never takes
23359
+ * the last inference permit — so the switch exists for the camera where the
23360
+ * history is worthless (a doorbell whose subject is always already at the
23361
+ * door), not as a hedge against the feature.
23362
+ */
23363
+ preRollInferenceEnabled: boolean().default(true),
23364
+ preRollInferenceFrames: number().min(preRollInferenceFramesField.min).max(preRollInferenceFramesField.max).default(preRollInferenceFramesField.default),
23324
23365
  occupancyRecheckEnabled: boolean().default(true),
23325
23366
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
23326
23367
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
@@ -23349,7 +23390,7 @@ var RunnerCameraConfigSchema = object({
23349
23390
  */
23350
23391
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
23351
23392
  });
23352
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
23393
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
23353
23394
  /**
23354
23395
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
23355
23396
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -23058,6 +23058,22 @@ var occupancyRecheckFramesField = {
23058
23058
  step: 1
23059
23059
  };
23060
23060
  /**
23061
+ * Frames one PRE-ROLL catch-up burst may send to inference at session open.
23062
+ *
23063
+ * The default of 12 over a ~4 s retained window is one frame per ~333 ms of
23064
+ * footage, and costs ~480 ms of ONE inference permit at the 40 ms/frame this
23065
+ * fleet measures — one-shot, and only ever taken while a permit would still be
23066
+ * spare for live. `0` turns the burst off entirely (the history is still
23067
+ * decoded, because that is how the dial reaches a decodable GOP, and is then
23068
+ * discarded — the pre-D411 behaviour).
23069
+ */
23070
+ var preRollInferenceFramesField = {
23071
+ min: 0,
23072
+ max: 30,
23073
+ default: 12,
23074
+ step: 1
23075
+ };
23076
+ /**
23061
23077
  * Source enum for motion signals fed to the runner. Extensible — add
23062
23078
  * new variants here when new motion-trigger paths are wired in
23063
23079
  * (`wasm-cross-camera`, `event-bus-relay`, etc.). The runner uses
@@ -23298,6 +23314,31 @@ var RunnerCameraConfigSchema = object({
23298
23314
  * to every occupancy rule until something moved in front of it. The churn is
23299
23315
  * now paid on the interval instead — see `occupancyRecheckSecField`.
23300
23316
  */
23317
+ /**
23318
+ * Infer the session's PRE-ROLL — the retained history the restreamer replays
23319
+ * at session open — instead of decoding it and throwing it away.
23320
+ *
23321
+ * DEFAULT `true`, and the default is the argument. This is not a new
23322
+ * capability being offered cautiously: the pre-roll is ALREADY requested,
23323
+ * already served, already decoded and already paid for on every on-motion
23324
+ * detection session that asks for history. What shipped was a last-moment
23325
+ * discard — `FrameSlot`'s throttle compares wall clocks, and ~4 s of media
23326
+ * arriving inside a few hundred ms of wall clock looks to it like one frame's
23327
+ * worth. Device 617, 2026-09-08: a re-run of the SAME model over the stored
23328
+ * recording scored the subject `vehicle 0.5669` — above the 0.50 floor —
23329
+ * 1.68 s BEFORE the live track was born, on a frame that was in the pre-roll,
23330
+ * was decoded, and was freed.
23331
+ *
23332
+ * Shipping that as opt-in would ship the bug: an operator cannot discover a
23333
+ * setting whose absence looks exactly like a camera that noticed the cyclist
23334
+ * late. What makes ON safe is not a switch but the BOUND —
23335
+ * `preRollInferenceFrames`, a wall-clock ceiling, and a lane that never takes
23336
+ * the last inference permit — so the switch exists for the camera where the
23337
+ * history is worthless (a doorbell whose subject is always already at the
23338
+ * door), not as a hedge against the feature.
23339
+ */
23340
+ preRollInferenceEnabled: boolean().default(true),
23341
+ preRollInferenceFrames: number().min(preRollInferenceFramesField.min).max(preRollInferenceFramesField.max).default(preRollInferenceFramesField.default),
23301
23342
  occupancyRecheckEnabled: boolean().default(true),
23302
23343
  occupancyRecheckSec: number().min(occupancyRecheckSecField.min).max(occupancyRecheckSecField.max).default(occupancyRecheckSecField.default),
23303
23344
  occupancyRecheckFrames: number().min(occupancyRecheckFramesField.min).max(occupancyRecheckFramesField.max).default(occupancyRecheckFramesField.default),
@@ -23326,7 +23367,7 @@ var RunnerCameraConfigSchema = object({
23326
23367
  */
23327
23368
  inferenceDevices: array(RunnerInferenceDeviceSchema).readonly().optional()
23328
23369
  });
23329
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
23370
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
23330
23371
  /**
23331
23372
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
23332
23373
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CyfP2nFh.js");
1
+ const require_dist = require("./dist-DoHcimdu.js");
2
2
  let node_path = require("node:path");
3
3
  node_path = require_dist.__toESM(node_path);
4
4
  let node_url = require("node:url");
@@ -2,8 +2,8 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CyfP2nFh.js");
6
- const require_lazy_sharp = require("../lazy-sharp-BawPy_a9.js");
5
+ const require_dist = require("../dist-DoHcimdu.js");
6
+ const require_lazy_sharp = require("../lazy-sharp-BeeQGCUu.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
9
9
  //#region src/motion-wasm/region-grouping.ts
@@ -1,4 +1,4 @@
1
- import { Jt as hydrateSchema, Kt as DeviceType, Ut as BaseAddon, X as evaluateZoneRules, ft as motionDetectionCapability } from "../dist-CsI90TVi.mjs";
1
+ import { Jt as hydrateSchema, Kt as DeviceType, Ut as BaseAddon, X as evaluateZoneRules, ft as motionDetectionCapability } from "../dist-DttqKvsQ.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-CyfP2nFh.js");
1
+ const require_dist = require("./dist-DoHcimdu.js");
2
2
  let node_crypto = require("node:crypto");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_dist.__toESM(node_fs, 1);
@@ -1,4 +1,4 @@
1
- import { It as errMsg, Rt as Fmp4BoxSplitter, Vt as isSoftwareDecode, zt as buildFfmpegArgs } from "./dist-CsI90TVi.mjs";
1
+ import { It as errMsg, Rt as Fmp4BoxSplitter, Vt as isSoftwareDecode, zt as buildFfmpegArgs } from "./dist-DttqKvsQ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -2,12 +2,12 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-CyfP2nFh.js");
6
- const require_node = require("../node-CkBqyM-0.js");
7
- const require_default_detection_model = require("../default-detection-model-BZTjgdod.js");
8
- const require_worker_protocol = require("../worker-protocol-CYopQrkK.js");
5
+ const require_dist = require("../dist-DoHcimdu.js");
6
+ const require_node = require("../node-6uF72wri.js");
7
+ const require_default_detection_model = require("../default-detection-model-f0PImZnh.js");
8
+ const require_worker_protocol = require("../worker-protocol-BWO2WNjw.js");
9
9
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
10
- const require_lazy_sharp = require("../lazy-sharp-BawPy_a9.js");
10
+ const require_lazy_sharp = require("../lazy-sharp-BeeQGCUu.js");
11
11
  const require_restream_intent = require("../restream-intent-B1Difcnq.js");
12
12
  const require_event_loop_stall_monitor = require("../event-loop-stall-monitor-DaUBcb13.js");
13
13
  const require_remote_restream = require("../remote-restream-CO36Sr30.js");
@@ -1010,6 +1010,7 @@ var SessionDecodeSession = class {
1010
1010
  frameTimeoutMs;
1011
1011
  firstFrameMaxWaitMs;
1012
1012
  onDialTiming;
1013
+ onPreRollReport;
1013
1014
  pendingPull = null;
1014
1015
  pendingToBuffers = /* @__PURE__ */ new Map();
1015
1016
  /** Outstanding `nativeCrop` requests, keyed by the monotonic `requestId`. */
@@ -1023,7 +1024,7 @@ var SessionDecodeSession = class {
1023
1024
  firstFrameReceived = false;
1024
1025
  /** `Date.now()` of the FIRST `pull` — the origin of the cold-start ceiling. */
1025
1026
  firstPullAt = 0;
1026
- constructor(child, logger, deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs, onDialTiming) {
1027
+ constructor(child, logger, deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs, onDialTiming, onPreRollReport) {
1027
1028
  this.child = child;
1028
1029
  this.logger = logger;
1029
1030
  this.deviceId = deviceId;
@@ -1031,6 +1032,7 @@ var SessionDecodeSession = class {
1031
1032
  this.frameTimeoutMs = frameTimeoutMs;
1032
1033
  this.firstFrameMaxWaitMs = firstFrameMaxWaitMs;
1033
1034
  this.onDialTiming = onDialTiming;
1035
+ this.onPreRollReport = onPreRollReport;
1034
1036
  this.child.on("message", (m) => this.handleMessage(m));
1035
1037
  this.child.on("exit", (m) => this.handleExit(toWorkerExitInfo(m)));
1036
1038
  }
@@ -1221,6 +1223,17 @@ var SessionDecodeSession = class {
1221
1223
  streamInfoProbed: raw.streamInfoProbed
1222
1224
  });
1223
1225
  return;
1226
+ case "preRollReport":
1227
+ this.onPreRollReport?.({
1228
+ offered: raw.offered,
1229
+ inferred: raw.inferred,
1230
+ dropped: raw.dropped,
1231
+ droppedByReason: raw.droppedByReason,
1232
+ strideMs: raw.strideMs,
1233
+ subsampled: raw.subsampled,
1234
+ spanMs: raw.spanMs
1235
+ });
1236
+ return;
1224
1237
  }
1225
1238
  }
1226
1239
  handleExit(info) {
@@ -1445,7 +1458,7 @@ function createSessionDecodeCoordinator(deps) {
1445
1458
  const frameTimeoutMs = deps.frameTimeoutMs ?? DEFAULT_FRAME_TIMEOUT_MS;
1446
1459
  const firstFrameMaxWaitMs = Math.max(deps.firstFrameMaxWaitMs ?? DEFAULT_FIRST_FRAME_MAX_WAIT_MS, firstFrameTimeoutMs);
1447
1460
  return { async *generateVideoFrames(source, opts) {
1448
- const session = new SessionDecodeSession(deps.fork(source), deps.logger, source.deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs, deps.onDialTiming);
1461
+ const session = new SessionDecodeSession(deps.fork(source), deps.logger, source.deviceId, firstFrameTimeoutMs, frameTimeoutMs, firstFrameMaxWaitMs, deps.onDialTiming, deps.onPreRollReport);
1449
1462
  session.start(source, opts);
1450
1463
  try {
1451
1464
  while (true) {
@@ -1454,7 +1467,9 @@ function createSessionDecodeCoordinator(deps) {
1454
1467
  const { reply } = outcome;
1455
1468
  yield {
1456
1469
  timestamp: reply.timestamp,
1457
- image: buildFrameImage(session, reply)
1470
+ image: buildFrameImage(session, reply),
1471
+ ...reply.capturedAt !== void 0 ? { capturedAt: reply.capturedAt } : {},
1472
+ ...reply.preRoll === true ? { preRoll: true } : {}
1458
1473
  };
1459
1474
  }
1460
1475
  } finally {
@@ -1468,10 +1483,18 @@ function createSessionDecodeCoordinator(deps) {
1468
1483
  * Build a {@link DecodedFrame} from one pulled {@link VideoFrame}. Downscales to
1469
1484
  * the detection working width (see {@link resolveWorkingResize}) so the ROI
1470
1485
  * scaler ships a small frame instead of the full source resolution.
1471
- * `capturedAt` is set to `Date.now()` so downstream frameAge metrics diff
1472
- * against the wall clock (mirrors the shm-ring source, which stamps commit
1473
- * time) — this is what lets Step 5 MEASURE session-decode frameAge vs the
1474
- * shm-ring cameras.
1486
+ * `capturedAt` is the frame's OWN moment: the worker's media clock when it has
1487
+ * one (`media-clock.ts`), else `Date.now()` as before, so downstream frameAge
1488
+ * metrics keep diffing against the wall clock (mirroring the shm-ring source,
1489
+ * which stamps commit time) — this is what lets Step 5 MEASURE session-decode
1490
+ * frameAge vs the shm-ring cameras.
1491
+ *
1492
+ * The fallback is deliberate and must stay: a `Date.now()` here for a frame
1493
+ * replayed out of ~4 s of retained history would date its detections, its
1494
+ * track's `firstSeen`, its media and the notification's instant at SESSION
1495
+ * OPEN. That is a silently wrong timestamp — nothing downstream can tell it
1496
+ * from a right one — and it is a worse failure than the late birth the pre-roll
1497
+ * pass exists to fix.
1475
1498
  */
1476
1499
  async function toDecodedFrame(frame, format, viewSpec) {
1477
1500
  const geometry = viewSpec ? require_default_detection_model.resolveFrameViewGeometry(frame.image.width, frame.image.height, viewSpec) : void 0;
@@ -1486,7 +1509,8 @@ async function toDecodedFrame(frame, format, viewSpec) {
1486
1509
  height: geometry?.output.height ?? frame.image.height,
1487
1510
  format: viewSpec?.format ?? format,
1488
1511
  timestamp: frame.timestamp,
1489
- capturedAt: Date.now()
1512
+ capturedAt: frame.capturedAt ?? Date.now(),
1513
+ ...frame.preRoll === true ? { preRoll: true } : {}
1490
1514
  };
1491
1515
  }
1492
1516
  /**
@@ -1507,7 +1531,8 @@ function startSessionDecodePump(deps) {
1507
1531
  dialTiming = timing;
1508
1532
  },
1509
1533
  ...deps.firstFrameTimeoutMs !== void 0 ? { firstFrameTimeoutMs: deps.firstFrameTimeoutMs } : {},
1510
- ...deps.frameTimeoutMs !== void 0 ? { frameTimeoutMs: deps.frameTimeoutMs } : {}
1534
+ ...deps.frameTimeoutMs !== void 0 ? { frameTimeoutMs: deps.frameTimeoutMs } : {},
1535
+ ...deps.onPreRollReport ? { onPreRollReport: deps.onPreRollReport } : {}
1511
1536
  }).generateVideoFrames(deps.source, deps.opts);
1512
1537
  let stopped = false;
1513
1538
  let releaseSpawnSlot = null;
@@ -1549,7 +1574,7 @@ function startSessionDecodePump(deps) {
1549
1574
  }
1550
1575
  let imageOwnershipTransferred = false;
1551
1576
  try {
1552
- if (deps.shouldMaterializeFrame && !deps.shouldMaterializeFrame()) continue;
1577
+ if (deps.shouldMaterializeFrame && !deps.shouldMaterializeFrame(frame.preRoll === true)) continue;
1553
1578
  const image = frame.image;
1554
1579
  const cropFn = image.nativeCrop;
1555
1580
  const nativeCrop = cropFn ? Object.assign((bbox, maxWidth) => cropFn(bbox, maxWidth), {
@@ -1558,7 +1583,7 @@ function startSessionDecodePump(deps) {
1558
1583
  markInferred: () => image.markInferred?.()
1559
1584
  }) : void 0;
1560
1585
  if (deps.frameRegistry && deps.onFrameRef && deps.inlineWhen?.() !== true) {
1561
- const capturedAt = Date.now();
1586
+ const capturedAt = frame.capturedAt ?? Date.now();
1562
1587
  const ref = deps.frameRegistry.register(image, {
1563
1588
  timestamp: frame.timestamp,
1564
1589
  capturedAt
@@ -1572,7 +1597,8 @@ function startSessionDecodePump(deps) {
1572
1597
  format: ref.format,
1573
1598
  timestamp: ref.timestamp,
1574
1599
  capturedAt,
1575
- frameRef: ref
1600
+ frameRef: ref,
1601
+ ...frame.preRoll === true ? { preRoll: true } : {}
1576
1602
  };
1577
1603
  try {
1578
1604
  if (!await deps.onFrameRef(lazyFrame, nativeCrop)) deps.frameRegistry.release(ref, "drop");
@@ -4703,6 +4729,44 @@ function ownFrame(frame) {
4703
4729
  };
4704
4730
  }
4705
4731
  //#endregion
4732
+ //#region src/pipeline-runner/pre-roll-lane.ts
4733
+ var PreRollLane = class {
4734
+ items = [];
4735
+ capacity;
4736
+ clone;
4737
+ dispose;
4738
+ _refused = 0;
4739
+ constructor(options) {
4740
+ this.capacity = Math.max(0, options.capacity);
4741
+ this.clone = options.clone;
4742
+ this.dispose = options.dispose;
4743
+ }
4744
+ /** Append one entry. `false` ⇒ the lane is full and the caller must log it. */
4745
+ enqueue(item) {
4746
+ if (this.items.length >= this.capacity) {
4747
+ this._refused++;
4748
+ return false;
4749
+ }
4750
+ this.items.push(this.clone(item));
4751
+ return true;
4752
+ }
4753
+ /** Oldest first — the point of the lane. */
4754
+ dequeue() {
4755
+ return this.items.shift();
4756
+ }
4757
+ get size() {
4758
+ return this.items.length;
4759
+ }
4760
+ /** Entries refused because the lane was full, for the session's report line. */
4761
+ get refused() {
4762
+ return this._refused;
4763
+ }
4764
+ clear() {
4765
+ for (const item of this.items) this.dispose?.(item);
4766
+ this.items.length = 0;
4767
+ }
4768
+ };
4769
+ //#endregion
4706
4770
  //#region src/pipeline-runner/semaphore.ts
4707
4771
  /**
4708
4772
  * Counting semaphore with FIFO waiter queue. Used by the runner to bound
@@ -5021,8 +5085,6 @@ var PipelineTimingSampler = class {
5021
5085
  this.audioSamples.clear();
5022
5086
  }
5023
5087
  };
5024
- //#endregion
5025
- //#region src/pipeline-runner/runner.ts
5026
5088
  /**
5027
5089
  * Bound on one `processFrame` call. Chosen well above the slowest legitimate
5028
5090
  * inference seen in production (8 617 ms, GPU contention on the hub) so the
@@ -5197,6 +5259,14 @@ var PipelineRunner = class {
5197
5259
  registration,
5198
5260
  motionQueue,
5199
5261
  detectionQueue,
5262
+ preRollQueue: new PreRollLane({
5263
+ capacity: this.config.maxPreRollQueueDepth ?? 12,
5264
+ clone: ownDetectionEntry,
5265
+ dispose: (entry) => {
5266
+ if (entry.handle) this.config.releaseRetention?.(entry.handle, "drop");
5267
+ if (entry.frame.frameRef) this.config.releaseFrameRef?.(entry.frame.frameRef, "drop");
5268
+ }
5269
+ }),
5200
5270
  inferenceTimes: [],
5201
5271
  shedFrames: 0,
5202
5272
  inflight: 0,
@@ -5243,6 +5313,7 @@ var PipelineRunner = class {
5243
5313
  if (state.phase === "active") this.detectionStreamHandler?.(deviceId, "unsubscribe");
5244
5314
  state.motionQueue.clear();
5245
5315
  state.detectionQueue.clear();
5316
+ state.preRollQueue.clear();
5246
5317
  this.timingSampler.clearDroppedFramesSource(deviceId);
5247
5318
  this.cameras.delete(deviceId);
5248
5319
  this.lastAdmittedAt.delete(deviceId);
@@ -5324,6 +5395,59 @@ var PipelineRunner = class {
5324
5395
  maxInflightPerCamera: cap
5325
5396
  };
5326
5397
  }
5398
+ /**
5399
+ * Queue one PRE-ROLL frame — a frame decoded out of the restreamer's retained
5400
+ * history at session open, carrying its own capture moment
5401
+ * (`session-decode/media-clock.ts`), not the moment it was decoded.
5402
+ *
5403
+ * Deliberately NOT routed through {@link reserveDetectionFrame}: that gate
5404
+ * paces on WALL CLOCK, and a catch-up burst delivers seconds of media inside
5405
+ * milliseconds of wall clock, so the governor would reject the whole burst
5406
+ * bar one frame. The burst's own bound is a MEDIA-time stride applied
5407
+ * upstream, under an explicit frame + wall-clock budget
5408
+ * (`session-decode/pre-roll-burst.ts`); by the time a frame reaches here it
5409
+ * has already been chosen.
5410
+ *
5411
+ * `false` means the lane is full or the camera is not taking detection
5412
+ * frames. The caller LOGS that: a burst silently truncated by the lane and a
5413
+ * burst that fit read identically otherwise, and this is a branch that drops
5414
+ * work.
5415
+ */
5416
+ enqueuePreRollFrame(deviceId, frame, handle) {
5417
+ const state = this.cameras.get(deviceId);
5418
+ if (!state || state.phase !== "active") return false;
5419
+ return state.preRollQueue.enqueue({
5420
+ frame,
5421
+ handle
5422
+ });
5423
+ }
5424
+ /**
5425
+ * Whether one more history frame would be taken. Asked BEFORE pixels are
5426
+ * materialized so a burst that has already filled the lane costs no RGB
5427
+ * conversion, no IPC and no retention work — the same pixel-free discipline
5428
+ * the live path gets from `reserveDetectionFrame`.
5429
+ */
5430
+ preRollLaneAccepting(deviceId) {
5431
+ const state = this.cameras.get(deviceId);
5432
+ if (!state || state.phase !== "active") return false;
5433
+ return state.preRollQueue.size < (this.config.maxPreRollQueueDepth ?? 12);
5434
+ }
5435
+ /** Frames of history waiting for a spare permit, for the session report line. */
5436
+ preRollQueueDepth(deviceId) {
5437
+ return this.cameras.get(deviceId)?.preRollQueue.size ?? 0;
5438
+ }
5439
+ /** Frames this camera lane REFUSED because it was full. */
5440
+ preRollRefusedCount(deviceId) {
5441
+ return this.cameras.get(deviceId)?.preRollQueue.refused ?? 0;
5442
+ }
5443
+ /**
5444
+ * Whether the node has a permit to spare for history right now — i.e. one
5445
+ * would still be free after the burst took one. See
5446
+ * {@link PRE_ROLL_PERMIT_RESERVE}.
5447
+ */
5448
+ preRollDispatchable() {
5449
+ return this.semaphore.available > 1;
5450
+ }
5327
5451
  enqueueReservedDetectionFrame(deviceId, frame, handle) {
5328
5452
  const state = this.cameras.get(deviceId);
5329
5453
  if (!state || state.phase !== "active") return false;
@@ -5754,7 +5878,7 @@ var PipelineRunner = class {
5754
5878
  entry: state.detectionQueue.dequeue(),
5755
5879
  state
5756
5880
  };
5757
- if (this.defaultRoundRobinKeys.length === 0) return null;
5881
+ if (this.defaultRoundRobinKeys.length === 0) return this.pickNextPreRollFrame();
5758
5882
  const startIndex = this.defaultRoundRobinIndex;
5759
5883
  for (let i = 0; i < this.defaultRoundRobinKeys.length; i++) {
5760
5884
  const idx = (startIndex + i) % this.defaultRoundRobinKeys.length;
@@ -5773,6 +5897,30 @@ var PipelineRunner = class {
5773
5897
  };
5774
5898
  }
5775
5899
  }
5900
+ return this.pickNextPreRollFrame();
5901
+ }
5902
+ /**
5903
+ * The catch-up lane, reached ONLY after every camera's live queue came back
5904
+ * empty. That ordering IS the priority rule: a live frame is always picked
5905
+ * first, so a pre-roll frame can never delay one — and the burst simply
5906
+ * stops progressing while the camera is busy, which is the correct behaviour
5907
+ * for one-shot work whose value does not decay.
5908
+ */
5909
+ pickNextPreRollFrame() {
5910
+ if (!this.preRollDispatchable()) return null;
5911
+ for (const [deviceId, state] of this.cameras) {
5912
+ if (state.phase !== "active") continue;
5913
+ if (state.preRollQueue.size === 0) continue;
5914
+ if (state.detectionQueue.size > 0) continue;
5915
+ if (state.inflight >= this.maxInflightPerCamera()) continue;
5916
+ const entry = state.preRollQueue.dequeue();
5917
+ if (!entry) continue;
5918
+ return {
5919
+ deviceId,
5920
+ entry,
5921
+ state
5922
+ };
5923
+ }
5776
5924
  return null;
5777
5925
  }
5778
5926
  };
@@ -8576,10 +8724,23 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8576
8724
  /** Attach → first decoded detection frame, one line per attach. See
8577
8725
  * {@link FirstDetectionFrameTimer}. */
8578
8726
  firstDetectionFrame = new FirstDetectionFrameTimer();
8727
+ /**
8728
+ * Frames this camera's pre-roll catch-up burst may send to inference.
8729
+ *
8730
+ * The switch and the budget are ONE number here on purpose: `false` and
8731
+ * `0` mean the same thing to every layer below (no burst is constructed, the
8732
+ * history is decoded to reach a GOP and then discarded exactly as it was
8733
+ * before D411), and two independent ways to say it is two ways to disagree.
8734
+ */
8735
+ preRollBudgetFor(config) {
8736
+ if (!config.preRollInferenceEnabled) return 0;
8737
+ const frames = config.preRollInferenceFrames;
8738
+ return Number.isFinite(frames) && frames > 0 ? frames : 0;
8739
+ }
8579
8740
  enqueueSharedDetectionFrame(deviceId, frame, handle, reservation) {
8580
8741
  const runner = this.runner;
8581
8742
  if (!runner) return false;
8582
- const admitted = reservation ? reservation.detection && runner.enqueueReservedDetectionFrame(deviceId, frame, handle) : runner.enqueueDetectionFrame(deviceId, frame, handle);
8743
+ const admitted = frame.preRoll === true ? runner.enqueuePreRollFrame(deviceId, frame, handle) : reservation ? reservation.detection && runner.enqueueReservedDetectionFrame(deviceId, frame, handle) : runner.enqueueDetectionFrame(deviceId, frame, handle);
8583
8744
  if (admitted) {
8584
8745
  const latencyMs = this.firstDetectionFrame.takeLatency(deviceId, Date.now());
8585
8746
  if (latencyMs !== null) this.ctx?.logger.info("session-decode: first detection frame after attach", {
@@ -8697,9 +8858,19 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8697
8858
  source,
8698
8859
  opts: {
8699
8860
  fps: config.detectionFps,
8700
- format: "rgb"
8861
+ format: "rgb",
8862
+ preRollMs: require_restream_intent.preRollRequestOf(source.restreamUrl),
8863
+ preRollMaxFrames: this.preRollBudgetFor(config)
8701
8864
  },
8702
- shouldMaterializeFrame: () => {
8865
+ shouldMaterializeFrame: (preRoll) => {
8866
+ if (preRoll) {
8867
+ const accepting = runner.preRollLaneAccepting(config.deviceId);
8868
+ reservation = {
8869
+ detection: accepting,
8870
+ motion: false
8871
+ };
8872
+ return accepting;
8873
+ }
8703
8874
  reservation = this.reserveSharedDetectionFrame(config.deviceId);
8704
8875
  const materialize = reservation.detection || reservation.motion;
8705
8876
  this.retention?.noteFrame({
@@ -8733,7 +8904,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8733
8904
  materialized: false,
8734
8905
  workerToRunnerBytes: 0
8735
8906
  });
8736
- const admitted = current.detection && runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle);
8907
+ const admitted = current.detection && (frame.preRoll === true ? runner.enqueuePreRollFrame(config.deviceId, frame, handle) : runner.enqueueReservedDetectionFrame(config.deviceId, frame, handle));
8737
8908
  if (admitted) nativeCrop?.markInferred();
8738
8909
  if (current.motion) {
8739
8910
  const motion = await resolveMotionFrameView(require_default_detection_model.localFrameRegistry, frame.frameRef);
@@ -8750,6 +8921,24 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8750
8921
  }
8751
8922
  } : { inlineViewSpec: SPLIT_DETECTION_VIEW },
8752
8923
  acquireSpawnSlot: this.buildDecodeSpawnAcquire(log, "detect"),
8924
+ onPreRollReport: (report) => {
8925
+ log.info("session-decode: pre-roll catch-up burst", {
8926
+ tags: { deviceId: config.deviceId },
8927
+ meta: {
8928
+ streamId: config.detectionStreamId,
8929
+ requestedMs: require_restream_intent.preRollRequestOf(source.restreamUrl),
8930
+ spanMs: report.spanMs,
8931
+ offered: report.offered,
8932
+ inferred: report.inferred,
8933
+ dropped: report.dropped,
8934
+ droppedByReason: report.droppedByReason,
8935
+ strideMs: report.strideMs,
8936
+ subsampled: report.subsampled,
8937
+ laneRefused: runner.preRollRefusedCount(config.deviceId),
8938
+ budgetFrames: this.preRollBudgetFor(config)
8939
+ }
8940
+ });
8941
+ },
8753
8942
  onFirstFrame: (timing) => {
8754
8943
  const phaseActiveAt = this.lastMotionAt.get(config.deviceId);
8755
8944
  log.info("session-decode: detection first frame", {