@camstack/addon-import-alexa 0.1.24 → 0.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.
Files changed (3) hide show
  1. package/dist/addon.js +560 -104
  2. package/dist/addon.mjs +560 -104
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -7137,6 +7137,17 @@ var ModelCatalogEntrySchema = object({
7137
7137
  "imagenet",
7138
7138
  "none"
7139
7139
  ]).optional(),
7140
+ /**
7141
+ * The model already applies softmax IN-GRAPH — its raw output is a
7142
+ * probability distribution, not logits. When set, the `softmax`
7143
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7144
+ * probability vector collapses it toward uniform (top-1 score craters far
7145
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7146
+ * the output is raw logits and the postprocessor applies softmax (the normal
7147
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7148
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7149
+ */
7150
+ outputProbabilities: boolean().optional(),
7140
7151
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7141
7152
  /**
7142
7153
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -7813,6 +7824,160 @@ var EncodeProfileSchema = object({
7813
7824
  */
7814
7825
  outputArgs: array(string()).optional()
7815
7826
  });
7827
+ var COCO_TO_MACRO = {
7828
+ mapping: {
7829
+ person: "person",
7830
+ bicycle: "vehicle",
7831
+ car: "vehicle",
7832
+ motorcycle: "vehicle",
7833
+ airplane: "vehicle",
7834
+ bus: "vehicle",
7835
+ train: "vehicle",
7836
+ truck: "vehicle",
7837
+ boat: "vehicle",
7838
+ bird: "animal",
7839
+ cat: "animal",
7840
+ dog: "animal",
7841
+ horse: "animal",
7842
+ sheep: "animal",
7843
+ cow: "animal",
7844
+ elephant: "animal",
7845
+ bear: "animal",
7846
+ zebra: "animal",
7847
+ giraffe: "animal",
7848
+ suitcase: "package",
7849
+ backpack: "package",
7850
+ handbag: "package"
7851
+ },
7852
+ preserveOriginal: false
7853
+ };
7854
+ var AUDIO_MACRO_LABELS = [
7855
+ {
7856
+ id: "speech",
7857
+ name: "Speech",
7858
+ icon: "🗣️"
7859
+ },
7860
+ {
7861
+ id: "scream",
7862
+ name: "Scream / Shout",
7863
+ icon: "😱"
7864
+ },
7865
+ {
7866
+ id: "crying",
7867
+ name: "Crying / Baby",
7868
+ icon: "😢"
7869
+ },
7870
+ {
7871
+ id: "laughter",
7872
+ name: "Laughter",
7873
+ icon: "😂"
7874
+ },
7875
+ {
7876
+ id: "music",
7877
+ name: "Music",
7878
+ icon: "🎵"
7879
+ },
7880
+ {
7881
+ id: "dog",
7882
+ name: "Dog",
7883
+ icon: "🐕"
7884
+ },
7885
+ {
7886
+ id: "cat",
7887
+ name: "Cat",
7888
+ icon: "🐈"
7889
+ },
7890
+ {
7891
+ id: "bird",
7892
+ name: "Bird",
7893
+ icon: "🐦"
7894
+ },
7895
+ {
7896
+ id: "animal",
7897
+ name: "Animal (other)",
7898
+ icon: "🐾"
7899
+ },
7900
+ {
7901
+ id: "alarm",
7902
+ name: "Alarm / Siren",
7903
+ icon: "🚨"
7904
+ },
7905
+ {
7906
+ id: "doorbell",
7907
+ name: "Doorbell / Knock",
7908
+ icon: "🔔"
7909
+ },
7910
+ {
7911
+ id: "glass_breaking",
7912
+ name: "Glass Breaking",
7913
+ icon: "💥"
7914
+ },
7915
+ {
7916
+ id: "gunshot",
7917
+ name: "Gunshot / Explosion",
7918
+ icon: "💣"
7919
+ },
7920
+ {
7921
+ id: "vehicle",
7922
+ name: "Vehicle",
7923
+ icon: "🚗"
7924
+ },
7925
+ {
7926
+ id: "siren",
7927
+ name: "Emergency Siren",
7928
+ icon: "🚑"
7929
+ },
7930
+ {
7931
+ id: "fire",
7932
+ name: "Fire / Smoke",
7933
+ icon: "🔥"
7934
+ },
7935
+ {
7936
+ id: "water",
7937
+ name: "Water",
7938
+ icon: "💧"
7939
+ },
7940
+ {
7941
+ id: "wind",
7942
+ name: "Wind / Weather",
7943
+ icon: "🌬️"
7944
+ },
7945
+ {
7946
+ id: "door",
7947
+ name: "Door",
7948
+ icon: "🚪"
7949
+ },
7950
+ {
7951
+ id: "footsteps",
7952
+ name: "Footsteps",
7953
+ icon: "👣"
7954
+ },
7955
+ {
7956
+ id: "crowd",
7957
+ name: "Crowd / Chatter",
7958
+ icon: "👥"
7959
+ },
7960
+ {
7961
+ id: "telephone",
7962
+ name: "Telephone",
7963
+ icon: "📞"
7964
+ },
7965
+ {
7966
+ id: "engine",
7967
+ name: "Engine / Motor",
7968
+ icon: "⚙️"
7969
+ },
7970
+ {
7971
+ id: "tools",
7972
+ name: "Tools / Construction",
7973
+ icon: "🔨"
7974
+ },
7975
+ {
7976
+ id: "silence",
7977
+ name: "Silence",
7978
+ icon: "🤫"
7979
+ }
7980
+ ];
7816
7981
  var YAMNET_TO_MACRO = {
7817
7982
  mapping: {
7818
7983
  Speech: "speech",
@@ -8079,6 +8244,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8079
8244
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8080
8245
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8081
8246
  /**
8247
+ * Unified event-kind taxonomy — THE single source of truth for
8248
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8249
+ * icon }`.
8250
+ *
8251
+ * This dictionary folds together what used to be scattered across four
8252
+ * copies:
8253
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8254
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8255
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8256
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8257
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8258
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8259
+ *
8260
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8261
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8262
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8263
+ * color or an icon: they read this dictionary (server descriptors carry the
8264
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8265
+ *
8266
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8267
+ */
8268
+ var TAXONOMY_COLORS = {
8269
+ motion: "#f59e0b",
8270
+ audio: "#06b6d4",
8271
+ person: "#22c55e",
8272
+ vehicle: "#3b82f6",
8273
+ animal: "#f97316",
8274
+ package: "#a855f7",
8275
+ sensor: "#8b5cf6",
8276
+ control: "#10b981",
8277
+ genericDetection: "#64748b"
8278
+ };
8279
+ var DETECTION_SUB_COLORS = {
8280
+ car: "#f59e0b",
8281
+ truck: "#d97706",
8282
+ bus: "#b45309",
8283
+ motorcycle: "#eab308",
8284
+ bicycle: "#ca8a04",
8285
+ airplane: "#60a5fa",
8286
+ boat: "#2563eb",
8287
+ train: "#1d4ed8",
8288
+ bird: "#14b8a6",
8289
+ dog: "#84cc16",
8290
+ cat: "#f97316",
8291
+ horse: "#a16207",
8292
+ sheep: "#a3a3a3",
8293
+ cow: "#78716c",
8294
+ elephant: "#6b7280",
8295
+ bear: "#7c2d12",
8296
+ zebra: "#404040",
8297
+ giraffe: "#d4a373"
8298
+ };
8299
+ function titleCase(id) {
8300
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8301
+ }
8302
+ var entries = /* @__PURE__ */ new Map();
8303
+ function macro(kind, category, color, iconId, label) {
8304
+ entries.set(kind, {
8305
+ kind,
8306
+ parentKind: null,
8307
+ level: "macro",
8308
+ category,
8309
+ color,
8310
+ iconId,
8311
+ labelKey: `eventKind.${kind}`,
8312
+ label
8313
+ });
8314
+ }
8315
+ function sub(kind, parentKind, category, color, iconId, label) {
8316
+ entries.set(kind, {
8317
+ kind,
8318
+ parentKind,
8319
+ level: "sub",
8320
+ category,
8321
+ color,
8322
+ iconId,
8323
+ labelKey: `eventKind.${kind}`,
8324
+ label
8325
+ });
8326
+ }
8327
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8328
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8329
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8330
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8331
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8332
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8333
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8334
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8335
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8336
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8337
+ if (entries.has(cocoClass)) continue;
8338
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8339
+ }
8340
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8341
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8342
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8343
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8344
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8345
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8346
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8347
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8348
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8349
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8350
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8351
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8352
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8353
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8354
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8355
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8356
+ sub("button", "control", "control", "#10b981", "button", "Button");
8357
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8358
+ for (const l of AUDIO_MACRO_LABELS) {
8359
+ const kind = `audio-${l.id}`;
8360
+ if (entries.has(kind)) continue;
8361
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8362
+ }
8363
+ /** The complete taxonomy dictionary, keyed by kind. */
8364
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8365
+ /**
8082
8366
  * Error types for the safe expression engine. Two distinct classes so callers
8083
8367
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8084
8368
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -12271,10 +12555,7 @@ var ConfigUISchemaNullableBridge = custom();
12271
12555
  var InferenceCapabilitiesBridge = custom();
12272
12556
  var ModelAvailabilityListBridge = custom();
12273
12557
  var PipelineRunResultBridge = custom();
12274
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12275
- kind: "mutation",
12276
- auth: "admin"
12277
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12558
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12278
12559
  modelId: string(),
12279
12560
  settings: record(string(), unknown()).readonly()
12280
12561
  }))), method(object({ steps: record(string(), object({
@@ -12334,13 +12615,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12334
12615
  * (inputClasses ≠ null) are skipped and served per-track via
12335
12616
  * pipelineRunner.runDetailSubtree (two-plane design).
12336
12617
  */
12337
- plane: _enum(["full", "frame"]).optional()
12618
+ plane: _enum(["full", "frame"]).optional(),
12619
+ /**
12620
+ * Inference-device selector (Phase 2 multi-device). Format
12621
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12622
+ * Omitted ⇒ the runner's default device (current single-engine
12623
+ * behaviour). Selects WHICH device pool of the node runs the call.
12624
+ */
12625
+ deviceKey: string().optional()
12338
12626
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12339
12627
  engine: PipelineEngineChoiceSchema.optional(),
12340
12628
  steps: array(PipelineStepInputSchema).min(1),
12341
12629
  frames: array(FrameInputSchema).min(1).max(255),
12342
12630
  deviceId: number().optional(),
12343
- sessionId: string().optional()
12631
+ sessionId: string().optional(),
12632
+ /**
12633
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12634
+ * the batch to the Python pool's bench preprocess cache
12635
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12636
+ * preprocessed ONCE and every later inference is a pure-inference cache
12637
+ * hit — the sustained-throughput run measures inference, not
12638
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12639
+ * full preprocess every call, correct). Fresh per sustained run;
12640
+ * released via `uncacheFrame`.
12641
+ */
12642
+ frameId: number().int().nonnegative().optional(),
12643
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12644
+ deviceKey: string().optional()
12344
12645
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12345
12646
  data: _instanceof(Uint8Array),
12346
12647
  width: number().int().positive(),
@@ -12372,8 +12673,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12372
12673
  * - `runtime` — main camera-serving engine (no idle TTL).
12373
12674
  * - `warm-override` — benchmark/test override held in the warm
12374
12675
  * cache; auto-disposed after the idle TTL.
12676
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12677
+ * multi-device, keyed by `deviceKey`) resolved
12678
+ * via `resolveDeviceFactory`. Runs alongside the
12679
+ * `runtime` engine on a DIFFERENT accelerator
12680
+ * (NPU / iGPU / Coral) — this is how the
12681
+ * Engines tab shows all pools running at once.
12375
12682
  */
12376
- kind: _enum(["runtime", "warm-override"]),
12683
+ kind: _enum([
12684
+ "runtime",
12685
+ "warm-override",
12686
+ "device-pool"
12687
+ ]),
12377
12688
  /** Native pid of the underlying Python pool (null when no pool). */
12378
12689
  poolPid: number().nullable(),
12379
12690
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12748,7 +13059,14 @@ var RunnerCameraConfigSchema = object({
12748
13059
  * camera's detect node differs from its source-owner (P2d, gated by the
12749
13060
  * `remoteSourcingNodes` rollout setting).
12750
13061
  */
12751
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13062
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13063
+ /**
13064
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13065
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13066
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13067
+ * this only selects WHICH device pool of that node runs the session.
13068
+ */
13069
+ deviceKey: string().optional()
12752
13070
  });
12753
13071
  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;
12754
13072
  /**
@@ -12769,6 +13087,19 @@ var RunnerLocalLoadSchema = object({
12769
13087
  avgInferenceTimeMs: number(),
12770
13088
  /** Total queue depth across motion + detection queues. */
12771
13089
  queueDepthTotal: number(),
13090
+ /**
13091
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13092
+ * this runner currently has attached cameras on, so the orchestrator's second
13093
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13094
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13095
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13096
+ */
13097
+ devices: array(object({
13098
+ deviceKey: string(),
13099
+ backend: string(),
13100
+ attachedCameras: number(),
13101
+ queueDepthTotal: number()
13102
+ })).default([]),
12772
13103
  /** Hardware capability flags reported by this node. */
12773
13104
  hardware: object({
12774
13105
  hasGpu: boolean(),
@@ -15917,6 +16248,8 @@ var BaseDeviceProvider = class extends BaseAddon {
15917
16248
  return toDeviceSummary(device, this.addonId);
15918
16249
  }
15919
16250
  };
16251
+ 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;
16252
+ new Set(Object.values(DeviceType));
15920
16253
  /**
15921
16254
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
15922
16255
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -19096,17 +19429,30 @@ var EventKindCategorySchema = _enum([
19096
19429
  "audio",
19097
19430
  "detection",
19098
19431
  "sensor",
19432
+ "control",
19099
19433
  "custom",
19100
19434
  "package"
19101
19435
  ]);
19436
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
19437
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
19102
19438
  var EventKindDescriptorSchema = object({
19103
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
19439
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
19104
19440
  kind: string(),
19441
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
19442
+ labelKey: string(),
19443
+ /** English fallback label (kept for clients that don't translate). */
19105
19444
  label: string(),
19106
19445
  /** Hex color for timeline/legend rendering. */
19107
19446
  color: string(),
19447
+ /** Dictionary id → lucide component on the UI side. */
19448
+ iconId: string(),
19449
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
19108
19450
  icon: EventKindIconSchema,
19109
19451
  category: EventKindCategorySchema,
19452
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
19453
+ parentKind: string().nullable(),
19454
+ /** Derived from `parentKind`, explicit for the client tree. */
19455
+ level: EventKindLevelSchema,
19110
19456
  /** Which cap + device contributes this kind. For built-ins the camera
19111
19457
  * itself; for sensor kinds the LINKED source device. */
19112
19458
  source: object({
@@ -19179,11 +19525,21 @@ var TrackAudioLabelSchema = object({
19179
19525
  firstAt: number(),
19180
19526
  lastAt: number()
19181
19527
  });
19528
+ /**
19529
+ * How a track was produced. `pipeline` (default / absent) = the spatial
19530
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
19531
+ * linked sensor/control state change (no positions; carries a snapshot). The
19532
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
19533
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
19534
+ */
19535
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
19182
19536
  var TrackSchema = object({
19183
19537
  trackId: string(),
19184
19538
  deviceId: number(),
19185
19539
  className: string(),
19186
19540
  label: string().optional(),
19541
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19542
+ source: TrackSourceSchema.optional(),
19187
19543
  firstSeen: number(),
19188
19544
  lastSeen: number(),
19189
19545
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -19560,6 +19916,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
19560
19916
  eventId: string(),
19561
19917
  timestamp: number()
19562
19918
  });
19919
+ /**
19920
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
19921
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
19922
+ * caps into per-camera event-kind descriptors.
19923
+ *
19924
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
19925
+ * is NOT duplicated here — every entry is derived from the single
19926
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
19927
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
19928
+ * control cap means adding one line here (and a taxonomy entry); the anti-
19929
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
19930
+ * eventful cap is missing.
19931
+ */
19932
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
19933
+ var LEGACY_ICON = {
19934
+ motion: "motion",
19935
+ audio: "audio",
19936
+ person: "person",
19937
+ vehicle: "vehicle",
19938
+ animal: "animal",
19939
+ package: "package",
19940
+ door: "door",
19941
+ pir: "pir",
19942
+ smoke: "smoke",
19943
+ water: "water",
19944
+ button: "button",
19945
+ generic: "generic",
19946
+ gas: "smoke",
19947
+ vibration: "generic",
19948
+ tamper: "generic",
19949
+ presence: "person",
19950
+ lock: "generic",
19951
+ siren: "generic",
19952
+ switch: "generic",
19953
+ doorbell: "button"
19954
+ };
19955
+ function legacyIcon(iconId) {
19956
+ return LEGACY_ICON[iconId] ?? "generic";
19957
+ }
19958
+ /**
19959
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
19960
+ * The anti-drift guard cross-checks this against the eventful caps declared
19961
+ * in `packages/types/src/capabilities/*.cap.ts`.
19962
+ */
19963
+ var CAP_TO_KIND = {
19964
+ contact: "contact",
19965
+ motion: "motion-sensor",
19966
+ smoke: "smoke",
19967
+ flood: "flood",
19968
+ gas: "gas",
19969
+ "carbon-monoxide": "carbon-monoxide",
19970
+ vibration: "vibration",
19971
+ tamper: "tamper",
19972
+ presence: "presence",
19973
+ "enum-sensor": "enum-sensor",
19974
+ "event-emitter": "device-event",
19975
+ "lock-control": "lock",
19976
+ switch: "switch",
19977
+ button: "button",
19978
+ doorbell: "doorbell"
19979
+ };
19980
+ function buildDescriptor(capName, kind) {
19981
+ const t = EVENT_TAXONOMY[kind];
19982
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
19983
+ return {
19984
+ ...t,
19985
+ icon: legacyIcon(t.iconId)
19986
+ };
19987
+ }
19988
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
19563
19989
  var CameraPipelineConfigSchema = object({
19564
19990
  engine: PipelineEngineChoiceSchema.optional(),
19565
19991
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19578,13 +20004,11 @@ var PipelineTemplateSchema = object({
19578
20004
  createdAt: string(),
19579
20005
  updatedAt: string()
19580
20006
  });
19581
- var AgentAddonConfigSchema = object({
19582
- enabled: boolean(),
20007
+ var DeviceStepConfigSchema = object({
19583
20008
  modelId: string().optional(),
19584
- settings: record(string(), unknown()).readonly()
20009
+ settings: record(string(), unknown()).optional()
19585
20010
  });
19586
20011
  var AgentPipelineSettingsSchema = object({
19587
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
19588
20012
  maxCameras: number().int().nonnegative().nullable().default(null),
19589
20013
  /** Per-node detection weight (relative share for the quota balancer). */
19590
20014
  detectWeight: number().positive().optional(),
@@ -19608,7 +20032,22 @@ var AgentPipelineSettingsSchema = object({
19608
20032
  * it already uses to reach the hub). Set this only when the auto-detected
19609
20033
  * address is wrong (multi-homed host, NAT, custom interface).
19610
20034
  */
19611
- reachableHost: string().optional()
20035
+ reachableHost: string().optional(),
20036
+ /**
20037
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20038
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20039
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20040
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20041
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20042
+ * the default model/settings for every camera landing on that accelerator;
20043
+ * a stepId absent ⇒ the step uses that device's format default.
20044
+ */
20045
+ inferenceDevices: record(string(), object({
20046
+ enabled: boolean(),
20047
+ weight: number().positive().optional(),
20048
+ maxSessions: number().int().positive().optional(),
20049
+ steps: record(string(), DeviceStepConfigSchema).optional()
20050
+ })).optional()
19612
20051
  });
19613
20052
  var CameraPipelineForAgentSchema = object({
19614
20053
  steps: array(PipelineStepInputSchema).readonly(),
@@ -19618,14 +20057,13 @@ var CameraPipelineForAgentSchema = object({
19618
20057
  }).nullable()
19619
20058
  });
19620
20059
  var CameraStepOverridePatchSchema = object({
19621
- enabled: boolean().optional(),
19622
20060
  modelId: string().optional(),
19623
20061
  settings: record(string(), unknown()).readonly().optional()
19624
20062
  });
19625
20063
  var CameraPipelineSettingsSchema = object({
19626
20064
  pinnedAgentNodeId: string().optional(),
19627
20065
  stepToggles: record(string(), boolean()).optional(),
19628
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20066
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
19629
20067
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
19630
20068
  });
19631
20069
  /**
@@ -19839,6 +20277,44 @@ var CameraStatusSchema = object({
19839
20277
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
19840
20278
  fetchedAt: number()
19841
20279
  });
20280
+ var NodeInferenceDeviceSchema = object({
20281
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20282
+ key: string(),
20283
+ backend: string(),
20284
+ device: string(),
20285
+ format: _enum(MODEL_FORMATS),
20286
+ /** Whether the node's live probe reports the device as usable right now. */
20287
+ available: boolean(),
20288
+ /**
20289
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20290
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20291
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20292
+ * not a balanced target). An explicit stored value always wins; a stored-only
20293
+ * (unavailable) key keeps its stored value.
20294
+ */
20295
+ enabled: boolean(),
20296
+ /** Relative balancer weight for the enabled device (default 1). */
20297
+ weight: number(),
20298
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20299
+ maxSessions: number().nullable(),
20300
+ /** Object-detection model the executor defaults to for this deviceKey. */
20301
+ defaultModelId: string(),
20302
+ /**
20303
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20304
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20305
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20306
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20307
+ * available per format; this is the stored selection that becomes the
20308
+ * default for EVERY camera landing on this accelerator.
20309
+ */
20310
+ steps: record(string(), DeviceStepConfigSchema).optional()
20311
+ });
20312
+ var NodeInferenceDevicesSchema = object({
20313
+ nodeId: string(),
20314
+ /** False when the node's platform-probe was unreachable (no live device set). */
20315
+ reachable: boolean(),
20316
+ devices: array(NodeInferenceDeviceSchema).readonly()
20317
+ });
19842
20318
  method(object({
19843
20319
  deviceId: number(),
19844
20320
  agentNodeId: string()
@@ -19848,7 +20324,13 @@ method(object({
19848
20324
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
19849
20325
  kind: "mutation",
19850
20326
  auth: "admin"
19851
- }), method(_void(), object({ migrated: number() }), {
20327
+ }), method(object({
20328
+ deviceId: number(),
20329
+ deviceKey: string()
20330
+ }), object({ success: literal(true) }), {
20331
+ kind: "mutation",
20332
+ auth: "admin"
20333
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
19852
20334
  kind: "mutation",
19853
20335
  auth: "admin"
19854
20336
  }), 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({
@@ -19882,13 +20364,7 @@ method(object({
19882
20364
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
19883
20365
  nodeId: string(),
19884
20366
  settings: AgentPipelineSettingsSchema
19885
- })).readonly()), method(object({
19886
- agentNodeId: string(),
19887
- defaults: record(string(), AgentAddonConfigSchema)
19888
- }), object({ success: literal(true) }), {
19889
- kind: "mutation",
19890
- auth: "admin"
19891
- }), method(object({ agentNodeId: string() }), object({
20367
+ })).readonly()), method(object({ agentNodeId: string() }), object({
19892
20368
  success: boolean(),
19893
20369
  removed: boolean()
19894
20370
  }), {
@@ -19920,7 +20396,18 @@ method(object({
19920
20396
  }), object({ success: literal(true) }), {
19921
20397
  kind: "mutation",
19922
20398
  auth: "admin"
19923
- }), method(object({ agentNodeId: string() }), object({
20399
+ }), method(object({
20400
+ agentNodeId: string(),
20401
+ inferenceDevices: record(string(), object({
20402
+ enabled: boolean(),
20403
+ weight: number().positive().optional(),
20404
+ maxSessions: number().int().positive().optional(),
20405
+ steps: record(string(), DeviceStepConfigSchema).optional()
20406
+ }))
20407
+ }), object({ success: literal(true) }), {
20408
+ kind: "mutation",
20409
+ auth: "admin"
20410
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
19924
20411
  success: literal(true),
19925
20412
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
19926
20413
  effectiveModelId: string().nullable(),
@@ -19936,9 +20423,10 @@ method(object({
19936
20423
  }), object({ success: literal(true) }), {
19937
20424
  kind: "mutation",
19938
20425
  auth: "admin"
19939
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20426
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
19940
20427
  deviceId: number(),
19941
20428
  agentNodeId: string(),
20429
+ deviceKey: string(),
19942
20430
  addonId: string(),
19943
20431
  patch: CameraStepOverridePatchSchema.nullable()
19944
20432
  }), object({ success: literal(true) }), {
@@ -19975,14 +20463,13 @@ method(object({
19975
20463
  });
19976
20464
  /**
19977
20465
  * server-management — per-NODE singleton capability for a node's ROOT
19978
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
19979
- * agents).
20466
+ * package lifecycle (runtime-updatable node packages).
19980
20467
  *
19981
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
19982
- * on agents) carries the whole software stack in its npm dep tree, so ONE
19983
- * version describes the node. Updates install into
19984
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
19985
- * starter (probation boot + auto-rollback to N-1).
20468
+ * Every node role runs the SAME root package (`@camstack/server`), which
20469
+ * carries the whole software stack in its npm dep tree, so ONE version
20470
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20471
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20472
+ * no auto-rollback).
19986
20473
  *
19987
20474
  * Providers:
19988
20475
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20090,7 +20577,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20090
20577
  /** Explicit target version; omitted = latest from the registry. */
20091
20578
  version: string().optional() }), ServerUpdateActionResultSchema, {
20092
20579
  kind: "mutation",
20093
- auth: "admin"
20580
+ auth: "admin",
20581
+ timeoutMs: 16 * 6e4
20094
20582
  }), method(_void(), ServerUpdateActionResultSchema, {
20095
20583
  kind: "mutation",
20096
20584
  auth: "admin"
@@ -21134,22 +21622,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21134
21622
  var RestartAddonResultSchema = unknown();
21135
21623
  var InstallPackageResultSchema = unknown();
21136
21624
  var ReloadPackagesResultSchema = unknown();
21137
- /**
21138
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21139
- * server restarts so the admin UI can react to the `restartingAt`
21140
- * timestamp (shows reconnect overlay). The transition from
21141
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21142
- * `system.restart-completed` event after the new process boots.
21143
- *
21144
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21145
- */
21146
- var UpdateFrameworkPackageResultSchema = object({
21147
- packageName: string(),
21148
- fromVersion: string(),
21149
- toVersion: string(),
21150
- /** Ms-epoch the server scheduled its self-restart. */
21151
- restartingAt: number()
21152
- });
21153
21625
  var BulkUpdateItemStatusSchema = _enum([
21154
21626
  "queued",
21155
21627
  "updating",
@@ -21277,13 +21749,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21277
21749
  }), object({ success: literal(true) }), {
21278
21750
  kind: "mutation",
21279
21751
  auth: "admin"
21280
- }), method(object({
21281
- packageName: string().min(1),
21282
- version: string().optional(),
21283
- deferRestart: boolean().optional()
21284
- }), UpdateFrameworkPackageResultSchema, {
21285
- kind: "mutation",
21286
- auth: "admin"
21287
21752
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21288
21753
  kind: "mutation",
21289
21754
  auth: "admin"
@@ -22166,10 +22631,10 @@ var TopologyCategorySchema = object({
22166
22631
  addons: array(TopologyCategoryAddonSchema).readonly()
22167
22632
  });
22168
22633
  /**
22169
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22170
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22171
- * version visibility for the Server management surface. Nullable: offline
22172
- * rows and pre-phase-2 nodes report none.
22634
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22635
+ * root package for every node role) as reported by its `registerNode`
22636
+ * manifest — version visibility for the Server management surface. Nullable:
22637
+ * offline rows and nodes that never reported one.
22173
22638
  */
22174
22639
  var TopologyRootPackageSchema = object({
22175
22640
  name: string(),
@@ -22557,17 +23022,28 @@ var PlatformScoreSchema = object({
22557
23022
  format: _enum([
22558
23023
  "onnx",
22559
23024
  "coreml",
22560
- "openvino"
23025
+ "openvino",
23026
+ "tflite"
22561
23027
  ]),
22562
23028
  score: number(),
22563
23029
  reason: string(),
22564
23030
  available: boolean()
22565
23031
  });
23032
+ var InferenceDeviceDescriptorSchema = object({
23033
+ key: string(),
23034
+ backend: string(),
23035
+ device: string(),
23036
+ format: ModelFormatSchema,
23037
+ runtime: literal("python"),
23038
+ score: number(),
23039
+ available: boolean()
23040
+ });
22566
23041
  var PlatformCapabilitiesSchema = object({
22567
23042
  hardware: HardwareInfoSchema,
22568
23043
  scores: array(PlatformScoreSchema).readonly(),
22569
23044
  bestScore: PlatformScoreSchema,
22570
- pythonPath: string().nullable()
23045
+ pythonPath: string().nullable(),
23046
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
22571
23047
  });
22572
23048
  var ModelRequirementSchema = object({
22573
23049
  modelId: string(),
@@ -23446,12 +23922,6 @@ Object.freeze({
23446
23922
  addonId: null,
23447
23923
  access: "delete"
23448
23924
  },
23449
- "addons.updateFrameworkPackage": {
23450
- capName: "addons",
23451
- capScope: "system",
23452
- addonId: null,
23453
- access: "create"
23454
- },
23455
23925
  "addons.updatePackage": {
23456
23926
  capName: "addons",
23457
23927
  capScope: "system",
@@ -26224,12 +26694,6 @@ Object.freeze({
26224
26694
  addonId: null,
26225
26695
  access: "view"
26226
26696
  },
26227
- "pipelineExecutor.reprobeEngine": {
26228
- capName: "pipeline-executor",
26229
- capScope: "system",
26230
- addonId: null,
26231
- access: "create"
26232
- },
26233
26697
  "pipelineExecutor.runAudioTest": {
26234
26698
  capName: "pipeline-executor",
26235
26699
  capScope: "system",
@@ -26380,6 +26844,12 @@ Object.freeze({
26380
26844
  addonId: null,
26381
26845
  access: "view"
26382
26846
  },
26847
+ "pipelineOrchestrator.getNodeInferenceDevices": {
26848
+ capName: "pipeline-orchestrator",
26849
+ capScope: "system",
26850
+ addonId: null,
26851
+ access: "view"
26852
+ },
26383
26853
  "pipelineOrchestrator.getPipelineAssignment": {
26384
26854
  capName: "pipeline-orchestrator",
26385
26855
  capScope: "system",
@@ -26392,6 +26862,12 @@ Object.freeze({
26392
26862
  addonId: null,
26393
26863
  access: "view"
26394
26864
  },
26865
+ "pipelineOrchestrator.getPipelineDevicePin": {
26866
+ capName: "pipeline-orchestrator",
26867
+ capScope: "system",
26868
+ addonId: null,
26869
+ access: "view"
26870
+ },
26395
26871
  "pipelineOrchestrator.listAgentSettings": {
26396
26872
  capName: "pipeline-orchestrator",
26397
26873
  capScope: "system",
@@ -26434,19 +26910,19 @@ Object.freeze({
26434
26910
  addonId: null,
26435
26911
  access: "create"
26436
26912
  },
26437
- "pipelineOrchestrator.setAgentAddonDefaults": {
26913
+ "pipelineOrchestrator.setAgentCapabilities": {
26438
26914
  capName: "pipeline-orchestrator",
26439
26915
  capScope: "system",
26440
26916
  addonId: null,
26441
26917
  access: "create"
26442
26918
  },
26443
- "pipelineOrchestrator.setAgentCapabilities": {
26919
+ "pipelineOrchestrator.setAgentDetectWeight": {
26444
26920
  capName: "pipeline-orchestrator",
26445
26921
  capScope: "system",
26446
26922
  addonId: null,
26447
26923
  access: "create"
26448
26924
  },
26449
- "pipelineOrchestrator.setAgentDetectWeight": {
26925
+ "pipelineOrchestrator.setAgentInferenceDevices": {
26450
26926
  capName: "pipeline-orchestrator",
26451
26927
  capScope: "system",
26452
26928
  addonId: null,
@@ -26488,6 +26964,12 @@ Object.freeze({
26488
26964
  addonId: null,
26489
26965
  access: "create"
26490
26966
  },
26967
+ "pipelineOrchestrator.setPipelineDevicePin": {
26968
+ capName: "pipeline-orchestrator",
26969
+ capScope: "system",
26970
+ addonId: null,
26971
+ access: "create"
26972
+ },
26491
26973
  "pipelineOrchestrator.unassignAudio": {
26492
26974
  capName: "pipeline-orchestrator",
26493
26975
  capScope: "system",
@@ -28040,32 +28522,6 @@ Object.freeze({
28040
28522
  "network-access": "ingress",
28041
28523
  "smtp-provider": "email"
28042
28524
  });
28043
- var frameworkSwapPackageSchema = object({
28044
- name: string(),
28045
- stagedPath: string(),
28046
- backupPath: string(),
28047
- toVersion: string(),
28048
- fromVersion: string().nullable()
28049
- });
28050
- object({
28051
- jobId: string(),
28052
- taskId: string(),
28053
- packages: array(frameworkSwapPackageSchema),
28054
- requestedAtMs: number(),
28055
- schemaVersion: literal(1)
28056
- });
28057
- object({
28058
- jobId: string(),
28059
- taskId: string(),
28060
- backups: array(object({
28061
- name: string(),
28062
- backupPath: string(),
28063
- livePath: string()
28064
- })),
28065
- appliedAtMs: number(),
28066
- bootAttempts: number(),
28067
- schemaVersion: literal(1)
28068
- });
28069
28525
  //#endregion
28070
28526
  //#region src/config.ts
28071
28527
  /**