@camstack/addon-provider-homeassistant 1.1.32 → 1.2.1

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 +213 -106
  2. package/dist/addon.mjs +213 -106
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7086,6 +7086,17 @@ var ModelCatalogEntrySchema = object({
7086
7086
  "imagenet",
7087
7087
  "none"
7088
7088
  ]).optional(),
7089
+ /**
7090
+ * The model already applies softmax IN-GRAPH — its raw output is a
7091
+ * probability distribution, not logits. When set, the `softmax`
7092
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7093
+ * probability vector collapses it toward uniform (top-1 score craters far
7094
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7095
+ * the output is raw logits and the postprocessor applies softmax (the normal
7096
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7097
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7098
+ */
7099
+ outputProbabilities: boolean().optional(),
7089
7100
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7090
7101
  /**
7091
7102
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12731,10 +12742,7 @@ var ConfigUISchemaNullableBridge = custom();
12731
12742
  var InferenceCapabilitiesBridge = custom();
12732
12743
  var ModelAvailabilityListBridge = custom();
12733
12744
  var PipelineRunResultBridge = custom();
12734
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12735
- kind: "mutation",
12736
- auth: "admin"
12737
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12745
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12738
12746
  modelId: string(),
12739
12747
  settings: record(string(), unknown()).readonly()
12740
12748
  }))), method(object({ steps: record(string(), object({
@@ -12794,13 +12802,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12794
12802
  * (inputClasses ≠ null) are skipped and served per-track via
12795
12803
  * pipelineRunner.runDetailSubtree (two-plane design).
12796
12804
  */
12797
- plane: _enum(["full", "frame"]).optional()
12805
+ plane: _enum(["full", "frame"]).optional(),
12806
+ /**
12807
+ * Inference-device selector (Phase 2 multi-device). Format
12808
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12809
+ * Omitted ⇒ the runner's default device (current single-engine
12810
+ * behaviour). Selects WHICH device pool of the node runs the call.
12811
+ */
12812
+ deviceKey: string().optional()
12798
12813
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12799
12814
  engine: PipelineEngineChoiceSchema.optional(),
12800
12815
  steps: array(PipelineStepInputSchema).min(1),
12801
12816
  frames: array(FrameInputSchema).min(1).max(255),
12802
12817
  deviceId: number().optional(),
12803
- sessionId: string().optional()
12818
+ sessionId: string().optional(),
12819
+ /**
12820
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12821
+ * the batch to the Python pool's bench preprocess cache
12822
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12823
+ * preprocessed ONCE and every later inference is a pure-inference cache
12824
+ * hit — the sustained-throughput run measures inference, not
12825
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12826
+ * full preprocess every call, correct). Fresh per sustained run;
12827
+ * released via `uncacheFrame`.
12828
+ */
12829
+ frameId: number().int().nonnegative().optional(),
12830
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12831
+ deviceKey: string().optional()
12804
12832
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12805
12833
  data: _instanceof(Uint8Array),
12806
12834
  width: number().int().positive(),
@@ -12832,8 +12860,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12832
12860
  * - `runtime` — main camera-serving engine (no idle TTL).
12833
12861
  * - `warm-override` — benchmark/test override held in the warm
12834
12862
  * cache; auto-disposed after the idle TTL.
12863
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12864
+ * multi-device, keyed by `deviceKey`) resolved
12865
+ * via `resolveDeviceFactory`. Runs alongside the
12866
+ * `runtime` engine on a DIFFERENT accelerator
12867
+ * (NPU / iGPU / Coral) — this is how the
12868
+ * Engines tab shows all pools running at once.
12835
12869
  */
12836
- kind: _enum(["runtime", "warm-override"]),
12870
+ kind: _enum([
12871
+ "runtime",
12872
+ "warm-override",
12873
+ "device-pool"
12874
+ ]),
12837
12875
  /** Native pid of the underlying Python pool (null when no pool). */
12838
12876
  poolPid: number().nullable(),
12839
12877
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12968,7 +13006,21 @@ var NativeCropResultSchema = object({
12968
13006
  /** Packed rgb (24-bit) pixels of the crop. */
12969
13007
  bytes: _instanceof(Uint8Array),
12970
13008
  width: number().int().positive(),
12971
- height: number().int().positive()
13009
+ height: number().int().positive(),
13010
+ /**
13011
+ * Which source served this crop, so a quality-sensitive consumer (the native
13012
+ * `keyFrame`) can reject a degraded fallback:
13013
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
13014
+ * quality path).
13015
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
13016
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
13017
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
13018
+ *
13019
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
13020
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
13021
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
13022
+ */
13023
+ tier: _enum(["native", "ram-fullframe"]).optional()
12972
13024
  });
12973
13025
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12974
13026
  * originating detection, in FRAME-space coordinates. Reuses
@@ -13208,7 +13260,14 @@ var RunnerCameraConfigSchema = object({
13208
13260
  * camera's detect node differs from its source-owner (P2d, gated by the
13209
13261
  * `remoteSourcingNodes` rollout setting).
13210
13262
  */
13211
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13263
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13264
+ /**
13265
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13266
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13267
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13268
+ * this only selects WHICH device pool of that node runs the session.
13269
+ */
13270
+ deviceKey: string().optional()
13212
13271
  });
13213
13272
  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;
13214
13273
  /**
@@ -13229,6 +13288,19 @@ var RunnerLocalLoadSchema = object({
13229
13288
  avgInferenceTimeMs: number(),
13230
13289
  /** Total queue depth across motion + detection queues. */
13231
13290
  queueDepthTotal: number(),
13291
+ /**
13292
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13293
+ * this runner currently has attached cameras on, so the orchestrator's second
13294
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13295
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13296
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13297
+ */
13298
+ devices: array(object({
13299
+ deviceKey: string(),
13300
+ backend: string(),
13301
+ attachedCameras: number(),
13302
+ queueDepthTotal: number()
13303
+ })).default([]),
13232
13304
  /** Hardware capability flags reported by this node. */
13233
13305
  hardware: object({
13234
13306
  hasGpu: boolean(),
@@ -16377,6 +16449,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16377
16449
  return toDeviceSummary(device, this.addonId);
16378
16450
  }
16379
16451
  };
16452
+ 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;
16453
+ new Set(Object.values(DeviceType));
16380
16454
  /**
16381
16455
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16382
16456
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -18035,7 +18109,8 @@ var LinkedDeviceSchema = object({
18035
18109
  deviceId: number(),
18036
18110
  name: string(),
18037
18111
  location: string().nullable(),
18038
- features: array(string())
18112
+ features: array(string()),
18113
+ producesTrackedEvents: boolean().optional()
18039
18114
  });
18040
18115
  var SavedDeviceRowSchema = object({
18041
18116
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19679,6 +19754,7 @@ var TrackSchema = object({
19679
19754
  deviceId: number(),
19680
19755
  className: string(),
19681
19756
  label: string().optional(),
19757
+ producingDeviceName: string().optional(),
19682
19758
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19683
19759
  source: TrackSourceSchema.optional(),
19684
19760
  firstSeen: number(),
@@ -19816,7 +19892,8 @@ var MediaFileKindEnum = _enum([
19816
19892
  "fullFrameBoxed",
19817
19893
  "faceCrop",
19818
19894
  "plateCrop",
19819
- "keyFrame"
19895
+ "keyFrame",
19896
+ "keyFrameSmall"
19820
19897
  ]);
19821
19898
  var MediaFileSchema = object({
19822
19899
  key: string(),
@@ -20145,13 +20222,11 @@ var PipelineTemplateSchema = object({
20145
20222
  createdAt: string(),
20146
20223
  updatedAt: string()
20147
20224
  });
20148
- var AgentAddonConfigSchema = object({
20149
- enabled: boolean(),
20225
+ var DeviceStepConfigSchema = object({
20150
20226
  modelId: string().optional(),
20151
- settings: record(string(), unknown()).readonly()
20227
+ settings: record(string(), unknown()).optional()
20152
20228
  });
20153
20229
  var AgentPipelineSettingsSchema = object({
20154
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
20155
20230
  maxCameras: number().int().nonnegative().nullable().default(null),
20156
20231
  /** Per-node detection weight (relative share for the quota balancer). */
20157
20232
  detectWeight: number().positive().optional(),
@@ -20175,7 +20250,22 @@ var AgentPipelineSettingsSchema = object({
20175
20250
  * it already uses to reach the hub). Set this only when the auto-detected
20176
20251
  * address is wrong (multi-homed host, NAT, custom interface).
20177
20252
  */
20178
- reachableHost: string().optional()
20253
+ reachableHost: string().optional(),
20254
+ /**
20255
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20256
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20257
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20258
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20259
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20260
+ * the default model/settings for every camera landing on that accelerator;
20261
+ * a stepId absent ⇒ the step uses that device's format default.
20262
+ */
20263
+ inferenceDevices: record(string(), object({
20264
+ enabled: boolean(),
20265
+ weight: number().positive().optional(),
20266
+ maxSessions: number().int().positive().optional(),
20267
+ steps: record(string(), DeviceStepConfigSchema).optional()
20268
+ })).optional()
20179
20269
  });
20180
20270
  var CameraPipelineForAgentSchema = object({
20181
20271
  steps: array(PipelineStepInputSchema).readonly(),
@@ -20185,14 +20275,13 @@ var CameraPipelineForAgentSchema = object({
20185
20275
  }).nullable()
20186
20276
  });
20187
20277
  var CameraStepOverridePatchSchema = object({
20188
- enabled: boolean().optional(),
20189
20278
  modelId: string().optional(),
20190
20279
  settings: record(string(), unknown()).readonly().optional()
20191
20280
  });
20192
20281
  var CameraPipelineSettingsSchema = object({
20193
20282
  pinnedAgentNodeId: string().optional(),
20194
20283
  stepToggles: record(string(), boolean()).optional(),
20195
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20284
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
20196
20285
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
20197
20286
  });
20198
20287
  /**
@@ -20406,6 +20495,44 @@ var CameraStatusSchema = object({
20406
20495
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20407
20496
  fetchedAt: number()
20408
20497
  });
20498
+ var NodeInferenceDeviceSchema = object({
20499
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20500
+ key: string(),
20501
+ backend: string(),
20502
+ device: string(),
20503
+ format: _enum(MODEL_FORMATS),
20504
+ /** Whether the node's live probe reports the device as usable right now. */
20505
+ available: boolean(),
20506
+ /**
20507
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20508
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20509
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20510
+ * not a balanced target). An explicit stored value always wins; a stored-only
20511
+ * (unavailable) key keeps its stored value.
20512
+ */
20513
+ enabled: boolean(),
20514
+ /** Relative balancer weight for the enabled device (default 1). */
20515
+ weight: number(),
20516
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20517
+ maxSessions: number().nullable(),
20518
+ /** Object-detection model the executor defaults to for this deviceKey. */
20519
+ defaultModelId: string(),
20520
+ /**
20521
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20522
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20523
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20524
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20525
+ * available per format; this is the stored selection that becomes the
20526
+ * default for EVERY camera landing on this accelerator.
20527
+ */
20528
+ steps: record(string(), DeviceStepConfigSchema).optional()
20529
+ });
20530
+ var NodeInferenceDevicesSchema = object({
20531
+ nodeId: string(),
20532
+ /** False when the node's platform-probe was unreachable (no live device set). */
20533
+ reachable: boolean(),
20534
+ devices: array(NodeInferenceDeviceSchema).readonly()
20535
+ });
20409
20536
  method(object({
20410
20537
  deviceId: number(),
20411
20538
  agentNodeId: string()
@@ -20415,7 +20542,13 @@ method(object({
20415
20542
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20416
20543
  kind: "mutation",
20417
20544
  auth: "admin"
20418
- }), method(_void(), object({ migrated: number() }), {
20545
+ }), method(object({
20546
+ deviceId: number(),
20547
+ deviceKey: string()
20548
+ }), object({ success: literal(true) }), {
20549
+ kind: "mutation",
20550
+ auth: "admin"
20551
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20419
20552
  kind: "mutation",
20420
20553
  auth: "admin"
20421
20554
  }), 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({
@@ -20449,13 +20582,7 @@ method(object({
20449
20582
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20450
20583
  nodeId: string(),
20451
20584
  settings: AgentPipelineSettingsSchema
20452
- })).readonly()), method(object({
20453
- agentNodeId: string(),
20454
- defaults: record(string(), AgentAddonConfigSchema)
20455
- }), object({ success: literal(true) }), {
20456
- kind: "mutation",
20457
- auth: "admin"
20458
- }), method(object({ agentNodeId: string() }), object({
20585
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20459
20586
  success: boolean(),
20460
20587
  removed: boolean()
20461
20588
  }), {
@@ -20487,7 +20614,18 @@ method(object({
20487
20614
  }), object({ success: literal(true) }), {
20488
20615
  kind: "mutation",
20489
20616
  auth: "admin"
20490
- }), method(object({ agentNodeId: string() }), object({
20617
+ }), method(object({
20618
+ agentNodeId: string(),
20619
+ inferenceDevices: record(string(), object({
20620
+ enabled: boolean(),
20621
+ weight: number().positive().optional(),
20622
+ maxSessions: number().int().positive().optional(),
20623
+ steps: record(string(), DeviceStepConfigSchema).optional()
20624
+ }))
20625
+ }), object({ success: literal(true) }), {
20626
+ kind: "mutation",
20627
+ auth: "admin"
20628
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20491
20629
  success: literal(true),
20492
20630
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20493
20631
  effectiveModelId: string().nullable(),
@@ -20503,9 +20641,10 @@ method(object({
20503
20641
  }), object({ success: literal(true) }), {
20504
20642
  kind: "mutation",
20505
20643
  auth: "admin"
20506
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20644
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20507
20645
  deviceId: number(),
20508
20646
  agentNodeId: string(),
20647
+ deviceKey: string(),
20509
20648
  addonId: string(),
20510
20649
  patch: CameraStepOverridePatchSchema.nullable()
20511
20650
  }), object({ success: literal(true) }), {
@@ -20542,14 +20681,13 @@ method(object({
20542
20681
  });
20543
20682
  /**
20544
20683
  * server-management — per-NODE singleton capability for a node's ROOT
20545
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20546
- * agents).
20684
+ * package lifecycle (runtime-updatable node packages).
20547
20685
  *
20548
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20549
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20550
- * version describes the node. Updates install into
20551
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20552
- * starter (probation boot + auto-rollback to N-1).
20686
+ * Every node role runs the SAME root package (`@camstack/server`), which
20687
+ * carries the whole software stack in its npm dep tree, so ONE version
20688
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20689
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20690
+ * no auto-rollback).
20553
20691
  *
20554
20692
  * Providers:
20555
20693
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20657,7 +20795,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20657
20795
  /** Explicit target version; omitted = latest from the registry. */
20658
20796
  version: string().optional() }), ServerUpdateActionResultSchema, {
20659
20797
  kind: "mutation",
20660
- auth: "admin"
20798
+ auth: "admin",
20799
+ timeoutMs: 16 * 6e4
20661
20800
  }), method(_void(), ServerUpdateActionResultSchema, {
20662
20801
  kind: "mutation",
20663
20802
  auth: "admin"
@@ -21773,22 +21912,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21773
21912
  var RestartAddonResultSchema = unknown();
21774
21913
  var InstallPackageResultSchema = unknown();
21775
21914
  var ReloadPackagesResultSchema = unknown();
21776
- /**
21777
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21778
- * server restarts so the admin UI can react to the `restartingAt`
21779
- * timestamp (shows reconnect overlay). The transition from
21780
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21781
- * `system.restart-completed` event after the new process boots.
21782
- *
21783
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21784
- */
21785
- var UpdateFrameworkPackageResultSchema = object({
21786
- packageName: string(),
21787
- fromVersion: string(),
21788
- toVersion: string(),
21789
- /** Ms-epoch the server scheduled its self-restart. */
21790
- restartingAt: number()
21791
- });
21792
21915
  var BulkUpdateItemStatusSchema = _enum([
21793
21916
  "queued",
21794
21917
  "updating",
@@ -21916,13 +22039,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21916
22039
  }), object({ success: literal(true) }), {
21917
22040
  kind: "mutation",
21918
22041
  auth: "admin"
21919
- }), method(object({
21920
- packageName: string().min(1),
21921
- version: string().optional(),
21922
- deferRestart: boolean().optional()
21923
- }), UpdateFrameworkPackageResultSchema, {
21924
- kind: "mutation",
21925
- auth: "admin"
21926
22042
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21927
22043
  kind: "mutation",
21928
22044
  auth: "admin"
@@ -22805,10 +22921,10 @@ var TopologyCategorySchema = object({
22805
22921
  addons: array(TopologyCategoryAddonSchema).readonly()
22806
22922
  });
22807
22923
  /**
22808
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22809
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22810
- * version visibility for the Server management surface. Nullable: offline
22811
- * rows and pre-phase-2 nodes report none.
22924
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22925
+ * root package for every node role) as reported by its `registerNode`
22926
+ * manifest — version visibility for the Server management surface. Nullable:
22927
+ * offline rows and nodes that never reported one.
22812
22928
  */
22813
22929
  var TopologyRootPackageSchema = object({
22814
22930
  name: string(),
@@ -23196,17 +23312,28 @@ var PlatformScoreSchema = object({
23196
23312
  format: _enum([
23197
23313
  "onnx",
23198
23314
  "coreml",
23199
- "openvino"
23315
+ "openvino",
23316
+ "tflite"
23200
23317
  ]),
23201
23318
  score: number(),
23202
23319
  reason: string(),
23203
23320
  available: boolean()
23204
23321
  });
23322
+ var InferenceDeviceDescriptorSchema = object({
23323
+ key: string(),
23324
+ backend: string(),
23325
+ device: string(),
23326
+ format: ModelFormatSchema,
23327
+ runtime: literal("python"),
23328
+ score: number(),
23329
+ available: boolean()
23330
+ });
23205
23331
  var PlatformCapabilitiesSchema = object({
23206
23332
  hardware: HardwareInfoSchema,
23207
23333
  scores: array(PlatformScoreSchema).readonly(),
23208
23334
  bestScore: PlatformScoreSchema,
23209
- pythonPath: string().nullable()
23335
+ pythonPath: string().nullable(),
23336
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
23210
23337
  });
23211
23338
  var ModelRequirementSchema = object({
23212
23339
  modelId: string(),
@@ -24085,12 +24212,6 @@ Object.freeze({
24085
24212
  addonId: null,
24086
24213
  access: "delete"
24087
24214
  },
24088
- "addons.updateFrameworkPackage": {
24089
- capName: "addons",
24090
- capScope: "system",
24091
- addonId: null,
24092
- access: "create"
24093
- },
24094
24215
  "addons.updatePackage": {
24095
24216
  capName: "addons",
24096
24217
  capScope: "system",
@@ -26863,12 +26984,6 @@ Object.freeze({
26863
26984
  addonId: null,
26864
26985
  access: "view"
26865
26986
  },
26866
- "pipelineExecutor.reprobeEngine": {
26867
- capName: "pipeline-executor",
26868
- capScope: "system",
26869
- addonId: null,
26870
- access: "create"
26871
- },
26872
26987
  "pipelineExecutor.runAudioTest": {
26873
26988
  capName: "pipeline-executor",
26874
26989
  capScope: "system",
@@ -27019,6 +27134,12 @@ Object.freeze({
27019
27134
  addonId: null,
27020
27135
  access: "view"
27021
27136
  },
27137
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27138
+ capName: "pipeline-orchestrator",
27139
+ capScope: "system",
27140
+ addonId: null,
27141
+ access: "view"
27142
+ },
27022
27143
  "pipelineOrchestrator.getPipelineAssignment": {
27023
27144
  capName: "pipeline-orchestrator",
27024
27145
  capScope: "system",
@@ -27031,6 +27152,12 @@ Object.freeze({
27031
27152
  addonId: null,
27032
27153
  access: "view"
27033
27154
  },
27155
+ "pipelineOrchestrator.getPipelineDevicePin": {
27156
+ capName: "pipeline-orchestrator",
27157
+ capScope: "system",
27158
+ addonId: null,
27159
+ access: "view"
27160
+ },
27034
27161
  "pipelineOrchestrator.listAgentSettings": {
27035
27162
  capName: "pipeline-orchestrator",
27036
27163
  capScope: "system",
@@ -27073,19 +27200,19 @@ Object.freeze({
27073
27200
  addonId: null,
27074
27201
  access: "create"
27075
27202
  },
27076
- "pipelineOrchestrator.setAgentAddonDefaults": {
27203
+ "pipelineOrchestrator.setAgentCapabilities": {
27077
27204
  capName: "pipeline-orchestrator",
27078
27205
  capScope: "system",
27079
27206
  addonId: null,
27080
27207
  access: "create"
27081
27208
  },
27082
- "pipelineOrchestrator.setAgentCapabilities": {
27209
+ "pipelineOrchestrator.setAgentDetectWeight": {
27083
27210
  capName: "pipeline-orchestrator",
27084
27211
  capScope: "system",
27085
27212
  addonId: null,
27086
27213
  access: "create"
27087
27214
  },
27088
- "pipelineOrchestrator.setAgentDetectWeight": {
27215
+ "pipelineOrchestrator.setAgentInferenceDevices": {
27089
27216
  capName: "pipeline-orchestrator",
27090
27217
  capScope: "system",
27091
27218
  addonId: null,
@@ -27127,6 +27254,12 @@ Object.freeze({
27127
27254
  addonId: null,
27128
27255
  access: "create"
27129
27256
  },
27257
+ "pipelineOrchestrator.setPipelineDevicePin": {
27258
+ capName: "pipeline-orchestrator",
27259
+ capScope: "system",
27260
+ addonId: null,
27261
+ access: "create"
27262
+ },
27130
27263
  "pipelineOrchestrator.unassignAudio": {
27131
27264
  capName: "pipeline-orchestrator",
27132
27265
  capScope: "system",
@@ -28679,32 +28812,6 @@ Object.freeze({
28679
28812
  "network-access": "ingress",
28680
28813
  "smtp-provider": "email"
28681
28814
  });
28682
- var frameworkSwapPackageSchema = object({
28683
- name: string(),
28684
- stagedPath: string(),
28685
- backupPath: string(),
28686
- toVersion: string(),
28687
- fromVersion: string().nullable()
28688
- });
28689
- object({
28690
- jobId: string(),
28691
- taskId: string(),
28692
- packages: array(frameworkSwapPackageSchema),
28693
- requestedAtMs: number(),
28694
- schemaVersion: literal(1)
28695
- });
28696
- object({
28697
- jobId: string(),
28698
- taskId: string(),
28699
- backups: array(object({
28700
- name: string(),
28701
- backupPath: string(),
28702
- livePath: string()
28703
- })),
28704
- appliedAtMs: number(),
28705
- bootAttempts: number(),
28706
- schemaVersion: literal(1)
28707
- });
28708
28815
  /**
28709
28816
  * Pure fuzzy matcher for adoption location import. Normalized
28710
28817
  * case-insensitive Levenshtein similarity, used to reuse an existing
package/dist/addon.mjs CHANGED
@@ -7085,6 +7085,17 @@ var ModelCatalogEntrySchema = object({
7085
7085
  "imagenet",
7086
7086
  "none"
7087
7087
  ]).optional(),
7088
+ /**
7089
+ * The model already applies softmax IN-GRAPH — its raw output is a
7090
+ * probability distribution, not logits. When set, the `softmax`
7091
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7092
+ * probability vector collapses it toward uniform (top-1 score craters far
7093
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7094
+ * the output is raw logits and the postprocessor applies softmax (the normal
7095
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7096
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7097
+ */
7098
+ outputProbabilities: boolean().optional(),
7088
7099
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7089
7100
  /**
7090
7101
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -12730,10 +12741,7 @@ var ConfigUISchemaNullableBridge = custom();
12730
12741
  var InferenceCapabilitiesBridge = custom();
12731
12742
  var ModelAvailabilityListBridge = custom();
12732
12743
  var PipelineRunResultBridge = custom();
12733
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
12734
- kind: "mutation",
12735
- auth: "admin"
12736
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12744
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
12737
12745
  modelId: string(),
12738
12746
  settings: record(string(), unknown()).readonly()
12739
12747
  }))), method(object({ steps: record(string(), object({
@@ -12793,13 +12801,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12793
12801
  * (inputClasses ≠ null) are skipped and served per-track via
12794
12802
  * pipelineRunner.runDetailSubtree (two-plane design).
12795
12803
  */
12796
- plane: _enum(["full", "frame"]).optional()
12804
+ plane: _enum(["full", "frame"]).optional(),
12805
+ /**
12806
+ * Inference-device selector (Phase 2 multi-device). Format
12807
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
12808
+ * Omitted ⇒ the runner's default device (current single-engine
12809
+ * behaviour). Selects WHICH device pool of the node runs the call.
12810
+ */
12811
+ deviceKey: string().optional()
12797
12812
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
12798
12813
  engine: PipelineEngineChoiceSchema.optional(),
12799
12814
  steps: array(PipelineStepInputSchema).min(1),
12800
12815
  frames: array(FrameInputSchema).min(1).max(255),
12801
12816
  deviceId: number().optional(),
12802
- sessionId: string().optional()
12817
+ sessionId: string().optional(),
12818
+ /**
12819
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
12820
+ * the batch to the Python pool's bench preprocess cache
12821
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
12822
+ * preprocessed ONCE and every later inference is a pure-inference cache
12823
+ * hit — the sustained-throughput run measures inference, not
12824
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
12825
+ * full preprocess every call, correct). Fresh per sustained run;
12826
+ * released via `uncacheFrame`.
12827
+ */
12828
+ frameId: number().int().nonnegative().optional(),
12829
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
12830
+ deviceKey: string().optional()
12803
12831
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
12804
12832
  data: _instanceof(Uint8Array),
12805
12833
  width: number().int().positive(),
@@ -12831,8 +12859,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
12831
12859
  * - `runtime` — main camera-serving engine (no idle TTL).
12832
12860
  * - `warm-override` — benchmark/test override held in the warm
12833
12861
  * cache; auto-disposed after the idle TTL.
12862
+ * - `device-pool` — a concurrent per-device pool (Phase 2
12863
+ * multi-device, keyed by `deviceKey`) resolved
12864
+ * via `resolveDeviceFactory`. Runs alongside the
12865
+ * `runtime` engine on a DIFFERENT accelerator
12866
+ * (NPU / iGPU / Coral) — this is how the
12867
+ * Engines tab shows all pools running at once.
12834
12868
  */
12835
- kind: _enum(["runtime", "warm-override"]),
12869
+ kind: _enum([
12870
+ "runtime",
12871
+ "warm-override",
12872
+ "device-pool"
12873
+ ]),
12836
12874
  /** Native pid of the underlying Python pool (null when no pool). */
12837
12875
  poolPid: number().nullable(),
12838
12876
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -12967,7 +13005,21 @@ var NativeCropResultSchema = object({
12967
13005
  /** Packed rgb (24-bit) pixels of the crop. */
12968
13006
  bytes: _instanceof(Uint8Array),
12969
13007
  width: number().int().positive(),
12970
- height: number().int().positive()
13008
+ height: number().int().positive(),
13009
+ /**
13010
+ * Which source served this crop, so a quality-sensitive consumer (the native
13011
+ * `keyFrame`) can reject a degraded fallback:
13012
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
13013
+ * quality path).
13014
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
13015
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
13016
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
13017
+ *
13018
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
13019
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
13020
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
13021
+ */
13022
+ tier: _enum(["native", "ram-fullframe"]).optional()
12971
13023
  });
12972
13024
  /** Parent detection context passed to `runDetailSubtree` — the crop's
12973
13025
  * originating detection, in FRAME-space coordinates. Reuses
@@ -13207,7 +13259,14 @@ var RunnerCameraConfigSchema = object({
13207
13259
  * camera's detect node differs from its source-owner (P2d, gated by the
13208
13260
  * `remoteSourcingNodes` rollout setting).
13209
13261
  */
13210
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13262
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13263
+ /**
13264
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13265
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13266
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13267
+ * this only selects WHICH device pool of that node runs the session.
13268
+ */
13269
+ deviceKey: string().optional()
13211
13270
  });
13212
13271
  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;
13213
13272
  /**
@@ -13228,6 +13287,19 @@ var RunnerLocalLoadSchema = object({
13228
13287
  avgInferenceTimeMs: number(),
13229
13288
  /** Total queue depth across motion + detection queues. */
13230
13289
  queueDepthTotal: number(),
13290
+ /**
13291
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13292
+ * this runner currently has attached cameras on, so the orchestrator's second
13293
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13294
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13295
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13296
+ */
13297
+ devices: array(object({
13298
+ deviceKey: string(),
13299
+ backend: string(),
13300
+ attachedCameras: number(),
13301
+ queueDepthTotal: number()
13302
+ })).default([]),
13231
13303
  /** Hardware capability flags reported by this node. */
13232
13304
  hardware: object({
13233
13305
  hasGpu: boolean(),
@@ -16376,6 +16448,8 @@ var BaseDeviceProvider = class extends BaseAddon {
16376
16448
  return toDeviceSummary(device, this.addonId);
16377
16449
  }
16378
16450
  };
16451
+ 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;
16452
+ new Set(Object.values(DeviceType));
16379
16453
  /**
16380
16454
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
16381
16455
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -18034,7 +18108,8 @@ var LinkedDeviceSchema = object({
18034
18108
  deviceId: number(),
18035
18109
  name: string(),
18036
18110
  location: string().nullable(),
18037
- features: array(string())
18111
+ features: array(string()),
18112
+ producesTrackedEvents: boolean().optional()
18038
18113
  });
18039
18114
  var SavedDeviceRowSchema = object({
18040
18115
  /** Numeric id reserved at allocateDeviceId time. */
@@ -19678,6 +19753,7 @@ var TrackSchema = object({
19678
19753
  deviceId: number(),
19679
19754
  className: string(),
19680
19755
  label: string().optional(),
19756
+ producingDeviceName: string().optional(),
19681
19757
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
19682
19758
  source: TrackSourceSchema.optional(),
19683
19759
  firstSeen: number(),
@@ -19815,7 +19891,8 @@ var MediaFileKindEnum = _enum([
19815
19891
  "fullFrameBoxed",
19816
19892
  "faceCrop",
19817
19893
  "plateCrop",
19818
- "keyFrame"
19894
+ "keyFrame",
19895
+ "keyFrameSmall"
19819
19896
  ]);
19820
19897
  var MediaFileSchema = object({
19821
19898
  key: string(),
@@ -20144,13 +20221,11 @@ var PipelineTemplateSchema = object({
20144
20221
  createdAt: string(),
20145
20222
  updatedAt: string()
20146
20223
  });
20147
- var AgentAddonConfigSchema = object({
20148
- enabled: boolean(),
20224
+ var DeviceStepConfigSchema = object({
20149
20225
  modelId: string().optional(),
20150
- settings: record(string(), unknown()).readonly()
20226
+ settings: record(string(), unknown()).optional()
20151
20227
  });
20152
20228
  var AgentPipelineSettingsSchema = object({
20153
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
20154
20229
  maxCameras: number().int().nonnegative().nullable().default(null),
20155
20230
  /** Per-node detection weight (relative share for the quota balancer). */
20156
20231
  detectWeight: number().positive().optional(),
@@ -20174,7 +20249,22 @@ var AgentPipelineSettingsSchema = object({
20174
20249
  * it already uses to reach the hub). Set this only when the auto-detected
20175
20250
  * address is wrong (multi-homed host, NAT, custom interface).
20176
20251
  */
20177
- reachableHost: string().optional()
20252
+ reachableHost: string().optional(),
20253
+ /**
20254
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20255
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20256
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20257
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20258
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20259
+ * the default model/settings for every camera landing on that accelerator;
20260
+ * a stepId absent ⇒ the step uses that device's format default.
20261
+ */
20262
+ inferenceDevices: record(string(), object({
20263
+ enabled: boolean(),
20264
+ weight: number().positive().optional(),
20265
+ maxSessions: number().int().positive().optional(),
20266
+ steps: record(string(), DeviceStepConfigSchema).optional()
20267
+ })).optional()
20178
20268
  });
20179
20269
  var CameraPipelineForAgentSchema = object({
20180
20270
  steps: array(PipelineStepInputSchema).readonly(),
@@ -20184,14 +20274,13 @@ var CameraPipelineForAgentSchema = object({
20184
20274
  }).nullable()
20185
20275
  });
20186
20276
  var CameraStepOverridePatchSchema = object({
20187
- enabled: boolean().optional(),
20188
20277
  modelId: string().optional(),
20189
20278
  settings: record(string(), unknown()).readonly().optional()
20190
20279
  });
20191
20280
  var CameraPipelineSettingsSchema = object({
20192
20281
  pinnedAgentNodeId: string().optional(),
20193
20282
  stepToggles: record(string(), boolean()).optional(),
20194
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20283
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
20195
20284
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
20196
20285
  });
20197
20286
  /**
@@ -20405,6 +20494,44 @@ var CameraStatusSchema = object({
20405
20494
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20406
20495
  fetchedAt: number()
20407
20496
  });
20497
+ var NodeInferenceDeviceSchema = object({
20498
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
20499
+ key: string(),
20500
+ backend: string(),
20501
+ device: string(),
20502
+ format: _enum(MODEL_FORMATS),
20503
+ /** Whether the node's live probe reports the device as usable right now. */
20504
+ available: boolean(),
20505
+ /**
20506
+ * Whether this device participates in dispatch. AUTO default (spec C2):
20507
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
20508
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
20509
+ * not a balanced target). An explicit stored value always wins; a stored-only
20510
+ * (unavailable) key keeps its stored value.
20511
+ */
20512
+ enabled: boolean(),
20513
+ /** Relative balancer weight for the enabled device (default 1). */
20514
+ weight: number(),
20515
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
20516
+ maxSessions: number().nullable(),
20517
+ /** Object-detection model the executor defaults to for this deviceKey. */
20518
+ defaultModelId: string(),
20519
+ /**
20520
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
20521
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
20522
+ * Absent/empty ⇒ no base (every step uses its device format default). The
20523
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
20524
+ * available per format; this is the stored selection that becomes the
20525
+ * default for EVERY camera landing on this accelerator.
20526
+ */
20527
+ steps: record(string(), DeviceStepConfigSchema).optional()
20528
+ });
20529
+ var NodeInferenceDevicesSchema = object({
20530
+ nodeId: string(),
20531
+ /** False when the node's platform-probe was unreachable (no live device set). */
20532
+ reachable: boolean(),
20533
+ devices: array(NodeInferenceDeviceSchema).readonly()
20534
+ });
20408
20535
  method(object({
20409
20536
  deviceId: number(),
20410
20537
  agentNodeId: string()
@@ -20414,7 +20541,13 @@ method(object({
20414
20541
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20415
20542
  kind: "mutation",
20416
20543
  auth: "admin"
20417
- }), method(_void(), object({ migrated: number() }), {
20544
+ }), method(object({
20545
+ deviceId: number(),
20546
+ deviceKey: string()
20547
+ }), object({ success: literal(true) }), {
20548
+ kind: "mutation",
20549
+ auth: "admin"
20550
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20418
20551
  kind: "mutation",
20419
20552
  auth: "admin"
20420
20553
  }), 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({
@@ -20448,13 +20581,7 @@ method(object({
20448
20581
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20449
20582
  nodeId: string(),
20450
20583
  settings: AgentPipelineSettingsSchema
20451
- })).readonly()), method(object({
20452
- agentNodeId: string(),
20453
- defaults: record(string(), AgentAddonConfigSchema)
20454
- }), object({ success: literal(true) }), {
20455
- kind: "mutation",
20456
- auth: "admin"
20457
- }), method(object({ agentNodeId: string() }), object({
20584
+ })).readonly()), method(object({ agentNodeId: string() }), object({
20458
20585
  success: boolean(),
20459
20586
  removed: boolean()
20460
20587
  }), {
@@ -20486,7 +20613,18 @@ method(object({
20486
20613
  }), object({ success: literal(true) }), {
20487
20614
  kind: "mutation",
20488
20615
  auth: "admin"
20489
- }), method(object({ agentNodeId: string() }), object({
20616
+ }), method(object({
20617
+ agentNodeId: string(),
20618
+ inferenceDevices: record(string(), object({
20619
+ enabled: boolean(),
20620
+ weight: number().positive().optional(),
20621
+ maxSessions: number().int().positive().optional(),
20622
+ steps: record(string(), DeviceStepConfigSchema).optional()
20623
+ }))
20624
+ }), object({ success: literal(true) }), {
20625
+ kind: "mutation",
20626
+ auth: "admin"
20627
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
20490
20628
  success: literal(true),
20491
20629
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
20492
20630
  effectiveModelId: string().nullable(),
@@ -20502,9 +20640,10 @@ method(object({
20502
20640
  }), object({ success: literal(true) }), {
20503
20641
  kind: "mutation",
20504
20642
  auth: "admin"
20505
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
20643
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
20506
20644
  deviceId: number(),
20507
20645
  agentNodeId: string(),
20646
+ deviceKey: string(),
20508
20647
  addonId: string(),
20509
20648
  patch: CameraStepOverridePatchSchema.nullable()
20510
20649
  }), object({ success: literal(true) }), {
@@ -20541,14 +20680,13 @@ method(object({
20541
20680
  });
20542
20681
  /**
20543
20682
  * server-management — per-NODE singleton capability for a node's ROOT
20544
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
20545
- * agents).
20683
+ * package lifecycle (runtime-updatable node packages).
20546
20684
  *
20547
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
20548
- * on agents) carries the whole software stack in its npm dep tree, so ONE
20549
- * version describes the node. Updates install into
20550
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
20551
- * starter (probation boot + auto-rollback to N-1).
20685
+ * Every node role runs the SAME root package (`@camstack/server`), which
20686
+ * carries the whole software stack in its npm dep tree, so ONE version
20687
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
20688
+ * on restart via the baked starter (single-copy in-place swap — no probation,
20689
+ * no auto-rollback).
20552
20690
  *
20553
20691
  * Providers:
20554
20692
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -20656,7 +20794,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
20656
20794
  /** Explicit target version; omitted = latest from the registry. */
20657
20795
  version: string().optional() }), ServerUpdateActionResultSchema, {
20658
20796
  kind: "mutation",
20659
- auth: "admin"
20797
+ auth: "admin",
20798
+ timeoutMs: 16 * 6e4
20660
20799
  }), method(_void(), ServerUpdateActionResultSchema, {
20661
20800
  kind: "mutation",
20662
20801
  auth: "admin"
@@ -21772,22 +21911,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
21772
21911
  var RestartAddonResultSchema = unknown();
21773
21912
  var InstallPackageResultSchema = unknown();
21774
21913
  var ReloadPackagesResultSchema = unknown();
21775
- /**
21776
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
21777
- * server restarts so the admin UI can react to the `restartingAt`
21778
- * timestamp (shows reconnect overlay). The transition from
21779
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
21780
- * `system.restart-completed` event after the new process boots.
21781
- *
21782
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
21783
- */
21784
- var UpdateFrameworkPackageResultSchema = object({
21785
- packageName: string(),
21786
- fromVersion: string(),
21787
- toVersion: string(),
21788
- /** Ms-epoch the server scheduled its self-restart. */
21789
- restartingAt: number()
21790
- });
21791
21914
  var BulkUpdateItemStatusSchema = _enum([
21792
21915
  "queued",
21793
21916
  "updating",
@@ -21915,13 +22038,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
21915
22038
  }), object({ success: literal(true) }), {
21916
22039
  kind: "mutation",
21917
22040
  auth: "admin"
21918
- }), method(object({
21919
- packageName: string().min(1),
21920
- version: string().optional(),
21921
- deferRestart: boolean().optional()
21922
- }), UpdateFrameworkPackageResultSchema, {
21923
- kind: "mutation",
21924
- auth: "admin"
21925
22041
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
21926
22042
  kind: "mutation",
21927
22043
  auth: "admin"
@@ -22804,10 +22920,10 @@ var TopologyCategorySchema = object({
22804
22920
  addons: array(TopologyCategoryAddonSchema).readonly()
22805
22921
  });
22806
22922
  /**
22807
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
22808
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
22809
- * version visibility for the Server management surface. Nullable: offline
22810
- * rows and pre-phase-2 nodes report none.
22923
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
22924
+ * root package for every node role) as reported by its `registerNode`
22925
+ * manifest — version visibility for the Server management surface. Nullable:
22926
+ * offline rows and nodes that never reported one.
22811
22927
  */
22812
22928
  var TopologyRootPackageSchema = object({
22813
22929
  name: string(),
@@ -23195,17 +23311,28 @@ var PlatformScoreSchema = object({
23195
23311
  format: _enum([
23196
23312
  "onnx",
23197
23313
  "coreml",
23198
- "openvino"
23314
+ "openvino",
23315
+ "tflite"
23199
23316
  ]),
23200
23317
  score: number(),
23201
23318
  reason: string(),
23202
23319
  available: boolean()
23203
23320
  });
23321
+ var InferenceDeviceDescriptorSchema = object({
23322
+ key: string(),
23323
+ backend: string(),
23324
+ device: string(),
23325
+ format: ModelFormatSchema,
23326
+ runtime: literal("python"),
23327
+ score: number(),
23328
+ available: boolean()
23329
+ });
23204
23330
  var PlatformCapabilitiesSchema = object({
23205
23331
  hardware: HardwareInfoSchema,
23206
23332
  scores: array(PlatformScoreSchema).readonly(),
23207
23333
  bestScore: PlatformScoreSchema,
23208
- pythonPath: string().nullable()
23334
+ pythonPath: string().nullable(),
23335
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
23209
23336
  });
23210
23337
  var ModelRequirementSchema = object({
23211
23338
  modelId: string(),
@@ -24084,12 +24211,6 @@ Object.freeze({
24084
24211
  addonId: null,
24085
24212
  access: "delete"
24086
24213
  },
24087
- "addons.updateFrameworkPackage": {
24088
- capName: "addons",
24089
- capScope: "system",
24090
- addonId: null,
24091
- access: "create"
24092
- },
24093
24214
  "addons.updatePackage": {
24094
24215
  capName: "addons",
24095
24216
  capScope: "system",
@@ -26862,12 +26983,6 @@ Object.freeze({
26862
26983
  addonId: null,
26863
26984
  access: "view"
26864
26985
  },
26865
- "pipelineExecutor.reprobeEngine": {
26866
- capName: "pipeline-executor",
26867
- capScope: "system",
26868
- addonId: null,
26869
- access: "create"
26870
- },
26871
26986
  "pipelineExecutor.runAudioTest": {
26872
26987
  capName: "pipeline-executor",
26873
26988
  capScope: "system",
@@ -27018,6 +27133,12 @@ Object.freeze({
27018
27133
  addonId: null,
27019
27134
  access: "view"
27020
27135
  },
27136
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27137
+ capName: "pipeline-orchestrator",
27138
+ capScope: "system",
27139
+ addonId: null,
27140
+ access: "view"
27141
+ },
27021
27142
  "pipelineOrchestrator.getPipelineAssignment": {
27022
27143
  capName: "pipeline-orchestrator",
27023
27144
  capScope: "system",
@@ -27030,6 +27151,12 @@ Object.freeze({
27030
27151
  addonId: null,
27031
27152
  access: "view"
27032
27153
  },
27154
+ "pipelineOrchestrator.getPipelineDevicePin": {
27155
+ capName: "pipeline-orchestrator",
27156
+ capScope: "system",
27157
+ addonId: null,
27158
+ access: "view"
27159
+ },
27033
27160
  "pipelineOrchestrator.listAgentSettings": {
27034
27161
  capName: "pipeline-orchestrator",
27035
27162
  capScope: "system",
@@ -27072,19 +27199,19 @@ Object.freeze({
27072
27199
  addonId: null,
27073
27200
  access: "create"
27074
27201
  },
27075
- "pipelineOrchestrator.setAgentAddonDefaults": {
27202
+ "pipelineOrchestrator.setAgentCapabilities": {
27076
27203
  capName: "pipeline-orchestrator",
27077
27204
  capScope: "system",
27078
27205
  addonId: null,
27079
27206
  access: "create"
27080
27207
  },
27081
- "pipelineOrchestrator.setAgentCapabilities": {
27208
+ "pipelineOrchestrator.setAgentDetectWeight": {
27082
27209
  capName: "pipeline-orchestrator",
27083
27210
  capScope: "system",
27084
27211
  addonId: null,
27085
27212
  access: "create"
27086
27213
  },
27087
- "pipelineOrchestrator.setAgentDetectWeight": {
27214
+ "pipelineOrchestrator.setAgentInferenceDevices": {
27088
27215
  capName: "pipeline-orchestrator",
27089
27216
  capScope: "system",
27090
27217
  addonId: null,
@@ -27126,6 +27253,12 @@ Object.freeze({
27126
27253
  addonId: null,
27127
27254
  access: "create"
27128
27255
  },
27256
+ "pipelineOrchestrator.setPipelineDevicePin": {
27257
+ capName: "pipeline-orchestrator",
27258
+ capScope: "system",
27259
+ addonId: null,
27260
+ access: "create"
27261
+ },
27129
27262
  "pipelineOrchestrator.unassignAudio": {
27130
27263
  capName: "pipeline-orchestrator",
27131
27264
  capScope: "system",
@@ -28678,32 +28811,6 @@ Object.freeze({
28678
28811
  "network-access": "ingress",
28679
28812
  "smtp-provider": "email"
28680
28813
  });
28681
- var frameworkSwapPackageSchema = object({
28682
- name: string(),
28683
- stagedPath: string(),
28684
- backupPath: string(),
28685
- toVersion: string(),
28686
- fromVersion: string().nullable()
28687
- });
28688
- object({
28689
- jobId: string(),
28690
- taskId: string(),
28691
- packages: array(frameworkSwapPackageSchema),
28692
- requestedAtMs: number(),
28693
- schemaVersion: literal(1)
28694
- });
28695
- object({
28696
- jobId: string(),
28697
- taskId: string(),
28698
- backups: array(object({
28699
- name: string(),
28700
- backupPath: string(),
28701
- livePath: string()
28702
- })),
28703
- appliedAtMs: number(),
28704
- bootAttempts: number(),
28705
- schemaVersion: literal(1)
28706
- });
28707
28814
  /**
28708
28815
  * Pure fuzzy matcher for adoption location import. Normalized
28709
28816
  * case-insensitive Levenshtein similarity, used to reuse an existing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-homeassistant",
3
- "version": "1.1.32",
3
+ "version": "1.2.1",
4
4
  "description": "Home Assistant device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",