@camstack/addon-post-analysis 1.2.298 → 1.2.300

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.
@@ -7660,7 +7660,7 @@ function errMsg(err) {
7660
7660
  return String(err);
7661
7661
  }
7662
7662
  //#endregion
7663
- //#region ../types/dist/composition-BK-GTFYb.mjs
7663
+ //#region ../types/dist/composition-Dq42XWp0.mjs
7664
7664
  /**
7665
7665
  * Deterministic SHA-256 hash of an arbitrary serialisable value. The
7666
7666
  * canonical form sorts object keys alphabetically at every depth so two
@@ -7693,6 +7693,58 @@ function replaceWithSortedKeys(_key, value) {
7693
7693
  return value;
7694
7694
  }
7695
7695
  /**
7696
+ * The ffmpeg build every CamStack node downloads, per platform and arch.
7697
+ *
7698
+ * ## Why a download, on every node, including the container
7699
+ *
7700
+ * The version of ffmpeg used to be CODE: baked into the image, changeable only
7701
+ * by rebuilding and rolling it. Now it is DATA — this file states the release,
7702
+ * and a node that boots with a different one downloads it. Changing ffmpeg is a
7703
+ * framework bump, not an image rebuild.
7704
+ *
7705
+ * It also settles the licence question by removing it. FFmpeg's licence is
7706
+ * chosen at compile time: the core is LGPL 2.1+, `--enable-gpl` makes it GPL
7707
+ * (for x264/x265), `--enable-version3` makes that v3, and `--enable-nonfree`
7708
+ * makes the result undistributable. These are GPL v3 builds, and we do not
7709
+ * distribute them: the user's own node fetches the archive from the project
7710
+ * that publishes it, so we convey no GPL work and inherit none of the
7711
+ * obligations that come with conveying one. That is the whole reason the image
7712
+ * no longer installs ffmpeg at all — not a technical preference.
7713
+ *
7714
+ * ## Why jellyfin-ffmpeg
7715
+ *
7716
+ * One vendor covering linux-amd64, linux-arm64, darwin-arm64 and darwin-x64
7717
+ * from one release, with the widest hardware surface of anything maintained:
7718
+ * VAAPI, QSV, NVENC/NVDEC, AMF, Vulkan, OpenCL and DRM on amd64, plus Rockchip
7719
+ * MPP on arm64. It reaches the vendor libraries through dlopen trampolines,
7720
+ * so the binary is not linked against a driver stack it may not find.
7721
+ *
7722
+ * **What it still needs from the host, measured 2026-09-19.** On the Unraid
7723
+ * HOST, which has no libva, `-vaapi_device` aborts the process outright —
7724
+ * `implib-gen: libva-drm.so.2: failed to load library ... Assertion '0 &&
7725
+ * "Assertion in generated code"' failed`. Inside our container, which installs
7726
+ * libva plus the iHD driver, the same command encodes: verified end to end with
7727
+ * `testsrc → format=nv12,hwupload → h264_vaapi`. So the image drops ffmpeg and
7728
+ * KEEPS the Intel media stack; the drivers are the part a binary cannot bring.
7729
+ *
7730
+ * ## Why each artifact carries a checksum and a claim
7731
+ *
7732
+ * The download is now the only source, so `sha256` is verified before anything
7733
+ * is extracted — jellyfin publishes no checksums on its release assets, so
7734
+ * these are ours, taken at the version bump. And `expectedHwaccels` is what the
7735
+ * build is supposed to be able to do: a binary that probes short of its own
7736
+ * claim is a source that changed under us, which is precisely what went
7737
+ * unnoticed for months when a static build turned out to carry no VAAPI at all
7738
+ * (D536).
7739
+ */
7740
+ /** The pinned jellyfin-ffmpeg release. Changing this is the version change. */
7741
+ var FFMPEG_RELEASE = "8.1.2-5";
7742
+ var BASE = `https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v${FFMPEG_RELEASE}`;
7743
+ function portable(target) {
7744
+ return `${BASE}/jellyfin-ffmpeg_${FFMPEG_RELEASE}_portable_${target}-gpl.tar.xz`;
7745
+ }
7746
+ portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac64");
7747
+ /**
7696
7748
  * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
7697
7749
  * valutatore puro del suo FRONTE.
7698
7750
  *
@@ -8781,6 +8833,110 @@ var BASE_LIVE_EGRESS_PROFILE = {
8781
8833
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
8782
8834
  ({ ...BASE_LIVE_EGRESS_PROFILE }), { ...BASE_LIVE_EGRESS_PROFILE.video };
8783
8835
  /**
8836
+ * The notification GIF's filter chain and argv — ONE definition for every
8837
+ * process that derives a gif from a notification video.
8838
+ *
8839
+ * ## Why this lives in `@camstack/types`
8840
+ *
8841
+ * Two addons derive a notification gif from an mp4, and they may never import
8842
+ * each other:
8843
+ *
8844
+ * - the stream broker (`addon-pipeline`), from the clip it just cut out of its
8845
+ * own ring (`gif-from-mp4.ts`);
8846
+ * - the notification centre (`addon-post-analysis`), from a clip cut out of
8847
+ * the RECORDINGS when the moment is older than any ring holds
8848
+ * ([D682](../../../../docs/decisions/adr-0682-a-pick-ups-footage-is-cut-around-the-pick-up.md)).
8849
+ *
8850
+ * Two copies of this chain would be two gifs of the same kind of moment that
8851
+ * look different depending on how old the moment was — the class of defect
8852
+ * D85/D91 already paid for once. So the chain is here, pure and dependency
8853
+ * free, and each caller owns only its own process lifecycle.
8854
+ *
8855
+ * ## The palette was never the problem
8856
+ *
8857
+ * The `split` + `palettegen(stats_mode=diff)` + `paletteuse(bayer)` pair is
8858
+ * right and stays. The measured cause of "the gif looks de-pixellated" was a
8859
+ * 640×360 substream scaled DOWN to 480 px, upstream of the palette. The `split`
8860
+ * form is a single READ of the input, not a lesser two-pass.
8861
+ */
8862
+ /**
8863
+ * Format a frame rate for an ffmpeg filter argument.
8864
+ *
8865
+ * `12 / 4` must reach the filter as `3` and `15 / 4` as `3.75`, not as a float
8866
+ * tail that `av_parse_video_rate` then has to guess at.
8867
+ */
8868
+ function ffmpegRate(rate) {
8869
+ return String(Math.round(rate * 1e3) / 1e3);
8870
+ }
8871
+ /**
8872
+ * The factor the derive must apply ON TOP of the speed the mp4 already carries.
8873
+ *
8874
+ * The gif is derived from a muxed mp4, so if that mux already ran `setpts` at
8875
+ * the caller's `speed`, applying the absolute gif rate again would compound
8876
+ * them — a 4× gif beside a 2× video would come out at 8×. Discounting here is
8877
+ * what keeps the gif rate ABSOLUTE against real time whatever the video does.
8878
+ */
8879
+ function gifDeriveSpeed(gifSpeed, videoSpeed) {
8880
+ if (!Number.isFinite(videoSpeed) || videoSpeed <= 0) return gifSpeed;
8881
+ return gifSpeed / videoSpeed;
8882
+ }
8883
+ /**
8884
+ * The filter chain. Extracted so a test can assert the palette pair without
8885
+ * spawning ffmpeg — an argv test cannot see quality, but it can see a filter
8886
+ * that somebody deleted.
8887
+ *
8888
+ * `fps` BEFORE `setpts`: decimating an already sped-up timeline yields `speed`
8889
+ * times the frames asked for, and a gif pays for every one. So the decimation
8890
+ * runs at `fps / speed` in SOURCE time and `setpts` multiplies what survived.
8891
+ * The trailing `fps` re-declares the rate, which is NOT redundant: `setpts`
8892
+ * leaves the filter link advertising the sampled rate and the gif muxer
8893
+ * believes the link — measured on 615 as a 3.01 s gif of ten frames.
8894
+ */
8895
+ function gifFilterComplex(input) {
8896
+ const speed = input.speed > 0 ? input.speed : 1;
8897
+ if (speed === 1) return `[0:v]fps=${ffmpegRate(input.fps)},scale=${input.maxWidth}:-2:flags=lanczos,split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`;
8898
+ return `[0:v]fps=${ffmpegRate(input.fps / speed)},setpts=PTS/${speed},fps=${ffmpegRate(input.fps)},scale=${input.maxWidth}:-2:flags=lanczos,split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle`;
8899
+ }
8900
+ /** The full argv: mp4 in, gif on stdout. */
8901
+ function gifFromMp4Args(input) {
8902
+ return [
8903
+ "-y",
8904
+ "-v",
8905
+ "error",
8906
+ "-f",
8907
+ "mp4",
8908
+ "-i",
8909
+ input.input ?? "pipe:0",
8910
+ "-filter_complex",
8911
+ gifFilterComplex(input),
8912
+ "-loop",
8913
+ "0",
8914
+ "-f",
8915
+ "gif",
8916
+ "pipe:1"
8917
+ ];
8918
+ }
8919
+ /**
8920
+ * Memoize `ensure` on SUCCESS only.
8921
+ *
8922
+ * A failure is not remembered: the next caller asks again, so a node whose
8923
+ * first download failed (no network at boot) recovers on the next spawn that
8924
+ * needs it — with no timer and no retry loop, because the ask is driven by
8925
+ * work that actually wants the binary.
8926
+ */
8927
+ function memoizePinnedFfmpeg(ensure) {
8928
+ let pending = null;
8929
+ return () => {
8930
+ if (pending !== null) return pending;
8931
+ const attempt = ensure();
8932
+ pending = attempt;
8933
+ attempt.catch(() => {
8934
+ if (pending === attempt) pending = null;
8935
+ });
8936
+ return attempt;
8937
+ };
8938
+ }
8939
+ /**
8784
8940
  * Deep wiring healthcheck — snapshot of active reachability probes across
8785
8941
  * every declared capability + widget of every installed plugin, on every
8786
8942
  * node. Produced by the backend `WiringHealthService` and surfaced via
@@ -9195,6 +9351,8 @@ var DETECTION_PIPELINE_CAP_NAME = "detection-pipeline";
9195
9351
  * oldest-first on append.
9196
9352
  */
9197
9353
  var MAX_BIRTH_DECISION_RECORDS = 3e4;
9354
+ /** Default page size for `listBirthDecisions` when the caller omits one. */
9355
+ var BIRTH_DECISION_DEFAULT_LIMIT = 500;
9198
9356
  /**
9199
9357
  * How stale a motion onset may be and still be offered as the birth-latency
9200
9358
  * proxy. Beyond this the row records `null`, never a large number.
@@ -9407,6 +9565,8 @@ var BirthDecisionQueryInputSchema = object({
9407
9565
  * `tracks` table measured at 4.6 MB for a single 501-row page.
9408
9566
  */
9409
9567
  var MAX_ARCHIVED_DEBUG_NOTES = 5e3;
9568
+ /** Default page size for `listArchivedDebugNotes` when the caller omits one. */
9569
+ var ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT = 200;
9410
9570
  /** One archived note — the operator's words plus enough context to find what
9411
9571
  * they were looking at, after the track itself is gone. */
9412
9572
  var ArchivedDebugNoteSchema = object({
@@ -9496,6 +9656,8 @@ var OpsLogQueryInputSchema = object({
9496
9656
  /** Max rows returned, newest-first. */
9497
9657
  limit: number().int().min(1).max(1e3).optional()
9498
9658
  });
9659
+ /** Default page size for a `listOpsLog` query when the caller omits `limit`. */
9660
+ var OPS_LOG_DEFAULT_LIMIT = 200;
9499
9661
  var LabelDefinitionSchema = object({
9500
9662
  id: string(),
9501
9663
  name: string(),
@@ -19111,6 +19273,12 @@ var NC_TANK_SYSTEM_EVENT_KINDS = [
19111
19273
  "device-tank-normal"
19112
19274
  ];
19113
19275
  /**
19276
+ * The consumable threshold a rule that does not state one judges at (D642).
19277
+ * The operator's number ("default 15 %, configurable on the rule"), and the
19278
+ * value the cluster-wide setting it replaces shipped with.
19279
+ */
19280
+ var NC_CONSUMABLE_LOW_PERCENT_DEFAULT = 15;
19281
+ /**
19114
19282
  * One coherent system-event condition. `kinds` is the required opt-in safety
19115
19283
  * gate; the remaining lists are optional narrowing filters relevant to the
19116
19284
  * selected kinds.
@@ -19803,6 +19971,24 @@ var NcMediaFrameSchema = _enum([
19803
19971
  * sensor linked to one camera ships that camera's plain snapshot either way.
19804
19972
  */
19805
19973
  var NcLinkedCameraModeSchema = _enum(["mosaic", "each"]);
19974
+ /**
19975
+ * The clip window a rule gets when it stores no `clipPreRollSec` /
19976
+ * `clipPostRollSec` (D654): 4 s before the moment, 6 s after.
19977
+ *
19978
+ * THE one owner. The Notification Center resolves an absent roll to these at
19979
+ * dispatch and sizes the broker's clip ring from them (`clip-retention-ask`).
19980
+ * The admin editor does not author the roll; the viewer editor still keeps its
19981
+ * own 3 / 7 until it imports these (viewer follow-up). A second copy is how the
19982
+ * viewer came to display 3 / 7 — copied from a stale comment on the schema
19983
+ * below — while the hub cut 4 / 6, and to prune a typed 3 back to "absent",
19984
+ * which the hub then read as 4.
19985
+ *
19986
+ * A product decision of the notification, not the broker's history window
19987
+ * (D381/D382): the clip is cut from the broker's per-camera clip ring, which
19988
+ * the rules size, not from a pre-roll dial.
19989
+ */
19990
+ var NC_CLIP_PRE_ROLL_DEFAULT_SEC = 4;
19991
+ var NC_CLIP_POST_ROLL_DEFAULT_SEC = 6;
19806
19992
  var NcMediaPolicySchema = object({
19807
19993
  attach: _enum([
19808
19994
  "best",
@@ -21335,6 +21521,24 @@ var NcAlarmConfigSchema = object({
21335
21521
  live: NcAlarmLiveSchema.optional()
21336
21522
  });
21337
21523
  /**
21524
+ * The most seconds of footage any notification rule may ask a camera to retain.
21525
+ *
21526
+ * Sixty, and it is the OCCUPANCY number. An occupancy edge is confirmed by an
21527
+ * independent read that lands long after the fact (D279) plus the condition's
21528
+ * own `sustainSeconds` (29 s on this hub), so its clip is anchored well in the
21529
+ * past — and the other source, the recordings, cannot cover a recent moment:
21530
+ * measured against `recording.getAvailability` for a 10 s window at several
21531
+ * anchors, full coverage arrived at −18 s on device 590 and −26 s on 615 and
21532
+ * 617. That edge is a PHASE of the 10-second segment cycle, not a number. The
21533
+ * ring therefore has to hold the moment until the recordings can take it over:
21534
+ * the worst measured phase (26 s) plus the confirmation lag (29 s) is 55 s, and
21535
+ * 60 is the round number above it.
21536
+ *
21537
+ * It is a ceiling, not a default. Every other rule kind asks for its own window
21538
+ * plus the dispatch margin, which is roughly a third of this.
21539
+ */
21540
+ var NC_CLIP_RETENTION_MAX_SEC = 60;
21541
+ /**
21338
21542
  * ONE rule's demand on ONE camera's clip ring.
21339
21543
  *
21340
21544
  * A rule, not a camera: the broker takes the MAX over the asks that reach a
@@ -23012,6 +23216,21 @@ var ObjectEventSchema = object({
23012
23216
  keyFrameMediaId: number().int().optional(),
23013
23217
  /** Populated by B5 (recording playback URL for this event). */
23014
23218
  mediaUrl: string().optional(),
23219
+ /**
23220
+ * When the change this row records HAPPENED, epoch ms, when that is earlier
23221
+ * than {@link timestamp} and the producer could date it (D684).
23222
+ *
23223
+ * Set on a package PICK-UP row only: its `timestamp` is when the departure
23224
+ * was CONCLUDED — five minutes of unseen observed time after the parcel went
23225
+ * — and a notification's footage has to be cut around the pick-up, not the
23226
+ * empty doormat after it (D682). Stored so every path that rebuilds a
23227
+ * notification subject from the row (the boot reconcile, a dry-run) anchors
23228
+ * on the same instant the live hook did.
23229
+ *
23230
+ * `timestamp` keeps its meaning. Absent on every other row and on every
23231
+ * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
23232
+ */
23233
+ observedAt: number().optional(),
23015
23234
  /** The parent track's key-event importance [0,1], propagated to every object
23016
23235
  * event of the track (so an event row can be sorted by importance without a
23017
23236
  * track join). Absent on legacy rows / before the track was scored. */
@@ -23427,6 +23646,21 @@ var EventDensityBucketSchema = object({
23427
23646
  audioDbfs: number().optional()
23428
23647
  });
23429
23648
  /**
23649
+ * Most cameras one `getEventDensityBatch` may name.
23650
+ *
23651
+ * The same ceiling the sibling batches on this cap already carry
23652
+ * (`listEventKindsBatch`, `getKeyEventsBatch`): a tablet grid is tens of
23653
+ * cameras, never hundreds, and one call's answer is N × (window / bucketMs)
23654
+ * buckets crossing a process boundary.
23655
+ *
23656
+ * The ceiling is on the SCHEMA, not only on the caller. An over-eager ask is
23657
+ * REFUSED — never truncated to the first {@link EVENT_DENSITY_BATCH_MAX} ids,
23658
+ * because that answer is indistinguishable from a complete one and the caller
23659
+ * would never learn it asked too much. A caller with more cameras sends
23660
+ * ⌈n/max⌉ calls.
23661
+ */
23662
+ var EVENT_DENSITY_BATCH_MAX = 200;
23663
+ /**
23430
23664
  * One camera's row in a `getEventDensityBatch` answer.
23431
23665
  *
23432
23666
  * TWO facts, and the timeline draws them differently:
@@ -37575,6 +37809,16 @@ var ExportDenseRangeSchema = object({
37575
37809
  toSec: number().nonnegative()
37576
37810
  }).refine((r) => r.toSec > r.fromSec, { message: "dense range must have toSec > fromSec" });
37577
37811
  /**
37812
+ * Hard ceiling on dense ranges in ONE render.
37813
+ *
37814
+ * The ranges become terms of a single ffmpeg `select` expression, so the count
37815
+ * is the length of a command-line argument. The producer (the timelapse
37816
+ * scheduler) coalesces and then falls back to the base cadence alone rather
37817
+ * than trimming — a truncated range list is a video that quietly omits the
37818
+ * evening.
37819
+ */
37820
+ var EXPORT_DENSE_MAX_RANGES = 200;
37821
+ /**
37578
37822
  * Dense-interval overlay for a timelapse: sample at `dense.everyMs` INSIDE the
37579
37823
  * listed ranges and at the base `everyMs` everywhere else.
37580
37824
  *
@@ -38396,11 +38640,23 @@ var SceneCheckSchema = discriminatedUnion("mode", [object({
38396
38640
  profileId: string().optional(),
38397
38641
  hysteresisCount: number().int().positive()
38398
38642
  })]);
38643
+ /** Defaults, as CONSTANTS rather than only as Zod `.default()`. A Zod default
38644
+ * does NOT run on the addon→addon cap path, so every runtime seam reads these
38645
+ * as absent-with-a-constant (the `NcConfirmSchema` lesson, three production
38646
+ * failures in one day). The schema defaults and these values are the same
38647
+ * numbers on purpose. */
38648
+ var SCENE_DEFAULT_QUIET_SECONDS = 60;
38649
+ var SCENE_DEFAULT_OBSERVATION_SPACING_SEC = 120;
38650
+ var SCENE_DEFAULT_CHECK_INTERVAL_SEC = 60;
38399
38651
  var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
38400
38652
  /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
38401
38653
  * out in silence rather than reporting a fault every night. */
38402
38654
  var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
38403
38655
  var SCENE_CONFIRM_DEFAULT_TIMEOUT_MS = 8e3;
38656
+ var SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX = 448;
38657
+ /** `resetScene({ recapture })` absent means TRUE — the operator decision. Read
38658
+ * at the seam, because a Zod `.optional()` carries no default at all. */
38659
+ var SCENE_RESET_RECAPTURES = true;
38404
38660
  /**
38405
38661
  * Vision-model adjudication of a candidate flip. Field names deliberately
38406
38662
  * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
@@ -52476,6 +52732,15 @@ function ituDbfsFromPreEpoch(law, authoredDbfs) {
52476
52732
  return authoredDbfs + G711_SCALE_CORRECTION_DB[law];
52477
52733
  }
52478
52734
  Math.round(ituDbfsFromPreEpoch("PCMU", -55));
52735
+ /**
52736
+ * LABEL-mode confirm-count defaults. Chosen against the live "Pianti" rule
52737
+ * (2026-08-23): 15 notifies in 12 h, each a single YAMNet `crying` frame,
52738
+ * nobody actually crying. Two labelled frames in 5 s drops the single-frame
52739
+ * false positives and still catches a real episode (YAMNet labels 2–3
52740
+ * frames of a genuine cry).
52741
+ */
52742
+ var NC_AUDIO_CONFIRM_HITS_DEFAULT = 2;
52743
+ var NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT = 5;
52479
52744
  /** Schema defaults — an untouched sub-field must author exactly these. */
52480
52745
  var NC_AUDIO_DEFAULTS = {
52481
52746
  hitPercent: 60,
@@ -55283,6 +55548,12 @@ function buildClipModelRegistry(entries) {
55283
55548
  /** The built-in catalog's registry. */
55284
55549
  var BUILTIN_CLIP_MODELS = buildClipModelRegistry(CLIP_IMAGE_MODELS);
55285
55550
  //#endregion
55551
+ Object.defineProperty(exports, "ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT", {
55552
+ enumerable: true,
55553
+ get: function() {
55554
+ return ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT;
55555
+ }
55556
+ });
55286
55557
  Object.defineProperty(exports, "AUDIO_MACRO_LABELS", {
55287
55558
  enumerable: true,
55288
55559
  get: function() {
@@ -55301,6 +55572,12 @@ Object.defineProperty(exports, "ArchivedDebugNoteSchema", {
55301
55572
  return ArchivedDebugNoteSchema;
55302
55573
  }
55303
55574
  });
55575
+ Object.defineProperty(exports, "BIRTH_DECISION_DEFAULT_LIMIT", {
55576
+ enumerable: true,
55577
+ get: function() {
55578
+ return BIRTH_DECISION_DEFAULT_LIMIT;
55579
+ }
55580
+ });
55304
55581
  Object.defineProperty(exports, "BUILTIN_CLIP_MODELS", {
55305
55582
  enumerable: true,
55306
55583
  get: function() {
@@ -55427,6 +55704,12 @@ Object.defineProperty(exports, "EMBEDDING_REQUIREMENTS_UNAVAILABLE", {
55427
55704
  return EMBEDDING_REQUIREMENTS_UNAVAILABLE;
55428
55705
  }
55429
55706
  });
55707
+ Object.defineProperty(exports, "EVENT_DENSITY_BATCH_MAX", {
55708
+ enumerable: true,
55709
+ get: function() {
55710
+ return EVENT_DENSITY_BATCH_MAX;
55711
+ }
55712
+ });
55430
55713
  Object.defineProperty(exports, "EVENT_KIND_BY_CAP", {
55431
55714
  enumerable: true,
55432
55715
  get: function() {
@@ -55439,6 +55722,12 @@ Object.defineProperty(exports, "EVENT_OWNER_TYPES", {
55439
55722
  return EVENT_OWNER_TYPES;
55440
55723
  }
55441
55724
  });
55725
+ Object.defineProperty(exports, "EXPORT_DENSE_MAX_RANGES", {
55726
+ enumerable: true,
55727
+ get: function() {
55728
+ return EXPORT_DENSE_MAX_RANGES;
55729
+ }
55730
+ });
55442
55731
  Object.defineProperty(exports, "EventCategory", {
55443
55732
  enumerable: true,
55444
55733
  get: function() {
@@ -55523,12 +55812,48 @@ Object.defineProperty(exports, "NC_ALARM_SYSTEM_EVENT_KINDS", {
55523
55812
  return NC_ALARM_SYSTEM_EVENT_KINDS;
55524
55813
  }
55525
55814
  });
55815
+ Object.defineProperty(exports, "NC_AUDIO_CONFIRM_HITS_DEFAULT", {
55816
+ enumerable: true,
55817
+ get: function() {
55818
+ return NC_AUDIO_CONFIRM_HITS_DEFAULT;
55819
+ }
55820
+ });
55821
+ Object.defineProperty(exports, "NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT", {
55822
+ enumerable: true,
55823
+ get: function() {
55824
+ return NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT;
55825
+ }
55826
+ });
55827
+ Object.defineProperty(exports, "NC_CLIP_POST_ROLL_DEFAULT_SEC", {
55828
+ enumerable: true,
55829
+ get: function() {
55830
+ return NC_CLIP_POST_ROLL_DEFAULT_SEC;
55831
+ }
55832
+ });
55833
+ Object.defineProperty(exports, "NC_CLIP_PRE_ROLL_DEFAULT_SEC", {
55834
+ enumerable: true,
55835
+ get: function() {
55836
+ return NC_CLIP_PRE_ROLL_DEFAULT_SEC;
55837
+ }
55838
+ });
55839
+ Object.defineProperty(exports, "NC_CLIP_RETENTION_MAX_SEC", {
55840
+ enumerable: true,
55841
+ get: function() {
55842
+ return NC_CLIP_RETENTION_MAX_SEC;
55843
+ }
55844
+ });
55526
55845
  Object.defineProperty(exports, "NC_CONDITION_CATALOG", {
55527
55846
  enumerable: true,
55528
55847
  get: function() {
55529
55848
  return NC_CONDITION_CATALOG;
55530
55849
  }
55531
55850
  });
55851
+ Object.defineProperty(exports, "NC_CONSUMABLE_LOW_PERCENT_DEFAULT", {
55852
+ enumerable: true,
55853
+ get: function() {
55854
+ return NC_CONSUMABLE_LOW_PERCENT_DEFAULT;
55855
+ }
55856
+ });
55532
55857
  Object.defineProperty(exports, "NC_CONSUMABLE_SYSTEM_EVENT_KINDS", {
55533
55858
  enumerable: true,
55534
55859
  get: function() {
@@ -55685,6 +56010,12 @@ Object.defineProperty(exports, "NcTemplateVarDescriptorSchema", {
55685
56010
  return NcTemplateVarDescriptorSchema;
55686
56011
  }
55687
56012
  });
56013
+ Object.defineProperty(exports, "OPS_LOG_DEFAULT_LIMIT", {
56014
+ enumerable: true,
56015
+ get: function() {
56016
+ return OPS_LOG_DEFAULT_LIMIT;
56017
+ }
56018
+ });
55688
56019
  Object.defineProperty(exports, "OpsLogEntrySchema", {
55689
56020
  enumerable: true,
55690
56021
  get: function() {
@@ -55721,6 +56052,12 @@ Object.defineProperty(exports, "RetrainStatusSchema", {
55721
56052
  return RetrainStatusSchema;
55722
56053
  }
55723
56054
  });
56055
+ Object.defineProperty(exports, "SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX", {
56056
+ enumerable: true,
56057
+ get: function() {
56058
+ return SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX;
56059
+ }
56060
+ });
55724
56061
  Object.defineProperty(exports, "SCENE_CONFIRM_DEFAULT_TIMEOUT_MS", {
55725
56062
  enumerable: true,
55726
56063
  get: function() {
@@ -55733,6 +56070,24 @@ Object.defineProperty(exports, "SCENE_DEFAULT_ANCHOR_THRESHOLD", {
55733
56070
  return SCENE_DEFAULT_ANCHOR_THRESHOLD;
55734
56071
  }
55735
56072
  });
56073
+ Object.defineProperty(exports, "SCENE_DEFAULT_CHECK_INTERVAL_SEC", {
56074
+ enumerable: true,
56075
+ get: function() {
56076
+ return SCENE_DEFAULT_CHECK_INTERVAL_SEC;
56077
+ }
56078
+ });
56079
+ Object.defineProperty(exports, "SCENE_DEFAULT_OBSERVATION_SPACING_SEC", {
56080
+ enumerable: true,
56081
+ get: function() {
56082
+ return SCENE_DEFAULT_OBSERVATION_SPACING_SEC;
56083
+ }
56084
+ });
56085
+ Object.defineProperty(exports, "SCENE_DEFAULT_QUIET_SECONDS", {
56086
+ enumerable: true,
56087
+ get: function() {
56088
+ return SCENE_DEFAULT_QUIET_SECONDS;
56089
+ }
56090
+ });
55736
56091
  Object.defineProperty(exports, "SCENE_DEFAULT_UNCOVERED_POLICY", {
55737
56092
  enumerable: true,
55738
56093
  get: function() {
@@ -55745,6 +56100,12 @@ Object.defineProperty(exports, "SCENE_DIVERGED", {
55745
56100
  return SCENE_DIVERGED;
55746
56101
  }
55747
56102
  });
56103
+ Object.defineProperty(exports, "SCENE_RESET_RECAPTURES", {
56104
+ enumerable: true,
56105
+ get: function() {
56106
+ return SCENE_RESET_RECAPTURES;
56107
+ }
56108
+ });
55748
56109
  Object.defineProperty(exports, "SceneMonitorSchema", {
55749
56110
  enumerable: true,
55750
56111
  get: function() {
@@ -55973,6 +56334,18 @@ Object.defineProperty(exports, "failureContributionCapability", {
55973
56334
  return failureContributionCapability;
55974
56335
  }
55975
56336
  });
56337
+ Object.defineProperty(exports, "gifDeriveSpeed", {
56338
+ enumerable: true,
56339
+ get: function() {
56340
+ return gifDeriveSpeed;
56341
+ }
56342
+ });
56343
+ Object.defineProperty(exports, "gifFromMp4Args", {
56344
+ enumerable: true,
56345
+ get: function() {
56346
+ return gifFromMp4Args;
56347
+ }
56348
+ });
55976
56349
  Object.defineProperty(exports, "hydrateSchema", {
55977
56350
  enumerable: true,
55978
56351
  get: function() {
@@ -56057,6 +56430,12 @@ Object.defineProperty(exports, "mayWriteToLocation", {
56057
56430
  return mayWriteToLocation;
56058
56431
  }
56059
56432
  });
56433
+ Object.defineProperty(exports, "memoizePinnedFfmpeg", {
56434
+ enumerable: true,
56435
+ get: function() {
56436
+ return memoizePinnedFfmpeg;
56437
+ }
56438
+ });
56060
56439
  Object.defineProperty(exports, "nodePin", {
56061
56440
  enumerable: true,
56062
56441
  get: function() {
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_clip_model_registry = require("../clip-model-registry-olxNKN7T.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-BeJUe0vk.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs);
8
8
  let node_path = require("node:path");
@@ -328,6 +328,10 @@ var PythonRawTensorEngine = class {
328
328
  await this.proc.dispose();
329
329
  }
330
330
  };
331
+ //#endregion
332
+ //#region src/embedding-encoder/addon/active-clip-model.ts
333
+ /** How long a cluster-row read is trusted. The operator's flip lands within this. */
334
+ var ENCODER_ROW_TTL_MS = 3e4;
331
335
  var ActiveClipModel = class {
332
336
  deps;
333
337
  cached = null;
@@ -345,7 +349,7 @@ var ActiveClipModel = class {
345
349
  }
346
350
  async get() {
347
351
  const now = this.deps.now();
348
- if (this.cached !== null && now - this.cached.at < 3e4) return this.cached.modelId;
352
+ if (this.cached !== null && now - this.cached.at < ENCODER_ROW_TTL_MS) return this.cached.modelId;
349
353
  const row = await this.deps.readRow();
350
354
  if (row === null) {
351
355
  const kept = this.lastRead ?? this.deps.fallbackModelId();
@@ -369,6 +373,10 @@ var ActiveClipModel = class {
369
373
  return row;
370
374
  }
371
375
  };
376
+ //#endregion
377
+ //#region src/embedding-encoder/addon/crash-budget.ts
378
+ var MAX_CRASHES = 3;
379
+ var CRASH_WINDOW_MS = 10 * 6e4;
372
380
  var CrashBudget = class {
373
381
  deps;
374
382
  recent = /* @__PURE__ */ new Map();
@@ -386,11 +394,11 @@ var CrashBudget = class {
386
394
  record(modelId, reason) {
387
395
  if (this.failedModels.has(modelId)) return false;
388
396
  const now = this.now();
389
- const windowMs = this.deps.windowMs ?? 6e5;
397
+ const windowMs = this.deps.windowMs ?? CRASH_WINDOW_MS;
390
398
  const times = (this.recent.get(modelId) ?? []).filter((t) => now - t < windowMs);
391
399
  times.push(now);
392
400
  this.recent.set(modelId, times);
393
- if (times.length < (this.deps.maxCrashes ?? 3)) return false;
401
+ if (times.length < (this.deps.maxCrashes ?? MAX_CRASHES)) return false;
394
402
  const failed = {
395
403
  modelId,
396
404
  tower: this.deps.tower,
@@ -566,6 +574,13 @@ var ModelEngineSlot = class {
566
574
  held.engine.dispose().catch(() => {});
567
575
  }
568
576
  };
577
+ //#endregion
578
+ //#region src/embedding-encoder/addon/model-requirements.ts
579
+ /** First cooldown after a failed attempt; doubles per failure up to the cap. */
580
+ var REQUIREMENTS_COOLDOWN_BASE_MS = 5e3;
581
+ var REQUIREMENTS_COOLDOWN_MAX_MS = 5 * 6e4;
582
+ /** Failed attempts at a PERMANENTLY unmeetable requirement before it is terminal. */
583
+ var MAX_REQUIREMENTS_ATTEMPTS = 3;
569
584
  /**
570
585
  * Thrown by the requirements stage when a retry can never help: the catalog
571
586
  * does not know the id, the server said the file does not exist (404/410), a
@@ -621,7 +636,7 @@ var RequirementsGate = class {
621
636
  const attempts = (prior?.attempts ?? 0) + 1;
622
637
  const permanent = isPermanentRequirementsFailure(err);
623
638
  const permanentAttempts = (prior?.permanentAttempts ?? 0) + (permanent ? 1 : 0);
624
- const cooldownMs = Math.min(this.deps.cooldownMaxMs ?? 3e5, (this.deps.cooldownBaseMs ?? 5e3) * 2 ** (attempts - 1));
639
+ const cooldownMs = Math.min(this.deps.cooldownMaxMs ?? REQUIREMENTS_COOLDOWN_MAX_MS, (this.deps.cooldownBaseMs ?? REQUIREMENTS_COOLDOWN_BASE_MS) * 2 ** (attempts - 1));
625
640
  const base = {
626
641
  sinceMs: prior?.sinceMs ?? now,
627
642
  reason,
@@ -638,7 +653,7 @@ var RequirementsGate = class {
638
653
  reason,
639
654
  cooldownMs
640
655
  } });
641
- if (permanent && permanentAttempts >= (this.deps.maxAttempts ?? 3)) {
656
+ if (permanent && permanentAttempts >= (this.deps.maxAttempts ?? MAX_REQUIREMENTS_ATTEMPTS)) {
642
657
  const terminal = {
643
658
  ...this.on(modelId),
644
659
  attempts: permanentAttempts,
@@ -750,6 +765,10 @@ function l2Normalize(vec) {
750
765
  if (norm > 0) for (let i = 0; i < vec.length; i++) vec[i] /= norm;
751
766
  return vec;
752
767
  }
768
+ //#endregion
769
+ //#region src/embedding-encoder/addon/clip-text-engines.ts
770
+ /** At most this many text towers stay loaded (SigLIP2's is ~565 MB). */
771
+ var MAX_TEXT_ENGINES = 2;
753
772
  /**
754
773
  * A tower unused this long is disposed ({@link ClipTextEngines.evictIdle}) —
755
774
  * EXCEPT the tower of the model the cluster row names, which stays loaded for
@@ -927,7 +946,7 @@ var ClipTextEngines = class {
927
946
  }
928
947
  /** Fire-and-log: eviction errors never reach an encode. */
929
948
  scheduleEviction() {
930
- this.evictBeyond(this.deps.maxEngines ?? 2).catch((err) => {
949
+ this.evictBeyond(this.deps.maxEngines ?? MAX_TEXT_ENGINES).catch((err) => {
931
950
  this.deps.logger.warn("CLIP text tower eviction failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
932
951
  });
933
952
  }