@camstack/addon-mqtt-broker 1.1.27 → 1.2.0

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.
@@ -7113,6 +7113,17 @@ var ModelCatalogEntrySchema = object({
7113
7113
  "imagenet",
7114
7114
  "none"
7115
7115
  ]).optional(),
7116
+ /**
7117
+ * The model already applies softmax IN-GRAPH — its raw output is a
7118
+ * probability distribution, not logits. When set, the `softmax`
7119
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7120
+ * probability vector collapses it toward uniform (top-1 score craters far
7121
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7122
+ * the output is raw logits and the postprocessor applies softmax (the normal
7123
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7124
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7125
+ */
7126
+ outputProbabilities: boolean().optional(),
7116
7127
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7117
7128
  /**
7118
7129
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -7672,6 +7683,160 @@ var EncodeProfileSchema = object({
7672
7683
  */
7673
7684
  outputArgs: array(string()).optional()
7674
7685
  });
7686
+ var COCO_TO_MACRO = {
7687
+ mapping: {
7688
+ person: "person",
7689
+ bicycle: "vehicle",
7690
+ car: "vehicle",
7691
+ motorcycle: "vehicle",
7692
+ airplane: "vehicle",
7693
+ bus: "vehicle",
7694
+ train: "vehicle",
7695
+ truck: "vehicle",
7696
+ boat: "vehicle",
7697
+ bird: "animal",
7698
+ cat: "animal",
7699
+ dog: "animal",
7700
+ horse: "animal",
7701
+ sheep: "animal",
7702
+ cow: "animal",
7703
+ elephant: "animal",
7704
+ bear: "animal",
7705
+ zebra: "animal",
7706
+ giraffe: "animal",
7707
+ suitcase: "package",
7708
+ backpack: "package",
7709
+ handbag: "package"
7710
+ },
7711
+ preserveOriginal: false
7712
+ };
7713
+ var AUDIO_MACRO_LABELS = [
7714
+ {
7715
+ id: "speech",
7716
+ name: "Speech",
7717
+ icon: "🗣️"
7718
+ },
7719
+ {
7720
+ id: "scream",
7721
+ name: "Scream / Shout",
7722
+ icon: "😱"
7723
+ },
7724
+ {
7725
+ id: "crying",
7726
+ name: "Crying / Baby",
7727
+ icon: "😢"
7728
+ },
7729
+ {
7730
+ id: "laughter",
7731
+ name: "Laughter",
7732
+ icon: "😂"
7733
+ },
7734
+ {
7735
+ id: "music",
7736
+ name: "Music",
7737
+ icon: "🎵"
7738
+ },
7739
+ {
7740
+ id: "dog",
7741
+ name: "Dog",
7742
+ icon: "🐕"
7743
+ },
7744
+ {
7745
+ id: "cat",
7746
+ name: "Cat",
7747
+ icon: "🐈"
7748
+ },
7749
+ {
7750
+ id: "bird",
7751
+ name: "Bird",
7752
+ icon: "🐦"
7753
+ },
7754
+ {
7755
+ id: "animal",
7756
+ name: "Animal (other)",
7757
+ icon: "🐾"
7758
+ },
7759
+ {
7760
+ id: "alarm",
7761
+ name: "Alarm / Siren",
7762
+ icon: "🚨"
7763
+ },
7764
+ {
7765
+ id: "doorbell",
7766
+ name: "Doorbell / Knock",
7767
+ icon: "🔔"
7768
+ },
7769
+ {
7770
+ id: "glass_breaking",
7771
+ name: "Glass Breaking",
7772
+ icon: "💥"
7773
+ },
7774
+ {
7775
+ id: "gunshot",
7776
+ name: "Gunshot / Explosion",
7777
+ icon: "💣"
7778
+ },
7779
+ {
7780
+ id: "vehicle",
7781
+ name: "Vehicle",
7782
+ icon: "🚗"
7783
+ },
7784
+ {
7785
+ id: "siren",
7786
+ name: "Emergency Siren",
7787
+ icon: "🚑"
7788
+ },
7789
+ {
7790
+ id: "fire",
7791
+ name: "Fire / Smoke",
7792
+ icon: "🔥"
7793
+ },
7794
+ {
7795
+ id: "water",
7796
+ name: "Water",
7797
+ icon: "💧"
7798
+ },
7799
+ {
7800
+ id: "wind",
7801
+ name: "Wind / Weather",
7802
+ icon: "🌬️"
7803
+ },
7804
+ {
7805
+ id: "door",
7806
+ name: "Door",
7807
+ icon: "🚪"
7808
+ },
7809
+ {
7810
+ id: "footsteps",
7811
+ name: "Footsteps",
7812
+ icon: "👣"
7813
+ },
7814
+ {
7815
+ id: "crowd",
7816
+ name: "Crowd / Chatter",
7817
+ icon: "👥"
7818
+ },
7819
+ {
7820
+ id: "telephone",
7821
+ name: "Telephone",
7822
+ icon: "📞"
7823
+ },
7824
+ {
7825
+ id: "engine",
7826
+ name: "Engine / Motor",
7827
+ icon: "⚙️"
7828
+ },
7829
+ {
7830
+ id: "tools",
7831
+ name: "Tools / Construction",
7832
+ icon: "🔨"
7833
+ },
7834
+ {
7835
+ id: "silence",
7836
+ name: "Silence",
7837
+ icon: "🤫"
7838
+ }
7839
+ ];
7675
7840
  var YAMNET_TO_MACRO = {
7676
7841
  mapping: {
7677
7842
  Speech: "speech",
@@ -7938,6 +8103,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
7938
8103
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7939
8104
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
7940
8105
  /**
8106
+ * Unified event-kind taxonomy — THE single source of truth for
8107
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8108
+ * icon }`.
8109
+ *
8110
+ * This dictionary folds together what used to be scattered across four
8111
+ * copies:
8112
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8113
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8114
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8115
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8116
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8117
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8118
+ *
8119
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8120
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8121
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8122
+ * color or an icon: they read this dictionary (server descriptors carry the
8123
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8124
+ *
8125
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8126
+ */
8127
+ var TAXONOMY_COLORS = {
8128
+ motion: "#f59e0b",
8129
+ audio: "#06b6d4",
8130
+ person: "#22c55e",
8131
+ vehicle: "#3b82f6",
8132
+ animal: "#f97316",
8133
+ package: "#a855f7",
8134
+ sensor: "#8b5cf6",
8135
+ control: "#10b981",
8136
+ genericDetection: "#64748b"
8137
+ };
8138
+ var DETECTION_SUB_COLORS = {
8139
+ car: "#f59e0b",
8140
+ truck: "#d97706",
8141
+ bus: "#b45309",
8142
+ motorcycle: "#eab308",
8143
+ bicycle: "#ca8a04",
8144
+ airplane: "#60a5fa",
8145
+ boat: "#2563eb",
8146
+ train: "#1d4ed8",
8147
+ bird: "#14b8a6",
8148
+ dog: "#84cc16",
8149
+ cat: "#f97316",
8150
+ horse: "#a16207",
8151
+ sheep: "#a3a3a3",
8152
+ cow: "#78716c",
8153
+ elephant: "#6b7280",
8154
+ bear: "#7c2d12",
8155
+ zebra: "#404040",
8156
+ giraffe: "#d4a373"
8157
+ };
8158
+ function titleCase(id) {
8159
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8160
+ }
8161
+ var entries = /* @__PURE__ */ new Map();
8162
+ function macro(kind, category, color, iconId, label) {
8163
+ entries.set(kind, {
8164
+ kind,
8165
+ parentKind: null,
8166
+ level: "macro",
8167
+ category,
8168
+ color,
8169
+ iconId,
8170
+ labelKey: `eventKind.${kind}`,
8171
+ label
8172
+ });
8173
+ }
8174
+ function sub(kind, parentKind, category, color, iconId, label) {
8175
+ entries.set(kind, {
8176
+ kind,
8177
+ parentKind,
8178
+ level: "sub",
8179
+ category,
8180
+ color,
8181
+ iconId,
8182
+ labelKey: `eventKind.${kind}`,
8183
+ label
8184
+ });
8185
+ }
8186
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8187
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8188
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8189
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8190
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8191
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8192
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8193
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8194
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8195
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8196
+ if (entries.has(cocoClass)) continue;
8197
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8198
+ }
8199
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8200
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8201
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8202
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8203
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8204
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8205
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8206
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8207
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8208
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8209
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8210
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8211
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8212
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8213
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8214
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8215
+ sub("button", "control", "control", "#10b981", "button", "Button");
8216
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8217
+ for (const l of AUDIO_MACRO_LABELS) {
8218
+ const kind = `audio-${l.id}`;
8219
+ if (entries.has(kind)) continue;
8220
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8221
+ }
8222
+ /** The complete taxonomy dictionary, keyed by kind. */
8223
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8224
+ /**
7941
8225
  * Error types for the safe expression engine. Two distinct classes so callers
7942
8226
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
7943
8227
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -10853,10 +11137,7 @@ var ConfigUISchemaNullableBridge = custom();
10853
11137
  var InferenceCapabilitiesBridge = custom();
10854
11138
  var ModelAvailabilityListBridge = custom();
10855
11139
  var PipelineRunResultBridge = custom();
10856
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
10857
- kind: "mutation",
10858
- auth: "admin"
10859
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11140
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
10860
11141
  modelId: string(),
10861
11142
  settings: record(string(), unknown()).readonly()
10862
11143
  }))), method(object({ steps: record(string(), object({
@@ -10916,13 +11197,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10916
11197
  * (inputClasses ≠ null) are skipped and served per-track via
10917
11198
  * pipelineRunner.runDetailSubtree (two-plane design).
10918
11199
  */
10919
- plane: _enum(["full", "frame"]).optional()
11200
+ plane: _enum(["full", "frame"]).optional(),
11201
+ /**
11202
+ * Inference-device selector (Phase 2 multi-device). Format
11203
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11204
+ * Omitted ⇒ the runner's default device (current single-engine
11205
+ * behaviour). Selects WHICH device pool of the node runs the call.
11206
+ */
11207
+ deviceKey: string().optional()
10920
11208
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
10921
11209
  engine: PipelineEngineChoiceSchema.optional(),
10922
11210
  steps: array(PipelineStepInputSchema).min(1),
10923
11211
  frames: array(FrameInputSchema).min(1).max(255),
10924
11212
  deviceId: number().optional(),
10925
- sessionId: string().optional()
11213
+ sessionId: string().optional(),
11214
+ /**
11215
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11216
+ * the batch to the Python pool's bench preprocess cache
11217
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11218
+ * preprocessed ONCE and every later inference is a pure-inference cache
11219
+ * hit — the sustained-throughput run measures inference, not
11220
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11221
+ * full preprocess every call, correct). Fresh per sustained run;
11222
+ * released via `uncacheFrame`.
11223
+ */
11224
+ frameId: number().int().nonnegative().optional(),
11225
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11226
+ deviceKey: string().optional()
10926
11227
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
10927
11228
  data: _instanceof(Uint8Array),
10928
11229
  width: number().int().positive(),
@@ -10954,8 +11255,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
10954
11255
  * - `runtime` — main camera-serving engine (no idle TTL).
10955
11256
  * - `warm-override` — benchmark/test override held in the warm
10956
11257
  * cache; auto-disposed after the idle TTL.
11258
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11259
+ * multi-device, keyed by `deviceKey`) resolved
11260
+ * via `resolveDeviceFactory`. Runs alongside the
11261
+ * `runtime` engine on a DIFFERENT accelerator
11262
+ * (NPU / iGPU / Coral) — this is how the
11263
+ * Engines tab shows all pools running at once.
10957
11264
  */
10958
- kind: _enum(["runtime", "warm-override"]),
11265
+ kind: _enum([
11266
+ "runtime",
11267
+ "warm-override",
11268
+ "device-pool"
11269
+ ]),
10959
11270
  /** Native pid of the underlying Python pool (null when no pool). */
10960
11271
  poolPid: number().nullable(),
10961
11272
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11297,7 +11608,14 @@ var RunnerCameraConfigSchema = object({
11297
11608
  * camera's detect node differs from its source-owner (P2d, gated by the
11298
11609
  * `remoteSourcingNodes` rollout setting).
11299
11610
  */
11300
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11611
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11612
+ /**
11613
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11614
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11615
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11616
+ * this only selects WHICH device pool of that node runs the session.
11617
+ */
11618
+ deviceKey: string().optional()
11301
11619
  });
11302
11620
  motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
11303
11621
  /**
@@ -11318,6 +11636,19 @@ var RunnerLocalLoadSchema = object({
11318
11636
  avgInferenceTimeMs: number(),
11319
11637
  /** Total queue depth across motion + detection queues. */
11320
11638
  queueDepthTotal: number(),
11639
+ /**
11640
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11641
+ * this runner currently has attached cameras on, so the orchestrator's second
11642
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11643
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11644
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11645
+ */
11646
+ devices: array(object({
11647
+ deviceKey: string(),
11648
+ backend: string(),
11649
+ attachedCameras: number(),
11650
+ queueDepthTotal: number()
11651
+ })).default([]),
11321
11652
  /** Hardware capability flags reported by this node. */
11322
11653
  hardware: object({
11323
11654
  hasGpu: boolean(),
@@ -12793,6 +13124,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
12793
13124
  kind: "mutation",
12794
13125
  auth: "admin"
12795
13126
  });
13127
+ DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Camera, DeviceType.Hub, DeviceType.Switch, DeviceType.Siren, DeviceType.Light, DeviceType.Fan, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder;
13128
+ new Set(Object.values(DeviceType));
12796
13129
  /**
12797
13130
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
12798
13131
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -15955,17 +16288,30 @@ var EventKindCategorySchema = _enum([
15955
16288
  "audio",
15956
16289
  "detection",
15957
16290
  "sensor",
16291
+ "control",
15958
16292
  "custom",
15959
16293
  "package"
15960
16294
  ]);
16295
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16296
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
15961
16297
  var EventKindDescriptorSchema = object({
15962
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16298
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
15963
16299
  kind: string(),
16300
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16301
+ labelKey: string(),
16302
+ /** English fallback label (kept for clients that don't translate). */
15964
16303
  label: string(),
15965
16304
  /** Hex color for timeline/legend rendering. */
15966
16305
  color: string(),
16306
+ /** Dictionary id → lucide component on the UI side. */
16307
+ iconId: string(),
16308
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
15967
16309
  icon: EventKindIconSchema,
15968
16310
  category: EventKindCategorySchema,
16311
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16312
+ parentKind: string().nullable(),
16313
+ /** Derived from `parentKind`, explicit for the client tree. */
16314
+ level: EventKindLevelSchema,
15969
16315
  /** Which cap + device contributes this kind. For built-ins the camera
15970
16316
  * itself; for sensor kinds the LINKED source device. */
15971
16317
  source: object({
@@ -16038,11 +16384,21 @@ var TrackAudioLabelSchema = object({
16038
16384
  firstAt: number(),
16039
16385
  lastAt: number()
16040
16386
  });
16387
+ /**
16388
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16389
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16390
+ * linked sensor/control state change (no positions; carries a snapshot). The
16391
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16392
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16393
+ */
16394
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16041
16395
  var TrackSchema = object({
16042
16396
  trackId: string(),
16043
16397
  deviceId: number(),
16044
16398
  className: string(),
16045
16399
  label: string().optional(),
16400
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16401
+ source: TrackSourceSchema.optional(),
16046
16402
  firstSeen: number(),
16047
16403
  lastSeen: number(),
16048
16404
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16419,6 +16775,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16419
16775
  eventId: string(),
16420
16776
  timestamp: number()
16421
16777
  });
16778
+ /**
16779
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16780
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16781
+ * caps into per-camera event-kind descriptors.
16782
+ *
16783
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16784
+ * is NOT duplicated here — every entry is derived from the single
16785
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16786
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16787
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16788
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16789
+ * eventful cap is missing.
16790
+ */
16791
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16792
+ var LEGACY_ICON = {
16793
+ motion: "motion",
16794
+ audio: "audio",
16795
+ person: "person",
16796
+ vehicle: "vehicle",
16797
+ animal: "animal",
16798
+ package: "package",
16799
+ door: "door",
16800
+ pir: "pir",
16801
+ smoke: "smoke",
16802
+ water: "water",
16803
+ button: "button",
16804
+ generic: "generic",
16805
+ gas: "smoke",
16806
+ vibration: "generic",
16807
+ tamper: "generic",
16808
+ presence: "person",
16809
+ lock: "generic",
16810
+ siren: "generic",
16811
+ switch: "generic",
16812
+ doorbell: "button"
16813
+ };
16814
+ function legacyIcon(iconId) {
16815
+ return LEGACY_ICON[iconId] ?? "generic";
16816
+ }
16817
+ /**
16818
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16819
+ * The anti-drift guard cross-checks this against the eventful caps declared
16820
+ * in `packages/types/src/capabilities/*.cap.ts`.
16821
+ */
16822
+ var CAP_TO_KIND = {
16823
+ contact: "contact",
16824
+ motion: "motion-sensor",
16825
+ smoke: "smoke",
16826
+ flood: "flood",
16827
+ gas: "gas",
16828
+ "carbon-monoxide": "carbon-monoxide",
16829
+ vibration: "vibration",
16830
+ tamper: "tamper",
16831
+ presence: "presence",
16832
+ "enum-sensor": "enum-sensor",
16833
+ "event-emitter": "device-event",
16834
+ "lock-control": "lock",
16835
+ switch: "switch",
16836
+ button: "button",
16837
+ doorbell: "doorbell"
16838
+ };
16839
+ function buildDescriptor(capName, kind) {
16840
+ const t = EVENT_TAXONOMY[kind];
16841
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16842
+ return {
16843
+ ...t,
16844
+ icon: legacyIcon(t.iconId)
16845
+ };
16846
+ }
16847
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16422
16848
  var CameraPipelineConfigSchema = object({
16423
16849
  engine: PipelineEngineChoiceSchema.optional(),
16424
16850
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16437,13 +16863,11 @@ var PipelineTemplateSchema = object({
16437
16863
  createdAt: string(),
16438
16864
  updatedAt: string()
16439
16865
  });
16440
- var AgentAddonConfigSchema = object({
16441
- enabled: boolean(),
16866
+ var DeviceStepConfigSchema = object({
16442
16867
  modelId: string().optional(),
16443
- settings: record(string(), unknown()).readonly()
16868
+ settings: record(string(), unknown()).optional()
16444
16869
  });
16445
16870
  var AgentPipelineSettingsSchema = object({
16446
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
16447
16871
  maxCameras: number().int().nonnegative().nullable().default(null),
16448
16872
  /** Per-node detection weight (relative share for the quota balancer). */
16449
16873
  detectWeight: number().positive().optional(),
@@ -16467,7 +16891,22 @@ var AgentPipelineSettingsSchema = object({
16467
16891
  * it already uses to reach the hub). Set this only when the auto-detected
16468
16892
  * address is wrong (multi-homed host, NAT, custom interface).
16469
16893
  */
16470
- reachableHost: string().optional()
16894
+ reachableHost: string().optional(),
16895
+ /**
16896
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
16897
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
16898
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
16899
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
16900
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
16901
+ * the default model/settings for every camera landing on that accelerator;
16902
+ * a stepId absent ⇒ the step uses that device's format default.
16903
+ */
16904
+ inferenceDevices: record(string(), object({
16905
+ enabled: boolean(),
16906
+ weight: number().positive().optional(),
16907
+ maxSessions: number().int().positive().optional(),
16908
+ steps: record(string(), DeviceStepConfigSchema).optional()
16909
+ })).optional()
16471
16910
  });
16472
16911
  var CameraPipelineForAgentSchema = object({
16473
16912
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16477,14 +16916,13 @@ var CameraPipelineForAgentSchema = object({
16477
16916
  }).nullable()
16478
16917
  });
16479
16918
  var CameraStepOverridePatchSchema = object({
16480
- enabled: boolean().optional(),
16481
16919
  modelId: string().optional(),
16482
16920
  settings: record(string(), unknown()).readonly().optional()
16483
16921
  });
16484
16922
  var CameraPipelineSettingsSchema = object({
16485
16923
  pinnedAgentNodeId: string().optional(),
16486
16924
  stepToggles: record(string(), boolean()).optional(),
16487
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
16925
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
16488
16926
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
16489
16927
  });
16490
16928
  /**
@@ -16698,6 +17136,44 @@ var CameraStatusSchema = object({
16698
17136
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
16699
17137
  fetchedAt: number()
16700
17138
  });
17139
+ var NodeInferenceDeviceSchema = object({
17140
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17141
+ key: string(),
17142
+ backend: string(),
17143
+ device: string(),
17144
+ format: _enum(MODEL_FORMATS),
17145
+ /** Whether the node's live probe reports the device as usable right now. */
17146
+ available: boolean(),
17147
+ /**
17148
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17149
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17150
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17151
+ * not a balanced target). An explicit stored value always wins; a stored-only
17152
+ * (unavailable) key keeps its stored value.
17153
+ */
17154
+ enabled: boolean(),
17155
+ /** Relative balancer weight for the enabled device (default 1). */
17156
+ weight: number(),
17157
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17158
+ maxSessions: number().nullable(),
17159
+ /** Object-detection model the executor defaults to for this deviceKey. */
17160
+ defaultModelId: string(),
17161
+ /**
17162
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17163
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17164
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17165
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17166
+ * available per format; this is the stored selection that becomes the
17167
+ * default for EVERY camera landing on this accelerator.
17168
+ */
17169
+ steps: record(string(), DeviceStepConfigSchema).optional()
17170
+ });
17171
+ var NodeInferenceDevicesSchema = object({
17172
+ nodeId: string(),
17173
+ /** False when the node's platform-probe was unreachable (no live device set). */
17174
+ reachable: boolean(),
17175
+ devices: array(NodeInferenceDeviceSchema).readonly()
17176
+ });
16701
17177
  method(object({
16702
17178
  deviceId: number(),
16703
17179
  agentNodeId: string()
@@ -16707,7 +17183,13 @@ method(object({
16707
17183
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
16708
17184
  kind: "mutation",
16709
17185
  auth: "admin"
16710
- }), method(_void(), object({ migrated: number() }), {
17186
+ }), method(object({
17187
+ deviceId: number(),
17188
+ deviceKey: string()
17189
+ }), object({ success: literal(true) }), {
17190
+ kind: "mutation",
17191
+ auth: "admin"
17192
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
16711
17193
  kind: "mutation",
16712
17194
  auth: "admin"
16713
17195
  }), method(_void(), array(PipelineAssignmentSchema).readonly()), method(object({ deviceId: number() }), PipelineAssignmentSchema.nullable()), method(_void(), array(AgentLoadSummarySchema).readonly()), method(_void(), GlobalMetricsSchema), method(object({ deviceId: number() }), CameraMetricsSchema.nullable()), method(object({ nodeId: string() }), CapabilityBindingsSchema), method(object({
@@ -16741,13 +17223,7 @@ method(object({
16741
17223
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
16742
17224
  nodeId: string(),
16743
17225
  settings: AgentPipelineSettingsSchema
16744
- })).readonly()), method(object({
16745
- agentNodeId: string(),
16746
- defaults: record(string(), AgentAddonConfigSchema)
16747
- }), object({ success: literal(true) }), {
16748
- kind: "mutation",
16749
- auth: "admin"
16750
- }), method(object({ agentNodeId: string() }), object({
17226
+ })).readonly()), method(object({ agentNodeId: string() }), object({
16751
17227
  success: boolean(),
16752
17228
  removed: boolean()
16753
17229
  }), {
@@ -16779,7 +17255,18 @@ method(object({
16779
17255
  }), object({ success: literal(true) }), {
16780
17256
  kind: "mutation",
16781
17257
  auth: "admin"
16782
- }), method(object({ agentNodeId: string() }), object({
17258
+ }), method(object({
17259
+ agentNodeId: string(),
17260
+ inferenceDevices: record(string(), object({
17261
+ enabled: boolean(),
17262
+ weight: number().positive().optional(),
17263
+ maxSessions: number().int().positive().optional(),
17264
+ steps: record(string(), DeviceStepConfigSchema).optional()
17265
+ }))
17266
+ }), object({ success: literal(true) }), {
17267
+ kind: "mutation",
17268
+ auth: "admin"
17269
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
16783
17270
  success: literal(true),
16784
17271
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
16785
17272
  effectiveModelId: string().nullable(),
@@ -16795,9 +17282,10 @@ method(object({
16795
17282
  }), object({ success: literal(true) }), {
16796
17283
  kind: "mutation",
16797
17284
  auth: "admin"
16798
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17285
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
16799
17286
  deviceId: number(),
16800
17287
  agentNodeId: string(),
17288
+ deviceKey: string(),
16801
17289
  addonId: string(),
16802
17290
  patch: CameraStepOverridePatchSchema.nullable()
16803
17291
  }), object({ success: literal(true) }), {
@@ -16834,14 +17322,13 @@ method(object({
16834
17322
  });
16835
17323
  /**
16836
17324
  * server-management — per-NODE singleton capability for a node's ROOT
16837
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
16838
- * agents).
17325
+ * package lifecycle (runtime-updatable node packages).
16839
17326
  *
16840
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
16841
- * on agents) carries the whole software stack in its npm dep tree, so ONE
16842
- * version describes the node. Updates install into
16843
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
16844
- * starter (probation boot + auto-rollback to N-1).
17327
+ * Every node role runs the SAME root package (`@camstack/server`), which
17328
+ * carries the whole software stack in its npm dep tree, so ONE version
17329
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17330
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17331
+ * no auto-rollback).
16845
17332
  *
16846
17333
  * Providers:
16847
17334
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -16949,7 +17436,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
16949
17436
  /** Explicit target version; omitted = latest from the registry. */
16950
17437
  version: string().optional() }), ServerUpdateActionResultSchema, {
16951
17438
  kind: "mutation",
16952
- auth: "admin"
17439
+ auth: "admin",
17440
+ timeoutMs: 16 * 6e4
16953
17441
  }), method(_void(), ServerUpdateActionResultSchema, {
16954
17442
  kind: "mutation",
16955
17443
  auth: "admin"
@@ -17993,22 +18481,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
17993
18481
  var RestartAddonResultSchema = unknown();
17994
18482
  var InstallPackageResultSchema = unknown();
17995
18483
  var ReloadPackagesResultSchema = unknown();
17996
- /**
17997
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
17998
- * server restarts so the admin UI can react to the `restartingAt`
17999
- * timestamp (shows reconnect overlay). The transition from
18000
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18001
- * `system.restart-completed` event after the new process boots.
18002
- *
18003
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18004
- */
18005
- var UpdateFrameworkPackageResultSchema = object({
18006
- packageName: string(),
18007
- fromVersion: string(),
18008
- toVersion: string(),
18009
- /** Ms-epoch the server scheduled its self-restart. */
18010
- restartingAt: number()
18011
- });
18012
18484
  var BulkUpdateItemStatusSchema = _enum([
18013
18485
  "queued",
18014
18486
  "updating",
@@ -18136,13 +18608,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18136
18608
  }), object({ success: literal(true) }), {
18137
18609
  kind: "mutation",
18138
18610
  auth: "admin"
18139
- }), method(object({
18140
- packageName: string().min(1),
18141
- version: string().optional(),
18142
- deferRestart: boolean().optional()
18143
- }), UpdateFrameworkPackageResultSchema, {
18144
- kind: "mutation",
18145
- auth: "admin"
18146
18611
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18147
18612
  kind: "mutation",
18148
18613
  auth: "admin"
@@ -19008,10 +19473,10 @@ var TopologyCategorySchema = object({
19008
19473
  addons: array(TopologyCategoryAddonSchema).readonly()
19009
19474
  });
19010
19475
  /**
19011
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19012
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19013
- * version visibility for the Server management surface. Nullable: offline
19014
- * rows and pre-phase-2 nodes report none.
19476
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19477
+ * root package for every node role) as reported by its `registerNode`
19478
+ * manifest — version visibility for the Server management surface. Nullable:
19479
+ * offline rows and nodes that never reported one.
19015
19480
  */
19016
19481
  var TopologyRootPackageSchema = object({
19017
19482
  name: string(),
@@ -19399,17 +19864,28 @@ var PlatformScoreSchema = object({
19399
19864
  format: _enum([
19400
19865
  "onnx",
19401
19866
  "coreml",
19402
- "openvino"
19867
+ "openvino",
19868
+ "tflite"
19403
19869
  ]),
19404
19870
  score: number(),
19405
19871
  reason: string(),
19406
19872
  available: boolean()
19407
19873
  });
19874
+ var InferenceDeviceDescriptorSchema = object({
19875
+ key: string(),
19876
+ backend: string(),
19877
+ device: string(),
19878
+ format: ModelFormatSchema,
19879
+ runtime: literal("python"),
19880
+ score: number(),
19881
+ available: boolean()
19882
+ });
19408
19883
  var PlatformCapabilitiesSchema = object({
19409
19884
  hardware: HardwareInfoSchema,
19410
19885
  scores: array(PlatformScoreSchema).readonly(),
19411
19886
  bestScore: PlatformScoreSchema,
19412
- pythonPath: string().nullable()
19887
+ pythonPath: string().nullable(),
19888
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
19413
19889
  });
19414
19890
  var ModelRequirementSchema = object({
19415
19891
  modelId: string(),
@@ -20288,12 +20764,6 @@ Object.freeze({
20288
20764
  addonId: null,
20289
20765
  access: "delete"
20290
20766
  },
20291
- "addons.updateFrameworkPackage": {
20292
- capName: "addons",
20293
- capScope: "system",
20294
- addonId: null,
20295
- access: "create"
20296
- },
20297
20767
  "addons.updatePackage": {
20298
20768
  capName: "addons",
20299
20769
  capScope: "system",
@@ -23066,12 +23536,6 @@ Object.freeze({
23066
23536
  addonId: null,
23067
23537
  access: "view"
23068
23538
  },
23069
- "pipelineExecutor.reprobeEngine": {
23070
- capName: "pipeline-executor",
23071
- capScope: "system",
23072
- addonId: null,
23073
- access: "create"
23074
- },
23075
23539
  "pipelineExecutor.runAudioTest": {
23076
23540
  capName: "pipeline-executor",
23077
23541
  capScope: "system",
@@ -23222,6 +23686,12 @@ Object.freeze({
23222
23686
  addonId: null,
23223
23687
  access: "view"
23224
23688
  },
23689
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23690
+ capName: "pipeline-orchestrator",
23691
+ capScope: "system",
23692
+ addonId: null,
23693
+ access: "view"
23694
+ },
23225
23695
  "pipelineOrchestrator.getPipelineAssignment": {
23226
23696
  capName: "pipeline-orchestrator",
23227
23697
  capScope: "system",
@@ -23234,6 +23704,12 @@ Object.freeze({
23234
23704
  addonId: null,
23235
23705
  access: "view"
23236
23706
  },
23707
+ "pipelineOrchestrator.getPipelineDevicePin": {
23708
+ capName: "pipeline-orchestrator",
23709
+ capScope: "system",
23710
+ addonId: null,
23711
+ access: "view"
23712
+ },
23237
23713
  "pipelineOrchestrator.listAgentSettings": {
23238
23714
  capName: "pipeline-orchestrator",
23239
23715
  capScope: "system",
@@ -23276,19 +23752,19 @@ Object.freeze({
23276
23752
  addonId: null,
23277
23753
  access: "create"
23278
23754
  },
23279
- "pipelineOrchestrator.setAgentAddonDefaults": {
23755
+ "pipelineOrchestrator.setAgentCapabilities": {
23280
23756
  capName: "pipeline-orchestrator",
23281
23757
  capScope: "system",
23282
23758
  addonId: null,
23283
23759
  access: "create"
23284
23760
  },
23285
- "pipelineOrchestrator.setAgentCapabilities": {
23761
+ "pipelineOrchestrator.setAgentDetectWeight": {
23286
23762
  capName: "pipeline-orchestrator",
23287
23763
  capScope: "system",
23288
23764
  addonId: null,
23289
23765
  access: "create"
23290
23766
  },
23291
- "pipelineOrchestrator.setAgentDetectWeight": {
23767
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23292
23768
  capName: "pipeline-orchestrator",
23293
23769
  capScope: "system",
23294
23770
  addonId: null,
@@ -23330,6 +23806,12 @@ Object.freeze({
23330
23806
  addonId: null,
23331
23807
  access: "create"
23332
23808
  },
23809
+ "pipelineOrchestrator.setPipelineDevicePin": {
23810
+ capName: "pipeline-orchestrator",
23811
+ capScope: "system",
23812
+ addonId: null,
23813
+ access: "create"
23814
+ },
23333
23815
  "pipelineOrchestrator.unassignAudio": {
23334
23816
  capName: "pipeline-orchestrator",
23335
23817
  capScope: "system",
@@ -24882,32 +25364,6 @@ Object.freeze({
24882
25364
  "network-access": "ingress",
24883
25365
  "smtp-provider": "email"
24884
25366
  });
24885
- var frameworkSwapPackageSchema = object({
24886
- name: string(),
24887
- stagedPath: string(),
24888
- backupPath: string(),
24889
- toVersion: string(),
24890
- fromVersion: string().nullable()
24891
- });
24892
- object({
24893
- jobId: string(),
24894
- taskId: string(),
24895
- packages: array(frameworkSwapPackageSchema),
24896
- requestedAtMs: number(),
24897
- schemaVersion: literal(1)
24898
- });
24899
- object({
24900
- jobId: string(),
24901
- taskId: string(),
24902
- backups: array(object({
24903
- name: string(),
24904
- backupPath: string(),
24905
- livePath: string()
24906
- })),
24907
- appliedAtMs: number(),
24908
- bootAttempts: number(),
24909
- schemaVersion: literal(1)
24910
- });
24911
25367
  //#endregion
24912
25368
  //#region ../../node_modules/xtend/immutable.js
24913
25369
  var require_immutable = /* @__PURE__ */ __commonJSMin(((exports, module) => {