@camstack/system 1.2.108 → 1.2.110

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 (56) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +22 -3
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +22 -3
  15. package/dist/builtins/device-manager/device-row-store.d.ts +20 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  18. package/dist/builtins/hub-forwarder/index.js +1 -1
  19. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  24. package/dist/builtins/local-network/local-network.addon.js +1 -1
  25. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  26. package/dist/builtins/loki-logging/index.js +1 -1
  27. package/dist/builtins/loki-logging/index.mjs +1 -1
  28. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  30. package/dist/builtins/platform-probe/index.js +1 -1
  31. package/dist/builtins/platform-probe/index.mjs +1 -1
  32. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  34. package/dist/builtins/snapshot/index.js +1 -1
  35. package/dist/builtins/snapshot/index.mjs +1 -1
  36. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/integration-registry.d.ts +15 -12
  39. package/dist/builtins/sqlite-storage/retired-collections.d.ts +52 -0
  40. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +22 -0
  41. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +102 -2
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +102 -2
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  45. package/dist/builtins/system-config/system-config.addon.js +1 -1
  46. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  47. package/dist/builtins/winston-logging/index.js +1 -1
  48. package/dist/builtins/winston-logging/index.mjs +1 -1
  49. package/dist/{dist-Ctfhlrtl.js → dist-CQkr5Kw0.js} +31 -51
  50. package/dist/{dist-_oC_QkQA.mjs → dist-CW4Cims4.mjs} +31 -51
  51. package/dist/index.js +12 -293
  52. package/dist/index.mjs +12 -293
  53. package/dist/kernel/host-externals.d.ts +1 -1
  54. package/dist/{retired-settings-keys-pNmoHPTg.js → retired-settings-keys-DCESas5O.js} +1 -1
  55. package/dist/{retired-settings-keys-Bsjf7HQ-.mjs → retired-settings-keys-DGgOxOir.mjs} +1 -1
  56. package/package.json +1 -2
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Ctfhlrtl.js");
7
- const require_retired_settings_keys = require("../../retired-settings-keys-pNmoHPTg.js");
6
+ const require_dist = require("../../dist-CQkr5Kw0.js");
7
+ const require_retired_settings_keys = require("../../retired-settings-keys-DCESas5O.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_fs = require("node:fs");
10
10
  let node_module = require("node:module");
@@ -85,6 +85,77 @@ var SQLITE_MMAP_SIZE_BYTES = 268435456;
85
85
  */
86
86
  var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
87
87
  //#endregion
88
+ //#region src/builtins/sqlite-storage/retired-collections.ts
89
+ /**
90
+ * The dead tables.
91
+ *
92
+ * `devices` / `device_settings_kv` — the device half of the
93
+ * integration-registry ([D183](../../../../../docs/decisions/adr-0183.md)).
94
+ * The registry's INTEGRATION half is live (`integrations`,
95
+ * `integration_settings`); its device half never had a production caller and
96
+ * never had a row. The fleet lives in `device-manager:devices`, under numeric
97
+ * ids, while these tables key on `dev_NNNN` — the two id-spaces could not be
98
+ * reconciled even if something had wanted to.
99
+ */
100
+ var RETIRED_COLLECTIONS = [{
101
+ collection: "devices",
102
+ owner: "integration-registry",
103
+ reason: "the device half of the integration-registry is deleted; the fleet is `device-manager:devices` and always was (0 rows here against 974 live devices)"
104
+ }, {
105
+ collection: "device_settings_kv",
106
+ owner: "integration-registry",
107
+ reason: "per-device settings of a device table that never had a row; the live per-camera store is the device-manager row plus `addon-device-settings`"
108
+ }];
109
+ /**
110
+ * Adapt the SQLite engine to {@link RetiredCollectionStore}.
111
+ *
112
+ * The DDL itself is the engine's — it owns the handle and the memos a drop has
113
+ * to evict. This module owns only the list and the reporting.
114
+ */
115
+ function retiredCollectionStoreOf(backend) {
116
+ return { async drop(collection) {
117
+ return backend.dropRetiredTable(collection);
118
+ } };
119
+ }
120
+ /**
121
+ * Drop every retired table that is still present. Returns one entry per table
122
+ * actually dropped — empty when the schema is already clean, which is the
123
+ * steady state after the first boot that ran it.
124
+ */
125
+ async function purgeRetiredCollections(store, logger, specs = RETIRED_COLLECTIONS) {
126
+ const results = [];
127
+ for (const spec of specs) {
128
+ let dropped;
129
+ try {
130
+ dropped = await store.drop(spec.collection);
131
+ } catch (err) {
132
+ logger.warn("retired collection — drop failed, table left in place", { meta: {
133
+ addon: spec.owner,
134
+ collection: spec.collection,
135
+ error: err instanceof Error ? err.message : String(err)
136
+ } });
137
+ continue;
138
+ }
139
+ if (dropped === null) continue;
140
+ const result = {
141
+ collection: spec.collection,
142
+ owner: spec.owner,
143
+ rows: dropped.rows
144
+ };
145
+ results.push(result);
146
+ const line = "dropped a retired collection";
147
+ if (dropped.rows > 0) logger.warn(`${line} — it still held rows`, { meta: {
148
+ ...result,
149
+ reason: spec.reason
150
+ } });
151
+ else logger.info(line, { meta: {
152
+ ...result,
153
+ reason: spec.reason
154
+ } });
155
+ }
156
+ return results;
157
+ }
158
+ //#endregion
88
159
  //#region src/builtins/sqlite-storage/filter-compiler.ts
89
160
  /** Thrown by `mutate` mode. Distinct type so a caller can map it to a 400. */
90
161
  var UnsafeFilterError = class extends Error {
@@ -290,6 +361,9 @@ function isImposedBound(source) {
290
361
  }
291
362
  //#endregion
292
363
  //#region src/builtins/sqlite-storage/sqlite-settings-backend.ts
364
+ function isCountRow(raw) {
365
+ return raw !== null && typeof raw === "object" && typeof Reflect.get(raw, "n") === "number";
366
+ }
293
367
  function parseRowData(raw) {
294
368
  return require_dist.asJsonObject(require_dist.parseJsonUnknown(raw)) ?? {};
295
369
  }
@@ -1017,6 +1091,31 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
1017
1091
  }
1018
1092
  this.structuredTables.set(table, fingerprint);
1019
1093
  }
1094
+ /**
1095
+ * Drop a table nothing declares any more, reporting what went with it.
1096
+ *
1097
+ * The DDL lives here because the engine owns the handle AND the two memos
1098
+ * that outlive a table: `structuredTables` (schema fingerprint) and
1099
+ * `declaredCollections` (the column set `compileFilter` resolves against).
1100
+ * Dropping behind their backs would leave a ghost — a later declaration of
1101
+ * the same name would be memoised as "already materialised" against a table
1102
+ * that no longer exists.
1103
+ *
1104
+ * WHICH tables are retired, and the logging, are policy: they live in
1105
+ * `retired-collections.ts`. This method knows only how to drop one.
1106
+ *
1107
+ * Answers `null` when the table is not there — the steady state after the
1108
+ * first boot that ran the purge, and the case on a fresh install.
1109
+ */
1110
+ dropRetiredTable(table) {
1111
+ if (this.getDb().prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`).get(table) === void 0) return null;
1112
+ const counted = this.getDb().prepare(`SELECT COUNT(*) AS n FROM "${table}"`).get();
1113
+ const rows = isCountRow(counted) ? counted.n : 0;
1114
+ this.getDb().exec(`DROP TABLE "${table}"`);
1115
+ this.structuredTables.delete(table);
1116
+ this.declaredCollections.delete(table);
1117
+ return { rows };
1118
+ }
1020
1119
  async tableInsert(table, row) {
1021
1120
  const keys = Object.keys(row);
1022
1121
  const cols = keys.map((k) => `"${k}"`).join(", ");
@@ -1653,6 +1752,7 @@ var SqliteSettingsAddon = class extends require_dist.BaseAddon {
1653
1752
  } catch (err) {
1654
1753
  this.ctx.logger.warn("Retired-key purge failed", { meta: { error: require_dist.errMsg(err) } });
1655
1754
  }
1755
+ await purgeRetiredCollections(retiredCollectionStoreOf(this.backend), this.ctx.logger.child("RetiredCollections"));
1656
1756
  const vectorIndex = this.createVectorIndex(this.backend);
1657
1757
  await vectorIndex.loadIndexRegistry();
1658
1758
  this.vectorIndex = vectorIndex;
@@ -1,5 +1,5 @@
1
- import { A as dataStoreProviderCapability, Bt as parseJsonUnknown, _t as vectorStoreCapability, f as RUNTIME_DEFAULTS, gt as vectorDimFromBase64, j as decodeVectorBase64, kt as asJsonObject, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-_oC_QkQA.mjs";
2
- import { r as retiredKeyStoreOf, t as purgeRetiredSettingsKeys } from "../../retired-settings-keys-Bsjf7HQ-.mjs";
1
+ import { A as dataStoreProviderCapability, Bt as parseJsonUnknown, _t as vectorStoreCapability, f as RUNTIME_DEFAULTS, gt as vectorDimFromBase64, j as decodeVectorBase64, kt as asJsonObject, vt as errMsg, w as bareAddonId, yt as BaseAddon } from "../../dist-CW4Cims4.mjs";
2
+ import { r as retiredKeyStoreOf, t as purgeRetiredSettingsKeys } from "../../retired-settings-keys-DGgOxOir.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import { statSync } from "node:fs";
@@ -79,6 +79,77 @@ var SQLITE_MMAP_SIZE_BYTES = 268435456;
79
79
  */
80
80
  var SQLITE_JOURNAL_SIZE_LIMIT_BYTES = 67108864;
81
81
  //#endregion
82
+ //#region src/builtins/sqlite-storage/retired-collections.ts
83
+ /**
84
+ * The dead tables.
85
+ *
86
+ * `devices` / `device_settings_kv` — the device half of the
87
+ * integration-registry ([D183](../../../../../docs/decisions/adr-0183.md)).
88
+ * The registry's INTEGRATION half is live (`integrations`,
89
+ * `integration_settings`); its device half never had a production caller and
90
+ * never had a row. The fleet lives in `device-manager:devices`, under numeric
91
+ * ids, while these tables key on `dev_NNNN` — the two id-spaces could not be
92
+ * reconciled even if something had wanted to.
93
+ */
94
+ var RETIRED_COLLECTIONS = [{
95
+ collection: "devices",
96
+ owner: "integration-registry",
97
+ reason: "the device half of the integration-registry is deleted; the fleet is `device-manager:devices` and always was (0 rows here against 974 live devices)"
98
+ }, {
99
+ collection: "device_settings_kv",
100
+ owner: "integration-registry",
101
+ reason: "per-device settings of a device table that never had a row; the live per-camera store is the device-manager row plus `addon-device-settings`"
102
+ }];
103
+ /**
104
+ * Adapt the SQLite engine to {@link RetiredCollectionStore}.
105
+ *
106
+ * The DDL itself is the engine's — it owns the handle and the memos a drop has
107
+ * to evict. This module owns only the list and the reporting.
108
+ */
109
+ function retiredCollectionStoreOf(backend) {
110
+ return { async drop(collection) {
111
+ return backend.dropRetiredTable(collection);
112
+ } };
113
+ }
114
+ /**
115
+ * Drop every retired table that is still present. Returns one entry per table
116
+ * actually dropped — empty when the schema is already clean, which is the
117
+ * steady state after the first boot that ran it.
118
+ */
119
+ async function purgeRetiredCollections(store, logger, specs = RETIRED_COLLECTIONS) {
120
+ const results = [];
121
+ for (const spec of specs) {
122
+ let dropped;
123
+ try {
124
+ dropped = await store.drop(spec.collection);
125
+ } catch (err) {
126
+ logger.warn("retired collection — drop failed, table left in place", { meta: {
127
+ addon: spec.owner,
128
+ collection: spec.collection,
129
+ error: err instanceof Error ? err.message : String(err)
130
+ } });
131
+ continue;
132
+ }
133
+ if (dropped === null) continue;
134
+ const result = {
135
+ collection: spec.collection,
136
+ owner: spec.owner,
137
+ rows: dropped.rows
138
+ };
139
+ results.push(result);
140
+ const line = "dropped a retired collection";
141
+ if (dropped.rows > 0) logger.warn(`${line} — it still held rows`, { meta: {
142
+ ...result,
143
+ reason: spec.reason
144
+ } });
145
+ else logger.info(line, { meta: {
146
+ ...result,
147
+ reason: spec.reason
148
+ } });
149
+ }
150
+ return results;
151
+ }
152
+ //#endregion
82
153
  //#region src/builtins/sqlite-storage/filter-compiler.ts
83
154
  /** Thrown by `mutate` mode. Distinct type so a caller can map it to a 400. */
84
155
  var UnsafeFilterError = class extends Error {
@@ -284,6 +355,9 @@ function isImposedBound(source) {
284
355
  }
285
356
  //#endregion
286
357
  //#region src/builtins/sqlite-storage/sqlite-settings-backend.ts
358
+ function isCountRow(raw) {
359
+ return raw !== null && typeof raw === "object" && typeof Reflect.get(raw, "n") === "number";
360
+ }
287
361
  function parseRowData(raw) {
288
362
  return asJsonObject(parseJsonUnknown(raw)) ?? {};
289
363
  }
@@ -1011,6 +1085,31 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
1011
1085
  }
1012
1086
  this.structuredTables.set(table, fingerprint);
1013
1087
  }
1088
+ /**
1089
+ * Drop a table nothing declares any more, reporting what went with it.
1090
+ *
1091
+ * The DDL lives here because the engine owns the handle AND the two memos
1092
+ * that outlive a table: `structuredTables` (schema fingerprint) and
1093
+ * `declaredCollections` (the column set `compileFilter` resolves against).
1094
+ * Dropping behind their backs would leave a ghost — a later declaration of
1095
+ * the same name would be memoised as "already materialised" against a table
1096
+ * that no longer exists.
1097
+ *
1098
+ * WHICH tables are retired, and the logging, are policy: they live in
1099
+ * `retired-collections.ts`. This method knows only how to drop one.
1100
+ *
1101
+ * Answers `null` when the table is not there — the steady state after the
1102
+ * first boot that ran the purge, and the case on a fresh install.
1103
+ */
1104
+ dropRetiredTable(table) {
1105
+ if (this.getDb().prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`).get(table) === void 0) return null;
1106
+ const counted = this.getDb().prepare(`SELECT COUNT(*) AS n FROM "${table}"`).get();
1107
+ const rows = isCountRow(counted) ? counted.n : 0;
1108
+ this.getDb().exec(`DROP TABLE "${table}"`);
1109
+ this.structuredTables.delete(table);
1110
+ this.declaredCollections.delete(table);
1111
+ return { rows };
1112
+ }
1014
1113
  async tableInsert(table, row) {
1015
1114
  const keys = Object.keys(row);
1016
1115
  const cols = keys.map((k) => `"${k}"`).join(", ");
@@ -1647,6 +1746,7 @@ var SqliteSettingsAddon = class extends BaseAddon {
1647
1746
  } catch (err) {
1648
1747
  this.ctx.logger.warn("Retired-key purge failed", { meta: { error: errMsg(err) } });
1649
1748
  }
1749
+ await purgeRetiredCollections(retiredCollectionStoreOf(this.backend), this.ctx.logger.child("RetiredCollections"));
1650
1750
  const vectorIndex = this.createVectorIndex(this.backend);
1651
1751
  await vectorIndex.loadIndexRegistry();
1652
1752
  this.vectorIndex = vectorIndex;
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Ctfhlrtl.js");
6
+ const require_dist = require("../../dist-CQkr5Kw0.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,4 +1,4 @@
1
- import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-_oC_QkQA.mjs";
1
+ import { dt as storageCapability, ft as storageMigrationCapability, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as settingsStoreCapability, yt as BaseAddon, zt as parseJsonObject } from "../../dist-CW4Cims4.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Ctfhlrtl.js");
6
+ const require_dist = require("../../dist-CQkr5Kw0.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-_oC_QkQA.mjs";
1
+ import { It as hydrateSchema, vt as errMsg, yt as BaseAddon } from "../../dist-CW4Cims4.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-Ctfhlrtl.js");
6
+ const require_dist = require("../../dist-CQkr5Kw0.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-_oC_QkQA.mjs";
1
+ import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-CW4Cims4.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -11653,128 +11653,103 @@ var COCO_TO_MACRO = {
11653
11653
  var AUDIO_MACRO_LABELS = [
11654
11654
  {
11655
11655
  id: "speech",
11656
- name: "Speech",
11657
- icon: "🗣️"
11656
+ name: "Speech"
11658
11657
  },
11659
11658
  {
11660
11659
  id: "scream",
11661
- name: "Scream / Shout",
11662
- icon: "😱"
11660
+ name: "Scream / Shout"
11663
11661
  },
11664
11662
  {
11665
11663
  id: "crying",
11666
- name: "Crying / Baby",
11667
- icon: "😢"
11664
+ name: "Crying / Baby"
11668
11665
  },
11669
11666
  {
11670
11667
  id: "laughter",
11671
- name: "Laughter",
11672
- icon: "😂"
11668
+ name: "Laughter"
11673
11669
  },
11674
11670
  {
11675
11671
  id: "music",
11676
- name: "Music",
11677
- icon: "🎵"
11672
+ name: "Music"
11678
11673
  },
11679
11674
  {
11680
11675
  id: "dog",
11681
- name: "Dog",
11682
- icon: "🐕"
11676
+ name: "Dog"
11683
11677
  },
11684
11678
  {
11685
11679
  id: "cat",
11686
- name: "Cat",
11687
- icon: "🐈"
11680
+ name: "Cat"
11688
11681
  },
11689
11682
  {
11690
11683
  id: "bird",
11691
- name: "Bird",
11692
- icon: "🐦"
11684
+ name: "Bird"
11693
11685
  },
11694
11686
  {
11695
11687
  id: "animal",
11696
- name: "Animal (other)",
11697
- icon: "🐾"
11688
+ name: "Animal (other)"
11698
11689
  },
11699
11690
  {
11700
11691
  id: "alarm",
11701
- name: "Alarm / Siren",
11702
- icon: "🚨"
11692
+ name: "Alarm / Siren"
11703
11693
  },
11704
11694
  {
11705
11695
  id: "doorbell",
11706
- name: "Doorbell / Knock",
11707
- icon: "🔔"
11696
+ name: "Doorbell / Knock"
11708
11697
  },
11709
11698
  {
11710
11699
  id: "glass_breaking",
11711
- name: "Glass Breaking",
11712
- icon: "💥"
11700
+ name: "Glass Breaking"
11713
11701
  },
11714
11702
  {
11715
11703
  id: "gunshot",
11716
- name: "Gunshot / Explosion",
11717
- icon: "💣"
11704
+ name: "Gunshot / Explosion"
11718
11705
  },
11719
11706
  {
11720
11707
  id: "vehicle",
11721
- name: "Vehicle",
11722
- icon: "🚗"
11708
+ name: "Vehicle"
11723
11709
  },
11724
11710
  {
11725
11711
  id: "siren",
11726
- name: "Emergency Siren",
11727
- icon: "🚑"
11712
+ name: "Emergency Siren"
11728
11713
  },
11729
11714
  {
11730
11715
  id: "fire",
11731
- name: "Fire / Smoke",
11732
- icon: "🔥"
11716
+ name: "Fire / Smoke"
11733
11717
  },
11734
11718
  {
11735
11719
  id: "water",
11736
- name: "Water",
11737
- icon: "💧"
11720
+ name: "Water"
11738
11721
  },
11739
11722
  {
11740
11723
  id: "wind",
11741
- name: "Wind / Weather",
11742
- icon: "🌬️"
11724
+ name: "Wind / Weather"
11743
11725
  },
11744
11726
  {
11745
11727
  id: "door",
11746
- name: "Door",
11747
- icon: "🚪"
11728
+ name: "Door"
11748
11729
  },
11749
11730
  {
11750
11731
  id: "footsteps",
11751
- name: "Footsteps",
11752
- icon: "👣"
11732
+ name: "Footsteps"
11753
11733
  },
11754
11734
  {
11755
11735
  id: "crowd",
11756
- name: "Crowd / Chatter",
11757
- icon: "👥"
11736
+ name: "Crowd / Chatter"
11758
11737
  },
11759
11738
  {
11760
11739
  id: "telephone",
11761
- name: "Telephone",
11762
- icon: "📞"
11740
+ name: "Telephone"
11763
11741
  },
11764
11742
  {
11765
11743
  id: "engine",
11766
- name: "Engine / Motor",
11767
- icon: "⚙️"
11744
+ name: "Engine / Motor"
11768
11745
  },
11769
11746
  {
11770
11747
  id: "tools",
11771
- name: "Tools / Construction",
11772
- icon: "🔨"
11748
+ name: "Tools / Construction"
11773
11749
  },
11774
11750
  {
11775
11751
  id: "silence",
11776
- name: "Silence",
11777
- icon: "🤫"
11752
+ name: "Silence"
11778
11753
  }
11779
11754
  ];
11780
11755
  var YAMNET_TO_MACRO = {
@@ -16634,6 +16609,12 @@ var DetailResultSchema = zod.z.object({
16634
16609
  /** FRAME-space bbox (already mapped back from crop space). */
16635
16610
  bbox: NativeCropBboxSchema.optional(),
16636
16611
  embedding: zod.z.string().optional(),
16612
+ /**
16613
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
16614
+ * so an older runner that does not send it degrades to "unmeasurable", which
16615
+ * the consuming gate treats as accept — never as reject.
16616
+ */
16617
+ embeddingMagnitude: zod.z.number().optional(),
16637
16618
  label: zod.z.string().optional(),
16638
16619
  /**
16639
16620
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -37784,7 +37765,6 @@ var NC_AUDIO_DEFAULTS = {
37784
37765
  hitPercent: 60,
37785
37766
  samplingSeconds: 10
37786
37767
  };
37787
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37788
37768
  new Set(["devices", "classes"]);
37789
37769
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
37790
37770
  /**
@@ -11653,128 +11653,103 @@ var COCO_TO_MACRO = {
11653
11653
  var AUDIO_MACRO_LABELS = [
11654
11654
  {
11655
11655
  id: "speech",
11656
- name: "Speech",
11657
- icon: "🗣️"
11656
+ name: "Speech"
11658
11657
  },
11659
11658
  {
11660
11659
  id: "scream",
11661
- name: "Scream / Shout",
11662
- icon: "😱"
11660
+ name: "Scream / Shout"
11663
11661
  },
11664
11662
  {
11665
11663
  id: "crying",
11666
- name: "Crying / Baby",
11667
- icon: "😢"
11664
+ name: "Crying / Baby"
11668
11665
  },
11669
11666
  {
11670
11667
  id: "laughter",
11671
- name: "Laughter",
11672
- icon: "😂"
11668
+ name: "Laughter"
11673
11669
  },
11674
11670
  {
11675
11671
  id: "music",
11676
- name: "Music",
11677
- icon: "🎵"
11672
+ name: "Music"
11678
11673
  },
11679
11674
  {
11680
11675
  id: "dog",
11681
- name: "Dog",
11682
- icon: "🐕"
11676
+ name: "Dog"
11683
11677
  },
11684
11678
  {
11685
11679
  id: "cat",
11686
- name: "Cat",
11687
- icon: "🐈"
11680
+ name: "Cat"
11688
11681
  },
11689
11682
  {
11690
11683
  id: "bird",
11691
- name: "Bird",
11692
- icon: "🐦"
11684
+ name: "Bird"
11693
11685
  },
11694
11686
  {
11695
11687
  id: "animal",
11696
- name: "Animal (other)",
11697
- icon: "🐾"
11688
+ name: "Animal (other)"
11698
11689
  },
11699
11690
  {
11700
11691
  id: "alarm",
11701
- name: "Alarm / Siren",
11702
- icon: "🚨"
11692
+ name: "Alarm / Siren"
11703
11693
  },
11704
11694
  {
11705
11695
  id: "doorbell",
11706
- name: "Doorbell / Knock",
11707
- icon: "🔔"
11696
+ name: "Doorbell / Knock"
11708
11697
  },
11709
11698
  {
11710
11699
  id: "glass_breaking",
11711
- name: "Glass Breaking",
11712
- icon: "💥"
11700
+ name: "Glass Breaking"
11713
11701
  },
11714
11702
  {
11715
11703
  id: "gunshot",
11716
- name: "Gunshot / Explosion",
11717
- icon: "💣"
11704
+ name: "Gunshot / Explosion"
11718
11705
  },
11719
11706
  {
11720
11707
  id: "vehicle",
11721
- name: "Vehicle",
11722
- icon: "🚗"
11708
+ name: "Vehicle"
11723
11709
  },
11724
11710
  {
11725
11711
  id: "siren",
11726
- name: "Emergency Siren",
11727
- icon: "🚑"
11712
+ name: "Emergency Siren"
11728
11713
  },
11729
11714
  {
11730
11715
  id: "fire",
11731
- name: "Fire / Smoke",
11732
- icon: "🔥"
11716
+ name: "Fire / Smoke"
11733
11717
  },
11734
11718
  {
11735
11719
  id: "water",
11736
- name: "Water",
11737
- icon: "💧"
11720
+ name: "Water"
11738
11721
  },
11739
11722
  {
11740
11723
  id: "wind",
11741
- name: "Wind / Weather",
11742
- icon: "🌬️"
11724
+ name: "Wind / Weather"
11743
11725
  },
11744
11726
  {
11745
11727
  id: "door",
11746
- name: "Door",
11747
- icon: "🚪"
11728
+ name: "Door"
11748
11729
  },
11749
11730
  {
11750
11731
  id: "footsteps",
11751
- name: "Footsteps",
11752
- icon: "👣"
11732
+ name: "Footsteps"
11753
11733
  },
11754
11734
  {
11755
11735
  id: "crowd",
11756
- name: "Crowd / Chatter",
11757
- icon: "👥"
11736
+ name: "Crowd / Chatter"
11758
11737
  },
11759
11738
  {
11760
11739
  id: "telephone",
11761
- name: "Telephone",
11762
- icon: "📞"
11740
+ name: "Telephone"
11763
11741
  },
11764
11742
  {
11765
11743
  id: "engine",
11766
- name: "Engine / Motor",
11767
- icon: "⚙️"
11744
+ name: "Engine / Motor"
11768
11745
  },
11769
11746
  {
11770
11747
  id: "tools",
11771
- name: "Tools / Construction",
11772
- icon: "🔨"
11748
+ name: "Tools / Construction"
11773
11749
  },
11774
11750
  {
11775
11751
  id: "silence",
11776
- name: "Silence",
11777
- icon: "🤫"
11752
+ name: "Silence"
11778
11753
  }
11779
11754
  ];
11780
11755
  var YAMNET_TO_MACRO = {
@@ -16634,6 +16609,12 @@ var DetailResultSchema = z.object({
16634
16609
  /** FRAME-space bbox (already mapped back from crop space). */
16635
16610
  bbox: NativeCropBboxSchema.optional(),
16636
16611
  embedding: z.string().optional(),
16612
+ /**
16613
+ * L2 magnitude of the RAW pre-normalization embedding. Additive and optional
16614
+ * so an older runner that does not send it degrades to "unmeasurable", which
16615
+ * the consuming gate treats as accept — never as reject.
16616
+ */
16617
+ embeddingMagnitude: z.number().optional(),
16637
16618
  label: z.string().optional(),
16638
16619
  /**
16639
16620
  * The tier `label` occupies, copied VERBATIM from the producing step's
@@ -37784,7 +37765,6 @@ var NC_AUDIO_DEFAULTS = {
37784
37765
  hitPercent: 60,
37785
37766
  samplingSeconds: 10
37786
37767
  };
37787
- new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37788
37768
  new Set(["devices", "classes"]);
37789
37769
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
37790
37770
  /**