@camstack/addon-pipeline 1.2.315 → 1.2.317

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 (32) 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-BTlf5MpT.mjs → default-detection-model-CILrD3sC.mjs} +1 -1
  4. package/dist/{default-detection-model-CteW4o8U.js → default-detection-model-DdIJSdQB.js} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-C8W_MWSz.mjs → dist-3isAnyIX.mjs} +679 -161
  8. package/dist/{dist-sCjZdzKn.js → dist-DXU3Fu7j.js} +679 -161
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-D4xdwpPU.js → node-DEpHS48Q.js} +1 -1
  12. package/dist/{node-CnLKPz1A.mjs → node-PY3N7_ym.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +4 -4
  14. package/dist/pipeline-runner/index.mjs +4 -4
  15. package/dist/{process-memory-CMnhjGyE.js → process-memory-DAGs0yZp.js} +1 -1
  16. package/dist/{process-memory-DqTBUteS.mjs → process-memory-DE0M2AiK.mjs} +1 -1
  17. package/dist/recorder/index.js +108 -108
  18. package/dist/recorder/index.mjs +108 -108
  19. package/dist/{segment-demux-js-DGpdNqsG.js → segment-demux-js-BZhn3qOl.js} +1 -1
  20. package/dist/{segment-demux-js-CCSsDyMw.mjs → segment-demux-js-k3D5kt3W.mjs} +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BOKNSh-e.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DqP6Ev3L.mjs} +2 -2
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CVT3OmxC.mjs +26 -0
  24. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DddXyc17.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DHNf76xj.mjs} +1 -1
  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-BkvtmJ2D.mjs → hostInit-BRiacIOY.mjs} +2 -2
  28. package/dist/stream-broker/index.js +6 -6
  29. package/dist/stream-broker/index.mjs +6 -6
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +1 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BTvQXobK.mjs +0 -26
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-sCjZdzKn.js");
5
+ const require_dist = require("../dist-DXU3Fu7j.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Sn as hydrateSchema, ct as evaluateZoneRules, dn as DeviceType, kt as motionDetectionCapability, mn as BaseAddon } from "../dist-C8W_MWSz.mjs";
1
+ import { Sn as hydrateSchema, ct as evaluateZoneRules, dn as DeviceType, kt as motionDetectionCapability, mn as BaseAddon } from "../dist-3isAnyIX.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,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-sCjZdzKn.js");
2
+ const require_dist = require("./dist-DXU3Fu7j.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -1,4 +1,4 @@
1
- import { an as Fmp4BoxSplitter, fn as errMsg, ln as isSoftwareDecode, on as buildFfmpegArgs } from "./dist-C8W_MWSz.mjs";
1
+ import { an as Fmp4BoxSplitter, fn as errMsg, ln as isSoftwareDecode, on as buildFfmpegArgs } from "./dist-3isAnyIX.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-sCjZdzKn.js");
7
- const require_node = require("../node-D4xdwpPU.js");
8
- const require_default_detection_model = require("../default-detection-model-CteW4o8U.js");
6
+ const require_dist = require("../dist-DXU3Fu7j.js");
7
+ const require_node = require("../node-DEpHS48Q.js");
8
+ const require_default_detection_model = require("../default-detection-model-DdIJSdQB.js");
9
9
  const require_worker_protocol = require("../worker-protocol-C-G8qmye.js");
10
10
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
11
11
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
@@ -12261,7 +12261,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
12261
12261
  frameHandle: handle,
12262
12262
  emittedAt: Date.now(),
12263
12263
  ...typeof capturedAt === "number" && capturedAt > 0 ? { capturedAt } : {},
12264
- ...detailSteps && detailSteps.length > 0 ? { detailSteps } : {},
12264
+ ...detailSteps !== void 0 ? { detailSteps } : {},
12265
12265
  source,
12266
12266
  occasion
12267
12267
  };
@@ -1,6 +1,6 @@
1
- import { D as NativeLeaseAdmissionSchema, En as nodePin, Fn as lazy, In as literal, It as pipelineRunnerCapability, Ln as number, Mn as array, Nn as boolean, Nt as pickDetailCropConvention, O as NativeLeaseSettingsSchema, Pt as pickNativeLeaseOverride, Rn as object, Un as EventCategory, Vn as string, a as BackgroundRunRegistry, bn as defineCustomActions, bt as loadContributionCapability, fn as errMsg, ft as failureContributionCapability, g as DEFAULT_DETAIL_CROP_CONVENTION, jn as _enum, mn as BaseAddon, nt as deriveDetailCropRect, q as cameraOwnsMotionViaSensor, v as DEFAULT_NATIVE_LEASE_SETTINGS, vn as createEvent, w as FailureCounters, yn as customAction } from "../dist-C8W_MWSz.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-CnLKPz1A.mjs";
3
- import { c as macroGateVerdict, d as resolveFrameViewGeometry, g as getStepDefinition, l as rootObjectGate, o as placeDetailStep, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as RETIRED_MODEL_IDS } from "../default-detection-model-BTlf5MpT.mjs";
1
+ import { D as NativeLeaseAdmissionSchema, En as nodePin, Fn as lazy, In as literal, It as pipelineRunnerCapability, Ln as number, Mn as array, Nn as boolean, Nt as pickDetailCropConvention, O as NativeLeaseSettingsSchema, Pt as pickNativeLeaseOverride, Rn as object, Un as EventCategory, Vn as string, a as BackgroundRunRegistry, bn as defineCustomActions, bt as loadContributionCapability, fn as errMsg, ft as failureContributionCapability, g as DEFAULT_DETAIL_CROP_CONVENTION, jn as _enum, mn as BaseAddon, nt as deriveDetailCropRect, q as cameraOwnsMotionViaSensor, v as DEFAULT_NATIVE_LEASE_SETTINGS, vn as createEvent, w as FailureCounters, yn as customAction } from "../dist-3isAnyIX.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-PY3N7_ym.mjs";
3
+ import { c as macroGateVerdict, d as resolveFrameViewGeometry, g as getStepDefinition, l as rootObjectGate, o as placeDetailStep, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as RETIRED_MODEL_IDS } from "../default-detection-model-CILrD3sC.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-D_NzPcnh.mjs";
5
5
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
6
6
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
@@ -12253,7 +12253,7 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
12253
12253
  frameHandle: handle,
12254
12254
  emittedAt: Date.now(),
12255
12255
  ...typeof capturedAt === "number" && capturedAt > 0 ? { capturedAt } : {},
12256
- ...detailSteps && detailSteps.length > 0 ? { detailSteps } : {},
12256
+ ...detailSteps !== void 0 ? { detailSteps } : {},
12257
12257
  source,
12258
12258
  occasion
12259
12259
  };
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-sCjZdzKn.js");
2
+ const require_dist = require("./dist-DXU3Fu7j.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { jt as parseProcStatus } from "./dist-C8W_MWSz.mjs";
1
+ import { jt as parseProcStatus } from "./dist-3isAnyIX.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-sCjZdzKn.js");
7
- const require_node = require("../node-D4xdwpPU.js");
6
+ const require_dist = require("../dist-DXU3Fu7j.js");
7
+ const require_node = require("../node-DEpHS48Q.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
@@ -19,32 +19,32 @@ let node_v8 = require("node:v8");
19
19
  node_v8 = require_chunk.__toESM(node_v8);
20
20
  //#region src/recorder/event-capture.ts
21
21
  /**
22
- * La sottoscrizione motion del recorder: bus → `onTrigger(deviceId, atMs)`.
22
+ * The recorder's motion subscription: bus → `onTrigger(deviceId, atMs)`.
23
23
  *
24
- * ## Cosa NON c'è più qui (2026-08-29)
24
+ * ## What is NOT here any more (2026-08-29)
25
25
  *
26
- * Fino al 2026-08-29 questo file costruiva anche un `EventMarker` per evento e
27
- * lo spingeva in un `EventMap` per-device — annotazioni di playback in RAM, con
28
- * dentro il payload `regions` grezzo del provider. `EventMap.events()` e
29
- * `EventMap.prune()` non avevano **nessun chiamante in tutto il repo**: la
30
- * mappa veniva solo scritta, e cresceva con l'uptime del runner.
26
+ * Until 2026-08-29 this file also built an `EventMarker` per event and
27
+ * pushed it into a per-device `EventMap` — playback annotations in RAM, with
28
+ * the provider's raw `regions` payload inside. `EventMap.events()` and
29
+ * `EventMap.prune()` had **no caller in the whole repo**: the
30
+ * map was only written, and grew with the runner's uptime.
31
31
  *
32
- * È stata cancellata, non cablata, e la ragione è nel suo stesso header: la
33
- * timeline che l'operatore vede viene dalle righe durabili di
34
- * `pipelineAnalytics` (`getMotionEvents`/`getObjectEvents`/`getAudioEvents`), i
35
- * marker non erano esposti su nessuna cap (`recording.getPlaybackManifest`
36
- * restituisce solo URL di playlist) e ADR-0194 aveva già rifiutato
37
- * esplicitamente di costruirci sopra il reconcile dei trigger, perché una
38
- * struttura in RAM non sopravvive né alla perdita dell'evento né al respawn del
39
- * runner. Un lettore non è mai esistito e non ne era previsto uno: tenerla
40
- * sarebbe stato "un residuo che descrive il design giusto", cioè una verifica
41
- * che non verifica niente.
32
+ * It was deleted, not wired, and the reason is in its own header: the
33
+ * timeline the operator sees comes from the durable rows of
34
+ * `pipelineAnalytics` (`getMotionEvents`/`getObjectEvents`/`getAudioEvents`), the
35
+ * markers were not exposed on any cap (`recording.getPlaybackManifest`
36
+ * returns only playlist URLs) and ADR-0194 had already explicitly refused
37
+ * to build the trigger reconcile on top of it, because an
38
+ * in-RAM structure survives neither the loss of the event nor the respawn of the
39
+ * runner. A reader never existed and none was planned: keeping it
40
+ * would have been "a leftover that describes the right design", i.e. a check
41
+ * that checks nothing.
42
42
  *
43
- * Quello che resta è l'unica cosa che qualcuno leggeva davvero: il fronte di
44
- * salita della motion che apre una finestra della banda `events` (B3).
43
+ * What remains is the only thing anyone really read: the rising edge of
44
+ * the motion that opens a window of the `events` band (B3).
45
45
  */
46
- /** Solo il fronte `detected: true` è un trigger. Il fronte di discesa non apre
47
- * niente — la finestra la chiude il post-buffer, non l'evento. */
46
+ /** Only the `detected: true` edge is a trigger. The falling edge opens
47
+ * nothing — the window is closed by the post-buffer, not by the event. */
48
48
  function isMotionTrigger(data) {
49
49
  return data.detected;
50
50
  }
@@ -542,40 +542,40 @@ var MfraTableStore = class {
542
542
  //#endregion
543
543
  //#region src/recorder/object-trigger.ts
544
544
  /**
545
- * Il gate fra `pipeline-analytics.detection-event` e "apri una finestra di
546
- * registrazione".
547
- *
548
- * Fratello di `recorder/event-capture.ts` e per lo stesso motivo: la qualifica
549
- * ha dei rami, e un ramo che scarta lavoro senza dirlo è indistinguibile da una
550
- * funzione mai cablata. Ogni verdetto negativo NOMINA il motivo, e il chiamante
551
- * lo logga.
552
- *
553
- * Puro: nessun bus, nessun orologio, nessuna config. La scelta di QUALI classi
554
- * ascoltare è della banda (`bandTriggerSources`); qui si decide solo se
555
- * l'evento è una detection utilizzabile e di che macro classe è.
556
- *
557
- * ## Perché la forma è DISCRIMINATA su `kind` (2026-08-29)
558
- *
559
- * Fino al 2026-08-29 questo file dichiarava UNA sola forma piatta — quella
560
- * dell'evento `object` — e la pretendeva da OGNI payload del topic. Ma
561
- * `EventCategory.PipelineAnalyticsDetectionEvent` è un topic condiviso da tre
562
- * emettitori in `addon-post-analysis/src/pipeline-analytics/index.ts`: object
563
- * (`className`, `source`, `confidence`, `zones`), motion e audio — e motion e
564
- * audio non portano `className`, perché non ne hanno una. Il risultato misurato:
565
- * `malformed` su **394 righe campionate su 394**, a 3.3–3.9 eventi/s, per
566
- * settimane. `malformed` significa "l'emettitore è andato in deriva" ed era
567
- * invece il traffico normale del topic; l'unico secchio che poteva segnalare
568
- * una vera rottura di protocollo era saturo al 100% di eventi sani, quindi
569
- * inservibile — ed è stato letto come "il trigger object non ha mai funzionato".
570
- *
571
- * Quindi: si valida prima la BUSTA comune (`deviceId`, `kind`, `timestamp`),
572
- * poi si discrimina su `kind`, e solo il ramo `object` pretende i campi
573
- * dell'object. Un `object` senza `className` resta un difetto e ha il suo nome
574
- * (`malformed-object`), distinto da una busta illeggibile.
575
- */
576
- /** Il `kind` dell'unico ramo del topic che può aprire una finestra. */
545
+ * The gate between `pipeline-analytics.detection-event` and "open a
546
+ * recording window".
547
+ *
548
+ * Sibling of `recorder/event-capture.ts` and for the same reason: the qualification
549
+ * has branches, and a branch that discards work without saying so is indistinguishable from a
550
+ * function that was never wired. Every negative verdict NAMES the reason, and the caller
551
+ * logs it.
552
+ *
553
+ * Pure: no bus, no clock, no config. The choice of WHICH classes to
554
+ * listen to belongs to the band (`bandTriggerSources`); here we only decide whether
555
+ * the event is a usable detection and which macro class it is.
556
+ *
557
+ * ## Why the shape is DISCRIMINATED on `kind` (2026-08-29)
558
+ *
559
+ * Until 2026-08-29 this file declared ONE flat shape — that
560
+ * of the `object` event — and demanded it from EVERY payload of the topic. But
561
+ * `EventCategory.PipelineAnalyticsDetectionEvent` is a topic shared by three
562
+ * emitters in `addon-post-analysis/src/pipeline-analytics/index.ts`: object
563
+ * (`className`, `source`, `confidence`, `zones`), motion and audio — and motion and
564
+ * audio do not carry `className`, because they have none. The measured result:
565
+ * `malformed` on **394 sampled rows out of 394**, at 3.3–3.9 events/s, for
566
+ * weeks. `malformed` means "the emitter has drifted" and was
567
+ * instead the normal traffic of the topic; the only bucket that could signal
568
+ * a real protocol break was saturated at 100% with healthy events, hence
569
+ * useless — and it was read as "the object trigger never worked".
570
+ *
571
+ * So: first the common ENVELOPE is validated (`deviceId`, `kind`, `timestamp`),
572
+ * then we discriminate on `kind`, and only the `object` branch demands the fields
573
+ * of the object. An `object` without `className` remains a defect and has its own name
574
+ * (`malformed-object`), distinct from an unreadable envelope.
575
+ */
576
+ /** The `kind` of the only branch of the topic that can open a window. */
577
577
  var OBJECT_EVENT_KIND = "object";
578
- /** Type guard, non cast: `event.data` è `unknown` finché non passa di qui. */
578
+ /** Type guard, not cast: `event.data` is `unknown` until it passes through here. */
579
579
  function isDetectionEventEnvelope(value) {
580
580
  if (typeof value !== "object" || value === null) return false;
581
581
  if (!("deviceId" in value) || typeof value.deviceId !== "number") return false;
@@ -585,7 +585,7 @@ function isDetectionEventEnvelope(value) {
585
585
  if (!Number.isFinite(value.timestamp) || value.timestamp <= 0) return false;
586
586
  return true;
587
587
  }
588
- /** I campi che SOLO il ramo object porta. Chiamata dopo la busta. */
588
+ /** The fields that ONLY the object branch carries. Called after the envelope. */
589
589
  function isObjectDetectionEventData(value) {
590
590
  if (!("className" in value) || typeof value.className !== "string") return false;
591
591
  if ("source" in value && value.source !== void 0 && typeof value.source !== "string") return false;
@@ -622,9 +622,9 @@ function qualifyObjectTrigger(data) {
622
622
  }
623
623
  //#endregion
624
624
  //#region src/recorder/object-trigger-health.ts
625
- /** Ampiezza della finestra di aggregazione. Un minuto: abbastanza corto perché
626
- * la riga arrivi entro il primo minuto di un deploy rotto, abbastanza lungo
627
- * perché a 4 eventi/s il campione sia significativo. */
625
+ /** Width of the aggregation window. One minute: short enough for
626
+ * the line to arrive within the first minute of a broken deploy, long enough
627
+ * for the sample to be significant at 4 events/s. */
628
628
  var OBJECT_TRIGGER_HEALTH_WINDOW_MS = 6e4;
629
629
  function emptyTally$2() {
630
630
  return {
@@ -660,10 +660,10 @@ var ObjectTriggerHealth = class {
660
660
  this.minSample = minSample;
661
661
  }
662
662
  /**
663
- * Registra un verdetto e, se la finestra si è chiusa con questo evento,
664
- * restituisce i report da stampare (e riapre la finestra). Nessun timer:
665
- * il feed viaggia a ~4/s, quindi la chiusura arriva sempre in tempo, e una
666
- * finestra che non si chiude è una finestra in cui non è successo nulla.
663
+ * Records a verdict and, if the window closed with this event,
664
+ * returns the reports to print (and reopens the window). No timer:
665
+ * the feed runs at ~4/s, so the close always arrives in time, and a
666
+ * window that does not close is a window in which nothing happened.
667
667
  */
668
668
  record(verdict, nowMs) {
669
669
  const reports = this.windowStartMs !== null && nowMs - this.windowStartMs >= this.windowMs ? this.drain(nowMs) : [];
@@ -694,7 +694,7 @@ var ObjectTriggerHealth = class {
694
694
  this.buckets.set(key, fresh);
695
695
  return fresh;
696
696
  }
697
- /** Chiude la finestra e restituisce un report per secchio non vuoto. */
697
+ /** Closes the window and returns one report per non-empty bucket. */
698
698
  drain(nowMs) {
699
699
  const windowMs = this.windowStartMs === null ? 0 : nowMs - this.windowStartMs;
700
700
  const out = [];
@@ -3227,29 +3227,29 @@ var macrotask$2 = () => new Promise((resolve) => {
3227
3227
  //#endregion
3228
3228
  //#region src/recorder/sensor-trigger.ts
3229
3229
  /**
3230
- * Da `DeviceStateChanged` a "quali camere devono registrare".
3231
- *
3232
- * Due responsabilità, entrambe in RAM e nessuna durevole:
3233
- * - la MEMORIA del fronte, per `(deviceId, capName)`, che rende `false→true`
3234
- * distinguibile da una ri-emissione. Il predicato è `evaluateSensorEdge`
3235
- * (`@camstack/types`) — la stessa tabella `SOURCE_CAP_ACTIVE_FIELD` del
3236
- * campanello composto e del bridge camera-sensors, non una copia;
3237
- * - l'INDICE INVERSO sorgente→camere, uno stato rispecchiato aggiornato FUORI
3238
- * dal percorso dell'evento (D49): `setBindings` è chiamato al boot dalle
3239
- * config già lette e a ogni `onConfigChanged`, che è un'RPC. Una lettura
3240
- * fallibile sul percorso del trigger potrebbe rispondere "nessuno ascolta"
3241
- * e perdere l'episodio, che è il fallimento contro cui D49 è scritta.
3242
- *
3243
- * L'ordine dei controlli è deliberato: `DeviceStateChanged` viaggia a ritmo di
3244
- * bus su tutta la flotta (batteria, la `motion` nativa di ogni camera, la
3245
- * connettività). L'indice inverso è una `Map.get` senza allocazioni e va per
3246
- * primo; il fronte — che legge due tabelle e la slice — viene dopo. È la stessa
3247
- * scelta che post-analysis scrive per la sua sottoscrizione: *"the
3230
+ * From `DeviceStateChanged` to "which cameras must record".
3231
+ *
3232
+ * Two responsibilities, both in RAM and neither durable:
3233
+ * - the edge MEMORY, per `(deviceId, capName)`, which makes `false→true`
3234
+ * distinguishable from a re-emission. The predicate is `evaluateSensorEdge`
3235
+ * (`@camstack/types`) — the same `SOURCE_CAP_ACTIVE_FIELD` table as the
3236
+ * composite doorbell and the camera-sensors bridge, not a copy;
3237
+ * - the INVERSE INDEX source→cameras, a mirrored state refreshed OFF
3238
+ * the event path (D49): `setBindings` is called at boot from the
3239
+ * already-read configs and on every `onConfigChanged`, which is an RPC. A
3240
+ * fallible read on the trigger path could answer "nobody listens"
3241
+ * and lose the episode, which is the failure D49 is written against.
3242
+ *
3243
+ * The order of the checks is deliberate: `DeviceStateChanged` travels at bus
3244
+ * rate across the whole fleet (battery, every camera's native `motion`,
3245
+ * connectivity). The inverse index is an allocation-free `Map.get` and goes
3246
+ * first; the edge — which reads two tables and the slice — comes after. It is the same
3247
+ * choice post-analysis makes for its subscription: *"the
3248
3248
  * EVENT_KIND_BY_CAP gate runs FIRST so non-sensor cap churn exits before any
3249
3249
  * async work"*.
3250
3250
  *
3251
- * Puro rispetto al mondo: nessun bus, nessun logger, nessun orologio proprio.
3252
- * Il chiamante logga il verdetto — un ramo che non accende nulla lo DICE.
3251
+ * Pure with respect to the world: no bus, no logger, no clock of its own.
3252
+ * The caller logs the verdict — a branch that lights nothing SAYS so.
3253
3253
  */
3254
3254
  /**
3255
3255
  * Type GUARD, not a cast: `event.data` is `unknown`, and a version-skewed
@@ -3298,7 +3298,7 @@ function buildSensorTriggerBindings(configs) {
3298
3298
  return index;
3299
3299
  }
3300
3300
  var SensorTriggerRouter = class {
3301
- /** sourceDeviceId → camere la cui config lo nomina. */
3301
+ /** sourceDeviceId → cameras whose config names it. */
3302
3302
  bindings = /* @__PURE__ */ new Map();
3303
3303
  /** Ultimo booleano osservato, per `${deviceId}:${capName}`. */
3304
3304
  lastValues = /* @__PURE__ */ new Map();
@@ -3311,15 +3311,15 @@ var SensorTriggerRouter = class {
3311
3311
  this.freshnessMs = options.firstSightingFreshnessMs ?? 3e4;
3312
3312
  }
3313
3313
  /**
3314
- * Sostituisce l'indice inverso. NON tocca `lastValues`: un sensore tolto e
3315
- * rimesso in una banda deve ritrovare il suo livello precedente, altrimenti
3316
- * la prima slice dopo il rientro sarebbe una "prima osservazione" e un
3317
- * contatto rimasto aperto tutto il tempo diventerebbe un fronte alto.
3314
+ * Replaces the inverse index. Does NOT touch `lastValues`: a sensor removed and
3315
+ * put back into a band must find its previous level again, otherwise
3316
+ * the first slice after the return would be a "first observation" and a
3317
+ * contact left open the whole time would become a rising edge.
3318
3318
  */
3319
3319
  setBindings(bindings) {
3320
3320
  this.bindings = bindings;
3321
3321
  }
3322
- /** Quali sorgenti l'indice contiene ora — per la riga di log al boot. */
3322
+ /** Which sources the index holds now — for the log line at boot. */
3323
3323
  listenedSourceIds() {
3324
3324
  return [...this.bindings.keys()];
3325
3325
  }
@@ -14538,10 +14538,10 @@ function eventsBandCanEverDemand(band) {
14538
14538
  return bandTriggerSources(band).length > 0 && resolveBandBufferMs(band).postMs > 0;
14539
14539
  }
14540
14540
  /**
14541
- * La chiave sotto cui una sorgente conserva le sue finestre. È anche la stringa
14542
- * che compare nei log — quattro sorgenti che aprono la stessa finestra senza
14543
- * dire chi è stato rendono impossibile rispondere a "perché questa camera
14544
- * registra tanto" — quindi è leggibile per progetto.
14541
+ * The key under which a source keeps its windows. It is also the string
14542
+ * that appears in the logs — four sources opening the same window without
14543
+ * saying who did it make it impossible to answer "why does this camera
14544
+ * record so much" — so it is readable by design.
14545
14545
  */
14546
14546
  function triggerKey(source) {
14547
14547
  switch (source.kind) {
@@ -14556,18 +14556,18 @@ function triggerKey(source) {
14556
14556
  * band never allocates a new one per call. */
14557
14557
  var SIGNAL_SOURCES = Object.freeze([{ kind: "signal" }]);
14558
14558
  /**
14559
- * LE sorgenti che questa banda ascolta — l'unico posto dove i `triggers` di
14560
- * una banda diventano `TriggerSource[]`.
14559
+ * THE sources this band listens to — the only place where a band's `triggers`
14560
+ * become `TriggerSource[]`.
14561
14561
  *
14562
- * Prima erano CINQUE copie dello stesso if-ladder motion/audio
14562
+ * Before there were FIVE copies of the same motion/audio if-ladder
14563
14563
  * (`eventsBandCanEverDemand`, `eventsBandDemanded`, `segmentRetainedForEvents`,
14564
- * `segmentMissedByMs`, `eventsWindowCloseMs`), che con quattro sorgenti
14565
- * sarebbero diventate cinque scale a quattro rami. `RecordingController`
14566
- * (`reportUnrecordable`, la riconciliazione durevole) legge la stessa lista.
14564
+ * `segmentMissedByMs`, `eventsWindowCloseMs`), which with four sources
14565
+ * would have become five four-branch ladders. `RecordingController`
14566
+ * (`reportUnrecordable`, the durable reconciliation) reads the same list.
14567
14567
  *
14568
- * Una chiave in {@link UNFED_TRIGGER_KEYS} non produce sorgenti: la banda che
14569
- * ascolta solo quella "non può mai chiedere", ed è così che il recorder la
14570
- * denuncia invece di restare zitto.
14568
+ * A key in {@link UNFED_TRIGGER_KEYS} produces no sources: the band that
14569
+ * listens only to it "can never demand", and that is how the recorder
14570
+ * reports it instead of staying silent.
14571
14571
  */
14572
14572
  function bandTriggerSources(band) {
14573
14573
  if (band.deviceSignal === true) return SIGNAL_SOURCES;
@@ -21145,15 +21145,15 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
21145
21145
  eventUnsub = null;
21146
21146
  /**
21147
21147
  * Unsubscribe handle for the `PipelineAudioInferenceResult` audio-level
21148
- * subscription — the feed `audioThresholdDbfs` was missing (D62, Traccia 0
21149
- * of `docs/design/plans/2026-08-26-trigger-detection-e-sensori.md`). Kept
21148
+ * subscription — the feed `audioThresholdDbfs` was missing (D62, Track 0
21149
+ * of `docs/design/plans/2026-08-26-detection-triggers-and-sensors.md`). Kept
21150
21150
  * separate from `eventUnsub` (a distinct bus category) but guarded and torn
21151
21151
  * down the same way, idempotent re-entry included.
21152
21152
  */
21153
21153
  audioTriggerUnsub = null;
21154
21154
  /**
21155
21155
  * Unsubscribe handle for the `PipelineAnalyticsDetectionEvent` subscription —
21156
- * the feed behind `RecordingTriggers.objectClasses` (Traccia A of the same
21156
+ * the feed behind `RecordingTriggers.objectClasses` (Track A of the same
21157
21157
  * plan). Same guard/teardown discipline as the two above.
21158
21158
  */
21159
21159
  detectionTriggerUnsub = null;
@@ -21167,7 +21167,7 @@ var RecorderV2Addon = class extends require_dist.BaseAddon {
21167
21167
  detectionTriggerHealth = new ObjectTriggerHealth();
21168
21168
  /**
21169
21169
  * Unsubscribe handle for the `DeviceStateChanged` subscription — the feed
21170
- * behind `RecordingTriggers.sensorDeviceIds` (Traccia B).
21170
+ * behind `RecordingTriggers.sensorDeviceIds` (Track B).
21171
21171
  */
21172
21172
  sensorTriggerUnsub = null;
21173
21173
  /**