@camstack/addon-export-alexa 1.1.26 → 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.
@@ -7092,6 +7092,17 @@ var ModelCatalogEntrySchema = object({
7092
7092
  "imagenet",
7093
7093
  "none"
7094
7094
  ]).optional(),
7095
+ /**
7096
+ * The model already applies softmax IN-GRAPH — its raw output is a
7097
+ * probability distribution, not logits. When set, the `softmax`
7098
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7099
+ * probability vector collapses it toward uniform (top-1 score craters far
7100
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7101
+ * the output is raw logits and the postprocessor applies softmax (the normal
7102
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7103
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7104
+ */
7105
+ outputProbabilities: boolean().optional(),
7095
7106
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7096
7107
  /**
7097
7108
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -7822,6 +7833,160 @@ function pickClosestResolution(entries, target) {
7822
7833
  }
7823
7834
  return bestAbove?.entry ?? bestBelow?.entry;
7824
7835
  }
7836
+ var COCO_TO_MACRO = {
7837
+ mapping: {
7838
+ person: "person",
7839
+ bicycle: "vehicle",
7840
+ car: "vehicle",
7841
+ motorcycle: "vehicle",
7842
+ airplane: "vehicle",
7843
+ bus: "vehicle",
7844
+ train: "vehicle",
7845
+ truck: "vehicle",
7846
+ boat: "vehicle",
7847
+ bird: "animal",
7848
+ cat: "animal",
7849
+ dog: "animal",
7850
+ horse: "animal",
7851
+ sheep: "animal",
7852
+ cow: "animal",
7853
+ elephant: "animal",
7854
+ bear: "animal",
7855
+ zebra: "animal",
7856
+ giraffe: "animal",
7857
+ suitcase: "package",
7858
+ backpack: "package",
7859
+ handbag: "package"
7860
+ },
7861
+ preserveOriginal: false
7862
+ };
7863
+ var AUDIO_MACRO_LABELS = [
7864
+ {
7865
+ id: "speech",
7866
+ name: "Speech",
7867
+ icon: "🗣️"
7868
+ },
7869
+ {
7870
+ id: "scream",
7871
+ name: "Scream / Shout",
7872
+ icon: "😱"
7873
+ },
7874
+ {
7875
+ id: "crying",
7876
+ name: "Crying / Baby",
7877
+ icon: "😢"
7878
+ },
7879
+ {
7880
+ id: "laughter",
7881
+ name: "Laughter",
7882
+ icon: "😂"
7883
+ },
7884
+ {
7885
+ id: "music",
7886
+ name: "Music",
7887
+ icon: "🎵"
7888
+ },
7889
+ {
7890
+ id: "dog",
7891
+ name: "Dog",
7892
+ icon: "🐕"
7893
+ },
7894
+ {
7895
+ id: "cat",
7896
+ name: "Cat",
7897
+ icon: "🐈"
7898
+ },
7899
+ {
7900
+ id: "bird",
7901
+ name: "Bird",
7902
+ icon: "🐦"
7903
+ },
7904
+ {
7905
+ id: "animal",
7906
+ name: "Animal (other)",
7907
+ icon: "🐾"
7908
+ },
7909
+ {
7910
+ id: "alarm",
7911
+ name: "Alarm / Siren",
7912
+ icon: "🚨"
7913
+ },
7914
+ {
7915
+ id: "doorbell",
7916
+ name: "Doorbell / Knock",
7917
+ icon: "🔔"
7918
+ },
7919
+ {
7920
+ id: "glass_breaking",
7921
+ name: "Glass Breaking",
7922
+ icon: "💥"
7923
+ },
7924
+ {
7925
+ id: "gunshot",
7926
+ name: "Gunshot / Explosion",
7927
+ icon: "💣"
7928
+ },
7929
+ {
7930
+ id: "vehicle",
7931
+ name: "Vehicle",
7932
+ icon: "🚗"
7933
+ },
7934
+ {
7935
+ id: "siren",
7936
+ name: "Emergency Siren",
7937
+ icon: "🚑"
7938
+ },
7939
+ {
7940
+ id: "fire",
7941
+ name: "Fire / Smoke",
7942
+ icon: "🔥"
7943
+ },
7944
+ {
7945
+ id: "water",
7946
+ name: "Water",
7947
+ icon: "💧"
7948
+ },
7949
+ {
7950
+ id: "wind",
7951
+ name: "Wind / Weather",
7952
+ icon: "🌬️"
7953
+ },
7954
+ {
7955
+ id: "door",
7956
+ name: "Door",
7957
+ icon: "🚪"
7958
+ },
7959
+ {
7960
+ id: "footsteps",
7961
+ name: "Footsteps",
7962
+ icon: "👣"
7963
+ },
7964
+ {
7965
+ id: "crowd",
7966
+ name: "Crowd / Chatter",
7967
+ icon: "👥"
7968
+ },
7969
+ {
7970
+ id: "telephone",
7971
+ name: "Telephone",
7972
+ icon: "📞"
7973
+ },
7974
+ {
7975
+ id: "engine",
7976
+ name: "Engine / Motor",
7977
+ icon: "⚙️"
7978
+ },
7979
+ {
7980
+ id: "tools",
7981
+ name: "Tools / Construction",
7982
+ icon: "🔨"
7983
+ },
7984
+ {
7985
+ id: "silence",
7986
+ name: "Silence",
7987
+ icon: "🤫"
7988
+ }
7989
+ ];
7825
7990
  var YAMNET_TO_MACRO = {
7826
7991
  mapping: {
7827
7992
  Speech: "speech",
@@ -8088,6 +8253,125 @@ var _macroLookup = /* @__PURE__ */ new Map();
8088
8253
  for (const [k, v] of Object.entries(YAMNET_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8089
8254
  for (const [k, v] of Object.entries(APPLE_SA_TO_MACRO.mapping)) _macroLookup.set(k.toLowerCase(), v);
8090
8255
  /**
8256
+ * Unified event-kind taxonomy — THE single source of truth for
8257
+ * `kind → { parentKind, category, level, color, iconId, labelKey, label,
8258
+ * icon }`.
8259
+ *
8260
+ * This dictionary folds together what used to be scattered across four
8261
+ * copies:
8262
+ * - `capabilities/sensor-event-kinds.ts` (sensor cap colors)
8263
+ * - `addon-post-analysis/.../services/event-kinds.ts`
8264
+ * (MOTION/PERSON/VEHICLE… _COLOR constants)
8265
+ * - `ui-library/composites/detection-colors.ts` (CLASS_COLORS)
8266
+ * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
8267
+ * - the COCO / audio class maps (macro ↔ sub relationships)
8268
+ *
8269
+ * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
8270
+ * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
8271
+ * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
8272
+ * color or an icon: they read this dictionary (server descriptors carry the
8273
+ * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
8274
+ *
8275
+ * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
8276
+ */
8277
+ var TAXONOMY_COLORS = {
8278
+ motion: "#f59e0b",
8279
+ audio: "#06b6d4",
8280
+ person: "#22c55e",
8281
+ vehicle: "#3b82f6",
8282
+ animal: "#f97316",
8283
+ package: "#a855f7",
8284
+ sensor: "#8b5cf6",
8285
+ control: "#10b981",
8286
+ genericDetection: "#64748b"
8287
+ };
8288
+ var DETECTION_SUB_COLORS = {
8289
+ car: "#f59e0b",
8290
+ truck: "#d97706",
8291
+ bus: "#b45309",
8292
+ motorcycle: "#eab308",
8293
+ bicycle: "#ca8a04",
8294
+ airplane: "#60a5fa",
8295
+ boat: "#2563eb",
8296
+ train: "#1d4ed8",
8297
+ bird: "#14b8a6",
8298
+ dog: "#84cc16",
8299
+ cat: "#f97316",
8300
+ horse: "#a16207",
8301
+ sheep: "#a3a3a3",
8302
+ cow: "#78716c",
8303
+ elephant: "#6b7280",
8304
+ bear: "#7c2d12",
8305
+ zebra: "#404040",
8306
+ giraffe: "#d4a373"
8307
+ };
8308
+ function titleCase(id) {
8309
+ return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
8310
+ }
8311
+ var entries = /* @__PURE__ */ new Map();
8312
+ function macro(kind, category, color, iconId, label) {
8313
+ entries.set(kind, {
8314
+ kind,
8315
+ parentKind: null,
8316
+ level: "macro",
8317
+ category,
8318
+ color,
8319
+ iconId,
8320
+ labelKey: `eventKind.${kind}`,
8321
+ label
8322
+ });
8323
+ }
8324
+ function sub(kind, parentKind, category, color, iconId, label) {
8325
+ entries.set(kind, {
8326
+ kind,
8327
+ parentKind,
8328
+ level: "sub",
8329
+ category,
8330
+ color,
8331
+ iconId,
8332
+ labelKey: `eventKind.${kind}`,
8333
+ label
8334
+ });
8335
+ }
8336
+ macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
8337
+ macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
8338
+ macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
8339
+ macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
8340
+ macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
8341
+ macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
8342
+ macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
8343
+ macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
8344
+ for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
8345
+ if (macroClass !== "vehicle" && macroClass !== "animal") continue;
8346
+ if (entries.has(cocoClass)) continue;
8347
+ sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
8348
+ }
8349
+ sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
8350
+ sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
8351
+ sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
8352
+ sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
8353
+ sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
8354
+ sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
8355
+ sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
8356
+ sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
8357
+ sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
8358
+ sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
8359
+ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
8360
+ sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
8361
+ sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
8362
+ sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
8363
+ sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
8364
+ sub("siren", "control", "control", "#dc2626", "siren", "Siren");
8365
+ sub("button", "control", "control", "#10b981", "button", "Button");
8366
+ sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
8367
+ for (const l of AUDIO_MACRO_LABELS) {
8368
+ const kind = `audio-${l.id}`;
8369
+ if (entries.has(kind)) continue;
8370
+ sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
8371
+ }
8372
+ /** The complete taxonomy dictionary, keyed by kind. */
8373
+ var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
8374
+ /**
8091
8375
  * Error types for the safe expression engine. Two distinct classes so callers
8092
8376
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
8093
8377
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -11003,10 +11287,7 @@ var ConfigUISchemaNullableBridge = custom();
11003
11287
  var InferenceCapabilitiesBridge = custom();
11004
11288
  var ModelAvailabilityListBridge = custom();
11005
11289
  var PipelineRunResultBridge = custom();
11006
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11007
- kind: "mutation",
11008
- auth: "admin"
11009
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11290
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11010
11291
  modelId: string(),
11011
11292
  settings: record(string(), unknown()).readonly()
11012
11293
  }))), method(object({ steps: record(string(), object({
@@ -11066,13 +11347,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11066
11347
  * (inputClasses ≠ null) are skipped and served per-track via
11067
11348
  * pipelineRunner.runDetailSubtree (two-plane design).
11068
11349
  */
11069
- plane: _enum(["full", "frame"]).optional()
11350
+ plane: _enum(["full", "frame"]).optional(),
11351
+ /**
11352
+ * Inference-device selector (Phase 2 multi-device). Format
11353
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11354
+ * Omitted ⇒ the runner's default device (current single-engine
11355
+ * behaviour). Selects WHICH device pool of the node runs the call.
11356
+ */
11357
+ deviceKey: string().optional()
11070
11358
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11071
11359
  engine: PipelineEngineChoiceSchema.optional(),
11072
11360
  steps: array(PipelineStepInputSchema).min(1),
11073
11361
  frames: array(FrameInputSchema).min(1).max(255),
11074
11362
  deviceId: number().optional(),
11075
- sessionId: string().optional()
11363
+ sessionId: string().optional(),
11364
+ /**
11365
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11366
+ * the batch to the Python pool's bench preprocess cache
11367
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11368
+ * preprocessed ONCE and every later inference is a pure-inference cache
11369
+ * hit — the sustained-throughput run measures inference, not
11370
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11371
+ * full preprocess every call, correct). Fresh per sustained run;
11372
+ * released via `uncacheFrame`.
11373
+ */
11374
+ frameId: number().int().nonnegative().optional(),
11375
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11376
+ deviceKey: string().optional()
11076
11377
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11077
11378
  data: _instanceof(Uint8Array),
11078
11379
  width: number().int().positive(),
@@ -11104,8 +11405,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11104
11405
  * - `runtime` — main camera-serving engine (no idle TTL).
11105
11406
  * - `warm-override` — benchmark/test override held in the warm
11106
11407
  * cache; auto-disposed after the idle TTL.
11408
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11409
+ * multi-device, keyed by `deviceKey`) resolved
11410
+ * via `resolveDeviceFactory`. Runs alongside the
11411
+ * `runtime` engine on a DIFFERENT accelerator
11412
+ * (NPU / iGPU / Coral) — this is how the
11413
+ * Engines tab shows all pools running at once.
11107
11414
  */
11108
- kind: _enum(["runtime", "warm-override"]),
11415
+ kind: _enum([
11416
+ "runtime",
11417
+ "warm-override",
11418
+ "device-pool"
11419
+ ]),
11109
11420
  /** Native pid of the underlying Python pool (null when no pool). */
11110
11421
  poolPid: number().nullable(),
11111
11422
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11447,7 +11758,14 @@ var RunnerCameraConfigSchema = object({
11447
11758
  * camera's detect node differs from its source-owner (P2d, gated by the
11448
11759
  * `remoteSourcingNodes` rollout setting).
11449
11760
  */
11450
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11761
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11762
+ /**
11763
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11764
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11765
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11766
+ * this only selects WHICH device pool of that node runs the session.
11767
+ */
11768
+ deviceKey: string().optional()
11451
11769
  });
11452
11770
  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;
11453
11771
  /**
@@ -11468,6 +11786,19 @@ var RunnerLocalLoadSchema = object({
11468
11786
  avgInferenceTimeMs: number(),
11469
11787
  /** Total queue depth across motion + detection queues. */
11470
11788
  queueDepthTotal: number(),
11789
+ /**
11790
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11791
+ * this runner currently has attached cameras on, so the orchestrator's second
11792
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11793
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11794
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11795
+ */
11796
+ devices: array(object({
11797
+ deviceKey: string(),
11798
+ backend: string(),
11799
+ attachedCameras: number(),
11800
+ queueDepthTotal: number()
11801
+ })).default([]),
11471
11802
  /** Hardware capability flags reported by this node. */
11472
11803
  hardware: object({
11473
11804
  hasGpu: boolean(),
@@ -12943,6 +13274,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
12943
13274
  kind: "mutation",
12944
13275
  auth: "admin"
12945
13276
  });
13277
+ 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;
13278
+ new Set(Object.values(DeviceType));
12946
13279
  /**
12947
13280
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
12948
13281
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -16108,17 +16441,30 @@ var EventKindCategorySchema = _enum([
16108
16441
  "audio",
16109
16442
  "detection",
16110
16443
  "sensor",
16444
+ "control",
16111
16445
  "custom",
16112
16446
  "package"
16113
16447
  ]);
16448
+ /** Taxonomy level — macro (timeline lane) vs sub (events-page leaf). */
16449
+ var EventKindLevelSchema = _enum(["macro", "sub"]);
16114
16450
  var EventKindDescriptorSchema = object({
16115
- /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
16451
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
16116
16452
  kind: string(),
16453
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
16454
+ labelKey: string(),
16455
+ /** English fallback label (kept for clients that don't translate). */
16117
16456
  label: string(),
16118
16457
  /** Hex color for timeline/legend rendering. */
16119
16458
  color: string(),
16459
+ /** Dictionary id → lucide component on the UI side. */
16460
+ iconId: string(),
16461
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
16120
16462
  icon: EventKindIconSchema,
16121
16463
  category: EventKindCategorySchema,
16464
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
16465
+ parentKind: string().nullable(),
16466
+ /** Derived from `parentKind`, explicit for the client tree. */
16467
+ level: EventKindLevelSchema,
16122
16468
  /** Which cap + device contributes this kind. For built-ins the camera
16123
16469
  * itself; for sensor kinds the LINKED source device. */
16124
16470
  source: object({
@@ -16191,11 +16537,21 @@ var TrackAudioLabelSchema = object({
16191
16537
  firstAt: number(),
16192
16538
  lastAt: number()
16193
16539
  });
16540
+ /**
16541
+ * How a track was produced. `pipeline` (default / absent) = the spatial
16542
+ * detection+tracking pipeline. `sensor` = a SYNTHETIC track projected from a
16543
+ * linked sensor/control state change (no positions; carries a snapshot). The
16544
+ * spatial subsystems (tracker association, occupancy count, re-id/embedding,
16545
+ * resurrection) MUST skip `sensor` tracks — they have no bbox trajectory.
16546
+ */
16547
+ var TrackSourceSchema = _enum(["pipeline", "sensor"]);
16194
16548
  var TrackSchema = object({
16195
16549
  trackId: string(),
16196
16550
  deviceId: number(),
16197
16551
  className: string(),
16198
16552
  label: string().optional(),
16553
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16554
+ source: TrackSourceSchema.optional(),
16199
16555
  firstSeen: number(),
16200
16556
  lastSeen: number(),
16201
16557
  /** Frame-rate position history (subject to maxPositionHistory cap). */
@@ -16572,6 +16928,76 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16572
16928
  eventId: string(),
16573
16929
  timestamp: number()
16574
16930
  });
16931
+ /**
16932
+ * Cap → event-kind mapping for the SENSOR / CONTROL cap families — the table
16933
+ * `pipeline-analytics.listEventKinds` uses to turn a linked device's bound
16934
+ * caps into per-camera event-kind descriptors.
16935
+ *
16936
+ * The descriptor DATA (color / iconId / labelKey / parentKind / category)
16937
+ * is NOT duplicated here — every entry is derived from the single
16938
+ * `EVENT_TAXONOMY` dictionary (`catalogs/event-taxonomy.ts`). This file owns
16939
+ * ONLY the cap-name → taxonomy-kind mapping. Adding a new eventful sensor /
16940
+ * control cap means adding one line here (and a taxonomy entry); the anti-
16941
+ * drift guard `scripts/check-event-kind-coverage.ts` fails the build if an
16942
+ * eventful cap is missing.
16943
+ */
16944
+ /** Map a taxonomy `iconId` onto the legacy closed `EventKindIcon` enum. */
16945
+ var LEGACY_ICON = {
16946
+ motion: "motion",
16947
+ audio: "audio",
16948
+ person: "person",
16949
+ vehicle: "vehicle",
16950
+ animal: "animal",
16951
+ package: "package",
16952
+ door: "door",
16953
+ pir: "pir",
16954
+ smoke: "smoke",
16955
+ water: "water",
16956
+ button: "button",
16957
+ generic: "generic",
16958
+ gas: "smoke",
16959
+ vibration: "generic",
16960
+ tamper: "generic",
16961
+ presence: "person",
16962
+ lock: "generic",
16963
+ siren: "generic",
16964
+ switch: "generic",
16965
+ doorbell: "button"
16966
+ };
16967
+ function legacyIcon(iconId) {
16968
+ return LEGACY_ICON[iconId] ?? "generic";
16969
+ }
16970
+ /**
16971
+ * Cap name → taxonomy kind id. Covers EVERY eventful sensor / control cap.
16972
+ * The anti-drift guard cross-checks this against the eventful caps declared
16973
+ * in `packages/types/src/capabilities/*.cap.ts`.
16974
+ */
16975
+ var CAP_TO_KIND = {
16976
+ contact: "contact",
16977
+ motion: "motion-sensor",
16978
+ smoke: "smoke",
16979
+ flood: "flood",
16980
+ gas: "gas",
16981
+ "carbon-monoxide": "carbon-monoxide",
16982
+ vibration: "vibration",
16983
+ tamper: "tamper",
16984
+ presence: "presence",
16985
+ "enum-sensor": "enum-sensor",
16986
+ "event-emitter": "device-event",
16987
+ "lock-control": "lock",
16988
+ switch: "switch",
16989
+ button: "button",
16990
+ doorbell: "doorbell"
16991
+ };
16992
+ function buildDescriptor(capName, kind) {
16993
+ const t = EVENT_TAXONOMY[kind];
16994
+ if (t === void 0) throw new Error(`EVENT_KIND_BY_CAP: cap '${capName}' maps to unknown taxonomy kind '${kind}'`);
16995
+ return {
16996
+ ...t,
16997
+ icon: legacyIcon(t.iconId)
16998
+ };
16999
+ }
17000
+ Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
16575
17001
  var CameraPipelineConfigSchema = object({
16576
17002
  engine: PipelineEngineChoiceSchema.optional(),
16577
17003
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16590,13 +17016,11 @@ var PipelineTemplateSchema = object({
16590
17016
  createdAt: string(),
16591
17017
  updatedAt: string()
16592
17018
  });
16593
- var AgentAddonConfigSchema = object({
16594
- enabled: boolean(),
17019
+ var DeviceStepConfigSchema = object({
16595
17020
  modelId: string().optional(),
16596
- settings: record(string(), unknown()).readonly()
17021
+ settings: record(string(), unknown()).optional()
16597
17022
  });
16598
17023
  var AgentPipelineSettingsSchema = object({
16599
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
16600
17024
  maxCameras: number().int().nonnegative().nullable().default(null),
16601
17025
  /** Per-node detection weight (relative share for the quota balancer). */
16602
17026
  detectWeight: number().positive().optional(),
@@ -16620,7 +17044,22 @@ var AgentPipelineSettingsSchema = object({
16620
17044
  * it already uses to reach the hub). Set this only when the auto-detected
16621
17045
  * address is wrong (multi-homed host, NAT, custom interface).
16622
17046
  */
16623
- reachableHost: string().optional()
17047
+ reachableHost: string().optional(),
17048
+ /**
17049
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
17050
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
17051
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
17052
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
17053
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
17054
+ * the default model/settings for every camera landing on that accelerator;
17055
+ * a stepId absent ⇒ the step uses that device's format default.
17056
+ */
17057
+ inferenceDevices: record(string(), object({
17058
+ enabled: boolean(),
17059
+ weight: number().positive().optional(),
17060
+ maxSessions: number().int().positive().optional(),
17061
+ steps: record(string(), DeviceStepConfigSchema).optional()
17062
+ })).optional()
16624
17063
  });
16625
17064
  var CameraPipelineForAgentSchema = object({
16626
17065
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16630,14 +17069,13 @@ var CameraPipelineForAgentSchema = object({
16630
17069
  }).nullable()
16631
17070
  });
16632
17071
  var CameraStepOverridePatchSchema = object({
16633
- enabled: boolean().optional(),
16634
17072
  modelId: string().optional(),
16635
17073
  settings: record(string(), unknown()).readonly().optional()
16636
17074
  });
16637
17075
  var CameraPipelineSettingsSchema = object({
16638
17076
  pinnedAgentNodeId: string().optional(),
16639
17077
  stepToggles: record(string(), boolean()).optional(),
16640
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
17078
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
16641
17079
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
16642
17080
  });
16643
17081
  /**
@@ -16851,6 +17289,44 @@ var CameraStatusSchema = object({
16851
17289
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
16852
17290
  fetchedAt: number()
16853
17291
  });
17292
+ var NodeInferenceDeviceSchema = object({
17293
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17294
+ key: string(),
17295
+ backend: string(),
17296
+ device: string(),
17297
+ format: _enum(MODEL_FORMATS),
17298
+ /** Whether the node's live probe reports the device as usable right now. */
17299
+ available: boolean(),
17300
+ /**
17301
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17302
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17303
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17304
+ * not a balanced target). An explicit stored value always wins; a stored-only
17305
+ * (unavailable) key keeps its stored value.
17306
+ */
17307
+ enabled: boolean(),
17308
+ /** Relative balancer weight for the enabled device (default 1). */
17309
+ weight: number(),
17310
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17311
+ maxSessions: number().nullable(),
17312
+ /** Object-detection model the executor defaults to for this deviceKey. */
17313
+ defaultModelId: string(),
17314
+ /**
17315
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17316
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17317
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17318
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17319
+ * available per format; this is the stored selection that becomes the
17320
+ * default for EVERY camera landing on this accelerator.
17321
+ */
17322
+ steps: record(string(), DeviceStepConfigSchema).optional()
17323
+ });
17324
+ var NodeInferenceDevicesSchema = object({
17325
+ nodeId: string(),
17326
+ /** False when the node's platform-probe was unreachable (no live device set). */
17327
+ reachable: boolean(),
17328
+ devices: array(NodeInferenceDeviceSchema).readonly()
17329
+ });
16854
17330
  method(object({
16855
17331
  deviceId: number(),
16856
17332
  agentNodeId: string()
@@ -16860,7 +17336,13 @@ method(object({
16860
17336
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
16861
17337
  kind: "mutation",
16862
17338
  auth: "admin"
16863
- }), method(_void(), object({ migrated: number() }), {
17339
+ }), method(object({
17340
+ deviceId: number(),
17341
+ deviceKey: string()
17342
+ }), object({ success: literal(true) }), {
17343
+ kind: "mutation",
17344
+ auth: "admin"
17345
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
16864
17346
  kind: "mutation",
16865
17347
  auth: "admin"
16866
17348
  }), 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({
@@ -16894,13 +17376,7 @@ method(object({
16894
17376
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
16895
17377
  nodeId: string(),
16896
17378
  settings: AgentPipelineSettingsSchema
16897
- })).readonly()), method(object({
16898
- agentNodeId: string(),
16899
- defaults: record(string(), AgentAddonConfigSchema)
16900
- }), object({ success: literal(true) }), {
16901
- kind: "mutation",
16902
- auth: "admin"
16903
- }), method(object({ agentNodeId: string() }), object({
17379
+ })).readonly()), method(object({ agentNodeId: string() }), object({
16904
17380
  success: boolean(),
16905
17381
  removed: boolean()
16906
17382
  }), {
@@ -16932,7 +17408,18 @@ method(object({
16932
17408
  }), object({ success: literal(true) }), {
16933
17409
  kind: "mutation",
16934
17410
  auth: "admin"
16935
- }), method(object({ agentNodeId: string() }), object({
17411
+ }), method(object({
17412
+ agentNodeId: string(),
17413
+ inferenceDevices: record(string(), object({
17414
+ enabled: boolean(),
17415
+ weight: number().positive().optional(),
17416
+ maxSessions: number().int().positive().optional(),
17417
+ steps: record(string(), DeviceStepConfigSchema).optional()
17418
+ }))
17419
+ }), object({ success: literal(true) }), {
17420
+ kind: "mutation",
17421
+ auth: "admin"
17422
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
16936
17423
  success: literal(true),
16937
17424
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
16938
17425
  effectiveModelId: string().nullable(),
@@ -16948,9 +17435,10 @@ method(object({
16948
17435
  }), object({ success: literal(true) }), {
16949
17436
  kind: "mutation",
16950
17437
  auth: "admin"
16951
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17438
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
16952
17439
  deviceId: number(),
16953
17440
  agentNodeId: string(),
17441
+ deviceKey: string(),
16954
17442
  addonId: string(),
16955
17443
  patch: CameraStepOverridePatchSchema.nullable()
16956
17444
  }), object({ success: literal(true) }), {
@@ -16987,14 +17475,13 @@ method(object({
16987
17475
  });
16988
17476
  /**
16989
17477
  * server-management — per-NODE singleton capability for a node's ROOT
16990
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
16991
- * agents).
17478
+ * package lifecycle (runtime-updatable node packages).
16992
17479
  *
16993
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
16994
- * on agents) carries the whole software stack in its npm dep tree, so ONE
16995
- * version describes the node. Updates install into
16996
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
16997
- * starter (probation boot + auto-rollback to N-1).
17480
+ * Every node role runs the SAME root package (`@camstack/server`), which
17481
+ * carries the whole software stack in its npm dep tree, so ONE version
17482
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17483
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17484
+ * no auto-rollback).
16998
17485
  *
16999
17486
  * Providers:
17000
17487
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17102,7 +17589,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17102
17589
  /** Explicit target version; omitted = latest from the registry. */
17103
17590
  version: string().optional() }), ServerUpdateActionResultSchema, {
17104
17591
  kind: "mutation",
17105
- auth: "admin"
17592
+ auth: "admin",
17593
+ timeoutMs: 16 * 6e4
17106
17594
  }), method(_void(), ServerUpdateActionResultSchema, {
17107
17595
  kind: "mutation",
17108
17596
  auth: "admin"
@@ -18146,22 +18634,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18146
18634
  var RestartAddonResultSchema = unknown();
18147
18635
  var InstallPackageResultSchema = unknown();
18148
18636
  var ReloadPackagesResultSchema = unknown();
18149
- /**
18150
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18151
- * server restarts so the admin UI can react to the `restartingAt`
18152
- * timestamp (shows reconnect overlay). The transition from
18153
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18154
- * `system.restart-completed` event after the new process boots.
18155
- *
18156
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18157
- */
18158
- var UpdateFrameworkPackageResultSchema = object({
18159
- packageName: string(),
18160
- fromVersion: string(),
18161
- toVersion: string(),
18162
- /** Ms-epoch the server scheduled its self-restart. */
18163
- restartingAt: number()
18164
- });
18165
18637
  var BulkUpdateItemStatusSchema = _enum([
18166
18638
  "queued",
18167
18639
  "updating",
@@ -18289,13 +18761,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18289
18761
  }), object({ success: literal(true) }), {
18290
18762
  kind: "mutation",
18291
18763
  auth: "admin"
18292
- }), method(object({
18293
- packageName: string().min(1),
18294
- version: string().optional(),
18295
- deferRestart: boolean().optional()
18296
- }), UpdateFrameworkPackageResultSchema, {
18297
- kind: "mutation",
18298
- auth: "admin"
18299
18764
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18300
18765
  kind: "mutation",
18301
18766
  auth: "admin"
@@ -19161,10 +19626,10 @@ var TopologyCategorySchema = object({
19161
19626
  addons: array(TopologyCategoryAddonSchema).readonly()
19162
19627
  });
19163
19628
  /**
19164
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19165
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19166
- * version visibility for the Server management surface. Nullable: offline
19167
- * rows and pre-phase-2 nodes report none.
19629
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19630
+ * root package for every node role) as reported by its `registerNode`
19631
+ * manifest — version visibility for the Server management surface. Nullable:
19632
+ * offline rows and nodes that never reported one.
19168
19633
  */
19169
19634
  var TopologyRootPackageSchema = object({
19170
19635
  name: string(),
@@ -19552,17 +20017,28 @@ var PlatformScoreSchema = object({
19552
20017
  format: _enum([
19553
20018
  "onnx",
19554
20019
  "coreml",
19555
- "openvino"
20020
+ "openvino",
20021
+ "tflite"
19556
20022
  ]),
19557
20023
  score: number(),
19558
20024
  reason: string(),
19559
20025
  available: boolean()
19560
20026
  });
20027
+ var InferenceDeviceDescriptorSchema = object({
20028
+ key: string(),
20029
+ backend: string(),
20030
+ device: string(),
20031
+ format: ModelFormatSchema,
20032
+ runtime: literal("python"),
20033
+ score: number(),
20034
+ available: boolean()
20035
+ });
19561
20036
  var PlatformCapabilitiesSchema = object({
19562
20037
  hardware: HardwareInfoSchema,
19563
20038
  scores: array(PlatformScoreSchema).readonly(),
19564
20039
  bestScore: PlatformScoreSchema,
19565
- pythonPath: string().nullable()
20040
+ pythonPath: string().nullable(),
20041
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
19566
20042
  });
19567
20043
  var ModelRequirementSchema = object({
19568
20044
  modelId: string(),
@@ -20441,12 +20917,6 @@ Object.freeze({
20441
20917
  addonId: null,
20442
20918
  access: "delete"
20443
20919
  },
20444
- "addons.updateFrameworkPackage": {
20445
- capName: "addons",
20446
- capScope: "system",
20447
- addonId: null,
20448
- access: "create"
20449
- },
20450
20920
  "addons.updatePackage": {
20451
20921
  capName: "addons",
20452
20922
  capScope: "system",
@@ -23219,12 +23689,6 @@ Object.freeze({
23219
23689
  addonId: null,
23220
23690
  access: "view"
23221
23691
  },
23222
- "pipelineExecutor.reprobeEngine": {
23223
- capName: "pipeline-executor",
23224
- capScope: "system",
23225
- addonId: null,
23226
- access: "create"
23227
- },
23228
23692
  "pipelineExecutor.runAudioTest": {
23229
23693
  capName: "pipeline-executor",
23230
23694
  capScope: "system",
@@ -23375,6 +23839,12 @@ Object.freeze({
23375
23839
  addonId: null,
23376
23840
  access: "view"
23377
23841
  },
23842
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23843
+ capName: "pipeline-orchestrator",
23844
+ capScope: "system",
23845
+ addonId: null,
23846
+ access: "view"
23847
+ },
23378
23848
  "pipelineOrchestrator.getPipelineAssignment": {
23379
23849
  capName: "pipeline-orchestrator",
23380
23850
  capScope: "system",
@@ -23387,6 +23857,12 @@ Object.freeze({
23387
23857
  addonId: null,
23388
23858
  access: "view"
23389
23859
  },
23860
+ "pipelineOrchestrator.getPipelineDevicePin": {
23861
+ capName: "pipeline-orchestrator",
23862
+ capScope: "system",
23863
+ addonId: null,
23864
+ access: "view"
23865
+ },
23390
23866
  "pipelineOrchestrator.listAgentSettings": {
23391
23867
  capName: "pipeline-orchestrator",
23392
23868
  capScope: "system",
@@ -23429,19 +23905,19 @@ Object.freeze({
23429
23905
  addonId: null,
23430
23906
  access: "create"
23431
23907
  },
23432
- "pipelineOrchestrator.setAgentAddonDefaults": {
23908
+ "pipelineOrchestrator.setAgentCapabilities": {
23433
23909
  capName: "pipeline-orchestrator",
23434
23910
  capScope: "system",
23435
23911
  addonId: null,
23436
23912
  access: "create"
23437
23913
  },
23438
- "pipelineOrchestrator.setAgentCapabilities": {
23914
+ "pipelineOrchestrator.setAgentDetectWeight": {
23439
23915
  capName: "pipeline-orchestrator",
23440
23916
  capScope: "system",
23441
23917
  addonId: null,
23442
23918
  access: "create"
23443
23919
  },
23444
- "pipelineOrchestrator.setAgentDetectWeight": {
23920
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23445
23921
  capName: "pipeline-orchestrator",
23446
23922
  capScope: "system",
23447
23923
  addonId: null,
@@ -23483,6 +23959,12 @@ Object.freeze({
23483
23959
  addonId: null,
23484
23960
  access: "create"
23485
23961
  },
23962
+ "pipelineOrchestrator.setPipelineDevicePin": {
23963
+ capName: "pipeline-orchestrator",
23964
+ capScope: "system",
23965
+ addonId: null,
23966
+ access: "create"
23967
+ },
23486
23968
  "pipelineOrchestrator.unassignAudio": {
23487
23969
  capName: "pipeline-orchestrator",
23488
23970
  capScope: "system",
@@ -25035,32 +25517,6 @@ Object.freeze({
25035
25517
  "network-access": "ingress",
25036
25518
  "smtp-provider": "email"
25037
25519
  });
25038
- var frameworkSwapPackageSchema = object({
25039
- name: string(),
25040
- stagedPath: string(),
25041
- backupPath: string(),
25042
- toVersion: string(),
25043
- fromVersion: string().nullable()
25044
- });
25045
- object({
25046
- jobId: string(),
25047
- taskId: string(),
25048
- packages: array(frameworkSwapPackageSchema),
25049
- requestedAtMs: number(),
25050
- schemaVersion: literal(1)
25051
- });
25052
- object({
25053
- jobId: string(),
25054
- taskId: string(),
25055
- backups: array(object({
25056
- name: string(),
25057
- backupPath: string(),
25058
- livePath: string()
25059
- })),
25060
- appliedAtMs: number(),
25061
- bootAttempts: number(),
25062
- schemaVersion: literal(1)
25063
- });
25064
25520
  //#endregion
25065
25521
  //#region src/custom-actions.ts
25066
25522
  /**