@camstack/types 1.2.178 → 1.2.180

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.
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 1027 method paths across 129 capabilities.
9
+ * Coverage: 1029 method paths across 129 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {
@@ -6,8 +6,8 @@
6
6
  * system-scope cap that takes a deviceId was previously never device-filtered
7
7
  * — see the generator header).
8
8
  *
9
- * Coverage: 372 methods carry a device reference, of which
10
- * 137 are on SYSTEM-scope caps.
9
+ * Coverage: 374 methods carry a device reference, of which
10
+ * 139 are on SYSTEM-scope caps.
11
11
  *
12
12
  * Top-level fields, number arrays, and one-level arrays of objects carrying
13
13
  * a numeric device field (targets[].deviceId) are all mapped; the matcher
@@ -59,7 +59,7 @@ export interface SystemProxy {
59
59
  readonly addonSettings: Pick<InferProvider<typeof addonSettingsCapability>, 'getGlobalSettings' | 'updateGlobalSettings' | 'getIntegrationSettings'>;
60
60
  readonly addonWidgets: Pick<InferProvider<typeof addonWidgetsCapability>, 'listWidgets'>;
61
61
  readonly alerts: Pick<InferProvider<typeof alertsCapability>, 'emit' | 'update' | 'list' | 'getUnreadCount' | 'markRead' | 'markAllRead' | 'dismiss'>;
62
- readonly audioAnalyzer: Pick<InferProvider<typeof audioAnalyzerCapability>, 'analyseChunk' | 'classify' | 'isReady' | 'dispose' | 'reprobeAudioEngine'>;
62
+ readonly audioAnalyzer: Pick<InferProvider<typeof audioAnalyzerCapability>, 'analyseChunk' | 'classify' | 'attachDevice' | 'isReady' | 'dispose' | 'reprobeAudioEngine'>;
63
63
  readonly audioCodec: Pick<InferProvider<typeof audioCodecCapability>, 'listSupportedCodecs' | 'canHandle' | 'createDecodeSession' | 'createEncodeSession' | 'closeSession' | 'pushEncodedFrame' | 'pullPcm' | 'pushPcm' | 'pullEncoded' | 'flushEncode' | 'listActiveSessions'>;
64
64
  readonly backup: Pick<InferProvider<typeof backupCapability>, 'listDestinations' | 'trigger' | 'listRuns' | 'cancel' | 'list' | 'listLocations' | 'getEntries' | 'restore' | 'delete' | 'listArchives' | 'upsertDestinationPolicy' | 'previewSchedule' | 'listSchedules' | 'upsertSchedule' | 'deleteSchedule'>;
65
65
  readonly broker: Pick<InferProvider<typeof brokerCapability>, 'list' | 'get' | 'listProviders' | 'add' | 'remove' | 'testConnection' | 'getSettings' | 'setSettings' | 'getBrokerConfig' | 'getSettingsSchema' | 'testSettings' | 'publish' | 'subscribe' | 'unsubscribe' | 'getState' | 'getStatus'>;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './audio/audio-chunk-format.js';
1
2
  export type { CapabilitiesAccess } from './addon/base-addon.js';
2
3
  export { BaseAddon, normalizeAddonInitResult } from './addon/base-addon.js';
3
4
  export type { AddonRouteInvokeRequest, AddonRouteReplyEnvelope, } from './addon/build-addon-route-provider.js';
@@ -28,7 +29,7 @@ export type * from './interfaces/analysis.js';
28
29
  export * from './interfaces/analysis-persistence.js';
29
30
  export type * from './interfaces/api-responses.js';
30
31
  export type * from './interfaces/api-shared.js';
31
- export type { AudioClassificationResult, IAudioAnalyzer } from './interfaces/audio-analyzer.js';
32
+ export type { AudioAttachDeviceInput, AudioAttachDeviceResult, AudioClassificationResult, AudioDetachDeviceResult, IAudioAnalyzer, } from './interfaces/audio-analyzer.js';
32
33
  export type * from './interfaces/audio-inference-engine.js';
33
34
  export type * from './interfaces/auth.js';
34
35
  export type * from './interfaces/camera-pipeline.js';
@@ -209,6 +210,7 @@ export type { AudioCodecInfo, AudioDecodeSessionConfig, AudioEncodedChunk, Audio
209
210
  export type { StreamQuality } from './interfaces/device-capabilities/camera.js';
210
211
  export { STREAM_QUALITY_LABELS, streamQualityLabel, } from './interfaces/device-capabilities/camera.js';
211
212
  export * from './lifecycle/index.js';
213
+ export { AUDIO_SCALE_EPOCH_ISO, AUDIO_SCALE_EPOCH_MS, type AudioScaleEra, audioScaleEraOf, G711_SCALE_CORRECTION_DB, type G711Law, ituDbfsFromPreEpoch, } from './audio/audio-scale-epoch.js';
212
214
  export { audioIsFailClosed, audioKindId, audioLabelChoices, audioModeOf, audioOrDefaults, isAudioLabelSelected, NC_AUDIO_CONFIRM_HITS_DEFAULT, NC_AUDIO_CONFIRM_HITS_MAX, NC_AUDIO_CONFIRM_HITS_MIN, NC_AUDIO_CONFIRM_WINDOW_MAX_SEC, NC_AUDIO_CONFIRM_WINDOW_MIN_SEC, NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, NC_AUDIO_DB_MAX, NC_AUDIO_DB_MIN, NC_AUDIO_DB_OFFERED, NC_AUDIO_DB_STEP, NC_AUDIO_DEFAULTS, NC_AUDIO_HIT_PERCENT_MAX, NC_AUDIO_HIT_PERCENT_MIN, NC_AUDIO_SAMPLING_MAX_SEC, NC_AUDIO_SAMPLING_MIN_SEC, type NcAudioLabelChoice, type NcAudioMode, type NcAudioPatch, normalizeAudioLabel, patchAudio, toggleAudioLabel, } from './notification/audio-condition.js';
213
215
  export { isBaseConditionKey, knownValues, NC_BASE_CONDITION_KEYS, type NcBaseConditionKey, pickerForCondition, type TaxonomyGroup, type TaxonomyOption, type TaxonomyPicker, } from './notification/condition-taxonomy.js';
214
216
  export { type PreparedAction, type PreparedAttachment, type PreparedNotification, prepareNotification, type ResolvedLevel, } from './notification/degrade-engine.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_sleep = require("./sleep-B1ZwxAhh.js");
2
3
  const require_event_category = require("./event-category-BNJ9EE5B.js");
3
- const require_sleep = require("./sleep-CTHjJilA.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");
@@ -1954,6 +1954,30 @@ var noDuplicates = (values) => new Set(values).size === values.length;
1954
1954
  * sensori su UNA camera è già oltre qualunque installazione osservata.
1955
1955
  */
1956
1956
  var MAX_SENSOR_TRIGGER_DEVICES = 16;
1957
+ /**
1958
+ * What the bands editor OFFERS when an operator first ticks "audio above
1959
+ * threshold" on a band. Not a schema default: an absent `audioThresholdDbfs`
1960
+ * means the audio source is not listened to at all, and it stays absent until
1961
+ * asked for.
1962
+ *
1963
+ * It lived as a bare `-30` inline in `RecordingBandsEditor.tsx`, which is how
1964
+ * a calibration hides: unnamed, unexplained, and invisible to any guard. D460
1965
+ * gave it a name so the claim below can be read and disagreed with.
1966
+ *
1967
+ * **Not shifted at the D459 epoch.** `-30 dBFS is loud enough to be worth
1968
+ * recording` is a statement about the scale, not a number anybody read off the
1969
+ * pre-epoch decoder — there is no recorded measurement behind it, and absent
1970
+ * evidence of measurement the provenance test says a scale statement stays put
1971
+ * (the reasoning is D460's, and the same answer `SILENCE_FLOOR_DBFS` gets).
1972
+ *
1973
+ * As with `audioMarkerMinDbfs`, the epoch makes this seed START working rather
1974
+ * than become too permissive: on the old scale a PCMU camera had to reach a
1975
+ * true -18 dBFS to read -30, so the trigger was nearly dead on 19 of 25
1976
+ * cameras. A band seeded with it now fires roughly as the editor implies.
1977
+ *
1978
+ * @dbfs-era itu
1979
+ */
1980
+ var RECORDING_AUDIO_TRIGGER_OFFERED_DBFS = -30;
1957
1981
  /** Which detectors trigger an `events`-mode band. */
1958
1982
  var RecordingTriggersSchema = zod.z.object({
1959
1983
  motion: zod.z.boolean().optional(),
@@ -6993,6 +7017,51 @@ var AudioAnalysisSettingsSchema = zod.z.object({
6993
7017
  minConfidence: zod.z.number().min(0).max(1).default(.3),
6994
7018
  allowedClasses: zod.z.array(zod.z.string()).default([])
6995
7019
  });
7020
+ /**
7021
+ * `attachDevice` — the analyzer PULLS a camera's audio from the broker (D461).
7022
+ *
7023
+ * Until D461 the orchestrator drained the broker's chunk plane, accumulated
7024
+ * ~1 s windows and pushed them back out as `analyseChunk`. It neither produced
7025
+ * nor consumed the audio: the PCM crossed hub-main twice for a process that
7026
+ * only buffered it. `attachDevice` inverts the direction — the analyzer opens
7027
+ * its own `subscribeAudioChunks` against the broker and the subscriber IS the
7028
+ * decoder, so the coded G.711 bytes D455 put on the plane stay coded all the
7029
+ * way to the one expansion that feeds the model.
7030
+ *
7031
+ * The orchestrator still owns the POLICY (the `audioMode` gate, the on-motion
7032
+ * window, the per-device node assignment, the settings read) and therefore
7033
+ * still owns the attach/detach pair. It no longer owns the bytes.
7034
+ */
7035
+ var AudioAttachDeviceInputSchema = zod.z.object({
7036
+ deviceId: zod.z.number(),
7037
+ /** Broker id (`<deviceId>/<camStreamId>`) carrying this camera's audio. */
7038
+ brokerId: zod.z.string(),
7039
+ /**
7040
+ * `clusterRoles.ingestNode` — the node whose broker owns the source dial.
7041
+ * Every `streamBroker` call the attachment makes is pinned to it, exactly as
7042
+ * the orchestrator's poller pinned them before the move.
7043
+ */
7044
+ ingestNodeId: zod.z.string(),
7045
+ /**
7046
+ * Resolved once by the orchestrator at attach time, exactly as it was read
7047
+ * once per subscription before D461. The analyzer does NOT re-resolve per
7048
+ * window: a settings change re-attaches, which is what always happened.
7049
+ */
7050
+ settings: AudioAnalysisSettingsSchema
7051
+ });
7052
+ var AudioAttachDeviceResultSchema = zod.z.object({
7053
+ /** False only when the analyzer is shutting down and refused to attach. */
7054
+ attached: zod.z.boolean(),
7055
+ /**
7056
+ * True when the attachment replaced a live one for the same device. An
7057
+ * attach is idempotent by REPLACEMENT — two pollers on one camera would
7058
+ * double the broker's fanout and neither would know about the other.
7059
+ */
7060
+ replaced: zod.z.boolean()
7061
+ });
7062
+ var AudioDetachDeviceResultSchema = zod.z.object({
7063
+ /** False when no attachment existed — detach is idempotent. */
7064
+ detached: zod.z.boolean() });
6996
7065
  var AudioClassificationResultSchema = zod.z.object({
6997
7066
  labels: zod.z.array(AudioClassificationLabelSchema).readonly(),
6998
7067
  rawLabels: zod.z.array(AudioClassificationLabelSchema).readonly().optional(),
@@ -7041,6 +7110,21 @@ var audioAnalyzerCapability = {
7041
7110
  settings: AudioAnalysisSettingsSchema
7042
7111
  }), AudioAnalysisResultSchema.nullable(), { kind: "mutation" }),
7043
7112
  classify: require_sleep.method(AudioChunkInputSchema, AudioClassificationResultSchema, { timeoutMs: 3e4 }),
7113
+ /**
7114
+ * Start pulling this camera's audio from the broker, in THIS process.
7115
+ * Returns as soon as the attachment exists — the first
7116
+ * `subscribeAudioChunks` retries with backoff behind it, because the
7117
+ * broker for a camStream may not be registered yet (that tolerance moved
7118
+ * with the poller, D461). Replaces any live attachment for the device.
7119
+ */
7120
+ attachDevice: require_sleep.method(AudioAttachDeviceInputSchema, AudioAttachDeviceResultSchema, { kind: "mutation" }),
7121
+ /**
7122
+ * Stop pulling this camera's audio and release the broker subscription.
7123
+ * Idempotent. A detach that never arrives is the one failure mode this
7124
+ * split adds, which is why `AudioChunkPlane` reaps a subscription nobody
7125
+ * drains rather than trusting it (D461).
7126
+ */
7127
+ detachDevice: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), AudioDetachDeviceResultSchema, { kind: "mutation" }),
7044
7128
  isReady: require_sleep.method(zod.z.void(), zod.z.boolean()),
7045
7129
  dispose: require_sleep.method(zod.z.void(), zod.z.void(), { kind: "mutation" }),
7046
7130
  /**
@@ -15751,6 +15835,12 @@ var NcOccupancyConditionSchema = zod.z.object({
15751
15835
  * number. An operator (or a UI) that writes `60` meaning "60 decibels, quite
15752
15836
  * loud" would author a threshold NO sample can ever reach, and the rule would
15753
15837
  * look broken rather than mis-configured. The schema range rejects it instead.
15838
+ *
15839
+ * Untouched by the D459 scale correction, and structurally so: -96 dBFS is
15840
+ * where 16-bit digital silence sits on ANY decoder, right or wrong. It
15841
+ * describes the container, not a calibration made through one.
15842
+ *
15843
+ * @dbfs-era scale-bound
15754
15844
  */
15755
15845
  var NC_AUDIO_DBFS_FLOOR = -96;
15756
15846
  /**
@@ -21181,6 +21271,14 @@ var NativeCropResultSchema = zod.z.object({
21181
21271
  * set `encodeJpeg: true`; `bytes` is then absent.
21182
21272
  */
21183
21273
  jpeg: zod.z.string().optional(),
21274
+ /**
21275
+ * The SAME compressed JPEG as `jpeg`, as bytes (D462). Present instead of
21276
+ * `jpeg` when the request set `acceptJpegBytes`; a request that did not gets
21277
+ * `jpeg` exactly as before. MsgPack and the mesh leg both carry binary —
21278
+ * `bytes` above has crossed this boundary as a `Uint8Array` all along — so
21279
+ * base64 was buying nothing but a multi-megabyte string in the relay's heap.
21280
+ */
21281
+ jpegBytes: zod.z.instanceof(Uint8Array).optional(),
21184
21282
  width: zod.z.number().int().positive(),
21185
21283
  height: zod.z.number().int().positive(),
21186
21284
  /**
@@ -21247,7 +21345,14 @@ var ParkTrackFrameResultSchema = zod.z.discriminatedUnion("parked", [zod.z.objec
21247
21345
  })]);
21248
21346
  /** A retrieved parcel — the runner's own JPEG, base64 for the wire. */
21249
21347
  var ParkedTrackFrameSchema = zod.z.object({
21250
- jpeg: zod.z.string(),
21348
+ /**
21349
+ * Base64 JPEG — the pre-D462 wire. OPTIONAL since D462: a request that set
21350
+ * `acceptJpegBytes` is answered in `jpegBytes` and this is then absent.
21351
+ * Exactly one of the two is present.
21352
+ */
21353
+ jpeg: zod.z.string().optional(),
21354
+ /** The same JPEG as bytes, for a caller that declared it reads them (D462). */
21355
+ jpegBytes: zod.z.instanceof(Uint8Array).optional(),
21251
21356
  width: zod.z.number().int().positive(),
21252
21357
  height: zod.z.number().int().positive(),
21253
21358
  /** The frame instant the parcel shows (the caller's clock, echoed back). */
@@ -22086,6 +22191,13 @@ var pipelineRunnerCapability = {
22086
22191
  bbox: NativeCropBboxSchema,
22087
22192
  maxWidth: zod.z.number().int().positive().optional(),
22088
22193
  /**
22194
+ * The caller reads a `Uint8Array` (D462). When set, a JPEG answer comes
22195
+ * back in `jpegBytes` instead of base64 `jpeg`. Absent means the old
22196
+ * wire — never assume consent: a pre-D462 caller parses the field as
22197
+ * base64 and bytes would decode to garbage rather than fail.
22198
+ */
22199
+ acceptJpegBytes: zod.z.boolean().optional(),
22200
+ /**
22089
22201
  * When `true`, the runner encodes the resolved crop to JPEG ON THE
22090
22202
  * OWNING NODE and returns it in `jpeg` (base64) INSTEAD of raw `bytes`.
22091
22203
  * Callers set this for CROSS-NODE fetches (`handle.nodeId` is a remote
@@ -22258,7 +22370,14 @@ var pipelineRunnerCapability = {
22258
22370
  getParkedTrackFrame: require_sleep.method(zod.z.object({
22259
22371
  deviceId: zod.z.number(),
22260
22372
  trackId: zod.z.string(),
22261
- kind: ParkedFrameKindSchema
22373
+ kind: ParkedFrameKindSchema,
22374
+ /**
22375
+ * The caller reads a `Uint8Array` (D462). When set, a JPEG answer comes
22376
+ * back in `jpegBytes` instead of base64 `jpeg`. Absent means the old
22377
+ * wire — never assume consent: a pre-D462 caller parses the field as
22378
+ * base64 and bytes would decode to garbage rather than fail.
22379
+ */
22380
+ acceptJpegBytes: zod.z.boolean().optional()
22262
22381
  }), ParkedTrackFrameSchema.nullable()),
22263
22382
  /**
22264
22383
  * Drop every parcel of a track — the hub closed it. Idempotent. A track
@@ -42285,12 +42404,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
42285
42404
  addonId: null,
42286
42405
  access: "create"
42287
42406
  },
42407
+ "audioAnalyzer.attachDevice": {
42408
+ capName: "audio-analyzer",
42409
+ capScope: "system",
42410
+ addonId: null,
42411
+ access: "create"
42412
+ },
42288
42413
  "audioAnalyzer.classify": {
42289
42414
  capName: "audio-analyzer",
42290
42415
  capScope: "system",
42291
42416
  addonId: null,
42292
42417
  access: "view"
42293
42418
  },
42419
+ "audioAnalyzer.detachDevice": {
42420
+ capName: "audio-analyzer",
42421
+ capScope: "system",
42422
+ addonId: null,
42423
+ access: "create"
42424
+ },
42294
42425
  "audioAnalyzer.dispose": {
42295
42426
  capName: "audio-analyzer",
42296
42427
  capScope: "system",
@@ -48403,11 +48534,21 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
48403
48534
  form: "single",
48404
48535
  optional: false
48405
48536
  }],
48537
+ "audioAnalyzer.attachDevice": [{
48538
+ name: "deviceId",
48539
+ form: "single",
48540
+ optional: false
48541
+ }],
48406
48542
  "audioAnalyzer.classify": [{
48407
48543
  name: "deviceId",
48408
48544
  form: "single",
48409
48545
  optional: true
48410
48546
  }],
48547
+ "audioAnalyzer.detachDevice": [{
48548
+ name: "deviceId",
48549
+ form: "single",
48550
+ optional: false
48551
+ }],
48411
48552
  "audioMetrics.getCurrentSnapshot": [{
48412
48553
  name: "deviceId",
48413
48554
  form: "single",
@@ -50255,7 +50396,9 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
50255
50396
  var SYSTEM_SCOPE_DEVICE_METHODS = [
50256
50397
  "addonSettings.getDeviceSettings",
50257
50398
  "addonSettings.updateDeviceSettings",
50399
+ "audioAnalyzer.attachDevice",
50258
50400
  "audioAnalyzer.classify",
50401
+ "audioAnalyzer.detachDevice",
50259
50402
  "decoder.createSession",
50260
50403
  "deviceAdoption.release",
50261
50404
  "deviceAdoption.resync",
@@ -50917,6 +51060,7 @@ function createSystemProxy(api) {
50917
51060
  audioAnalyzer: {
50918
51061
  analyseChunk: (input) => dispatch("audioAnalyzer", "analyseChunk", "mutation", input),
50919
51062
  classify: (input) => dispatch("audioAnalyzer", "classify", "query", input),
51063
+ attachDevice: (input) => dispatch("audioAnalyzer", "attachDevice", "mutation", input),
50920
51064
  isReady: (input) => dispatch("audioAnalyzer", "isReady", "query", input),
50921
51065
  dispose: (input) => dispatch("audioAnalyzer", "dispose", "mutation", input),
50922
51066
  reprobeAudioEngine: (input) => dispatch("audioAnalyzer", "reprobeAudioEngine", "mutation", input)
@@ -51490,6 +51634,112 @@ function createSystemProxy(api) {
51490
51634
  };
51491
51635
  }
51492
51636
  //#endregion
51637
+ //#region src/audio/audio-scale-epoch.ts
51638
+ /**
51639
+ * The instant the audio amplitude scale changed, and by how much (D459).
51640
+ *
51641
+ * ## What happened
51642
+ *
51643
+ * Until this epoch the native RTP G.711 decoder that feeds the audio chunk
51644
+ * plane — `stream-broker/rtsp/audio-rtp-decoder.ts`, the PCM the audio
51645
+ * CLASSIFIER and the dBFS level meter read — decoded μ-law at exactly 1/4 of
51646
+ * its ITU-T G.711 value and A-law at exactly −1/8 (magnitude *and* sign). The
51647
+ * WebRTC/Opus egress decoded the same bytes correctly in `g711-mulaw.ts`, so
51648
+ * **the audio an operator HEARD was right and the audio the detector MEASURED
51649
+ * was 4–8× quieter**, which is why it survived so long.
51650
+ *
51651
+ * Non-G.711 sources — AAC and Opus, decoded through the `audio-codec` cap —
51652
+ * were always on the ITU-T scale and are NOT affected. Nothing here should
51653
+ * ever be applied to them.
51654
+ *
51655
+ * ## What this module is for, and what it is NOT for
51656
+ *
51657
+ * It is the ONE place the two numbers live, so no caller re-derives them and
51658
+ * no second constant can disagree. It exists for two readers:
51659
+ *
51660
+ * - **A person re-calibrating a threshold.** Every absolute-dBFS gate
51661
+ * authored before the epoch against a native G.711 camera is now that many
51662
+ * dB too permissive: `NcAudioCondition.dbThreshold`,
51663
+ * `RecordingTriggers.audioThresholdDbfs`, `audioMarkerMinDbfs`, and the
51664
+ * viewer's per-grid `audioDb`.
51665
+ * - **Anything drawing a level across the epoch.** A graph that spans it
51666
+ * shows a step of 12.04 dB (PCMU) or 18.06 dB (PCMA) on the affected
51667
+ * cameras. `audioScaleEraOf` is how a reader says which side a sample is
51668
+ * on instead of leaving the step unexplained.
51669
+ *
51670
+ * **It is NOT a gain to apply to a sample.** Stored history is NOT rewritten
51671
+ * and live audio is NOT re-scaled: the decoder is correct now, and a second
51672
+ * authority that "fixes up" a number the decoder already got right is the
51673
+ * defect again with an extra place to argue with. Nor can history be
51674
+ * retro-corrected honestly — `DecodedAudioChunk` never carried the codec, so
51675
+ * a stored `AudioEvent.dbfs` does not say which of the two factors (or
51676
+ * neither) applied to it. Guessing per row is exactly the move that wrote the
51677
+ * original defect.
51678
+ */
51679
+ /** Spelling of the epoch, so the millisecond constant is readable. */
51680
+ var AUDIO_SCALE_EPOCH_ISO = "2026-09-11T00:00:00.000Z";
51681
+ /**
51682
+ * The instant, in epoch milliseconds. A reading at or after it is on the
51683
+ * ITU-T scale; a reading before it, from a native G.711 camera, is 12–18 dB
51684
+ * low.
51685
+ *
51686
+ * It is the DAY, not the deploy minute, and deliberately so: the correction
51687
+ * reaches each node when that node's `addon-pipeline` is redeployed, which is
51688
+ * neither simultaneous across the fleet nor knowable from here. A reader that
51689
+ * treats the boundary as fuzzy by a few hours is reading it correctly; one
51690
+ * that treats a sample 200 ms after it as authoritative is not.
51691
+ */
51692
+ var AUDIO_SCALE_EPOCH_MS = Date.parse(AUDIO_SCALE_EPOCH_ISO);
51693
+ var G711_SCALE_CORRECTION_DB = {
51694
+ PCMU: 20 * Math.log10(4),
51695
+ PCMA: 20 * Math.log10(8)
51696
+ };
51697
+ /**
51698
+ * Restate a dBFS number that was MEASURED through the pre-epoch decoder as the
51699
+ * same intent on the ITU-T scale (D460).
51700
+ *
51701
+ * ## When this applies, and when it is the wrong thing to reach for
51702
+ *
51703
+ * An absolute-dBFS number in this repo is one of two things, and only one of
51704
+ * them converts:
51705
+ *
51706
+ * - **A statement about the scale** — "-55 dBFS is near silence", "-25 dBFS
51707
+ * is loud". It was true on the ITU-T scale before the epoch and it is true
51708
+ * after. The defect was never in the number; it was that 19 of this hub's
51709
+ * 25 cameras did not obey it. Converting such a number takes something
51710
+ * correct and makes it wrong, in order to preserve a bug.
51711
+ * - **A measurement taken through the old decoder** — a value someone read
51712
+ * off a meter that under-reported by exactly 4× (PCMU) or 8× (PCMA). It
51713
+ * describes a sound that was really {@link G711_SCALE_CORRECTION_DB} dB
51714
+ * louder. That is what this function is for.
51715
+ *
51716
+ * Telling the two apart is a question about PROVENANCE, not about arithmetic,
51717
+ * and it cannot be answered from the number. It is answered by the comment the
51718
+ * author left — which is why `scripts/check-dbfs-era.mts` makes leaving one
51719
+ * mandatory.
51720
+ *
51721
+ * ## Why a function and not a typed-in number
51722
+ *
51723
+ * `-55 + 12.04` written into a source file is, six months later, completely
51724
+ * indistinguishable from a threshold somebody simply preferred. Calling this
51725
+ * keeps the derivation, the law, and the original measurement all visible at
51726
+ * the call site, so a future reader can disagree with the *premise* instead of
51727
+ * having to reverse-engineer the sum.
51728
+ *
51729
+ * **This is not a runtime gain.** It converts an authored CONSTANT once, where
51730
+ * it is declared. It must never be applied to a live sample or a stored
51731
+ * `AudioEvent.dbfs`: the decoder is correct now, and a second authority
51732
+ * adjusting numbers the decoder already got right is the original defect with
51733
+ * an extra place to argue with (D459).
51734
+ */
51735
+ function ituDbfsFromPreEpoch(law, authoredDbfs) {
51736
+ return authoredDbfs + G711_SCALE_CORRECTION_DB[law];
51737
+ }
51738
+ function audioScaleEraOf(timestampMs) {
51739
+ if (!Number.isFinite(timestampMs) || timestampMs <= 0) return "unknown";
51740
+ return timestampMs >= AUDIO_SCALE_EPOCH_MS ? "itu" : "pre-itu";
51741
+ }
51742
+ //#endregion
51493
51743
  //#region src/notification/audio-condition.ts
51494
51744
  /**
51495
51745
  * audio-condition — the authoring logic behind the sustained-sound condition
@@ -51547,8 +51797,21 @@ function audioKindId(macroId) {
51547
51797
  }
51548
51798
  /** Level bounds, in dBFS. Negative-going: the ceiling is full scale. */
51549
51799
  var NC_AUDIO_DB_MIN = -96;
51800
+ /**
51801
+ * Full scale. The ceiling of dBFS itself on any decoder, not a calibration —
51802
+ * untouched by D459 and untouchable by anything like it.
51803
+ *
51804
+ * @dbfs-era scale-bound
51805
+ */
51550
51806
  var NC_AUDIO_DB_MAX = 0;
51551
- /** Step of the level control — 3 dB is one perceptual notch, not one unit. */
51807
+ /**
51808
+ * Step of the level control — 3 dB is one perceptual notch, not one unit.
51809
+ *
51810
+ * A step between two levels, so it survives any uniform scale change: 3 dB is
51811
+ * the same interval before and after the D459 epoch.
51812
+ *
51813
+ * @dbfs-era relative
51814
+ */
51552
51815
  var NC_AUDIO_DB_STEP = 3;
51553
51816
  /**
51554
51817
  * Offered when the operator turns the level filter ON. NOT a schema default:
@@ -51563,8 +51826,42 @@ var NC_AUDIO_DB_STEP = 3;
51563
51826
  * operator turned the filter on for, and the rule looks broken rather than
51564
51827
  * mis-seeded. The offered floor is the QUIET end of that measured band; the
51565
51828
  * editor says which way the number runs rather than leaving it to be inferred.
51566
- */
51567
- var NC_AUDIO_DB_OFFERED = -55;
51829
+ *
51830
+ * ⚠ **That measurement was taken on the pre-D459 scale, and D460 moved this
51831
+ * constant because of it.** Until {@link AUDIO_SCALE_EPOCH_MS} the native RTP
51832
+ * G.711 decoder reported levels 12.04 dB low (PCMU) or 18.06 dB low (PCMA).
51833
+ *
51834
+ * This is the ONE absolute-dBFS number in the repo that is a MEASUREMENT
51835
+ * rather than a statement about the scale, and it is the only one with a
51836
+ * recorded provenance — which is exactly why it is also the only shared
51837
+ * constant that converts. Three things agree that the camera it was measured
51838
+ * on spoke PCMU:
51839
+ *
51840
+ * - 19 of this hub's 25 cameras with audio are PCMU, 5 AAC, 1 L16, 0 PCMA
51841
+ * (codec census over 10 h of `audio: track detected`, 2026-09-11);
51842
+ * - **-55..-26 dBFS is not a close cry on a true scale.** -26 is a quiet
51843
+ * room. Read as a PCMU measurement the band is -43..-14, which is exactly
51844
+ * where a cry a metre from the microphone sits;
51845
+ * - there is no PCMA camera on this installation, so the only candidate
51846
+ * correction is mu-law's.
51847
+ *
51848
+ * So the band is restated, not re-guessed: the offered floor stays "the QUIET
51849
+ * end of the measured band", and the band moves to where it was always really
51850
+ * sitting. {@link ituDbfsFromPreEpoch} does the arithmetic so the derivation
51851
+ * survives in the source instead of becoming a number somebody preferred.
51852
+ *
51853
+ * `Math.round` is the control's own granularity, not a fudge: the editor's
51854
+ * level field is an integer stepper and `patchAudio` clamps through
51855
+ * `clampInt`, so a fractional seed could never reach storage anyway.
51856
+ *
51857
+ * **This changes no existing rule.** It is what an editor OFFERS when the
51858
+ * level filter is switched on with nothing stored; every `dbThreshold` an
51859
+ * operator already authored is untouched, and those are a separate question
51860
+ * — see D460 on why they are declared rather than migrated.
51861
+ *
51862
+ * @dbfs-era pre-itu-pcmu
51863
+ */
51864
+ var NC_AUDIO_DB_OFFERED = Math.round(ituDbfsFromPreEpoch("PCMU", -55));
51568
51865
  var NC_AUDIO_HIT_PERCENT_MIN = 1;
51569
51866
  var NC_AUDIO_HIT_PERCENT_MAX = 100;
51570
51867
  var NC_AUDIO_SAMPLING_MIN_SEC = 1;
@@ -51656,7 +51953,7 @@ function patchAudio(current, patch) {
51656
51953
  const hasLabels = wantedLabels !== void 0 && wantedLabels.length > 0;
51657
51954
  const mode = patch.mode ?? (has(patch, "labels") && hasLabels ? "label" : has(patch, "dbThreshold") && wantedDb !== void 0 ? "level" : hasLabels ? "label" : wantedDb !== void 0 ? "level" : null);
51658
51955
  const labels = mode === "label" && hasLabels ? wantedLabels : void 0;
51659
- const dbThreshold = mode !== "level" ? void 0 : wantedDb ?? (patch.mode === "level" ? -55 : void 0);
51956
+ const dbThreshold = mode !== "level" ? void 0 : wantedDb ?? (patch.mode === "level" ? NC_AUDIO_DB_OFFERED : void 0);
51660
51957
  return {
51661
51958
  ...labels !== void 0 ? { labels: [...labels] } : {},
51662
51959
  ...dbThreshold !== void 0 ? { dbThreshold: clampInt(dbThreshold, NC_AUDIO_DB_MIN, 0) } : {},
@@ -52342,7 +52639,7 @@ var NC_OCCUPANCY_DEFAULTS = {
52342
52639
  var NC_AUDIO_SEED = {
52343
52640
  hitPercent: NC_AUDIO_DEFAULTS.hitPercent,
52344
52641
  samplingSeconds: NC_AUDIO_DEFAULTS.samplingSeconds,
52345
- dbThreshold: -55
52642
+ dbThreshold: NC_AUDIO_DB_OFFERED
52346
52643
  };
52347
52644
  function ruleSeedForSection(section) {
52348
52645
  switch (section) {
@@ -55440,8 +55737,11 @@ exports.APPLE_SA_TO_MACRO = APPLE_SA_TO_MACRO;
55440
55737
  exports.ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT = ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT;
55441
55738
  exports.AUDIO_ANALYSIS_CAP_NAME = AUDIO_ANALYSIS_CAP_NAME;
55442
55739
  exports.AUDIO_BACKEND_CHOICES = AUDIO_BACKEND_CHOICES;
55740
+ exports.AUDIO_CHUNK_FORMATS = require_sleep.AUDIO_CHUNK_FORMATS;
55443
55741
  exports.AUDIO_MACRO_LABELS = AUDIO_MACRO_LABELS;
55444
55742
  exports.AUDIO_PRESETS = require_canonical_hash.AUDIO_PRESETS;
55743
+ exports.AUDIO_SCALE_EPOCH_ISO = AUDIO_SCALE_EPOCH_ISO;
55744
+ exports.AUDIO_SCALE_EPOCH_MS = AUDIO_SCALE_EPOCH_MS;
55445
55745
  exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
55446
55746
  exports.AccessoryKind = AccessoryKind;
55447
55747
  exports.AddBrokerInputSchema = AddBrokerInputSchema;
@@ -55481,12 +55781,16 @@ exports.AttachmentMediaTypeSchema = AttachmentMediaTypeSchema;
55481
55781
  exports.AttachmentSchema = AttachmentSchema;
55482
55782
  exports.AudioAnalysisResultSchema = AudioAnalysisResultSchema;
55483
55783
  exports.AudioAnalysisSettingsSchema = AudioAnalysisSettingsSchema;
55784
+ exports.AudioAttachDeviceInputSchema = AudioAttachDeviceInputSchema;
55785
+ exports.AudioAttachDeviceResultSchema = AudioAttachDeviceResultSchema;
55786
+ exports.AudioChunkFormatSchema = require_sleep.AudioChunkFormatSchema;
55484
55787
  exports.AudioChunkInputSchema = AudioChunkInputSchema;
55485
55788
  exports.AudioClassSummarySchema = AudioClassSummarySchema;
55486
55789
  exports.AudioClassificationLabelSchema = AudioClassificationLabelSchema;
55487
55790
  exports.AudioClassificationResultSchema = AudioClassificationResultSchema;
55488
55791
  exports.AudioCodecInfoSchema = AudioCodecInfoSchema;
55489
55792
  exports.AudioDecodeSessionConfigSchema = AudioDecodeSessionConfigSchema;
55793
+ exports.AudioDetachDeviceResultSchema = AudioDetachDeviceResultSchema;
55490
55794
  exports.AudioEncodeSchema = AudioEncodeSchema;
55491
55795
  exports.AudioEncodeSessionConfigSchema = AudioEncodeSessionConfigSchema;
55492
55796
  exports.AudioEncodedChunkSchema = AudioEncodedChunkSchema;
@@ -55800,6 +56104,7 @@ exports.FrameHandleSchema = require_sleep.FrameHandleSchema;
55800
56104
  exports.FrameInputSchema = FrameInputSchema;
55801
56105
  exports.FrameLazyCountersSchema = FrameLazyCountersSchema;
55802
56106
  exports.FrameLazyMetricsSchema = FrameLazyMetricsSchema;
56107
+ exports.G711_SCALE_CORRECTION_DB = G711_SCALE_CORRECTION_DB;
55803
56108
  exports.GasStatusSchema = GasStatusSchema;
55804
56109
  exports.GetLoggingSettingsInputSchema = GetLoggingSettingsInputSchema;
55805
56110
  exports.GetStreamWithCodecInputSchema = GetStreamWithCodecInputSchema;
@@ -56188,6 +56493,7 @@ exports.REACHABILITY_FAILURES_TO_OFFLINE = REACHABILITY_FAILURES_TO_OFFLINE;
56188
56493
  exports.REACHABILITY_POLL_INTERVAL_MS = REACHABILITY_POLL_INTERVAL_MS;
56189
56494
  exports.REACHABILITY_PROBE_TIMEOUT_MS = REACHABILITY_PROBE_TIMEOUT_MS;
56190
56495
  exports.RECOGNITION_TYPES = RECOGNITION_TYPES;
56496
+ exports.RECORDING_AUDIO_TRIGGER_OFFERED_DBFS = RECORDING_AUDIO_TRIGGER_OFFERED_DBFS;
56191
56497
  exports.RECORDING_EXPORT_MAX_READ_BYTES = RECORDING_EXPORT_MAX_READ_BYTES;
56192
56498
  exports.RECORDING_TIMELINE_BATCH_MAX = RECORDING_TIMELINE_BATCH_MAX;
56193
56499
  exports.REDACTED_SECRET = REDACTED_SECRET;
@@ -56539,6 +56845,7 @@ exports.asString = require_sleep.asString;
56539
56845
  exports.assertTimelapseCadences = assertTimelapseCadences;
56540
56846
  exports.audioAnalysisCapability = audioAnalysisCapability;
56541
56847
  exports.audioAnalyzerCapability = audioAnalyzerCapability;
56848
+ exports.audioChunkBytesPerSample = require_sleep.audioChunkBytesPerSample;
56542
56849
  exports.audioCodecCapability = audioCodecCapability;
56543
56850
  exports.audioIsFailClosed = audioIsFailClosed;
56544
56851
  exports.audioKindId = audioKindId;
@@ -56547,6 +56854,7 @@ exports.audioMetricsCapability = audioMetricsCapability;
56547
56854
  exports.audioModeOf = audioModeOf;
56548
56855
  exports.audioOrDefaults = audioOrDefaults;
56549
56856
  exports.audioPlanFromEncodeProfile = require_canonical_hash.audioPlanFromEncodeProfile;
56857
+ exports.audioScaleEraOf = audioScaleEraOf;
56550
56858
  exports.authProviderCapability = authProviderCapability;
56551
56859
  exports.autoAssignProfiles = autoAssignProfiles;
56552
56860
  exports.automationControlCapability = automationControlCapability;
@@ -56674,6 +56982,7 @@ exports.eventEmitterCapability = eventEmitterCapability;
56674
56982
  exports.eventsCapability = eventsCapability;
56675
56983
  exports.evictionPolicyForMode = evictionPolicyForMode;
56676
56984
  exports.evictionPolicyOfLocation = evictionPolicyOfLocation;
56985
+ exports.expandAudioChunkToF32le = require_sleep.expandAudioChunkToF32le;
56677
56986
  exports.expandCapMethods = require_sleep.expandCapMethods;
56678
56987
  exports.extractNestedAddonId = extractNestedAddonId;
56679
56988
  exports.extractSourceInfoFromMetadata = extractSourceInfoFromMetadata;
@@ -56711,6 +57020,7 @@ exports.intercomCapability = intercomCapability;
56711
57020
  exports.invocationFromEncodeProfile = require_canonical_hash.invocationFromEncodeProfile;
56712
57021
  exports.isAgentOnlyPlacement = isAgentOnlyPlacement;
56713
57022
  exports.isArrayOutputSchema = isArrayOutputSchema;
57023
+ exports.isAudioChunkFormat = require_sleep.isAudioChunkFormat;
56714
57024
  exports.isAudioLabelSelected = isAudioLabelSelected;
56715
57025
  exports.isAudioRule = isAudioRule;
56716
57026
  exports.isBaseConditionKey = isBaseConditionKey;
@@ -56737,6 +57047,7 @@ exports.isSourceCap = isSourceCap;
56737
57047
  exports.isStorageLocationMode = isStorageLocationMode;
56738
57048
  exports.isSystemDelivery = isSystemDelivery;
56739
57049
  exports.isVoidInput = isVoidInput;
57050
+ exports.ituDbfsFromPreEpoch = ituDbfsFromPreEpoch;
56740
57051
  exports.jobKindSchema = jobKindSchema;
56741
57052
  exports.kebabToCamel = kebabToCamel;
56742
57053
  exports.knownValues = knownValues;