@camstack/types 1.2.158 → 1.2.160

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.
package/dist/addon.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-b8kBSOTT.js");
3
- const require_sleep = require("./sleep-BGiOgxye.js");
3
+ const require_sleep = require("./sleep-DDIFZGbc.js");
4
4
  const require_err_msg = require("./err-msg-COpsHMw2.js");
5
5
  //#region src/generated/cap-input-defaults.ts
6
6
  /**
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as EventCategory } from "./event-category-zAv7pMUz.mjs";
2
- import { D as adminUiCapability, E as DeviceType, G as scopeKey, H as ReadinessTimeoutError, M as expandCapMethods, R as nodePin, S as DEVICE_CHILDREN_BATCH_MAX, V as ReadinessRegistry, _t as DATAPLANE_SECRET_HEADER, a as asJsonObject, b as deviceOpsCapability, bt as BaseAddon, d as BOOT_RECOVERY_BACKOFF_MS, f as DEVICE_SCOPED_CAPS, h as createEventBusSliceSource, m as createDeviceProxy, p as isDeviceScopedCap, s as asString, t as sleep, u as parseJsonUnknown, wt as emitReadiness, x as viewerUiCapability, xt as normalizeAddonInitResult, yt as DisposerChain, z as readNodePin } from "./sleep-IaOr7P-z.mjs";
2
+ import { D as adminUiCapability, E as DeviceType, G as scopeKey, H as ReadinessTimeoutError, M as expandCapMethods, R as nodePin, S as DEVICE_CHILDREN_BATCH_MAX, V as ReadinessRegistry, _t as DATAPLANE_SECRET_HEADER, a as asJsonObject, b as deviceOpsCapability, bt as BaseAddon, d as BOOT_RECOVERY_BACKOFF_MS, f as DEVICE_SCOPED_CAPS, h as createEventBusSliceSource, m as createDeviceProxy, p as isDeviceScopedCap, s as asString, t as sleep, u as parseJsonUnknown, wt as emitReadiness, x as viewerUiCapability, xt as normalizeAddonInitResult, yt as DisposerChain, z as readNodePin } from "./sleep-Cfij6Jj9.mjs";
3
3
  import { t as errMsg } from "./err-msg-IQTHeDzc.mjs";
4
4
  //#region src/generated/cap-input-defaults.ts
5
5
  /**
@@ -320,11 +320,14 @@ declare const CameraAudioStatusSchema: z.ZodObject<{
320
320
  nodeId: z.ZodString;
321
321
  enabled: z.ZodBoolean;
322
322
  }, z.core.$strip>;
323
- /** Recording mode enum (matches recording cap). */
323
+ /** Recording mode — THE one definition, shared with the `recording` cap. A
324
+ * copy of the list is how `on-device-decision` could have reached the recorder
325
+ * and not this status block. */
324
326
  declare const CameraRecordingModeSchema: z.ZodEnum<{
325
327
  off: "off";
326
328
  events: "events";
327
329
  continuous: "continuous";
330
+ "on-device-decision": "on-device-decision";
328
331
  }>;
329
332
  /** Recording block — null when no recording cap is active for this device. */
330
333
  declare const CameraRecordingStatusSchema: z.ZodObject<{
@@ -332,6 +335,7 @@ declare const CameraRecordingStatusSchema: z.ZodObject<{
332
335
  off: "off";
333
336
  events: "events";
334
337
  continuous: "continuous";
338
+ "on-device-decision": "on-device-decision";
335
339
  }>;
336
340
  active: z.ZodBoolean;
337
341
  storageBytes: z.ZodNumber;
@@ -501,6 +505,7 @@ declare const CameraStatusSchema: z.ZodObject<{
501
505
  off: "off";
502
506
  events: "events";
503
507
  continuous: "continuous";
508
+ "on-device-decision": "on-device-decision";
504
509
  }>;
505
510
  active: z.ZodBoolean;
506
511
  storageBytes: z.ZodNumber;
@@ -1470,6 +1475,7 @@ export declare const pipelineOrchestratorCapability: {
1470
1475
  off: "off";
1471
1476
  events: "events";
1472
1477
  continuous: "continuous";
1478
+ "on-device-decision": "on-device-decision";
1473
1479
  }>;
1474
1480
  active: z.ZodBoolean;
1475
1481
  storageBytes: z.ZodNumber;
@@ -1605,6 +1611,7 @@ export declare const pipelineOrchestratorCapability: {
1605
1611
  off: "off";
1606
1612
  events: "events";
1607
1613
  continuous: "continuous";
1614
+ "on-device-decision": "on-device-decision";
1608
1615
  }>;
1609
1616
  active: z.ZodBoolean;
1610
1617
  storageBytes: z.ZodNumber;
@@ -17,6 +17,7 @@ export declare const RecordingStatusSchema: z.ZodObject<{
17
17
  off: "off";
18
18
  events: "events";
19
19
  continuous: "continuous";
20
+ "on-device-decision": "on-device-decision";
20
21
  }>;
21
22
  nodeId: z.ZodString;
22
23
  storageBytes: z.ZodNumber;
@@ -338,6 +339,7 @@ export declare const recordingCapability: {
338
339
  off: "off";
339
340
  events: "events";
340
341
  continuous: "continuous";
342
+ "on-device-decision": "on-device-decision";
341
343
  }>;
342
344
  nodeId: z.ZodString;
343
345
  storageBytes: z.ZodNumber;
@@ -462,6 +464,7 @@ export declare const recordingCapability: {
462
464
  off: "off";
463
465
  events: "events";
464
466
  continuous: "continuous";
467
+ "on-device-decision": "on-device-decision";
465
468
  }>>;
466
469
  profiles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
467
470
  high: "high";
@@ -486,11 +489,11 @@ export declare const recordingCapability: {
486
489
  animal: "animal";
487
490
  }>>>;
488
491
  sensorDeviceIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
489
- deviceSignal: z.ZodOptional<z.ZodBoolean>;
490
492
  }, z.core.$strip>>;
491
493
  preBufferSec: z.ZodOptional<z.ZodNumber>;
492
494
  postBufferSec: z.ZodOptional<z.ZodNumber>;
493
495
  }, z.core.$strip>>>;
496
+ deviceDecision: z.ZodOptional<z.ZodBoolean>;
494
497
  retention: z.ZodOptional<z.ZodObject<{
495
498
  maxAgeDays: z.ZodOptional<z.ZodNumber>;
496
499
  maxSizeGb: z.ZodOptional<z.ZodNumber>;
@@ -564,6 +567,7 @@ export declare const recordingCapability: {
564
567
  off: "off";
565
568
  events: "events";
566
569
  continuous: "continuous";
570
+ "on-device-decision": "on-device-decision";
567
571
  }>>;
568
572
  profiles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
569
573
  high: "high";
@@ -588,11 +592,11 @@ export declare const recordingCapability: {
588
592
  animal: "animal";
589
593
  }>>>;
590
594
  sensorDeviceIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
591
- deviceSignal: z.ZodOptional<z.ZodBoolean>;
592
595
  }, z.core.$strip>>;
593
596
  preBufferSec: z.ZodOptional<z.ZodNumber>;
594
597
  postBufferSec: z.ZodOptional<z.ZodNumber>;
595
598
  }, z.core.$strip>>>;
599
+ deviceDecision: z.ZodOptional<z.ZodBoolean>;
596
600
  retention: z.ZodOptional<z.ZodObject<{
597
601
  maxAgeDays: z.ZodOptional<z.ZodNumber>;
598
602
  maxSizeGb: z.ZodOptional<z.ZodNumber>;
@@ -604,6 +608,7 @@ export declare const recordingCapability: {
604
608
  off: "off";
605
609
  events: "events";
606
610
  continuous: "continuous";
611
+ "on-device-decision": "on-device-decision";
607
612
  }>>;
608
613
  profiles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
609
614
  high: "high";
@@ -628,11 +633,11 @@ export declare const recordingCapability: {
628
633
  animal: "animal";
629
634
  }>>>;
630
635
  sensorDeviceIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
631
- deviceSignal: z.ZodOptional<z.ZodBoolean>;
632
636
  }, z.core.$strip>>;
633
637
  preBufferSec: z.ZodOptional<z.ZodNumber>;
634
638
  postBufferSec: z.ZodOptional<z.ZodNumber>;
635
639
  }, z.core.$strip>>>;
640
+ deviceDecision: z.ZodOptional<z.ZodBoolean>;
636
641
  retention: z.ZodOptional<z.ZodObject<{
637
642
  maxAgeDays: z.ZodOptional<z.ZodNumber>;
638
643
  maxSizeGb: z.ZodOptional<z.ZodNumber>;
@@ -649,6 +654,7 @@ export declare const recordingCapability: {
649
654
  off: "off";
650
655
  events: "events";
651
656
  continuous: "continuous";
657
+ "on-device-decision": "on-device-decision";
652
658
  }>;
653
659
  nodeId: z.ZodString;
654
660
  storageBytes: z.ZodNumber;
@@ -5,7 +5,7 @@
5
5
  * can answer "may a rule actuate this?" without importing the schema barrel
6
6
  * (~144MB RSS per runner, D28).
7
7
  *
8
- * Coverage: 82 device-scoped capabilities.
8
+ * Coverage: 83 device-scoped capabilities.
9
9
  */
10
10
  export declare const DEVICE_SCOPED_CAPS: ReadonlySet<string>;
11
11
  /**
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-b8kBSOTT.js");
3
- const require_sleep = require("./sleep-BGiOgxye.js");
3
+ const require_sleep = require("./sleep-DDIFZGbc.js");
4
4
  const require_canonical_hash = require("./canonical-hash-DNV8S5ET.js");
5
5
  const require_enums = require("./enums.js");
6
6
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -1228,21 +1228,21 @@ function schemaDeclaresAnyField(schema) {
1228
1228
  for (const section of sectionsOf(schema)) if (fieldsOf(section).length > 0) return true;
1229
1229
  return false;
1230
1230
  }
1231
- function isRecord$2(value) {
1231
+ function isRecord$3(value) {
1232
1232
  return typeof value === "object" && value !== null && !Array.isArray(value);
1233
1233
  }
1234
1234
  function sectionsOf(schema) {
1235
- if (!isRecord$2(schema)) return [];
1235
+ if (!isRecord$3(schema)) return [];
1236
1236
  const sections = schema["sections"];
1237
1237
  return Array.isArray(sections) ? sections : [];
1238
1238
  }
1239
1239
  function fieldsOf(node) {
1240
- if (!isRecord$2(node)) return [];
1240
+ if (!isRecord$3(node)) return [];
1241
1241
  const fields = node["fields"];
1242
1242
  return Array.isArray(fields) ? fields : [];
1243
1243
  }
1244
1244
  function walkField(field, out) {
1245
- if (!isRecord$2(field)) return;
1245
+ if (!isRecord$3(field)) return;
1246
1246
  const type = field["type"];
1247
1247
  const key = field["key"];
1248
1248
  if ((type === "password" || field["secret"] === true) && typeof key === "string" && key.length > 0) out.add(key);
@@ -1327,12 +1327,12 @@ function toStreamSourceEntry(stream) {
1327
1327
  function isString(v) {
1328
1328
  return typeof v === "string";
1329
1329
  }
1330
- function isRecord$1(v) {
1330
+ function isRecord$2(v) {
1331
1331
  return typeof v === "object" && v !== null && !Array.isArray(v);
1332
1332
  }
1333
1333
  function parseStreamEntry(v) {
1334
1334
  if (isString(v) && v.trim()) return { url: v };
1335
- if (isRecord$1(v) && isString(v["url"]) && v["url"].trim()) return {
1335
+ if (isRecord$2(v) && isString(v["url"]) && v["url"].trim()) return {
1336
1336
  url: v["url"],
1337
1337
  label: isString(v["label"]) ? v["label"] : void 0
1338
1338
  };
@@ -1972,17 +1972,29 @@ var HHMM = /^([01]\d|2[0-3]):[0-5]\d$/;
1972
1972
  /**
1973
1973
  * DERIVED per-camera storage summary — the single field cheap consumers read
1974
1974
  * (the viewer's status dot, the camera list) instead of walking `bands`:
1975
- * - `off` — no band covers the camera (or it is disabled).
1976
- * - `events` — every band records around triggers only.
1977
- * - `continuous` — at least one band records continuously.
1978
- *
1979
- * NEVER authored: the recorder stamps it from the authoritative `bands` on
1980
- * every save (`activeModeForConfig`). Writing it has no effect.
1975
+ * - `off` — no band covers the camera (or it is disabled).
1976
+ * - `events` — every band records around triggers only.
1977
+ * - `continuous` — at least one band records continuously.
1978
+ * - `on-device-decision`— the DEVICE decides: recording runs for as long as the
1979
+ * camera raises its own `recording-signal` level (a robot that cleans). There
1980
+ * is no schedule to author, because there is no hour to program — see
1981
+ * {@link RecordingConfigSchema}`.deviceDecision`.
1982
+ *
1983
+ * NEVER authored: the recorder stamps it from the authoritative intent
1984
+ * (`bands` + `deviceDecision`) on every save (`activeModeForConfig`). Writing it
1985
+ * has no effect.
1986
+ *
1987
+ * `on-device-decision` is named for WHO decides, not for how the recording is
1988
+ * requested. `on-demand` was rejected: in this repo's vocabulary a "demand" is
1989
+ * something the operator makes (the live gate is the recorder's own "demand
1990
+ * window"), and a knob whose name suggests the operator starts it while the
1991
+ * device actually does is the D62 shape — a control nobody can predict.
1981
1992
  */
1982
1993
  var RecordingStorageModeSchema = zod.z.enum([
1983
1994
  "off",
1984
1995
  "events",
1985
- "continuous"
1996
+ "continuous",
1997
+ "on-device-decision"
1986
1998
  ]);
1987
1999
  /**
1988
2000
  * Le macro classi che possono aprire una finestra di registrazione — le stesse
@@ -2041,22 +2053,7 @@ var RecordingTriggersSchema = zod.z.object({
2041
2053
  * il livello: un contatto trovato già aperto al riavvio del runner non fa
2042
2054
  * registrare.
2043
2055
  */
2044
- sensorDeviceIds: zod.z.array(zod.z.number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional(),
2045
- /**
2046
- * La camera registra per tutta la durata del SEGNALE che lei stessa
2047
- * pubblica — la cap `recording-signal` (`active: true` mentre il device è in
2048
- * funzione: un robot che pulisce). È un LIVELLO, non un fronte: la finestra
2049
- * resta aperta finché il segnale è alto e si chiude `postBufferSec` dopo la
2050
- * caduta, così una pulizia di quaranta minuti è UNA clip.
2051
- *
2052
- * Non nomina un device: il segnale è della camera stessa, tenuto fresco dal
2053
- * suo provider (D224); il recorder lo ascolta su `DeviceStateChanged` e lo
2054
- * riconcilia leggendo la slice via RPC, con un tetto oltre il quale un
2055
- * segnale mai abbassato (un evento perso) non può tenere aperta una
2056
- * registrazione (D11). Vedi `recorder/addon/band-decision.ts`
2057
- * (`holdTrigger` / `releaseTrigger`).
2058
- */
2059
- deviceSignal: zod.z.boolean().optional()
2056
+ sensorDeviceIds: zod.z.array(zod.z.number().int().positive()).min(1).max(16).refine(noDuplicates, { message: "sensorDeviceIds must not repeat a device" }).optional()
2060
2057
  });
2061
2058
  /**
2062
2059
  * Mode of a single recording band — the recorder per-band vocabulary.
@@ -2086,6 +2083,24 @@ var RecordingBandSchema = zod.z.object({
2086
2083
  end: zod.z.string().regex(HHMM),
2087
2084
  mode: RecordingBandModeSchema,
2088
2085
  triggers: RecordingBandTriggersSchema.optional(),
2086
+ /**
2087
+ * RETIRED (D381). A band no longer carries a pre-buffer of its own.
2088
+ *
2089
+ * It was the retroactive KEEP bound — which already-written segments survive
2090
+ * a trigger — and it had to be at least as wide as the widest pre-roll the
2091
+ * broker could serve, or the gate would delete the seconds the ring had just
2092
+ * handed the writer. That "at least as wide as" is the tell: it was a second
2093
+ * expression of the broker's retention, in different units, in a different
2094
+ * addon, with an invariant the operator had to maintain by hand. The keep
2095
+ * bound is now derived from the one prebuffer ceiling and there is nothing
2096
+ * left to configure here.
2097
+ *
2098
+ * The key survives in the SHAPE so a config written before that record still
2099
+ * parses and can be migrated deliberately — `RecordingBandSchema` is not
2100
+ * strict, so simply deleting it would strip an operator's value in silence on
2101
+ * the next save (the failure D380 came one commit from shipping). Nothing
2102
+ * reads it: {@link stripRetiredBandPreBufferSec} removes it on load.
2103
+ */
2089
2104
  preBufferSec: zod.z.number().min(0).optional(),
2090
2105
  postBufferSec: zod.z.number().min(0).optional()
2091
2106
  });
@@ -2103,29 +2118,56 @@ var RecordingBandSchema = zod.z.object({
2103
2118
  * `postBufferSec` must therefore exceed one segment length plus the attach
2104
2119
  * latency (ffmpeg spawn + RTSP dial), or the window closes before the first
2105
2120
  * segment is ever finalized and the camera records nothing anyway. At the 10 s
2106
- * default segment length, 30 s leaves room for two full segments. `preBufferSec`
2107
- * is retroactive only — it costs nothing at record time, it merely keeps
2108
- * already-written segments.
2109
- *
2110
- * ## Why `preBufferSec` is 15 and not one segment length
2111
- *
2112
- * It has to cover the broker's RECORDING pre-roll, which is media handed to the
2113
- * writer from BEFORE the trigger (D192/D193). If this bound were narrower, the
2114
- * keep gate would delete the very seconds the ring just supplied — two
2115
- * authorities disagreeing about the same footage, which is the failure D191 was
2116
- * written against. It is therefore set to `RECORDING_PRE_ROLL_MAX_MS` (15 s),
2117
- * the widest window the broker can be configured to serve, so an operator
2118
- * raising the cluster pre-roll can never walk past the gate silently. The
2119
- * addon-side guard is
2120
- * `packages/addon-pipeline/src/recorder/addon/__tests__/band-decision.spec.ts`.
2121
- *
2122
- * An EXPLICIT `0` is left alone: that is an operator statement, not an omission,
2123
- * and it is excluded from that invariant by name.
2124
- */
2125
- var DEFAULT_EVENTS_BAND_BUFFER_SEC = {
2126
- preBufferSec: 15,
2127
- postBufferSec: 30
2128
- };
2121
+ * default segment length, 30 s leaves room for two full segments.
2122
+ *
2123
+ * ## There is no `preBufferSec` here any more (D381)
2124
+ *
2125
+ * There used to be, at 15 s, and its whole justification was that it had to
2126
+ * cover the widest pre-roll the broker could serve. A number whose only
2127
+ * correct value is another subsystem's ceiling is not a setting; it is that
2128
+ * ceiling, restated where it can drift. The keep bound is derived from the
2129
+ * prebuffer ceiling directly now, in `band-decision.ts`, so an operator
2130
+ * raising the prebuffer cannot walk past the gate at all rather than merely
2131
+ * being warned not to.
2132
+ */
2133
+ var DEFAULT_EVENTS_BAND_BUFFER_SEC = { postBufferSec: 30 };
2134
+ /**
2135
+ * Drop the RETIRED band `preBufferSec` from a persisted recording config.
2136
+ *
2137
+ * Explicit, and it reports what it removed, because the alternative is a
2138
+ * silent strip: `RecordingBandSchema` is a plain `z.object`, so the key
2139
+ * survives a parse and disappears on the next save with no line anywhere. An
2140
+ * operator who had typed a number would find it gone and nothing would say
2141
+ * when or why.
2142
+ *
2143
+ * Nothing is carried forward: the value it held was always required to be at
2144
+ * least the broker's widest pre-roll, and the gate is now derived from exactly
2145
+ * that ceiling. Every configuration that was legal therefore keeps a keep-gate
2146
+ * at least as wide as the one it had — including an explicit `0`, which widens.
2147
+ * That is deliberate and it is the only behaviour change: a band that kept
2148
+ * nothing before its trigger now keeps up to the prebuffer ceiling of
2149
+ * already-written footage, which is media that exists either way.
2150
+ */
2151
+ function stripRetiredBandPreBufferSec(config) {
2152
+ const strippedBands = [];
2153
+ const bands = config.bands.map((band, index) => {
2154
+ if (band.preBufferSec === void 0) return band;
2155
+ strippedBands.push(index);
2156
+ const { preBufferSec: _retired, ...rest } = band;
2157
+ return rest;
2158
+ });
2159
+ if (strippedBands.length === 0) return {
2160
+ config,
2161
+ strippedBands
2162
+ };
2163
+ return {
2164
+ config: {
2165
+ ...config,
2166
+ bands
2167
+ },
2168
+ strippedBands
2169
+ };
2170
+ }
2129
2171
  /**
2130
2172
  * Adjacent recorded ranges closer than this belong to the SAME videoclip visit.
2131
2173
  *
@@ -2187,8 +2229,29 @@ var RecordingConfigSchema = zod.z.object({
2187
2229
  * "off" is the absence of a covering band, never a band value.
2188
2230
  */
2189
2231
  bands: zod.z.array(RecordingBandSchema).default([]),
2232
+ /**
2233
+ * THE device-decided intent: record for as long as the camera itself raises
2234
+ * `recording-signal` (`active: true` while the device is in function — a
2235
+ * robot that cleans). AUTHORED, unlike `mode`, and the only authored
2236
+ * recording intent that is not a band.
2237
+ *
2238
+ * It carries NO schedule on purpose. A band is an HOUR, and this feature has
2239
+ * no hour to program: the device decides. What it costs the operator is one
2240
+ * flag — "this camera can record on its own" — and what it buys is the same
2241
+ * hold/release the recorder already implements (D371, `holdTrigger` /
2242
+ * `releaseTrigger`): the window opens on the rise, stays open for the whole
2243
+ * job however long it is, and closes one pad after the fall.
2244
+ *
2245
+ * EXCLUSIVE with `bands` (below): two authorities deciding when the same
2246
+ * camera records is the D62 failure. `off` remains `enabled: false`, so an
2247
+ * operator switching this camera off is REPORTED off, never as broken.
2248
+ */
2249
+ deviceDecision: zod.z.boolean().optional(),
2190
2250
  retention: RecordingRetentionSchema.optional()
2191
- }).strict();
2251
+ }).strict().refine((config) => config.deviceDecision !== true || config.bands.length === 0, {
2252
+ message: "deviceDecision is a whole-config mode and carries no schedule: it cannot be combined with bands",
2253
+ path: ["bands"]
2254
+ });
2192
2255
  /**
2193
2256
  * Derive the {@link RecordingStorageModeSchema} summary from the authoritative
2194
2257
  * bands: `continuous` when any band records continuously, `events` when a band
@@ -2201,7 +2264,9 @@ var RecordingConfigSchema = zod.z.object({
2201
2264
  * bug this field was added to fix.
2202
2265
  */
2203
2266
  function deriveRecordingMode(config) {
2204
- if (!config.enabled || config.bands.length === 0) return "off";
2267
+ if (!config.enabled) return "off";
2268
+ if (config.deviceDecision === true) return "on-device-decision";
2269
+ if (config.bands.length === 0) return "off";
2205
2270
  if (config.bands.some((band) => band.mode === "continuous")) return "continuous";
2206
2271
  return "events";
2207
2272
  }
@@ -2229,6 +2294,85 @@ function resolveRecordingProfiles(assigned, override) {
2229
2294
  const preferred = assigned.filter((profile) => DEFAULT_RECORDING_PROFILES.includes(profile));
2230
2295
  return preferred.length > 0 ? preferred : [...assigned];
2231
2296
  }
2297
+ /** Trigger keys that survive D380 — a band carrying one of these is a real band. */
2298
+ var SURVIVING_TRIGGER_KEYS = [
2299
+ "motion",
2300
+ "audioThresholdDbfs",
2301
+ "objectClasses",
2302
+ "sensorDeviceIds"
2303
+ ];
2304
+ function isRecord$1(value) {
2305
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2306
+ }
2307
+ function listensToLegacySignal(band) {
2308
+ return isRecord$1(band) && isRecord$1(band.triggers) && band.triggers.deviceSignal === true;
2309
+ }
2310
+ function hasSurvivingTrigger(band) {
2311
+ const triggers = band.triggers;
2312
+ if (!isRecord$1(triggers)) return false;
2313
+ return SURVIVING_TRIGGER_KEYS.some((key) => triggers[key] !== void 0);
2314
+ }
2315
+ function withoutLegacySignal(band) {
2316
+ const triggers = band.triggers;
2317
+ if (!isRecord$1(triggers)) return band;
2318
+ const { deviceSignal: _retired, ...rest } = triggers;
2319
+ return {
2320
+ ...band,
2321
+ triggers: rest
2322
+ };
2323
+ }
2324
+ /**
2325
+ * Turn a config stored under D371 (`bands[].triggers.deviceSignal: true`) into
2326
+ * a D380 one (`deviceDecision: true`, no bands). Pure, total and IDEMPOTENT —
2327
+ * a row with no legacy key comes back as the SAME reference, `migrated: false`.
2328
+ *
2329
+ * ## Why the schema cannot be left to do this
2330
+ *
2331
+ * `RecordingTriggersSchema` is a non-strict `z.object`, so the retired key is
2332
+ * SILENTLY STRIPPED on read. The camera whose only trigger it was would parse
2333
+ * cleanly into an `events` band that lists nothing — "enabled, and records
2334
+ * nothing, forever", the exact shape `eventsBandCanEverDemand` exists to shout
2335
+ * about, reached without a single line in the log. On the reference hub that is
2336
+ * camera 4374, the one camera an operator had configured by hand.
2337
+ *
2338
+ * `enabled` is carried through untouched: a camera the operator had switched
2339
+ * OFF comes out of a migration still off (D62).
2340
+ */
2341
+ function migrateLegacyDeviceSignalConfig(raw) {
2342
+ const unchanged = {
2343
+ value: raw,
2344
+ migrated: false,
2345
+ strippedOnly: false
2346
+ };
2347
+ if (!isRecord$1(raw) || !Array.isArray(raw.bands)) return unchanged;
2348
+ if (!raw.bands.some(listensToLegacySignal)) return unchanged;
2349
+ const kept = [];
2350
+ for (const band of raw.bands) {
2351
+ if (!listensToLegacySignal(band) || !isRecord$1(band)) {
2352
+ kept.push(band);
2353
+ continue;
2354
+ }
2355
+ if (hasSurvivingTrigger(band)) kept.push(withoutLegacySignal(band));
2356
+ }
2357
+ if (kept.length > 0) return {
2358
+ value: {
2359
+ ...raw,
2360
+ bands: kept
2361
+ },
2362
+ migrated: false,
2363
+ strippedOnly: true
2364
+ };
2365
+ const { mode: _derived, ...rest } = raw;
2366
+ return {
2367
+ value: {
2368
+ ...rest,
2369
+ bands: [],
2370
+ deviceDecision: true
2371
+ },
2372
+ migrated: true,
2373
+ strippedOnly: false
2374
+ };
2375
+ }
2232
2376
  //#endregion
2233
2377
  //#region src/interfaces/relocate.ts
2234
2378
  /**
@@ -21503,12 +21647,10 @@ var CameraAudioStatusSchema = zod.z.object({
21503
21647
  nodeId: zod.z.string(),
21504
21648
  enabled: zod.z.boolean()
21505
21649
  });
21506
- /** Recording mode enum (matches recording cap). */
21507
- var CameraRecordingModeSchema = zod.z.enum([
21508
- "off",
21509
- "continuous",
21510
- "events"
21511
- ]);
21650
+ /** Recording mode — THE one definition, shared with the `recording` cap. A
21651
+ * copy of the list is how `on-device-decision` could have reached the recorder
21652
+ * and not this status block. */
21653
+ var CameraRecordingModeSchema = RecordingStorageModeSchema;
21512
21654
  /** Recording block — null when no recording cap is active for this device. */
21513
21655
  var CameraRecordingStatusSchema = zod.z.object({
21514
21656
  mode: CameraRecordingModeSchema,
@@ -32115,11 +32257,10 @@ var rebootCapability = {
32115
32257
  var RecordingStatusSchema = zod.z.object({
32116
32258
  deviceId: zod.z.number(),
32117
32259
  enabled: zod.z.boolean(),
32118
- activeMode: zod.z.enum([
32119
- "off",
32120
- "continuous",
32121
- "events"
32122
- ]),
32260
+ /** THE derived storage mode, from the one definition
32261
+ * (`deriveRecordingMode`) — never a second enum. A duplicated list is how
32262
+ * `on-device-decision` could have reached the recorder and not the status. */
32263
+ activeMode: RecordingStorageModeSchema,
32123
32264
  nodeId: zod.z.string(),
32124
32265
  storageBytes: zod.z.number()
32125
32266
  });
@@ -55284,6 +55425,7 @@ exports.meshNetworkCapability = meshNetworkCapability;
55284
55425
  exports.method = require_sleep.method;
55285
55426
  exports.methodAccessForHttpMethod = methodAccessForHttpMethod;
55286
55427
  exports.metricsProviderCapability = metricsProviderCapability;
55428
+ exports.migrateLegacyDeviceSignalConfig = migrateLegacyDeviceSignalConfig;
55287
55429
  exports.modelConvertCapability = modelConvertCapability;
55288
55430
  exports.modelDistributorCapability = modelDistributorCapability;
55289
55431
  exports.modelFormatForRuntime = modelFormatForRuntime;
@@ -55431,6 +55573,7 @@ exports.streamCatalogCapability = streamCatalogCapability;
55431
55573
  exports.streamParamsCapability = streamParamsCapability;
55432
55574
  exports.streamPixels = streamPixels;
55433
55575
  exports.streamQualityLabel = streamQualityLabel;
55576
+ exports.stripRetiredBandPreBufferSec = stripRetiredBandPreBufferSec;
55434
55577
  exports.subKindsOf = subKindsOf;
55435
55578
  exports.summarisePrivacyAudio = summarisePrivacyAudio;
55436
55579
  exports.summarizeEffectiveScope = summarizeEffectiveScope;