@camstack/addon-notifiers 1.1.30 → 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
@@ -7072,6 +7072,17 @@ var ModelCatalogEntrySchema = object({
7072
7072
  "imagenet",
7073
7073
  "none"
7074
7074
  ]).optional(),
7075
+ /**
7076
+ * The model already applies softmax IN-GRAPH — its raw output is a
7077
+ * probability distribution, not logits. When set, the `softmax`
7078
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7079
+ * probability vector collapses it toward uniform (top-1 score craters far
7080
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7081
+ * the output is raw logits and the postprocessor applies softmax (the normal
7082
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7083
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7084
+ */
7085
+ outputProbabilities: boolean().optional(),
7075
7086
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7076
7087
  /**
7077
7088
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -11395,10 +11406,7 @@ var ConfigUISchemaNullableBridge = custom();
11395
11406
  var InferenceCapabilitiesBridge = custom();
11396
11407
  var ModelAvailabilityListBridge = custom();
11397
11408
  var PipelineRunResultBridge = custom();
11398
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11399
- kind: "mutation",
11400
- auth: "admin"
11401
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11409
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11402
11410
  modelId: string(),
11403
11411
  settings: record(string(), unknown()).readonly()
11404
11412
  }))), method(object({ steps: record(string(), object({
@@ -11458,13 +11466,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11458
11466
  * (inputClasses ≠ null) are skipped and served per-track via
11459
11467
  * pipelineRunner.runDetailSubtree (two-plane design).
11460
11468
  */
11461
- plane: _enum(["full", "frame"]).optional()
11469
+ plane: _enum(["full", "frame"]).optional(),
11470
+ /**
11471
+ * Inference-device selector (Phase 2 multi-device). Format
11472
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11473
+ * Omitted ⇒ the runner's default device (current single-engine
11474
+ * behaviour). Selects WHICH device pool of the node runs the call.
11475
+ */
11476
+ deviceKey: string().optional()
11462
11477
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11463
11478
  engine: PipelineEngineChoiceSchema.optional(),
11464
11479
  steps: array(PipelineStepInputSchema).min(1),
11465
11480
  frames: array(FrameInputSchema).min(1).max(255),
11466
11481
  deviceId: number().optional(),
11467
- sessionId: string().optional()
11482
+ sessionId: string().optional(),
11483
+ /**
11484
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11485
+ * the batch to the Python pool's bench preprocess cache
11486
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11487
+ * preprocessed ONCE and every later inference is a pure-inference cache
11488
+ * hit — the sustained-throughput run measures inference, not
11489
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11490
+ * full preprocess every call, correct). Fresh per sustained run;
11491
+ * released via `uncacheFrame`.
11492
+ */
11493
+ frameId: number().int().nonnegative().optional(),
11494
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11495
+ deviceKey: string().optional()
11468
11496
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11469
11497
  data: _instanceof(Uint8Array),
11470
11498
  width: number().int().positive(),
@@ -11496,8 +11524,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11496
11524
  * - `runtime` — main camera-serving engine (no idle TTL).
11497
11525
  * - `warm-override` — benchmark/test override held in the warm
11498
11526
  * cache; auto-disposed after the idle TTL.
11527
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11528
+ * multi-device, keyed by `deviceKey`) resolved
11529
+ * via `resolveDeviceFactory`. Runs alongside the
11530
+ * `runtime` engine on a DIFFERENT accelerator
11531
+ * (NPU / iGPU / Coral) — this is how the
11532
+ * Engines tab shows all pools running at once.
11499
11533
  */
11500
- kind: _enum(["runtime", "warm-override"]),
11534
+ kind: _enum([
11535
+ "runtime",
11536
+ "warm-override",
11537
+ "device-pool"
11538
+ ]),
11501
11539
  /** Native pid of the underlying Python pool (null when no pool). */
11502
11540
  poolPid: number().nullable(),
11503
11541
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11599,7 +11637,21 @@ var NativeCropResultSchema = object({
11599
11637
  /** Packed rgb (24-bit) pixels of the crop. */
11600
11638
  bytes: _instanceof(Uint8Array),
11601
11639
  width: number().int().positive(),
11602
- height: number().int().positive()
11640
+ height: number().int().positive(),
11641
+ /**
11642
+ * Which source served this crop, so a quality-sensitive consumer (the native
11643
+ * `keyFrame`) can reject a degraded fallback:
11644
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
11645
+ * quality path).
11646
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
11647
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
11648
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
11649
+ *
11650
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
11651
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
11652
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
11653
+ */
11654
+ tier: _enum(["native", "ram-fullframe"]).optional()
11603
11655
  });
11604
11656
  /** Parent detection context passed to `runDetailSubtree` — the crop's
11605
11657
  * originating detection, in FRAME-space coordinates. Reuses
@@ -11839,7 +11891,14 @@ var RunnerCameraConfigSchema = object({
11839
11891
  * camera's detect node differs from its source-owner (P2d, gated by the
11840
11892
  * `remoteSourcingNodes` rollout setting).
11841
11893
  */
11842
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11894
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11895
+ /**
11896
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11897
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11898
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11899
+ * this only selects WHICH device pool of that node runs the session.
11900
+ */
11901
+ deviceKey: string().optional()
11843
11902
  });
11844
11903
  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;
11845
11904
  /**
@@ -11860,6 +11919,19 @@ var RunnerLocalLoadSchema = object({
11860
11919
  avgInferenceTimeMs: number(),
11861
11920
  /** Total queue depth across motion + detection queues. */
11862
11921
  queueDepthTotal: number(),
11922
+ /**
11923
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11924
+ * this runner currently has attached cameras on, so the orchestrator's second
11925
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11926
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11927
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11928
+ */
11929
+ devices: array(object({
11930
+ deviceKey: string(),
11931
+ backend: string(),
11932
+ attachedCameras: number(),
11933
+ queueDepthTotal: number()
11934
+ })).default([]),
11863
11935
  /** Hardware capability flags reported by this node. */
11864
11936
  hardware: object({
11865
11937
  hasGpu: boolean(),
@@ -13335,6 +13407,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13335
13407
  kind: "mutation",
13336
13408
  auth: "admin"
13337
13409
  });
13410
+ 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;
13411
+ new Set(Object.values(DeviceType));
13338
13412
  /**
13339
13413
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13340
13414
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -14927,7 +15001,8 @@ var LinkedDeviceSchema = object({
14927
15001
  deviceId: number(),
14928
15002
  name: string(),
14929
15003
  location: string().nullable(),
14930
- features: array(string())
15004
+ features: array(string()),
15005
+ producesTrackedEvents: boolean().optional()
14931
15006
  });
14932
15007
  var SavedDeviceRowSchema = object({
14933
15008
  /** Numeric id reserved at allocateDeviceId time. */
@@ -16571,6 +16646,7 @@ var TrackSchema = object({
16571
16646
  deviceId: number(),
16572
16647
  className: string(),
16573
16648
  label: string().optional(),
16649
+ producingDeviceName: string().optional(),
16574
16650
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16575
16651
  source: TrackSourceSchema.optional(),
16576
16652
  firstSeen: number(),
@@ -16708,7 +16784,8 @@ var MediaFileKindEnum = _enum([
16708
16784
  "fullFrameBoxed",
16709
16785
  "faceCrop",
16710
16786
  "plateCrop",
16711
- "keyFrame"
16787
+ "keyFrame",
16788
+ "keyFrameSmall"
16712
16789
  ]);
16713
16790
  var MediaFileSchema = object({
16714
16791
  key: string(),
@@ -17037,13 +17114,11 @@ var PipelineTemplateSchema = object({
17037
17114
  createdAt: string(),
17038
17115
  updatedAt: string()
17039
17116
  });
17040
- var AgentAddonConfigSchema = object({
17041
- enabled: boolean(),
17117
+ var DeviceStepConfigSchema = object({
17042
17118
  modelId: string().optional(),
17043
- settings: record(string(), unknown()).readonly()
17119
+ settings: record(string(), unknown()).optional()
17044
17120
  });
17045
17121
  var AgentPipelineSettingsSchema = object({
17046
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
17047
17122
  maxCameras: number().int().nonnegative().nullable().default(null),
17048
17123
  /** Per-node detection weight (relative share for the quota balancer). */
17049
17124
  detectWeight: number().positive().optional(),
@@ -17067,7 +17142,22 @@ var AgentPipelineSettingsSchema = object({
17067
17142
  * it already uses to reach the hub). Set this only when the auto-detected
17068
17143
  * address is wrong (multi-homed host, NAT, custom interface).
17069
17144
  */
17070
- reachableHost: string().optional()
17145
+ reachableHost: string().optional(),
17146
+ /**
17147
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
17148
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
17149
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
17150
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
17151
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
17152
+ * the default model/settings for every camera landing on that accelerator;
17153
+ * a stepId absent ⇒ the step uses that device's format default.
17154
+ */
17155
+ inferenceDevices: record(string(), object({
17156
+ enabled: boolean(),
17157
+ weight: number().positive().optional(),
17158
+ maxSessions: number().int().positive().optional(),
17159
+ steps: record(string(), DeviceStepConfigSchema).optional()
17160
+ })).optional()
17071
17161
  });
17072
17162
  var CameraPipelineForAgentSchema = object({
17073
17163
  steps: array(PipelineStepInputSchema).readonly(),
@@ -17077,14 +17167,13 @@ var CameraPipelineForAgentSchema = object({
17077
17167
  }).nullable()
17078
17168
  });
17079
17169
  var CameraStepOverridePatchSchema = object({
17080
- enabled: boolean().optional(),
17081
17170
  modelId: string().optional(),
17082
17171
  settings: record(string(), unknown()).readonly().optional()
17083
17172
  });
17084
17173
  var CameraPipelineSettingsSchema = object({
17085
17174
  pinnedAgentNodeId: string().optional(),
17086
17175
  stepToggles: record(string(), boolean()).optional(),
17087
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
17176
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
17088
17177
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
17089
17178
  });
17090
17179
  /**
@@ -17298,6 +17387,44 @@ var CameraStatusSchema = object({
17298
17387
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17299
17388
  fetchedAt: number()
17300
17389
  });
17390
+ var NodeInferenceDeviceSchema = object({
17391
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17392
+ key: string(),
17393
+ backend: string(),
17394
+ device: string(),
17395
+ format: _enum(MODEL_FORMATS),
17396
+ /** Whether the node's live probe reports the device as usable right now. */
17397
+ available: boolean(),
17398
+ /**
17399
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17400
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17401
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17402
+ * not a balanced target). An explicit stored value always wins; a stored-only
17403
+ * (unavailable) key keeps its stored value.
17404
+ */
17405
+ enabled: boolean(),
17406
+ /** Relative balancer weight for the enabled device (default 1). */
17407
+ weight: number(),
17408
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17409
+ maxSessions: number().nullable(),
17410
+ /** Object-detection model the executor defaults to for this deviceKey. */
17411
+ defaultModelId: string(),
17412
+ /**
17413
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17414
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17415
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17416
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17417
+ * available per format; this is the stored selection that becomes the
17418
+ * default for EVERY camera landing on this accelerator.
17419
+ */
17420
+ steps: record(string(), DeviceStepConfigSchema).optional()
17421
+ });
17422
+ var NodeInferenceDevicesSchema = object({
17423
+ nodeId: string(),
17424
+ /** False when the node's platform-probe was unreachable (no live device set). */
17425
+ reachable: boolean(),
17426
+ devices: array(NodeInferenceDeviceSchema).readonly()
17427
+ });
17301
17428
  method(object({
17302
17429
  deviceId: number(),
17303
17430
  agentNodeId: string()
@@ -17307,7 +17434,13 @@ method(object({
17307
17434
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
17308
17435
  kind: "mutation",
17309
17436
  auth: "admin"
17310
- }), method(_void(), object({ migrated: number() }), {
17437
+ }), method(object({
17438
+ deviceId: number(),
17439
+ deviceKey: string()
17440
+ }), object({ success: literal(true) }), {
17441
+ kind: "mutation",
17442
+ auth: "admin"
17443
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
17311
17444
  kind: "mutation",
17312
17445
  auth: "admin"
17313
17446
  }), 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({
@@ -17341,13 +17474,7 @@ method(object({
17341
17474
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17342
17475
  nodeId: string(),
17343
17476
  settings: AgentPipelineSettingsSchema
17344
- })).readonly()), method(object({
17345
- agentNodeId: string(),
17346
- defaults: record(string(), AgentAddonConfigSchema)
17347
- }), object({ success: literal(true) }), {
17348
- kind: "mutation",
17349
- auth: "admin"
17350
- }), method(object({ agentNodeId: string() }), object({
17477
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17351
17478
  success: boolean(),
17352
17479
  removed: boolean()
17353
17480
  }), {
@@ -17379,7 +17506,18 @@ method(object({
17379
17506
  }), object({ success: literal(true) }), {
17380
17507
  kind: "mutation",
17381
17508
  auth: "admin"
17382
- }), method(object({ agentNodeId: string() }), object({
17509
+ }), method(object({
17510
+ agentNodeId: string(),
17511
+ inferenceDevices: record(string(), object({
17512
+ enabled: boolean(),
17513
+ weight: number().positive().optional(),
17514
+ maxSessions: number().int().positive().optional(),
17515
+ steps: record(string(), DeviceStepConfigSchema).optional()
17516
+ }))
17517
+ }), object({ success: literal(true) }), {
17518
+ kind: "mutation",
17519
+ auth: "admin"
17520
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17383
17521
  success: literal(true),
17384
17522
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17385
17523
  effectiveModelId: string().nullable(),
@@ -17395,9 +17533,10 @@ method(object({
17395
17533
  }), object({ success: literal(true) }), {
17396
17534
  kind: "mutation",
17397
17535
  auth: "admin"
17398
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17536
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17399
17537
  deviceId: number(),
17400
17538
  agentNodeId: string(),
17539
+ deviceKey: string(),
17401
17540
  addonId: string(),
17402
17541
  patch: CameraStepOverridePatchSchema.nullable()
17403
17542
  }), object({ success: literal(true) }), {
@@ -17434,14 +17573,13 @@ method(object({
17434
17573
  });
17435
17574
  /**
17436
17575
  * server-management — per-NODE singleton capability for a node's ROOT
17437
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17438
- * agents).
17576
+ * package lifecycle (runtime-updatable node packages).
17439
17577
  *
17440
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
17441
- * on agents) carries the whole software stack in its npm dep tree, so ONE
17442
- * version describes the node. Updates install into
17443
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
17444
- * starter (probation boot + auto-rollback to N-1).
17578
+ * Every node role runs the SAME root package (`@camstack/server`), which
17579
+ * carries the whole software stack in its npm dep tree, so ONE version
17580
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17581
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17582
+ * no auto-rollback).
17445
17583
  *
17446
17584
  * Providers:
17447
17585
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17549,7 +17687,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17549
17687
  /** Explicit target version; omitted = latest from the registry. */
17550
17688
  version: string().optional() }), ServerUpdateActionResultSchema, {
17551
17689
  kind: "mutation",
17552
- auth: "admin"
17690
+ auth: "admin",
17691
+ timeoutMs: 16 * 6e4
17553
17692
  }), method(_void(), ServerUpdateActionResultSchema, {
17554
17693
  kind: "mutation",
17555
17694
  auth: "admin"
@@ -18593,22 +18732,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18593
18732
  var RestartAddonResultSchema = unknown();
18594
18733
  var InstallPackageResultSchema = unknown();
18595
18734
  var ReloadPackagesResultSchema = unknown();
18596
- /**
18597
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18598
- * server restarts so the admin UI can react to the `restartingAt`
18599
- * timestamp (shows reconnect overlay). The transition from
18600
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18601
- * `system.restart-completed` event after the new process boots.
18602
- *
18603
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18604
- */
18605
- var UpdateFrameworkPackageResultSchema = object({
18606
- packageName: string(),
18607
- fromVersion: string(),
18608
- toVersion: string(),
18609
- /** Ms-epoch the server scheduled its self-restart. */
18610
- restartingAt: number()
18611
- });
18612
18735
  var BulkUpdateItemStatusSchema = _enum([
18613
18736
  "queued",
18614
18737
  "updating",
@@ -18736,13 +18859,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18736
18859
  }), object({ success: literal(true) }), {
18737
18860
  kind: "mutation",
18738
18861
  auth: "admin"
18739
- }), method(object({
18740
- packageName: string().min(1),
18741
- version: string().optional(),
18742
- deferRestart: boolean().optional()
18743
- }), UpdateFrameworkPackageResultSchema, {
18744
- kind: "mutation",
18745
- auth: "admin"
18746
18862
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18747
18863
  kind: "mutation",
18748
18864
  auth: "admin"
@@ -19608,10 +19724,10 @@ var TopologyCategorySchema = object({
19608
19724
  addons: array(TopologyCategoryAddonSchema).readonly()
19609
19725
  });
19610
19726
  /**
19611
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19612
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19613
- * version visibility for the Server management surface. Nullable: offline
19614
- * rows and pre-phase-2 nodes report none.
19727
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19728
+ * root package for every node role) as reported by its `registerNode`
19729
+ * manifest — version visibility for the Server management surface. Nullable:
19730
+ * offline rows and nodes that never reported one.
19615
19731
  */
19616
19732
  var TopologyRootPackageSchema = object({
19617
19733
  name: string(),
@@ -19999,17 +20115,28 @@ var PlatformScoreSchema = object({
19999
20115
  format: _enum([
20000
20116
  "onnx",
20001
20117
  "coreml",
20002
- "openvino"
20118
+ "openvino",
20119
+ "tflite"
20003
20120
  ]),
20004
20121
  score: number(),
20005
20122
  reason: string(),
20006
20123
  available: boolean()
20007
20124
  });
20125
+ var InferenceDeviceDescriptorSchema = object({
20126
+ key: string(),
20127
+ backend: string(),
20128
+ device: string(),
20129
+ format: ModelFormatSchema,
20130
+ runtime: literal("python"),
20131
+ score: number(),
20132
+ available: boolean()
20133
+ });
20008
20134
  var PlatformCapabilitiesSchema = object({
20009
20135
  hardware: HardwareInfoSchema,
20010
20136
  scores: array(PlatformScoreSchema).readonly(),
20011
20137
  bestScore: PlatformScoreSchema,
20012
- pythonPath: string().nullable()
20138
+ pythonPath: string().nullable(),
20139
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
20013
20140
  });
20014
20141
  var ModelRequirementSchema = object({
20015
20142
  modelId: string(),
@@ -20888,12 +21015,6 @@ Object.freeze({
20888
21015
  addonId: null,
20889
21016
  access: "delete"
20890
21017
  },
20891
- "addons.updateFrameworkPackage": {
20892
- capName: "addons",
20893
- capScope: "system",
20894
- addonId: null,
20895
- access: "create"
20896
- },
20897
21018
  "addons.updatePackage": {
20898
21019
  capName: "addons",
20899
21020
  capScope: "system",
@@ -23666,12 +23787,6 @@ Object.freeze({
23666
23787
  addonId: null,
23667
23788
  access: "view"
23668
23789
  },
23669
- "pipelineExecutor.reprobeEngine": {
23670
- capName: "pipeline-executor",
23671
- capScope: "system",
23672
- addonId: null,
23673
- access: "create"
23674
- },
23675
23790
  "pipelineExecutor.runAudioTest": {
23676
23791
  capName: "pipeline-executor",
23677
23792
  capScope: "system",
@@ -23822,6 +23937,12 @@ Object.freeze({
23822
23937
  addonId: null,
23823
23938
  access: "view"
23824
23939
  },
23940
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23941
+ capName: "pipeline-orchestrator",
23942
+ capScope: "system",
23943
+ addonId: null,
23944
+ access: "view"
23945
+ },
23825
23946
  "pipelineOrchestrator.getPipelineAssignment": {
23826
23947
  capName: "pipeline-orchestrator",
23827
23948
  capScope: "system",
@@ -23834,6 +23955,12 @@ Object.freeze({
23834
23955
  addonId: null,
23835
23956
  access: "view"
23836
23957
  },
23958
+ "pipelineOrchestrator.getPipelineDevicePin": {
23959
+ capName: "pipeline-orchestrator",
23960
+ capScope: "system",
23961
+ addonId: null,
23962
+ access: "view"
23963
+ },
23837
23964
  "pipelineOrchestrator.listAgentSettings": {
23838
23965
  capName: "pipeline-orchestrator",
23839
23966
  capScope: "system",
@@ -23876,19 +24003,19 @@ Object.freeze({
23876
24003
  addonId: null,
23877
24004
  access: "create"
23878
24005
  },
23879
- "pipelineOrchestrator.setAgentAddonDefaults": {
24006
+ "pipelineOrchestrator.setAgentCapabilities": {
23880
24007
  capName: "pipeline-orchestrator",
23881
24008
  capScope: "system",
23882
24009
  addonId: null,
23883
24010
  access: "create"
23884
24011
  },
23885
- "pipelineOrchestrator.setAgentCapabilities": {
24012
+ "pipelineOrchestrator.setAgentDetectWeight": {
23886
24013
  capName: "pipeline-orchestrator",
23887
24014
  capScope: "system",
23888
24015
  addonId: null,
23889
24016
  access: "create"
23890
24017
  },
23891
- "pipelineOrchestrator.setAgentDetectWeight": {
24018
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23892
24019
  capName: "pipeline-orchestrator",
23893
24020
  capScope: "system",
23894
24021
  addonId: null,
@@ -23930,6 +24057,12 @@ Object.freeze({
23930
24057
  addonId: null,
23931
24058
  access: "create"
23932
24059
  },
24060
+ "pipelineOrchestrator.setPipelineDevicePin": {
24061
+ capName: "pipeline-orchestrator",
24062
+ capScope: "system",
24063
+ addonId: null,
24064
+ access: "create"
24065
+ },
23933
24066
  "pipelineOrchestrator.unassignAudio": {
23934
24067
  capName: "pipeline-orchestrator",
23935
24068
  capScope: "system",
@@ -25482,32 +25615,6 @@ Object.freeze({
25482
25615
  "network-access": "ingress",
25483
25616
  "smtp-provider": "email"
25484
25617
  });
25485
- var frameworkSwapPackageSchema = object({
25486
- name: string(),
25487
- stagedPath: string(),
25488
- backupPath: string(),
25489
- toVersion: string(),
25490
- fromVersion: string().nullable()
25491
- });
25492
- object({
25493
- jobId: string(),
25494
- taskId: string(),
25495
- packages: array(frameworkSwapPackageSchema),
25496
- requestedAtMs: number(),
25497
- schemaVersion: literal(1)
25498
- });
25499
- object({
25500
- jobId: string(),
25501
- taskId: string(),
25502
- backups: array(object({
25503
- name: string(),
25504
- backupPath: string(),
25505
- livePath: string()
25506
- })),
25507
- appliedAtMs: number(),
25508
- bootAttempts: number(),
25509
- schemaVersion: literal(1)
25510
- });
25511
25618
  var NOTIFIER_ICONS = {
25512
25619
  telegram: {
25513
25620
  contentType: "image/svg+xml",
package/dist/addon.mjs CHANGED
@@ -7068,6 +7068,17 @@ var ModelCatalogEntrySchema = object({
7068
7068
  "imagenet",
7069
7069
  "none"
7070
7070
  ]).optional(),
7071
+ /**
7072
+ * The model already applies softmax IN-GRAPH — its raw output is a
7073
+ * probability distribution, not logits. When set, the `softmax`
7074
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7075
+ * probability vector collapses it toward uniform (top-1 score craters far
7076
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7077
+ * the output is raw logits and the postprocessor applies softmax (the normal
7078
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7079
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7080
+ */
7081
+ outputProbabilities: boolean().optional(),
7071
7082
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7072
7083
  /**
7073
7084
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -11391,10 +11402,7 @@ var ConfigUISchemaNullableBridge = custom();
11391
11402
  var InferenceCapabilitiesBridge = custom();
11392
11403
  var ModelAvailabilityListBridge = custom();
11393
11404
  var PipelineRunResultBridge = custom();
11394
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11395
- kind: "mutation",
11396
- auth: "admin"
11397
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11405
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11398
11406
  modelId: string(),
11399
11407
  settings: record(string(), unknown()).readonly()
11400
11408
  }))), method(object({ steps: record(string(), object({
@@ -11454,13 +11462,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11454
11462
  * (inputClasses ≠ null) are skipped and served per-track via
11455
11463
  * pipelineRunner.runDetailSubtree (two-plane design).
11456
11464
  */
11457
- plane: _enum(["full", "frame"]).optional()
11465
+ plane: _enum(["full", "frame"]).optional(),
11466
+ /**
11467
+ * Inference-device selector (Phase 2 multi-device). Format
11468
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11469
+ * Omitted ⇒ the runner's default device (current single-engine
11470
+ * behaviour). Selects WHICH device pool of the node runs the call.
11471
+ */
11472
+ deviceKey: string().optional()
11458
11473
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11459
11474
  engine: PipelineEngineChoiceSchema.optional(),
11460
11475
  steps: array(PipelineStepInputSchema).min(1),
11461
11476
  frames: array(FrameInputSchema).min(1).max(255),
11462
11477
  deviceId: number().optional(),
11463
- sessionId: string().optional()
11478
+ sessionId: string().optional(),
11479
+ /**
11480
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11481
+ * the batch to the Python pool's bench preprocess cache
11482
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11483
+ * preprocessed ONCE and every later inference is a pure-inference cache
11484
+ * hit — the sustained-throughput run measures inference, not
11485
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11486
+ * full preprocess every call, correct). Fresh per sustained run;
11487
+ * released via `uncacheFrame`.
11488
+ */
11489
+ frameId: number().int().nonnegative().optional(),
11490
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11491
+ deviceKey: string().optional()
11464
11492
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11465
11493
  data: _instanceof(Uint8Array),
11466
11494
  width: number().int().positive(),
@@ -11492,8 +11520,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11492
11520
  * - `runtime` — main camera-serving engine (no idle TTL).
11493
11521
  * - `warm-override` — benchmark/test override held in the warm
11494
11522
  * cache; auto-disposed after the idle TTL.
11523
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11524
+ * multi-device, keyed by `deviceKey`) resolved
11525
+ * via `resolveDeviceFactory`. Runs alongside the
11526
+ * `runtime` engine on a DIFFERENT accelerator
11527
+ * (NPU / iGPU / Coral) — this is how the
11528
+ * Engines tab shows all pools running at once.
11495
11529
  */
11496
- kind: _enum(["runtime", "warm-override"]),
11530
+ kind: _enum([
11531
+ "runtime",
11532
+ "warm-override",
11533
+ "device-pool"
11534
+ ]),
11497
11535
  /** Native pid of the underlying Python pool (null when no pool). */
11498
11536
  poolPid: number().nullable(),
11499
11537
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11595,7 +11633,21 @@ var NativeCropResultSchema = object({
11595
11633
  /** Packed rgb (24-bit) pixels of the crop. */
11596
11634
  bytes: _instanceof(Uint8Array),
11597
11635
  width: number().int().positive(),
11598
- height: number().int().positive()
11636
+ height: number().int().positive(),
11637
+ /**
11638
+ * Which source served this crop, so a quality-sensitive consumer (the native
11639
+ * `keyFrame`) can reject a degraded fallback:
11640
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
11641
+ * quality path).
11642
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
11643
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
11644
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
11645
+ *
11646
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
11647
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
11648
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
11649
+ */
11650
+ tier: _enum(["native", "ram-fullframe"]).optional()
11599
11651
  });
11600
11652
  /** Parent detection context passed to `runDetailSubtree` — the crop's
11601
11653
  * originating detection, in FRAME-space coordinates. Reuses
@@ -11835,7 +11887,14 @@ var RunnerCameraConfigSchema = object({
11835
11887
  * camera's detect node differs from its source-owner (P2d, gated by the
11836
11888
  * `remoteSourcingNodes` rollout setting).
11837
11889
  */
11838
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11890
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11891
+ /**
11892
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11893
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11894
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11895
+ * this only selects WHICH device pool of that node runs the session.
11896
+ */
11897
+ deviceKey: string().optional()
11839
11898
  });
11840
11899
  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;
11841
11900
  /**
@@ -11856,6 +11915,19 @@ var RunnerLocalLoadSchema = object({
11856
11915
  avgInferenceTimeMs: number(),
11857
11916
  /** Total queue depth across motion + detection queues. */
11858
11917
  queueDepthTotal: number(),
11918
+ /**
11919
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11920
+ * this runner currently has attached cameras on, so the orchestrator's second
11921
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11922
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11923
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11924
+ */
11925
+ devices: array(object({
11926
+ deviceKey: string(),
11927
+ backend: string(),
11928
+ attachedCameras: number(),
11929
+ queueDepthTotal: number()
11930
+ })).default([]),
11859
11931
  /** Hardware capability flags reported by this node. */
11860
11932
  hardware: object({
11861
11933
  hasGpu: boolean(),
@@ -13331,6 +13403,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13331
13403
  kind: "mutation",
13332
13404
  auth: "admin"
13333
13405
  });
13406
+ 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;
13407
+ new Set(Object.values(DeviceType));
13334
13408
  /**
13335
13409
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13336
13410
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -14923,7 +14997,8 @@ var LinkedDeviceSchema = object({
14923
14997
  deviceId: number(),
14924
14998
  name: string(),
14925
14999
  location: string().nullable(),
14926
- features: array(string())
15000
+ features: array(string()),
15001
+ producesTrackedEvents: boolean().optional()
14927
15002
  });
14928
15003
  var SavedDeviceRowSchema = object({
14929
15004
  /** Numeric id reserved at allocateDeviceId time. */
@@ -16567,6 +16642,7 @@ var TrackSchema = object({
16567
16642
  deviceId: number(),
16568
16643
  className: string(),
16569
16644
  label: string().optional(),
16645
+ producingDeviceName: string().optional(),
16570
16646
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16571
16647
  source: TrackSourceSchema.optional(),
16572
16648
  firstSeen: number(),
@@ -16704,7 +16780,8 @@ var MediaFileKindEnum = _enum([
16704
16780
  "fullFrameBoxed",
16705
16781
  "faceCrop",
16706
16782
  "plateCrop",
16707
- "keyFrame"
16783
+ "keyFrame",
16784
+ "keyFrameSmall"
16708
16785
  ]);
16709
16786
  var MediaFileSchema = object({
16710
16787
  key: string(),
@@ -17033,13 +17110,11 @@ var PipelineTemplateSchema = object({
17033
17110
  createdAt: string(),
17034
17111
  updatedAt: string()
17035
17112
  });
17036
- var AgentAddonConfigSchema = object({
17037
- enabled: boolean(),
17113
+ var DeviceStepConfigSchema = object({
17038
17114
  modelId: string().optional(),
17039
- settings: record(string(), unknown()).readonly()
17115
+ settings: record(string(), unknown()).optional()
17040
17116
  });
17041
17117
  var AgentPipelineSettingsSchema = object({
17042
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
17043
17118
  maxCameras: number().int().nonnegative().nullable().default(null),
17044
17119
  /** Per-node detection weight (relative share for the quota balancer). */
17045
17120
  detectWeight: number().positive().optional(),
@@ -17063,7 +17138,22 @@ var AgentPipelineSettingsSchema = object({
17063
17138
  * it already uses to reach the hub). Set this only when the auto-detected
17064
17139
  * address is wrong (multi-homed host, NAT, custom interface).
17065
17140
  */
17066
- reachableHost: string().optional()
17141
+ reachableHost: string().optional(),
17142
+ /**
17143
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
17144
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
17145
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
17146
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
17147
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
17148
+ * the default model/settings for every camera landing on that accelerator;
17149
+ * a stepId absent ⇒ the step uses that device's format default.
17150
+ */
17151
+ inferenceDevices: record(string(), object({
17152
+ enabled: boolean(),
17153
+ weight: number().positive().optional(),
17154
+ maxSessions: number().int().positive().optional(),
17155
+ steps: record(string(), DeviceStepConfigSchema).optional()
17156
+ })).optional()
17067
17157
  });
17068
17158
  var CameraPipelineForAgentSchema = object({
17069
17159
  steps: array(PipelineStepInputSchema).readonly(),
@@ -17073,14 +17163,13 @@ var CameraPipelineForAgentSchema = object({
17073
17163
  }).nullable()
17074
17164
  });
17075
17165
  var CameraStepOverridePatchSchema = object({
17076
- enabled: boolean().optional(),
17077
17166
  modelId: string().optional(),
17078
17167
  settings: record(string(), unknown()).readonly().optional()
17079
17168
  });
17080
17169
  var CameraPipelineSettingsSchema = object({
17081
17170
  pinnedAgentNodeId: string().optional(),
17082
17171
  stepToggles: record(string(), boolean()).optional(),
17083
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
17172
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
17084
17173
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
17085
17174
  });
17086
17175
  /**
@@ -17294,6 +17383,44 @@ var CameraStatusSchema = object({
17294
17383
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17295
17384
  fetchedAt: number()
17296
17385
  });
17386
+ var NodeInferenceDeviceSchema = object({
17387
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17388
+ key: string(),
17389
+ backend: string(),
17390
+ device: string(),
17391
+ format: _enum(MODEL_FORMATS),
17392
+ /** Whether the node's live probe reports the device as usable right now. */
17393
+ available: boolean(),
17394
+ /**
17395
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17396
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17397
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17398
+ * not a balanced target). An explicit stored value always wins; a stored-only
17399
+ * (unavailable) key keeps its stored value.
17400
+ */
17401
+ enabled: boolean(),
17402
+ /** Relative balancer weight for the enabled device (default 1). */
17403
+ weight: number(),
17404
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17405
+ maxSessions: number().nullable(),
17406
+ /** Object-detection model the executor defaults to for this deviceKey. */
17407
+ defaultModelId: string(),
17408
+ /**
17409
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17410
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17411
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17412
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17413
+ * available per format; this is the stored selection that becomes the
17414
+ * default for EVERY camera landing on this accelerator.
17415
+ */
17416
+ steps: record(string(), DeviceStepConfigSchema).optional()
17417
+ });
17418
+ var NodeInferenceDevicesSchema = object({
17419
+ nodeId: string(),
17420
+ /** False when the node's platform-probe was unreachable (no live device set). */
17421
+ reachable: boolean(),
17422
+ devices: array(NodeInferenceDeviceSchema).readonly()
17423
+ });
17297
17424
  method(object({
17298
17425
  deviceId: number(),
17299
17426
  agentNodeId: string()
@@ -17303,7 +17430,13 @@ method(object({
17303
17430
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
17304
17431
  kind: "mutation",
17305
17432
  auth: "admin"
17306
- }), method(_void(), object({ migrated: number() }), {
17433
+ }), method(object({
17434
+ deviceId: number(),
17435
+ deviceKey: string()
17436
+ }), object({ success: literal(true) }), {
17437
+ kind: "mutation",
17438
+ auth: "admin"
17439
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
17307
17440
  kind: "mutation",
17308
17441
  auth: "admin"
17309
17442
  }), 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({
@@ -17337,13 +17470,7 @@ method(object({
17337
17470
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17338
17471
  nodeId: string(),
17339
17472
  settings: AgentPipelineSettingsSchema
17340
- })).readonly()), method(object({
17341
- agentNodeId: string(),
17342
- defaults: record(string(), AgentAddonConfigSchema)
17343
- }), object({ success: literal(true) }), {
17344
- kind: "mutation",
17345
- auth: "admin"
17346
- }), method(object({ agentNodeId: string() }), object({
17473
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17347
17474
  success: boolean(),
17348
17475
  removed: boolean()
17349
17476
  }), {
@@ -17375,7 +17502,18 @@ method(object({
17375
17502
  }), object({ success: literal(true) }), {
17376
17503
  kind: "mutation",
17377
17504
  auth: "admin"
17378
- }), method(object({ agentNodeId: string() }), object({
17505
+ }), method(object({
17506
+ agentNodeId: string(),
17507
+ inferenceDevices: record(string(), object({
17508
+ enabled: boolean(),
17509
+ weight: number().positive().optional(),
17510
+ maxSessions: number().int().positive().optional(),
17511
+ steps: record(string(), DeviceStepConfigSchema).optional()
17512
+ }))
17513
+ }), object({ success: literal(true) }), {
17514
+ kind: "mutation",
17515
+ auth: "admin"
17516
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17379
17517
  success: literal(true),
17380
17518
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17381
17519
  effectiveModelId: string().nullable(),
@@ -17391,9 +17529,10 @@ method(object({
17391
17529
  }), object({ success: literal(true) }), {
17392
17530
  kind: "mutation",
17393
17531
  auth: "admin"
17394
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17532
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17395
17533
  deviceId: number(),
17396
17534
  agentNodeId: string(),
17535
+ deviceKey: string(),
17397
17536
  addonId: string(),
17398
17537
  patch: CameraStepOverridePatchSchema.nullable()
17399
17538
  }), object({ success: literal(true) }), {
@@ -17430,14 +17569,13 @@ method(object({
17430
17569
  });
17431
17570
  /**
17432
17571
  * server-management — per-NODE singleton capability for a node's ROOT
17433
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17434
- * agents).
17572
+ * package lifecycle (runtime-updatable node packages).
17435
17573
  *
17436
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
17437
- * on agents) carries the whole software stack in its npm dep tree, so ONE
17438
- * version describes the node. Updates install into
17439
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
17440
- * starter (probation boot + auto-rollback to N-1).
17574
+ * Every node role runs the SAME root package (`@camstack/server`), which
17575
+ * carries the whole software stack in its npm dep tree, so ONE version
17576
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17577
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17578
+ * no auto-rollback).
17441
17579
  *
17442
17580
  * Providers:
17443
17581
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17545,7 +17683,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17545
17683
  /** Explicit target version; omitted = latest from the registry. */
17546
17684
  version: string().optional() }), ServerUpdateActionResultSchema, {
17547
17685
  kind: "mutation",
17548
- auth: "admin"
17686
+ auth: "admin",
17687
+ timeoutMs: 16 * 6e4
17549
17688
  }), method(_void(), ServerUpdateActionResultSchema, {
17550
17689
  kind: "mutation",
17551
17690
  auth: "admin"
@@ -18589,22 +18728,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18589
18728
  var RestartAddonResultSchema = unknown();
18590
18729
  var InstallPackageResultSchema = unknown();
18591
18730
  var ReloadPackagesResultSchema = unknown();
18592
- /**
18593
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18594
- * server restarts so the admin UI can react to the `restartingAt`
18595
- * timestamp (shows reconnect overlay). The transition from
18596
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18597
- * `system.restart-completed` event after the new process boots.
18598
- *
18599
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18600
- */
18601
- var UpdateFrameworkPackageResultSchema = object({
18602
- packageName: string(),
18603
- fromVersion: string(),
18604
- toVersion: string(),
18605
- /** Ms-epoch the server scheduled its self-restart. */
18606
- restartingAt: number()
18607
- });
18608
18731
  var BulkUpdateItemStatusSchema = _enum([
18609
18732
  "queued",
18610
18733
  "updating",
@@ -18732,13 +18855,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18732
18855
  }), object({ success: literal(true) }), {
18733
18856
  kind: "mutation",
18734
18857
  auth: "admin"
18735
- }), method(object({
18736
- packageName: string().min(1),
18737
- version: string().optional(),
18738
- deferRestart: boolean().optional()
18739
- }), UpdateFrameworkPackageResultSchema, {
18740
- kind: "mutation",
18741
- auth: "admin"
18742
18858
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18743
18859
  kind: "mutation",
18744
18860
  auth: "admin"
@@ -19604,10 +19720,10 @@ var TopologyCategorySchema = object({
19604
19720
  addons: array(TopologyCategoryAddonSchema).readonly()
19605
19721
  });
19606
19722
  /**
19607
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19608
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19609
- * version visibility for the Server management surface. Nullable: offline
19610
- * rows and pre-phase-2 nodes report none.
19723
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19724
+ * root package for every node role) as reported by its `registerNode`
19725
+ * manifest — version visibility for the Server management surface. Nullable:
19726
+ * offline rows and nodes that never reported one.
19611
19727
  */
19612
19728
  var TopologyRootPackageSchema = object({
19613
19729
  name: string(),
@@ -19995,17 +20111,28 @@ var PlatformScoreSchema = object({
19995
20111
  format: _enum([
19996
20112
  "onnx",
19997
20113
  "coreml",
19998
- "openvino"
20114
+ "openvino",
20115
+ "tflite"
19999
20116
  ]),
20000
20117
  score: number(),
20001
20118
  reason: string(),
20002
20119
  available: boolean()
20003
20120
  });
20121
+ var InferenceDeviceDescriptorSchema = object({
20122
+ key: string(),
20123
+ backend: string(),
20124
+ device: string(),
20125
+ format: ModelFormatSchema,
20126
+ runtime: literal("python"),
20127
+ score: number(),
20128
+ available: boolean()
20129
+ });
20004
20130
  var PlatformCapabilitiesSchema = object({
20005
20131
  hardware: HardwareInfoSchema,
20006
20132
  scores: array(PlatformScoreSchema).readonly(),
20007
20133
  bestScore: PlatformScoreSchema,
20008
- pythonPath: string().nullable()
20134
+ pythonPath: string().nullable(),
20135
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
20009
20136
  });
20010
20137
  var ModelRequirementSchema = object({
20011
20138
  modelId: string(),
@@ -20884,12 +21011,6 @@ Object.freeze({
20884
21011
  addonId: null,
20885
21012
  access: "delete"
20886
21013
  },
20887
- "addons.updateFrameworkPackage": {
20888
- capName: "addons",
20889
- capScope: "system",
20890
- addonId: null,
20891
- access: "create"
20892
- },
20893
21014
  "addons.updatePackage": {
20894
21015
  capName: "addons",
20895
21016
  capScope: "system",
@@ -23662,12 +23783,6 @@ Object.freeze({
23662
23783
  addonId: null,
23663
23784
  access: "view"
23664
23785
  },
23665
- "pipelineExecutor.reprobeEngine": {
23666
- capName: "pipeline-executor",
23667
- capScope: "system",
23668
- addonId: null,
23669
- access: "create"
23670
- },
23671
23786
  "pipelineExecutor.runAudioTest": {
23672
23787
  capName: "pipeline-executor",
23673
23788
  capScope: "system",
@@ -23818,6 +23933,12 @@ Object.freeze({
23818
23933
  addonId: null,
23819
23934
  access: "view"
23820
23935
  },
23936
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23937
+ capName: "pipeline-orchestrator",
23938
+ capScope: "system",
23939
+ addonId: null,
23940
+ access: "view"
23941
+ },
23821
23942
  "pipelineOrchestrator.getPipelineAssignment": {
23822
23943
  capName: "pipeline-orchestrator",
23823
23944
  capScope: "system",
@@ -23830,6 +23951,12 @@ Object.freeze({
23830
23951
  addonId: null,
23831
23952
  access: "view"
23832
23953
  },
23954
+ "pipelineOrchestrator.getPipelineDevicePin": {
23955
+ capName: "pipeline-orchestrator",
23956
+ capScope: "system",
23957
+ addonId: null,
23958
+ access: "view"
23959
+ },
23833
23960
  "pipelineOrchestrator.listAgentSettings": {
23834
23961
  capName: "pipeline-orchestrator",
23835
23962
  capScope: "system",
@@ -23872,19 +23999,19 @@ Object.freeze({
23872
23999
  addonId: null,
23873
24000
  access: "create"
23874
24001
  },
23875
- "pipelineOrchestrator.setAgentAddonDefaults": {
24002
+ "pipelineOrchestrator.setAgentCapabilities": {
23876
24003
  capName: "pipeline-orchestrator",
23877
24004
  capScope: "system",
23878
24005
  addonId: null,
23879
24006
  access: "create"
23880
24007
  },
23881
- "pipelineOrchestrator.setAgentCapabilities": {
24008
+ "pipelineOrchestrator.setAgentDetectWeight": {
23882
24009
  capName: "pipeline-orchestrator",
23883
24010
  capScope: "system",
23884
24011
  addonId: null,
23885
24012
  access: "create"
23886
24013
  },
23887
- "pipelineOrchestrator.setAgentDetectWeight": {
24014
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23888
24015
  capName: "pipeline-orchestrator",
23889
24016
  capScope: "system",
23890
24017
  addonId: null,
@@ -23926,6 +24053,12 @@ Object.freeze({
23926
24053
  addonId: null,
23927
24054
  access: "create"
23928
24055
  },
24056
+ "pipelineOrchestrator.setPipelineDevicePin": {
24057
+ capName: "pipeline-orchestrator",
24058
+ capScope: "system",
24059
+ addonId: null,
24060
+ access: "create"
24061
+ },
23929
24062
  "pipelineOrchestrator.unassignAudio": {
23930
24063
  capName: "pipeline-orchestrator",
23931
24064
  capScope: "system",
@@ -25478,32 +25611,6 @@ Object.freeze({
25478
25611
  "network-access": "ingress",
25479
25612
  "smtp-provider": "email"
25480
25613
  });
25481
- var frameworkSwapPackageSchema = object({
25482
- name: string(),
25483
- stagedPath: string(),
25484
- backupPath: string(),
25485
- toVersion: string(),
25486
- fromVersion: string().nullable()
25487
- });
25488
- object({
25489
- jobId: string(),
25490
- taskId: string(),
25491
- packages: array(frameworkSwapPackageSchema),
25492
- requestedAtMs: number(),
25493
- schemaVersion: literal(1)
25494
- });
25495
- object({
25496
- jobId: string(),
25497
- taskId: string(),
25498
- backups: array(object({
25499
- name: string(),
25500
- backupPath: string(),
25501
- livePath: string()
25502
- })),
25503
- appliedAtMs: number(),
25504
- bootAttempts: number(),
25505
- schemaVersion: literal(1)
25506
- });
25507
25614
  var NOTIFIER_ICONS = {
25508
25615
  telegram: {
25509
25616
  contentType: "image/svg+xml",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-notifiers",
3
- "version": "1.1.30",
3
+ "version": "1.2.1",
4
4
  "description": "System notifiers addon for CamStack — a `notification-output` collection provider hosting per-kind notifier adapters (ntfy, pushover, gotify, telegram, discord, webhook, zentik).",
5
5
  "keywords": [
6
6
  "camstack",