@camstack/addon-provider-rademacher 0.1.14 → 0.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
@@ -8083,6 +8083,17 @@ var ModelCatalogEntrySchema = object({
8083
8083
  "imagenet",
8084
8084
  "none"
8085
8085
  ]).optional(),
8086
+ /**
8087
+ * The model already applies softmax IN-GRAPH — its raw output is a
8088
+ * probability distribution, not logits. When set, the `softmax`
8089
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
8090
+ * probability vector collapses it toward uniform (top-1 score craters far
8091
+ * below its true value, making every confidence gate meaningless). Absent ⇒
8092
+ * the output is raw logits and the postprocessor applies softmax (the normal
8093
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
8094
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
8095
+ */
8096
+ outputProbabilities: boolean().optional(),
8086
8097
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
8087
8098
  /**
8088
8099
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -13373,10 +13384,7 @@ var ConfigUISchemaNullableBridge = custom();
13373
13384
  var InferenceCapabilitiesBridge = custom();
13374
13385
  var ModelAvailabilityListBridge = custom();
13375
13386
  var PipelineRunResultBridge = custom();
13376
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
13377
- kind: "mutation",
13378
- auth: "admin"
13379
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
13387
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
13380
13388
  modelId: string(),
13381
13389
  settings: record(string(), unknown()).readonly()
13382
13390
  }))), method(object({ steps: record(string(), object({
@@ -13436,13 +13444,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
13436
13444
  * (inputClasses ≠ null) are skipped and served per-track via
13437
13445
  * pipelineRunner.runDetailSubtree (two-plane design).
13438
13446
  */
13439
- plane: _enum(["full", "frame"]).optional()
13447
+ plane: _enum(["full", "frame"]).optional(),
13448
+ /**
13449
+ * Inference-device selector (Phase 2 multi-device). Format
13450
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
13451
+ * Omitted ⇒ the runner's default device (current single-engine
13452
+ * behaviour). Selects WHICH device pool of the node runs the call.
13453
+ */
13454
+ deviceKey: string().optional()
13440
13455
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
13441
13456
  engine: PipelineEngineChoiceSchema.optional(),
13442
13457
  steps: array(PipelineStepInputSchema).min(1),
13443
13458
  frames: array(FrameInputSchema).min(1).max(255),
13444
13459
  deviceId: number().optional(),
13445
- sessionId: string().optional()
13460
+ sessionId: string().optional(),
13461
+ /**
13462
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
13463
+ * the batch to the Python pool's bench preprocess cache
13464
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
13465
+ * preprocessed ONCE and every later inference is a pure-inference cache
13466
+ * hit — the sustained-throughput run measures inference, not
13467
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
13468
+ * full preprocess every call, correct). Fresh per sustained run;
13469
+ * released via `uncacheFrame`.
13470
+ */
13471
+ frameId: number().int().nonnegative().optional(),
13472
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
13473
+ deviceKey: string().optional()
13446
13474
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
13447
13475
  data: _instanceof(Uint8Array),
13448
13476
  width: number().int().positive(),
@@ -13474,8 +13502,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
13474
13502
  * - `runtime` — main camera-serving engine (no idle TTL).
13475
13503
  * - `warm-override` — benchmark/test override held in the warm
13476
13504
  * cache; auto-disposed after the idle TTL.
13505
+ * - `device-pool` — a concurrent per-device pool (Phase 2
13506
+ * multi-device, keyed by `deviceKey`) resolved
13507
+ * via `resolveDeviceFactory`. Runs alongside the
13508
+ * `runtime` engine on a DIFFERENT accelerator
13509
+ * (NPU / iGPU / Coral) — this is how the
13510
+ * Engines tab shows all pools running at once.
13477
13511
  */
13478
- kind: _enum(["runtime", "warm-override"]),
13512
+ kind: _enum([
13513
+ "runtime",
13514
+ "warm-override",
13515
+ "device-pool"
13516
+ ]),
13479
13517
  /** Native pid of the underlying Python pool (null when no pool). */
13480
13518
  poolPid: number().nullable(),
13481
13519
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -13610,7 +13648,21 @@ var NativeCropResultSchema = object({
13610
13648
  /** Packed rgb (24-bit) pixels of the crop. */
13611
13649
  bytes: _instanceof(Uint8Array),
13612
13650
  width: number().int().positive(),
13613
- height: number().int().positive()
13651
+ height: number().int().positive(),
13652
+ /**
13653
+ * Which source served this crop, so a quality-sensitive consumer (the native
13654
+ * `keyFrame`) can reject a degraded fallback:
13655
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
13656
+ * quality path).
13657
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
13658
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
13659
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
13660
+ *
13661
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
13662
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
13663
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
13664
+ */
13665
+ tier: _enum(["native", "ram-fullframe"]).optional()
13614
13666
  });
13615
13667
  /** Parent detection context passed to `runDetailSubtree` — the crop's
13616
13668
  * originating detection, in FRAME-space coordinates. Reuses
@@ -13850,7 +13902,14 @@ var RunnerCameraConfigSchema = object({
13850
13902
  * camera's detect node differs from its source-owner (P2d, gated by the
13851
13903
  * `remoteSourcingNodes` rollout setting).
13852
13904
  */
13853
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13905
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13906
+ /**
13907
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13908
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13909
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13910
+ * this only selects WHICH device pool of that node runs the session.
13911
+ */
13912
+ deviceKey: string().optional()
13854
13913
  });
13855
13914
  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;
13856
13915
  /**
@@ -13871,6 +13930,19 @@ var RunnerLocalLoadSchema = object({
13871
13930
  avgInferenceTimeMs: number(),
13872
13931
  /** Total queue depth across motion + detection queues. */
13873
13932
  queueDepthTotal: number(),
13933
+ /**
13934
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13935
+ * this runner currently has attached cameras on, so the orchestrator's second
13936
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13937
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13938
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13939
+ */
13940
+ devices: array(object({
13941
+ deviceKey: string(),
13942
+ backend: string(),
13943
+ attachedCameras: number(),
13944
+ queueDepthTotal: number()
13945
+ })).default([]),
13874
13946
  /** Hardware capability flags reported by this node. */
13875
13947
  hardware: object({
13876
13948
  hasGpu: boolean(),
@@ -17019,6 +17091,8 @@ var BaseDeviceProvider = class extends BaseAddon {
17019
17091
  return toDeviceSummary(device, this.addonId);
17020
17092
  }
17021
17093
  };
17094
+ 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;
17095
+ new Set(Object.values(DeviceType));
17022
17096
  /**
17023
17097
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
17024
17098
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -18592,7 +18666,8 @@ var LinkedDeviceSchema = object({
18592
18666
  deviceId: number(),
18593
18667
  name: string(),
18594
18668
  location: string().nullable(),
18595
- features: array(string())
18669
+ features: array(string()),
18670
+ producesTrackedEvents: boolean().optional()
18596
18671
  });
18597
18672
  var SavedDeviceRowSchema = object({
18598
18673
  /** Numeric id reserved at allocateDeviceId time. */
@@ -20222,6 +20297,7 @@ var TrackSchema = object({
20222
20297
  deviceId: number(),
20223
20298
  className: string(),
20224
20299
  label: string().optional(),
20300
+ producingDeviceName: string().optional(),
20225
20301
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
20226
20302
  source: TrackSourceSchema.optional(),
20227
20303
  firstSeen: number(),
@@ -20359,7 +20435,8 @@ var MediaFileKindEnum = _enum([
20359
20435
  "fullFrameBoxed",
20360
20436
  "faceCrop",
20361
20437
  "plateCrop",
20362
- "keyFrame"
20438
+ "keyFrame",
20439
+ "keyFrameSmall"
20363
20440
  ]);
20364
20441
  var MediaFileSchema = object({
20365
20442
  key: string(),
@@ -20688,13 +20765,11 @@ var PipelineTemplateSchema = object({
20688
20765
  createdAt: string(),
20689
20766
  updatedAt: string()
20690
20767
  });
20691
- var AgentAddonConfigSchema = object({
20692
- enabled: boolean(),
20768
+ var DeviceStepConfigSchema = object({
20693
20769
  modelId: string().optional(),
20694
- settings: record(string(), unknown()).readonly()
20770
+ settings: record(string(), unknown()).optional()
20695
20771
  });
20696
20772
  var AgentPipelineSettingsSchema = object({
20697
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
20698
20773
  maxCameras: number().int().nonnegative().nullable().default(null),
20699
20774
  /** Per-node detection weight (relative share for the quota balancer). */
20700
20775
  detectWeight: number().positive().optional(),
@@ -20718,7 +20793,22 @@ var AgentPipelineSettingsSchema = object({
20718
20793
  * it already uses to reach the hub). Set this only when the auto-detected
20719
20794
  * address is wrong (multi-homed host, NAT, custom interface).
20720
20795
  */
20721
- reachableHost: string().optional()
20796
+ reachableHost: string().optional(),
20797
+ /**
20798
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20799
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20800
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20801
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20802
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20803
+ * the default model/settings for every camera landing on that accelerator;
20804
+ * a stepId absent ⇒ the step uses that device's format default.
20805
+ */
20806
+ inferenceDevices: record(string(), object({
20807
+ enabled: boolean(),
20808
+ weight: number().positive().optional(),
20809
+ maxSessions: number().int().positive().optional(),
20810
+ steps: record(string(), DeviceStepConfigSchema).optional()
20811
+ })).optional()
20722
20812
  });
20723
20813
  var CameraPipelineForAgentSchema = object({
20724
20814
  steps: array(PipelineStepInputSchema).readonly(),
@@ -20728,14 +20818,13 @@ var CameraPipelineForAgentSchema = object({
20728
20818
  }).nullable()
20729
20819
  });
20730
20820
  var CameraStepOverridePatchSchema = object({
20731
- enabled: boolean().optional(),
20732
20821
  modelId: string().optional(),
20733
20822
  settings: record(string(), unknown()).readonly().optional()
20734
20823
  });
20735
20824
  var CameraPipelineSettingsSchema = object({
20736
20825
  pinnedAgentNodeId: string().optional(),
20737
20826
  stepToggles: record(string(), boolean()).optional(),
20738
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20827
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
20739
20828
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
20740
20829
  });
20741
20830
  /**
@@ -20949,6 +21038,44 @@ var CameraStatusSchema = object({
20949
21038
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20950
21039
  fetchedAt: number()
20951
21040
  });
21041
+ var NodeInferenceDeviceSchema = object({
21042
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
21043
+ key: string(),
21044
+ backend: string(),
21045
+ device: string(),
21046
+ format: _enum(MODEL_FORMATS),
21047
+ /** Whether the node's live probe reports the device as usable right now. */
21048
+ available: boolean(),
21049
+ /**
21050
+ * Whether this device participates in dispatch. AUTO default (spec C2):
21051
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
21052
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
21053
+ * not a balanced target). An explicit stored value always wins; a stored-only
21054
+ * (unavailable) key keeps its stored value.
21055
+ */
21056
+ enabled: boolean(),
21057
+ /** Relative balancer weight for the enabled device (default 1). */
21058
+ weight: number(),
21059
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
21060
+ maxSessions: number().nullable(),
21061
+ /** Object-detection model the executor defaults to for this deviceKey. */
21062
+ defaultModelId: string(),
21063
+ /**
21064
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
21065
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
21066
+ * Absent/empty ⇒ no base (every step uses its device format default). The
21067
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
21068
+ * available per format; this is the stored selection that becomes the
21069
+ * default for EVERY camera landing on this accelerator.
21070
+ */
21071
+ steps: record(string(), DeviceStepConfigSchema).optional()
21072
+ });
21073
+ var NodeInferenceDevicesSchema = object({
21074
+ nodeId: string(),
21075
+ /** False when the node's platform-probe was unreachable (no live device set). */
21076
+ reachable: boolean(),
21077
+ devices: array(NodeInferenceDeviceSchema).readonly()
21078
+ });
20952
21079
  method(object({
20953
21080
  deviceId: number(),
20954
21081
  agentNodeId: string()
@@ -20958,7 +21085,13 @@ method(object({
20958
21085
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20959
21086
  kind: "mutation",
20960
21087
  auth: "admin"
20961
- }), method(_void(), object({ migrated: number() }), {
21088
+ }), method(object({
21089
+ deviceId: number(),
21090
+ deviceKey: string()
21091
+ }), object({ success: literal(true) }), {
21092
+ kind: "mutation",
21093
+ auth: "admin"
21094
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20962
21095
  kind: "mutation",
20963
21096
  auth: "admin"
20964
21097
  }), 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({
@@ -20992,13 +21125,7 @@ method(object({
20992
21125
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20993
21126
  nodeId: string(),
20994
21127
  settings: AgentPipelineSettingsSchema
20995
- })).readonly()), method(object({
20996
- agentNodeId: string(),
20997
- defaults: record(string(), AgentAddonConfigSchema)
20998
- }), object({ success: literal(true) }), {
20999
- kind: "mutation",
21000
- auth: "admin"
21001
- }), method(object({ agentNodeId: string() }), object({
21128
+ })).readonly()), method(object({ agentNodeId: string() }), object({
21002
21129
  success: boolean(),
21003
21130
  removed: boolean()
21004
21131
  }), {
@@ -21030,7 +21157,18 @@ method(object({
21030
21157
  }), object({ success: literal(true) }), {
21031
21158
  kind: "mutation",
21032
21159
  auth: "admin"
21033
- }), method(object({ agentNodeId: string() }), object({
21160
+ }), method(object({
21161
+ agentNodeId: string(),
21162
+ inferenceDevices: record(string(), object({
21163
+ enabled: boolean(),
21164
+ weight: number().positive().optional(),
21165
+ maxSessions: number().int().positive().optional(),
21166
+ steps: record(string(), DeviceStepConfigSchema).optional()
21167
+ }))
21168
+ }), object({ success: literal(true) }), {
21169
+ kind: "mutation",
21170
+ auth: "admin"
21171
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
21034
21172
  success: literal(true),
21035
21173
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
21036
21174
  effectiveModelId: string().nullable(),
@@ -21046,9 +21184,10 @@ method(object({
21046
21184
  }), object({ success: literal(true) }), {
21047
21185
  kind: "mutation",
21048
21186
  auth: "admin"
21049
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
21187
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
21050
21188
  deviceId: number(),
21051
21189
  agentNodeId: string(),
21190
+ deviceKey: string(),
21052
21191
  addonId: string(),
21053
21192
  patch: CameraStepOverridePatchSchema.nullable()
21054
21193
  }), object({ success: literal(true) }), {
@@ -21085,14 +21224,13 @@ method(object({
21085
21224
  });
21086
21225
  /**
21087
21226
  * server-management — per-NODE singleton capability for a node's ROOT
21088
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
21089
- * agents).
21227
+ * package lifecycle (runtime-updatable node packages).
21090
21228
  *
21091
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
21092
- * on agents) carries the whole software stack in its npm dep tree, so ONE
21093
- * version describes the node. Updates install into
21094
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
21095
- * starter (probation boot + auto-rollback to N-1).
21229
+ * Every node role runs the SAME root package (`@camstack/server`), which
21230
+ * carries the whole software stack in its npm dep tree, so ONE version
21231
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
21232
+ * on restart via the baked starter (single-copy in-place swap — no probation,
21233
+ * no auto-rollback).
21096
21234
  *
21097
21235
  * Providers:
21098
21236
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -21200,7 +21338,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
21200
21338
  /** Explicit target version; omitted = latest from the registry. */
21201
21339
  version: string().optional() }), ServerUpdateActionResultSchema, {
21202
21340
  kind: "mutation",
21203
- auth: "admin"
21341
+ auth: "admin",
21342
+ timeoutMs: 16 * 6e4
21204
21343
  }), method(_void(), ServerUpdateActionResultSchema, {
21205
21344
  kind: "mutation",
21206
21345
  auth: "admin"
@@ -22244,22 +22383,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
22244
22383
  var RestartAddonResultSchema = unknown();
22245
22384
  var InstallPackageResultSchema = unknown();
22246
22385
  var ReloadPackagesResultSchema = unknown();
22247
- /**
22248
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
22249
- * server restarts so the admin UI can react to the `restartingAt`
22250
- * timestamp (shows reconnect overlay). The transition from
22251
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
22252
- * `system.restart-completed` event after the new process boots.
22253
- *
22254
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
22255
- */
22256
- var UpdateFrameworkPackageResultSchema = object({
22257
- packageName: string(),
22258
- fromVersion: string(),
22259
- toVersion: string(),
22260
- /** Ms-epoch the server scheduled its self-restart. */
22261
- restartingAt: number()
22262
- });
22263
22386
  var BulkUpdateItemStatusSchema = _enum([
22264
22387
  "queued",
22265
22388
  "updating",
@@ -22387,13 +22510,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
22387
22510
  }), object({ success: literal(true) }), {
22388
22511
  kind: "mutation",
22389
22512
  auth: "admin"
22390
- }), method(object({
22391
- packageName: string().min(1),
22392
- version: string().optional(),
22393
- deferRestart: boolean().optional()
22394
- }), UpdateFrameworkPackageResultSchema, {
22395
- kind: "mutation",
22396
- auth: "admin"
22397
22513
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
22398
22514
  kind: "mutation",
22399
22515
  auth: "admin"
@@ -23259,10 +23375,10 @@ var TopologyCategorySchema = object({
23259
23375
  addons: array(TopologyCategoryAddonSchema).readonly()
23260
23376
  });
23261
23377
  /**
23262
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
23263
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
23264
- * version visibility for the Server management surface. Nullable: offline
23265
- * rows and pre-phase-2 nodes report none.
23378
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
23379
+ * root package for every node role) as reported by its `registerNode`
23380
+ * manifest — version visibility for the Server management surface. Nullable:
23381
+ * offline rows and nodes that never reported one.
23266
23382
  */
23267
23383
  var TopologyRootPackageSchema = object({
23268
23384
  name: string(),
@@ -23650,17 +23766,28 @@ var PlatformScoreSchema = object({
23650
23766
  format: _enum([
23651
23767
  "onnx",
23652
23768
  "coreml",
23653
- "openvino"
23769
+ "openvino",
23770
+ "tflite"
23654
23771
  ]),
23655
23772
  score: number(),
23656
23773
  reason: string(),
23657
23774
  available: boolean()
23658
23775
  });
23776
+ var InferenceDeviceDescriptorSchema = object({
23777
+ key: string(),
23778
+ backend: string(),
23779
+ device: string(),
23780
+ format: ModelFormatSchema,
23781
+ runtime: literal("python"),
23782
+ score: number(),
23783
+ available: boolean()
23784
+ });
23659
23785
  var PlatformCapabilitiesSchema = object({
23660
23786
  hardware: HardwareInfoSchema,
23661
23787
  scores: array(PlatformScoreSchema).readonly(),
23662
23788
  bestScore: PlatformScoreSchema,
23663
- pythonPath: string().nullable()
23789
+ pythonPath: string().nullable(),
23790
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
23664
23791
  });
23665
23792
  var ModelRequirementSchema = object({
23666
23793
  modelId: string(),
@@ -24539,12 +24666,6 @@ Object.freeze({
24539
24666
  addonId: null,
24540
24667
  access: "delete"
24541
24668
  },
24542
- "addons.updateFrameworkPackage": {
24543
- capName: "addons",
24544
- capScope: "system",
24545
- addonId: null,
24546
- access: "create"
24547
- },
24548
24669
  "addons.updatePackage": {
24549
24670
  capName: "addons",
24550
24671
  capScope: "system",
@@ -27317,12 +27438,6 @@ Object.freeze({
27317
27438
  addonId: null,
27318
27439
  access: "view"
27319
27440
  },
27320
- "pipelineExecutor.reprobeEngine": {
27321
- capName: "pipeline-executor",
27322
- capScope: "system",
27323
- addonId: null,
27324
- access: "create"
27325
- },
27326
27441
  "pipelineExecutor.runAudioTest": {
27327
27442
  capName: "pipeline-executor",
27328
27443
  capScope: "system",
@@ -27473,6 +27588,12 @@ Object.freeze({
27473
27588
  addonId: null,
27474
27589
  access: "view"
27475
27590
  },
27591
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27592
+ capName: "pipeline-orchestrator",
27593
+ capScope: "system",
27594
+ addonId: null,
27595
+ access: "view"
27596
+ },
27476
27597
  "pipelineOrchestrator.getPipelineAssignment": {
27477
27598
  capName: "pipeline-orchestrator",
27478
27599
  capScope: "system",
@@ -27485,6 +27606,12 @@ Object.freeze({
27485
27606
  addonId: null,
27486
27607
  access: "view"
27487
27608
  },
27609
+ "pipelineOrchestrator.getPipelineDevicePin": {
27610
+ capName: "pipeline-orchestrator",
27611
+ capScope: "system",
27612
+ addonId: null,
27613
+ access: "view"
27614
+ },
27488
27615
  "pipelineOrchestrator.listAgentSettings": {
27489
27616
  capName: "pipeline-orchestrator",
27490
27617
  capScope: "system",
@@ -27527,19 +27654,19 @@ Object.freeze({
27527
27654
  addonId: null,
27528
27655
  access: "create"
27529
27656
  },
27530
- "pipelineOrchestrator.setAgentAddonDefaults": {
27657
+ "pipelineOrchestrator.setAgentCapabilities": {
27531
27658
  capName: "pipeline-orchestrator",
27532
27659
  capScope: "system",
27533
27660
  addonId: null,
27534
27661
  access: "create"
27535
27662
  },
27536
- "pipelineOrchestrator.setAgentCapabilities": {
27663
+ "pipelineOrchestrator.setAgentDetectWeight": {
27537
27664
  capName: "pipeline-orchestrator",
27538
27665
  capScope: "system",
27539
27666
  addonId: null,
27540
27667
  access: "create"
27541
27668
  },
27542
- "pipelineOrchestrator.setAgentDetectWeight": {
27669
+ "pipelineOrchestrator.setAgentInferenceDevices": {
27543
27670
  capName: "pipeline-orchestrator",
27544
27671
  capScope: "system",
27545
27672
  addonId: null,
@@ -27581,6 +27708,12 @@ Object.freeze({
27581
27708
  addonId: null,
27582
27709
  access: "create"
27583
27710
  },
27711
+ "pipelineOrchestrator.setPipelineDevicePin": {
27712
+ capName: "pipeline-orchestrator",
27713
+ capScope: "system",
27714
+ addonId: null,
27715
+ access: "create"
27716
+ },
27584
27717
  "pipelineOrchestrator.unassignAudio": {
27585
27718
  capName: "pipeline-orchestrator",
27586
27719
  capScope: "system",
@@ -29133,32 +29266,6 @@ Object.freeze({
29133
29266
  "network-access": "ingress",
29134
29267
  "smtp-provider": "email"
29135
29268
  });
29136
- var frameworkSwapPackageSchema = object({
29137
- name: string(),
29138
- stagedPath: string(),
29139
- backupPath: string(),
29140
- toVersion: string(),
29141
- fromVersion: string().nullable()
29142
- });
29143
- object({
29144
- jobId: string(),
29145
- taskId: string(),
29146
- packages: array(frameworkSwapPackageSchema),
29147
- requestedAtMs: number(),
29148
- schemaVersion: literal(1)
29149
- });
29150
- object({
29151
- jobId: string(),
29152
- taskId: string(),
29153
- backups: array(object({
29154
- name: string(),
29155
- backupPath: string(),
29156
- livePath: string()
29157
- })),
29158
- appliedAtMs: number(),
29159
- bootAttempts: number(),
29160
- schemaVersion: literal(1)
29161
- });
29162
29269
  //#endregion
29163
29270
  //#region src/config.ts
29164
29271
  /**
package/dist/addon.mjs CHANGED
@@ -8082,6 +8082,17 @@ var ModelCatalogEntrySchema = object({
8082
8082
  "imagenet",
8083
8083
  "none"
8084
8084
  ]).optional(),
8085
+ /**
8086
+ * The model already applies softmax IN-GRAPH — its raw output is a
8087
+ * probability distribution, not logits. When set, the `softmax`
8088
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
8089
+ * probability vector collapses it toward uniform (top-1 score craters far
8090
+ * below its true value, making every confidence gate meaningless). Absent ⇒
8091
+ * the output is raw logits and the postprocessor applies softmax (the normal
8092
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
8093
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
8094
+ */
8095
+ outputProbabilities: boolean().optional(),
8085
8096
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
8086
8097
  /**
8087
8098
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -13372,10 +13383,7 @@ var ConfigUISchemaNullableBridge = custom();
13372
13383
  var InferenceCapabilitiesBridge = custom();
13373
13384
  var ModelAvailabilityListBridge = custom();
13374
13385
  var PipelineRunResultBridge = custom();
13375
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
13376
- kind: "mutation",
13377
- auth: "admin"
13378
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
13386
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
13379
13387
  modelId: string(),
13380
13388
  settings: record(string(), unknown()).readonly()
13381
13389
  }))), method(object({ steps: record(string(), object({
@@ -13435,13 +13443,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
13435
13443
  * (inputClasses ≠ null) are skipped and served per-track via
13436
13444
  * pipelineRunner.runDetailSubtree (two-plane design).
13437
13445
  */
13438
- plane: _enum(["full", "frame"]).optional()
13446
+ plane: _enum(["full", "frame"]).optional(),
13447
+ /**
13448
+ * Inference-device selector (Phase 2 multi-device). Format
13449
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
13450
+ * Omitted ⇒ the runner's default device (current single-engine
13451
+ * behaviour). Selects WHICH device pool of the node runs the call.
13452
+ */
13453
+ deviceKey: string().optional()
13439
13454
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
13440
13455
  engine: PipelineEngineChoiceSchema.optional(),
13441
13456
  steps: array(PipelineStepInputSchema).min(1),
13442
13457
  frames: array(FrameInputSchema).min(1).max(255),
13443
13458
  deviceId: number().optional(),
13444
- sessionId: string().optional()
13459
+ sessionId: string().optional(),
13460
+ /**
13461
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
13462
+ * the batch to the Python pool's bench preprocess cache
13463
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
13464
+ * preprocessed ONCE and every later inference is a pure-inference cache
13465
+ * hit — the sustained-throughput run measures inference, not
13466
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
13467
+ * full preprocess every call, correct). Fresh per sustained run;
13468
+ * released via `uncacheFrame`.
13469
+ */
13470
+ frameId: number().int().nonnegative().optional(),
13471
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
13472
+ deviceKey: string().optional()
13445
13473
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
13446
13474
  data: _instanceof(Uint8Array),
13447
13475
  width: number().int().positive(),
@@ -13473,8 +13501,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
13473
13501
  * - `runtime` — main camera-serving engine (no idle TTL).
13474
13502
  * - `warm-override` — benchmark/test override held in the warm
13475
13503
  * cache; auto-disposed after the idle TTL.
13504
+ * - `device-pool` — a concurrent per-device pool (Phase 2
13505
+ * multi-device, keyed by `deviceKey`) resolved
13506
+ * via `resolveDeviceFactory`. Runs alongside the
13507
+ * `runtime` engine on a DIFFERENT accelerator
13508
+ * (NPU / iGPU / Coral) — this is how the
13509
+ * Engines tab shows all pools running at once.
13476
13510
  */
13477
- kind: _enum(["runtime", "warm-override"]),
13511
+ kind: _enum([
13512
+ "runtime",
13513
+ "warm-override",
13514
+ "device-pool"
13515
+ ]),
13478
13516
  /** Native pid of the underlying Python pool (null when no pool). */
13479
13517
  poolPid: number().nullable(),
13480
13518
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -13609,7 +13647,21 @@ var NativeCropResultSchema = object({
13609
13647
  /** Packed rgb (24-bit) pixels of the crop. */
13610
13648
  bytes: _instanceof(Uint8Array),
13611
13649
  width: number().int().positive(),
13612
- height: number().int().positive()
13650
+ height: number().int().positive(),
13651
+ /**
13652
+ * Which source served this crop, so a quality-sensitive consumer (the native
13653
+ * `keyFrame`) can reject a degraded fallback:
13654
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
13655
+ * quality path).
13656
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
13657
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
13658
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
13659
+ *
13660
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
13661
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
13662
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
13663
+ */
13664
+ tier: _enum(["native", "ram-fullframe"]).optional()
13613
13665
  });
13614
13666
  /** Parent detection context passed to `runDetailSubtree` — the crop's
13615
13667
  * originating detection, in FRAME-space coordinates. Reuses
@@ -13849,7 +13901,14 @@ var RunnerCameraConfigSchema = object({
13849
13901
  * camera's detect node differs from its source-owner (P2d, gated by the
13850
13902
  * `remoteSourcingNodes` rollout setting).
13851
13903
  */
13852
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
13904
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
13905
+ /**
13906
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
13907
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
13908
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
13909
+ * this only selects WHICH device pool of that node runs the session.
13910
+ */
13911
+ deviceKey: string().optional()
13853
13912
  });
13854
13913
  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;
13855
13914
  /**
@@ -13870,6 +13929,19 @@ var RunnerLocalLoadSchema = object({
13870
13929
  avgInferenceTimeMs: number(),
13871
13930
  /** Total queue depth across motion + detection queues. */
13872
13931
  queueDepthTotal: number(),
13932
+ /**
13933
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
13934
+ * this runner currently has attached cameras on, so the orchestrator's second
13935
+ * `balance()` pass (over a node's devices) weights on real per-pool session
13936
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
13937
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
13938
+ */
13939
+ devices: array(object({
13940
+ deviceKey: string(),
13941
+ backend: string(),
13942
+ attachedCameras: number(),
13943
+ queueDepthTotal: number()
13944
+ })).default([]),
13873
13945
  /** Hardware capability flags reported by this node. */
13874
13946
  hardware: object({
13875
13947
  hasGpu: boolean(),
@@ -17018,6 +17090,8 @@ var BaseDeviceProvider = class extends BaseAddon {
17018
17090
  return toDeviceSummary(device, this.addonId);
17019
17091
  }
17020
17092
  };
17093
+ 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;
17094
+ new Set(Object.values(DeviceType));
17021
17095
  /**
17022
17096
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
17023
17097
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -18591,7 +18665,8 @@ var LinkedDeviceSchema = object({
18591
18665
  deviceId: number(),
18592
18666
  name: string(),
18593
18667
  location: string().nullable(),
18594
- features: array(string())
18668
+ features: array(string()),
18669
+ producesTrackedEvents: boolean().optional()
18595
18670
  });
18596
18671
  var SavedDeviceRowSchema = object({
18597
18672
  /** Numeric id reserved at allocateDeviceId time. */
@@ -20221,6 +20296,7 @@ var TrackSchema = object({
20221
20296
  deviceId: number(),
20222
20297
  className: string(),
20223
20298
  label: string().optional(),
20299
+ producingDeviceName: string().optional(),
20224
20300
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
20225
20301
  source: TrackSourceSchema.optional(),
20226
20302
  firstSeen: number(),
@@ -20358,7 +20434,8 @@ var MediaFileKindEnum = _enum([
20358
20434
  "fullFrameBoxed",
20359
20435
  "faceCrop",
20360
20436
  "plateCrop",
20361
- "keyFrame"
20437
+ "keyFrame",
20438
+ "keyFrameSmall"
20362
20439
  ]);
20363
20440
  var MediaFileSchema = object({
20364
20441
  key: string(),
@@ -20687,13 +20764,11 @@ var PipelineTemplateSchema = object({
20687
20764
  createdAt: string(),
20688
20765
  updatedAt: string()
20689
20766
  });
20690
- var AgentAddonConfigSchema = object({
20691
- enabled: boolean(),
20767
+ var DeviceStepConfigSchema = object({
20692
20768
  modelId: string().optional(),
20693
- settings: record(string(), unknown()).readonly()
20769
+ settings: record(string(), unknown()).optional()
20694
20770
  });
20695
20771
  var AgentPipelineSettingsSchema = object({
20696
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
20697
20772
  maxCameras: number().int().nonnegative().nullable().default(null),
20698
20773
  /** Per-node detection weight (relative share for the quota balancer). */
20699
20774
  detectWeight: number().positive().optional(),
@@ -20717,7 +20792,22 @@ var AgentPipelineSettingsSchema = object({
20717
20792
  * it already uses to reach the hub). Set this only when the auto-detected
20718
20793
  * address is wrong (multi-homed host, NAT, custom interface).
20719
20794
  */
20720
- reachableHost: string().optional()
20795
+ reachableHost: string().optional(),
20796
+ /**
20797
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
20798
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
20799
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
20800
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
20801
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
20802
+ * the default model/settings for every camera landing on that accelerator;
20803
+ * a stepId absent ⇒ the step uses that device's format default.
20804
+ */
20805
+ inferenceDevices: record(string(), object({
20806
+ enabled: boolean(),
20807
+ weight: number().positive().optional(),
20808
+ maxSessions: number().int().positive().optional(),
20809
+ steps: record(string(), DeviceStepConfigSchema).optional()
20810
+ })).optional()
20721
20811
  });
20722
20812
  var CameraPipelineForAgentSchema = object({
20723
20813
  steps: array(PipelineStepInputSchema).readonly(),
@@ -20727,14 +20817,13 @@ var CameraPipelineForAgentSchema = object({
20727
20817
  }).nullable()
20728
20818
  });
20729
20819
  var CameraStepOverridePatchSchema = object({
20730
- enabled: boolean().optional(),
20731
20820
  modelId: string().optional(),
20732
20821
  settings: record(string(), unknown()).readonly().optional()
20733
20822
  });
20734
20823
  var CameraPipelineSettingsSchema = object({
20735
20824
  pinnedAgentNodeId: string().optional(),
20736
20825
  stepToggles: record(string(), boolean()).optional(),
20737
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
20826
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
20738
20827
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
20739
20828
  });
20740
20829
  /**
@@ -20948,6 +21037,44 @@ var CameraStatusSchema = object({
20948
21037
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
20949
21038
  fetchedAt: number()
20950
21039
  });
21040
+ var NodeInferenceDeviceSchema = object({
21041
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
21042
+ key: string(),
21043
+ backend: string(),
21044
+ device: string(),
21045
+ format: _enum(MODEL_FORMATS),
21046
+ /** Whether the node's live probe reports the device as usable right now. */
21047
+ available: boolean(),
21048
+ /**
21049
+ * Whether this device participates in dispatch. AUTO default (spec C2):
21050
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
21051
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
21052
+ * not a balanced target). An explicit stored value always wins; a stored-only
21053
+ * (unavailable) key keeps its stored value.
21054
+ */
21055
+ enabled: boolean(),
21056
+ /** Relative balancer weight for the enabled device (default 1). */
21057
+ weight: number(),
21058
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
21059
+ maxSessions: number().nullable(),
21060
+ /** Object-detection model the executor defaults to for this deviceKey. */
21061
+ defaultModelId: string(),
21062
+ /**
21063
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
21064
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
21065
+ * Absent/empty ⇒ no base (every step uses its device format default). The
21066
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
21067
+ * available per format; this is the stored selection that becomes the
21068
+ * default for EVERY camera landing on this accelerator.
21069
+ */
21070
+ steps: record(string(), DeviceStepConfigSchema).optional()
21071
+ });
21072
+ var NodeInferenceDevicesSchema = object({
21073
+ nodeId: string(),
21074
+ /** False when the node's platform-probe was unreachable (no live device set). */
21075
+ reachable: boolean(),
21076
+ devices: array(NodeInferenceDeviceSchema).readonly()
21077
+ });
20951
21078
  method(object({
20952
21079
  deviceId: number(),
20953
21080
  agentNodeId: string()
@@ -20957,7 +21084,13 @@ method(object({
20957
21084
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
20958
21085
  kind: "mutation",
20959
21086
  auth: "admin"
20960
- }), method(_void(), object({ migrated: number() }), {
21087
+ }), method(object({
21088
+ deviceId: number(),
21089
+ deviceKey: string()
21090
+ }), object({ success: literal(true) }), {
21091
+ kind: "mutation",
21092
+ auth: "admin"
21093
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
20961
21094
  kind: "mutation",
20962
21095
  auth: "admin"
20963
21096
  }), 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({
@@ -20991,13 +21124,7 @@ method(object({
20991
21124
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
20992
21125
  nodeId: string(),
20993
21126
  settings: AgentPipelineSettingsSchema
20994
- })).readonly()), method(object({
20995
- agentNodeId: string(),
20996
- defaults: record(string(), AgentAddonConfigSchema)
20997
- }), object({ success: literal(true) }), {
20998
- kind: "mutation",
20999
- auth: "admin"
21000
- }), method(object({ agentNodeId: string() }), object({
21127
+ })).readonly()), method(object({ agentNodeId: string() }), object({
21001
21128
  success: boolean(),
21002
21129
  removed: boolean()
21003
21130
  }), {
@@ -21029,7 +21156,18 @@ method(object({
21029
21156
  }), object({ success: literal(true) }), {
21030
21157
  kind: "mutation",
21031
21158
  auth: "admin"
21032
- }), method(object({ agentNodeId: string() }), object({
21159
+ }), method(object({
21160
+ agentNodeId: string(),
21161
+ inferenceDevices: record(string(), object({
21162
+ enabled: boolean(),
21163
+ weight: number().positive().optional(),
21164
+ maxSessions: number().int().positive().optional(),
21165
+ steps: record(string(), DeviceStepConfigSchema).optional()
21166
+ }))
21167
+ }), object({ success: literal(true) }), {
21168
+ kind: "mutation",
21169
+ auth: "admin"
21170
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
21033
21171
  success: literal(true),
21034
21172
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
21035
21173
  effectiveModelId: string().nullable(),
@@ -21045,9 +21183,10 @@ method(object({
21045
21183
  }), object({ success: literal(true) }), {
21046
21184
  kind: "mutation",
21047
21185
  auth: "admin"
21048
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
21186
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
21049
21187
  deviceId: number(),
21050
21188
  agentNodeId: string(),
21189
+ deviceKey: string(),
21051
21190
  addonId: string(),
21052
21191
  patch: CameraStepOverridePatchSchema.nullable()
21053
21192
  }), object({ success: literal(true) }), {
@@ -21084,14 +21223,13 @@ method(object({
21084
21223
  });
21085
21224
  /**
21086
21225
  * server-management — per-NODE singleton capability for a node's ROOT
21087
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
21088
- * agents).
21226
+ * package lifecycle (runtime-updatable node packages).
21089
21227
  *
21090
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
21091
- * on agents) carries the whole software stack in its npm dep tree, so ONE
21092
- * version describes the node. Updates install into
21093
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
21094
- * starter (probation boot + auto-rollback to N-1).
21228
+ * Every node role runs the SAME root package (`@camstack/server`), which
21229
+ * carries the whole software stack in its npm dep tree, so ONE version
21230
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
21231
+ * on restart via the baked starter (single-copy in-place swap — no probation,
21232
+ * no auto-rollback).
21095
21233
  *
21096
21234
  * Providers:
21097
21235
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -21199,7 +21337,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
21199
21337
  /** Explicit target version; omitted = latest from the registry. */
21200
21338
  version: string().optional() }), ServerUpdateActionResultSchema, {
21201
21339
  kind: "mutation",
21202
- auth: "admin"
21340
+ auth: "admin",
21341
+ timeoutMs: 16 * 6e4
21203
21342
  }), method(_void(), ServerUpdateActionResultSchema, {
21204
21343
  kind: "mutation",
21205
21344
  auth: "admin"
@@ -22243,22 +22382,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
22243
22382
  var RestartAddonResultSchema = unknown();
22244
22383
  var InstallPackageResultSchema = unknown();
22245
22384
  var ReloadPackagesResultSchema = unknown();
22246
- /**
22247
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
22248
- * server restarts so the admin UI can react to the `restartingAt`
22249
- * timestamp (shows reconnect overlay). The transition from
22250
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
22251
- * `system.restart-completed` event after the new process boots.
22252
- *
22253
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
22254
- */
22255
- var UpdateFrameworkPackageResultSchema = object({
22256
- packageName: string(),
22257
- fromVersion: string(),
22258
- toVersion: string(),
22259
- /** Ms-epoch the server scheduled its self-restart. */
22260
- restartingAt: number()
22261
- });
22262
22385
  var BulkUpdateItemStatusSchema = _enum([
22263
22386
  "queued",
22264
22387
  "updating",
@@ -22386,13 +22509,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
22386
22509
  }), object({ success: literal(true) }), {
22387
22510
  kind: "mutation",
22388
22511
  auth: "admin"
22389
- }), method(object({
22390
- packageName: string().min(1),
22391
- version: string().optional(),
22392
- deferRestart: boolean().optional()
22393
- }), UpdateFrameworkPackageResultSchema, {
22394
- kind: "mutation",
22395
- auth: "admin"
22396
22512
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
22397
22513
  kind: "mutation",
22398
22514
  auth: "admin"
@@ -23258,10 +23374,10 @@ var TopologyCategorySchema = object({
23258
23374
  addons: array(TopologyCategoryAddonSchema).readonly()
23259
23375
  });
23260
23376
  /**
23261
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
23262
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
23263
- * version visibility for the Server management surface. Nullable: offline
23264
- * rows and pre-phase-2 nodes report none.
23377
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
23378
+ * root package for every node role) as reported by its `registerNode`
23379
+ * manifest — version visibility for the Server management surface. Nullable:
23380
+ * offline rows and nodes that never reported one.
23265
23381
  */
23266
23382
  var TopologyRootPackageSchema = object({
23267
23383
  name: string(),
@@ -23649,17 +23765,28 @@ var PlatformScoreSchema = object({
23649
23765
  format: _enum([
23650
23766
  "onnx",
23651
23767
  "coreml",
23652
- "openvino"
23768
+ "openvino",
23769
+ "tflite"
23653
23770
  ]),
23654
23771
  score: number(),
23655
23772
  reason: string(),
23656
23773
  available: boolean()
23657
23774
  });
23775
+ var InferenceDeviceDescriptorSchema = object({
23776
+ key: string(),
23777
+ backend: string(),
23778
+ device: string(),
23779
+ format: ModelFormatSchema,
23780
+ runtime: literal("python"),
23781
+ score: number(),
23782
+ available: boolean()
23783
+ });
23658
23784
  var PlatformCapabilitiesSchema = object({
23659
23785
  hardware: HardwareInfoSchema,
23660
23786
  scores: array(PlatformScoreSchema).readonly(),
23661
23787
  bestScore: PlatformScoreSchema,
23662
- pythonPath: string().nullable()
23788
+ pythonPath: string().nullable(),
23789
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
23663
23790
  });
23664
23791
  var ModelRequirementSchema = object({
23665
23792
  modelId: string(),
@@ -24538,12 +24665,6 @@ Object.freeze({
24538
24665
  addonId: null,
24539
24666
  access: "delete"
24540
24667
  },
24541
- "addons.updateFrameworkPackage": {
24542
- capName: "addons",
24543
- capScope: "system",
24544
- addonId: null,
24545
- access: "create"
24546
- },
24547
24668
  "addons.updatePackage": {
24548
24669
  capName: "addons",
24549
24670
  capScope: "system",
@@ -27316,12 +27437,6 @@ Object.freeze({
27316
27437
  addonId: null,
27317
27438
  access: "view"
27318
27439
  },
27319
- "pipelineExecutor.reprobeEngine": {
27320
- capName: "pipeline-executor",
27321
- capScope: "system",
27322
- addonId: null,
27323
- access: "create"
27324
- },
27325
27440
  "pipelineExecutor.runAudioTest": {
27326
27441
  capName: "pipeline-executor",
27327
27442
  capScope: "system",
@@ -27472,6 +27587,12 @@ Object.freeze({
27472
27587
  addonId: null,
27473
27588
  access: "view"
27474
27589
  },
27590
+ "pipelineOrchestrator.getNodeInferenceDevices": {
27591
+ capName: "pipeline-orchestrator",
27592
+ capScope: "system",
27593
+ addonId: null,
27594
+ access: "view"
27595
+ },
27475
27596
  "pipelineOrchestrator.getPipelineAssignment": {
27476
27597
  capName: "pipeline-orchestrator",
27477
27598
  capScope: "system",
@@ -27484,6 +27605,12 @@ Object.freeze({
27484
27605
  addonId: null,
27485
27606
  access: "view"
27486
27607
  },
27608
+ "pipelineOrchestrator.getPipelineDevicePin": {
27609
+ capName: "pipeline-orchestrator",
27610
+ capScope: "system",
27611
+ addonId: null,
27612
+ access: "view"
27613
+ },
27487
27614
  "pipelineOrchestrator.listAgentSettings": {
27488
27615
  capName: "pipeline-orchestrator",
27489
27616
  capScope: "system",
@@ -27526,19 +27653,19 @@ Object.freeze({
27526
27653
  addonId: null,
27527
27654
  access: "create"
27528
27655
  },
27529
- "pipelineOrchestrator.setAgentAddonDefaults": {
27656
+ "pipelineOrchestrator.setAgentCapabilities": {
27530
27657
  capName: "pipeline-orchestrator",
27531
27658
  capScope: "system",
27532
27659
  addonId: null,
27533
27660
  access: "create"
27534
27661
  },
27535
- "pipelineOrchestrator.setAgentCapabilities": {
27662
+ "pipelineOrchestrator.setAgentDetectWeight": {
27536
27663
  capName: "pipeline-orchestrator",
27537
27664
  capScope: "system",
27538
27665
  addonId: null,
27539
27666
  access: "create"
27540
27667
  },
27541
- "pipelineOrchestrator.setAgentDetectWeight": {
27668
+ "pipelineOrchestrator.setAgentInferenceDevices": {
27542
27669
  capName: "pipeline-orchestrator",
27543
27670
  capScope: "system",
27544
27671
  addonId: null,
@@ -27580,6 +27707,12 @@ Object.freeze({
27580
27707
  addonId: null,
27581
27708
  access: "create"
27582
27709
  },
27710
+ "pipelineOrchestrator.setPipelineDevicePin": {
27711
+ capName: "pipeline-orchestrator",
27712
+ capScope: "system",
27713
+ addonId: null,
27714
+ access: "create"
27715
+ },
27583
27716
  "pipelineOrchestrator.unassignAudio": {
27584
27717
  capName: "pipeline-orchestrator",
27585
27718
  capScope: "system",
@@ -29132,32 +29265,6 @@ Object.freeze({
29132
29265
  "network-access": "ingress",
29133
29266
  "smtp-provider": "email"
29134
29267
  });
29135
- var frameworkSwapPackageSchema = object({
29136
- name: string(),
29137
- stagedPath: string(),
29138
- backupPath: string(),
29139
- toVersion: string(),
29140
- fromVersion: string().nullable()
29141
- });
29142
- object({
29143
- jobId: string(),
29144
- taskId: string(),
29145
- packages: array(frameworkSwapPackageSchema),
29146
- requestedAtMs: number(),
29147
- schemaVersion: literal(1)
29148
- });
29149
- object({
29150
- jobId: string(),
29151
- taskId: string(),
29152
- backups: array(object({
29153
- name: string(),
29154
- backupPath: string(),
29155
- livePath: string()
29156
- })),
29157
- appliedAtMs: number(),
29158
- bootAttempts: number(),
29159
- schemaVersion: literal(1)
29160
- });
29161
29268
  //#endregion
29162
29269
  //#region src/config.ts
29163
29270
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rademacher",
3
- "version": "0.1.14",
3
+ "version": "0.2.1",
4
4
  "description": "Rademacher HomePilot device-provider addon for CamStack — wraps the @apocaliss92/noderademacher local-hub client (roller shutters over the cover cap)",
5
5
  "keywords": [
6
6
  "camstack",