@camstack/addon-notifiers 1.1.30 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +193 -103
  2. package/dist/addon.mjs +193 -103
  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). */
@@ -11839,7 +11877,14 @@ var RunnerCameraConfigSchema = object({
11839
11877
  * camera's detect node differs from its source-owner (P2d, gated by the
11840
11878
  * `remoteSourcingNodes` rollout setting).
11841
11879
  */
11842
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11880
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11881
+ /**
11882
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11883
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11884
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11885
+ * this only selects WHICH device pool of that node runs the session.
11886
+ */
11887
+ deviceKey: string().optional()
11843
11888
  });
11844
11889
  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
11890
  /**
@@ -11860,6 +11905,19 @@ var RunnerLocalLoadSchema = object({
11860
11905
  avgInferenceTimeMs: number(),
11861
11906
  /** Total queue depth across motion + detection queues. */
11862
11907
  queueDepthTotal: number(),
11908
+ /**
11909
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11910
+ * this runner currently has attached cameras on, so the orchestrator's second
11911
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11912
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11913
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11914
+ */
11915
+ devices: array(object({
11916
+ deviceKey: string(),
11917
+ backend: string(),
11918
+ attachedCameras: number(),
11919
+ queueDepthTotal: number()
11920
+ })).default([]),
11863
11921
  /** Hardware capability flags reported by this node. */
11864
11922
  hardware: object({
11865
11923
  hasGpu: boolean(),
@@ -13335,6 +13393,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13335
13393
  kind: "mutation",
13336
13394
  auth: "admin"
13337
13395
  });
13396
+ 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;
13397
+ new Set(Object.values(DeviceType));
13338
13398
  /**
13339
13399
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13340
13400
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17037,13 +17097,11 @@ var PipelineTemplateSchema = object({
17037
17097
  createdAt: string(),
17038
17098
  updatedAt: string()
17039
17099
  });
17040
- var AgentAddonConfigSchema = object({
17041
- enabled: boolean(),
17100
+ var DeviceStepConfigSchema = object({
17042
17101
  modelId: string().optional(),
17043
- settings: record(string(), unknown()).readonly()
17102
+ settings: record(string(), unknown()).optional()
17044
17103
  });
17045
17104
  var AgentPipelineSettingsSchema = object({
17046
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
17047
17105
  maxCameras: number().int().nonnegative().nullable().default(null),
17048
17106
  /** Per-node detection weight (relative share for the quota balancer). */
17049
17107
  detectWeight: number().positive().optional(),
@@ -17067,7 +17125,22 @@ var AgentPipelineSettingsSchema = object({
17067
17125
  * it already uses to reach the hub). Set this only when the auto-detected
17068
17126
  * address is wrong (multi-homed host, NAT, custom interface).
17069
17127
  */
17070
- reachableHost: string().optional()
17128
+ reachableHost: string().optional(),
17129
+ /**
17130
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
17131
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
17132
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
17133
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
17134
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
17135
+ * the default model/settings for every camera landing on that accelerator;
17136
+ * a stepId absent ⇒ the step uses that device's format default.
17137
+ */
17138
+ inferenceDevices: record(string(), object({
17139
+ enabled: boolean(),
17140
+ weight: number().positive().optional(),
17141
+ maxSessions: number().int().positive().optional(),
17142
+ steps: record(string(), DeviceStepConfigSchema).optional()
17143
+ })).optional()
17071
17144
  });
17072
17145
  var CameraPipelineForAgentSchema = object({
17073
17146
  steps: array(PipelineStepInputSchema).readonly(),
@@ -17077,14 +17150,13 @@ var CameraPipelineForAgentSchema = object({
17077
17150
  }).nullable()
17078
17151
  });
17079
17152
  var CameraStepOverridePatchSchema = object({
17080
- enabled: boolean().optional(),
17081
17153
  modelId: string().optional(),
17082
17154
  settings: record(string(), unknown()).readonly().optional()
17083
17155
  });
17084
17156
  var CameraPipelineSettingsSchema = object({
17085
17157
  pinnedAgentNodeId: string().optional(),
17086
17158
  stepToggles: record(string(), boolean()).optional(),
17087
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
17159
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
17088
17160
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
17089
17161
  });
17090
17162
  /**
@@ -17298,6 +17370,44 @@ var CameraStatusSchema = object({
17298
17370
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17299
17371
  fetchedAt: number()
17300
17372
  });
17373
+ var NodeInferenceDeviceSchema = object({
17374
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17375
+ key: string(),
17376
+ backend: string(),
17377
+ device: string(),
17378
+ format: _enum(MODEL_FORMATS),
17379
+ /** Whether the node's live probe reports the device as usable right now. */
17380
+ available: boolean(),
17381
+ /**
17382
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17383
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17384
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17385
+ * not a balanced target). An explicit stored value always wins; a stored-only
17386
+ * (unavailable) key keeps its stored value.
17387
+ */
17388
+ enabled: boolean(),
17389
+ /** Relative balancer weight for the enabled device (default 1). */
17390
+ weight: number(),
17391
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17392
+ maxSessions: number().nullable(),
17393
+ /** Object-detection model the executor defaults to for this deviceKey. */
17394
+ defaultModelId: string(),
17395
+ /**
17396
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17397
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17398
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17399
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17400
+ * available per format; this is the stored selection that becomes the
17401
+ * default for EVERY camera landing on this accelerator.
17402
+ */
17403
+ steps: record(string(), DeviceStepConfigSchema).optional()
17404
+ });
17405
+ var NodeInferenceDevicesSchema = object({
17406
+ nodeId: string(),
17407
+ /** False when the node's platform-probe was unreachable (no live device set). */
17408
+ reachable: boolean(),
17409
+ devices: array(NodeInferenceDeviceSchema).readonly()
17410
+ });
17301
17411
  method(object({
17302
17412
  deviceId: number(),
17303
17413
  agentNodeId: string()
@@ -17307,7 +17417,13 @@ method(object({
17307
17417
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
17308
17418
  kind: "mutation",
17309
17419
  auth: "admin"
17310
- }), method(_void(), object({ migrated: number() }), {
17420
+ }), method(object({
17421
+ deviceId: number(),
17422
+ deviceKey: string()
17423
+ }), object({ success: literal(true) }), {
17424
+ kind: "mutation",
17425
+ auth: "admin"
17426
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
17311
17427
  kind: "mutation",
17312
17428
  auth: "admin"
17313
17429
  }), 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 +17457,7 @@ method(object({
17341
17457
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17342
17458
  nodeId: string(),
17343
17459
  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({
17460
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17351
17461
  success: boolean(),
17352
17462
  removed: boolean()
17353
17463
  }), {
@@ -17379,7 +17489,18 @@ method(object({
17379
17489
  }), object({ success: literal(true) }), {
17380
17490
  kind: "mutation",
17381
17491
  auth: "admin"
17382
- }), method(object({ agentNodeId: string() }), object({
17492
+ }), method(object({
17493
+ agentNodeId: string(),
17494
+ inferenceDevices: record(string(), object({
17495
+ enabled: boolean(),
17496
+ weight: number().positive().optional(),
17497
+ maxSessions: number().int().positive().optional(),
17498
+ steps: record(string(), DeviceStepConfigSchema).optional()
17499
+ }))
17500
+ }), object({ success: literal(true) }), {
17501
+ kind: "mutation",
17502
+ auth: "admin"
17503
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17383
17504
  success: literal(true),
17384
17505
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17385
17506
  effectiveModelId: string().nullable(),
@@ -17395,9 +17516,10 @@ method(object({
17395
17516
  }), object({ success: literal(true) }), {
17396
17517
  kind: "mutation",
17397
17518
  auth: "admin"
17398
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17519
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17399
17520
  deviceId: number(),
17400
17521
  agentNodeId: string(),
17522
+ deviceKey: string(),
17401
17523
  addonId: string(),
17402
17524
  patch: CameraStepOverridePatchSchema.nullable()
17403
17525
  }), object({ success: literal(true) }), {
@@ -17434,14 +17556,13 @@ method(object({
17434
17556
  });
17435
17557
  /**
17436
17558
  * server-management — per-NODE singleton capability for a node's ROOT
17437
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17438
- * agents).
17559
+ * package lifecycle (runtime-updatable node packages).
17439
17560
  *
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).
17561
+ * Every node role runs the SAME root package (`@camstack/server`), which
17562
+ * carries the whole software stack in its npm dep tree, so ONE version
17563
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17564
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17565
+ * no auto-rollback).
17445
17566
  *
17446
17567
  * Providers:
17447
17568
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17549,7 +17670,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17549
17670
  /** Explicit target version; omitted = latest from the registry. */
17550
17671
  version: string().optional() }), ServerUpdateActionResultSchema, {
17551
17672
  kind: "mutation",
17552
- auth: "admin"
17673
+ auth: "admin",
17674
+ timeoutMs: 16 * 6e4
17553
17675
  }), method(_void(), ServerUpdateActionResultSchema, {
17554
17676
  kind: "mutation",
17555
17677
  auth: "admin"
@@ -18593,22 +18715,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18593
18715
  var RestartAddonResultSchema = unknown();
18594
18716
  var InstallPackageResultSchema = unknown();
18595
18717
  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
18718
  var BulkUpdateItemStatusSchema = _enum([
18613
18719
  "queued",
18614
18720
  "updating",
@@ -18736,13 +18842,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18736
18842
  }), object({ success: literal(true) }), {
18737
18843
  kind: "mutation",
18738
18844
  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
18845
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18747
18846
  kind: "mutation",
18748
18847
  auth: "admin"
@@ -19608,10 +19707,10 @@ var TopologyCategorySchema = object({
19608
19707
  addons: array(TopologyCategoryAddonSchema).readonly()
19609
19708
  });
19610
19709
  /**
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.
19710
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19711
+ * root package for every node role) as reported by its `registerNode`
19712
+ * manifest — version visibility for the Server management surface. Nullable:
19713
+ * offline rows and nodes that never reported one.
19615
19714
  */
19616
19715
  var TopologyRootPackageSchema = object({
19617
19716
  name: string(),
@@ -19999,17 +20098,28 @@ var PlatformScoreSchema = object({
19999
20098
  format: _enum([
20000
20099
  "onnx",
20001
20100
  "coreml",
20002
- "openvino"
20101
+ "openvino",
20102
+ "tflite"
20003
20103
  ]),
20004
20104
  score: number(),
20005
20105
  reason: string(),
20006
20106
  available: boolean()
20007
20107
  });
20108
+ var InferenceDeviceDescriptorSchema = object({
20109
+ key: string(),
20110
+ backend: string(),
20111
+ device: string(),
20112
+ format: ModelFormatSchema,
20113
+ runtime: literal("python"),
20114
+ score: number(),
20115
+ available: boolean()
20116
+ });
20008
20117
  var PlatformCapabilitiesSchema = object({
20009
20118
  hardware: HardwareInfoSchema,
20010
20119
  scores: array(PlatformScoreSchema).readonly(),
20011
20120
  bestScore: PlatformScoreSchema,
20012
- pythonPath: string().nullable()
20121
+ pythonPath: string().nullable(),
20122
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
20013
20123
  });
20014
20124
  var ModelRequirementSchema = object({
20015
20125
  modelId: string(),
@@ -20888,12 +20998,6 @@ Object.freeze({
20888
20998
  addonId: null,
20889
20999
  access: "delete"
20890
21000
  },
20891
- "addons.updateFrameworkPackage": {
20892
- capName: "addons",
20893
- capScope: "system",
20894
- addonId: null,
20895
- access: "create"
20896
- },
20897
21001
  "addons.updatePackage": {
20898
21002
  capName: "addons",
20899
21003
  capScope: "system",
@@ -23666,12 +23770,6 @@ Object.freeze({
23666
23770
  addonId: null,
23667
23771
  access: "view"
23668
23772
  },
23669
- "pipelineExecutor.reprobeEngine": {
23670
- capName: "pipeline-executor",
23671
- capScope: "system",
23672
- addonId: null,
23673
- access: "create"
23674
- },
23675
23773
  "pipelineExecutor.runAudioTest": {
23676
23774
  capName: "pipeline-executor",
23677
23775
  capScope: "system",
@@ -23822,6 +23920,12 @@ Object.freeze({
23822
23920
  addonId: null,
23823
23921
  access: "view"
23824
23922
  },
23923
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23924
+ capName: "pipeline-orchestrator",
23925
+ capScope: "system",
23926
+ addonId: null,
23927
+ access: "view"
23928
+ },
23825
23929
  "pipelineOrchestrator.getPipelineAssignment": {
23826
23930
  capName: "pipeline-orchestrator",
23827
23931
  capScope: "system",
@@ -23834,6 +23938,12 @@ Object.freeze({
23834
23938
  addonId: null,
23835
23939
  access: "view"
23836
23940
  },
23941
+ "pipelineOrchestrator.getPipelineDevicePin": {
23942
+ capName: "pipeline-orchestrator",
23943
+ capScope: "system",
23944
+ addonId: null,
23945
+ access: "view"
23946
+ },
23837
23947
  "pipelineOrchestrator.listAgentSettings": {
23838
23948
  capName: "pipeline-orchestrator",
23839
23949
  capScope: "system",
@@ -23876,19 +23986,19 @@ Object.freeze({
23876
23986
  addonId: null,
23877
23987
  access: "create"
23878
23988
  },
23879
- "pipelineOrchestrator.setAgentAddonDefaults": {
23989
+ "pipelineOrchestrator.setAgentCapabilities": {
23880
23990
  capName: "pipeline-orchestrator",
23881
23991
  capScope: "system",
23882
23992
  addonId: null,
23883
23993
  access: "create"
23884
23994
  },
23885
- "pipelineOrchestrator.setAgentCapabilities": {
23995
+ "pipelineOrchestrator.setAgentDetectWeight": {
23886
23996
  capName: "pipeline-orchestrator",
23887
23997
  capScope: "system",
23888
23998
  addonId: null,
23889
23999
  access: "create"
23890
24000
  },
23891
- "pipelineOrchestrator.setAgentDetectWeight": {
24001
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23892
24002
  capName: "pipeline-orchestrator",
23893
24003
  capScope: "system",
23894
24004
  addonId: null,
@@ -23930,6 +24040,12 @@ Object.freeze({
23930
24040
  addonId: null,
23931
24041
  access: "create"
23932
24042
  },
24043
+ "pipelineOrchestrator.setPipelineDevicePin": {
24044
+ capName: "pipeline-orchestrator",
24045
+ capScope: "system",
24046
+ addonId: null,
24047
+ access: "create"
24048
+ },
23933
24049
  "pipelineOrchestrator.unassignAudio": {
23934
24050
  capName: "pipeline-orchestrator",
23935
24051
  capScope: "system",
@@ -25482,32 +25598,6 @@ Object.freeze({
25482
25598
  "network-access": "ingress",
25483
25599
  "smtp-provider": "email"
25484
25600
  });
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
25601
  var NOTIFIER_ICONS = {
25512
25602
  telegram: {
25513
25603
  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). */
@@ -11835,7 +11873,14 @@ var RunnerCameraConfigSchema = object({
11835
11873
  * camera's detect node differs from its source-owner (P2d, gated by the
11836
11874
  * `remoteSourcingNodes` rollout setting).
11837
11875
  */
11838
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11876
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11877
+ /**
11878
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11879
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11880
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11881
+ * this only selects WHICH device pool of that node runs the session.
11882
+ */
11883
+ deviceKey: string().optional()
11839
11884
  });
11840
11885
  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
11886
  /**
@@ -11856,6 +11901,19 @@ var RunnerLocalLoadSchema = object({
11856
11901
  avgInferenceTimeMs: number(),
11857
11902
  /** Total queue depth across motion + detection queues. */
11858
11903
  queueDepthTotal: number(),
11904
+ /**
11905
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11906
+ * this runner currently has attached cameras on, so the orchestrator's second
11907
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11908
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11909
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11910
+ */
11911
+ devices: array(object({
11912
+ deviceKey: string(),
11913
+ backend: string(),
11914
+ attachedCameras: number(),
11915
+ queueDepthTotal: number()
11916
+ })).default([]),
11859
11917
  /** Hardware capability flags reported by this node. */
11860
11918
  hardware: object({
11861
11919
  hasGpu: boolean(),
@@ -13331,6 +13389,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13331
13389
  kind: "mutation",
13332
13390
  auth: "admin"
13333
13391
  });
13392
+ 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;
13393
+ new Set(Object.values(DeviceType));
13334
13394
  /**
13335
13395
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13336
13396
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -17033,13 +17093,11 @@ var PipelineTemplateSchema = object({
17033
17093
  createdAt: string(),
17034
17094
  updatedAt: string()
17035
17095
  });
17036
- var AgentAddonConfigSchema = object({
17037
- enabled: boolean(),
17096
+ var DeviceStepConfigSchema = object({
17038
17097
  modelId: string().optional(),
17039
- settings: record(string(), unknown()).readonly()
17098
+ settings: record(string(), unknown()).optional()
17040
17099
  });
17041
17100
  var AgentPipelineSettingsSchema = object({
17042
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
17043
17101
  maxCameras: number().int().nonnegative().nullable().default(null),
17044
17102
  /** Per-node detection weight (relative share for the quota balancer). */
17045
17103
  detectWeight: number().positive().optional(),
@@ -17063,7 +17121,22 @@ var AgentPipelineSettingsSchema = object({
17063
17121
  * it already uses to reach the hub). Set this only when the auto-detected
17064
17122
  * address is wrong (multi-homed host, NAT, custom interface).
17065
17123
  */
17066
- reachableHost: string().optional()
17124
+ reachableHost: string().optional(),
17125
+ /**
17126
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
17127
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
17128
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
17129
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
17130
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
17131
+ * the default model/settings for every camera landing on that accelerator;
17132
+ * a stepId absent ⇒ the step uses that device's format default.
17133
+ */
17134
+ inferenceDevices: record(string(), object({
17135
+ enabled: boolean(),
17136
+ weight: number().positive().optional(),
17137
+ maxSessions: number().int().positive().optional(),
17138
+ steps: record(string(), DeviceStepConfigSchema).optional()
17139
+ })).optional()
17067
17140
  });
17068
17141
  var CameraPipelineForAgentSchema = object({
17069
17142
  steps: array(PipelineStepInputSchema).readonly(),
@@ -17073,14 +17146,13 @@ var CameraPipelineForAgentSchema = object({
17073
17146
  }).nullable()
17074
17147
  });
17075
17148
  var CameraStepOverridePatchSchema = object({
17076
- enabled: boolean().optional(),
17077
17149
  modelId: string().optional(),
17078
17150
  settings: record(string(), unknown()).readonly().optional()
17079
17151
  });
17080
17152
  var CameraPipelineSettingsSchema = object({
17081
17153
  pinnedAgentNodeId: string().optional(),
17082
17154
  stepToggles: record(string(), boolean()).optional(),
17083
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
17155
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
17084
17156
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
17085
17157
  });
17086
17158
  /**
@@ -17294,6 +17366,44 @@ var CameraStatusSchema = object({
17294
17366
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17295
17367
  fetchedAt: number()
17296
17368
  });
17369
+ var NodeInferenceDeviceSchema = object({
17370
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17371
+ key: string(),
17372
+ backend: string(),
17373
+ device: string(),
17374
+ format: _enum(MODEL_FORMATS),
17375
+ /** Whether the node's live probe reports the device as usable right now. */
17376
+ available: boolean(),
17377
+ /**
17378
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17379
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17380
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17381
+ * not a balanced target). An explicit stored value always wins; a stored-only
17382
+ * (unavailable) key keeps its stored value.
17383
+ */
17384
+ enabled: boolean(),
17385
+ /** Relative balancer weight for the enabled device (default 1). */
17386
+ weight: number(),
17387
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17388
+ maxSessions: number().nullable(),
17389
+ /** Object-detection model the executor defaults to for this deviceKey. */
17390
+ defaultModelId: string(),
17391
+ /**
17392
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17393
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17394
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17395
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17396
+ * available per format; this is the stored selection that becomes the
17397
+ * default for EVERY camera landing on this accelerator.
17398
+ */
17399
+ steps: record(string(), DeviceStepConfigSchema).optional()
17400
+ });
17401
+ var NodeInferenceDevicesSchema = object({
17402
+ nodeId: string(),
17403
+ /** False when the node's platform-probe was unreachable (no live device set). */
17404
+ reachable: boolean(),
17405
+ devices: array(NodeInferenceDeviceSchema).readonly()
17406
+ });
17297
17407
  method(object({
17298
17408
  deviceId: number(),
17299
17409
  agentNodeId: string()
@@ -17303,7 +17413,13 @@ method(object({
17303
17413
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
17304
17414
  kind: "mutation",
17305
17415
  auth: "admin"
17306
- }), method(_void(), object({ migrated: number() }), {
17416
+ }), method(object({
17417
+ deviceId: number(),
17418
+ deviceKey: string()
17419
+ }), object({ success: literal(true) }), {
17420
+ kind: "mutation",
17421
+ auth: "admin"
17422
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
17307
17423
  kind: "mutation",
17308
17424
  auth: "admin"
17309
17425
  }), 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 +17453,7 @@ method(object({
17337
17453
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17338
17454
  nodeId: string(),
17339
17455
  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({
17456
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17347
17457
  success: boolean(),
17348
17458
  removed: boolean()
17349
17459
  }), {
@@ -17375,7 +17485,18 @@ method(object({
17375
17485
  }), object({ success: literal(true) }), {
17376
17486
  kind: "mutation",
17377
17487
  auth: "admin"
17378
- }), method(object({ agentNodeId: string() }), object({
17488
+ }), method(object({
17489
+ agentNodeId: string(),
17490
+ inferenceDevices: record(string(), object({
17491
+ enabled: boolean(),
17492
+ weight: number().positive().optional(),
17493
+ maxSessions: number().int().positive().optional(),
17494
+ steps: record(string(), DeviceStepConfigSchema).optional()
17495
+ }))
17496
+ }), object({ success: literal(true) }), {
17497
+ kind: "mutation",
17498
+ auth: "admin"
17499
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17379
17500
  success: literal(true),
17380
17501
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17381
17502
  effectiveModelId: string().nullable(),
@@ -17391,9 +17512,10 @@ method(object({
17391
17512
  }), object({ success: literal(true) }), {
17392
17513
  kind: "mutation",
17393
17514
  auth: "admin"
17394
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17515
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17395
17516
  deviceId: number(),
17396
17517
  agentNodeId: string(),
17518
+ deviceKey: string(),
17397
17519
  addonId: string(),
17398
17520
  patch: CameraStepOverridePatchSchema.nullable()
17399
17521
  }), object({ success: literal(true) }), {
@@ -17430,14 +17552,13 @@ method(object({
17430
17552
  });
17431
17553
  /**
17432
17554
  * server-management — per-NODE singleton capability for a node's ROOT
17433
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17434
- * agents).
17555
+ * package lifecycle (runtime-updatable node packages).
17435
17556
  *
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).
17557
+ * Every node role runs the SAME root package (`@camstack/server`), which
17558
+ * carries the whole software stack in its npm dep tree, so ONE version
17559
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17560
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17561
+ * no auto-rollback).
17441
17562
  *
17442
17563
  * Providers:
17443
17564
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17545,7 +17666,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17545
17666
  /** Explicit target version; omitted = latest from the registry. */
17546
17667
  version: string().optional() }), ServerUpdateActionResultSchema, {
17547
17668
  kind: "mutation",
17548
- auth: "admin"
17669
+ auth: "admin",
17670
+ timeoutMs: 16 * 6e4
17549
17671
  }), method(_void(), ServerUpdateActionResultSchema, {
17550
17672
  kind: "mutation",
17551
17673
  auth: "admin"
@@ -18589,22 +18711,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18589
18711
  var RestartAddonResultSchema = unknown();
18590
18712
  var InstallPackageResultSchema = unknown();
18591
18713
  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
18714
  var BulkUpdateItemStatusSchema = _enum([
18609
18715
  "queued",
18610
18716
  "updating",
@@ -18732,13 +18838,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18732
18838
  }), object({ success: literal(true) }), {
18733
18839
  kind: "mutation",
18734
18840
  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
18841
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18743
18842
  kind: "mutation",
18744
18843
  auth: "admin"
@@ -19604,10 +19703,10 @@ var TopologyCategorySchema = object({
19604
19703
  addons: array(TopologyCategoryAddonSchema).readonly()
19605
19704
  });
19606
19705
  /**
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.
19706
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19707
+ * root package for every node role) as reported by its `registerNode`
19708
+ * manifest — version visibility for the Server management surface. Nullable:
19709
+ * offline rows and nodes that never reported one.
19611
19710
  */
19612
19711
  var TopologyRootPackageSchema = object({
19613
19712
  name: string(),
@@ -19995,17 +20094,28 @@ var PlatformScoreSchema = object({
19995
20094
  format: _enum([
19996
20095
  "onnx",
19997
20096
  "coreml",
19998
- "openvino"
20097
+ "openvino",
20098
+ "tflite"
19999
20099
  ]),
20000
20100
  score: number(),
20001
20101
  reason: string(),
20002
20102
  available: boolean()
20003
20103
  });
20104
+ var InferenceDeviceDescriptorSchema = object({
20105
+ key: string(),
20106
+ backend: string(),
20107
+ device: string(),
20108
+ format: ModelFormatSchema,
20109
+ runtime: literal("python"),
20110
+ score: number(),
20111
+ available: boolean()
20112
+ });
20004
20113
  var PlatformCapabilitiesSchema = object({
20005
20114
  hardware: HardwareInfoSchema,
20006
20115
  scores: array(PlatformScoreSchema).readonly(),
20007
20116
  bestScore: PlatformScoreSchema,
20008
- pythonPath: string().nullable()
20117
+ pythonPath: string().nullable(),
20118
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
20009
20119
  });
20010
20120
  var ModelRequirementSchema = object({
20011
20121
  modelId: string(),
@@ -20884,12 +20994,6 @@ Object.freeze({
20884
20994
  addonId: null,
20885
20995
  access: "delete"
20886
20996
  },
20887
- "addons.updateFrameworkPackage": {
20888
- capName: "addons",
20889
- capScope: "system",
20890
- addonId: null,
20891
- access: "create"
20892
- },
20893
20997
  "addons.updatePackage": {
20894
20998
  capName: "addons",
20895
20999
  capScope: "system",
@@ -23662,12 +23766,6 @@ Object.freeze({
23662
23766
  addonId: null,
23663
23767
  access: "view"
23664
23768
  },
23665
- "pipelineExecutor.reprobeEngine": {
23666
- capName: "pipeline-executor",
23667
- capScope: "system",
23668
- addonId: null,
23669
- access: "create"
23670
- },
23671
23769
  "pipelineExecutor.runAudioTest": {
23672
23770
  capName: "pipeline-executor",
23673
23771
  capScope: "system",
@@ -23818,6 +23916,12 @@ Object.freeze({
23818
23916
  addonId: null,
23819
23917
  access: "view"
23820
23918
  },
23919
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23920
+ capName: "pipeline-orchestrator",
23921
+ capScope: "system",
23922
+ addonId: null,
23923
+ access: "view"
23924
+ },
23821
23925
  "pipelineOrchestrator.getPipelineAssignment": {
23822
23926
  capName: "pipeline-orchestrator",
23823
23927
  capScope: "system",
@@ -23830,6 +23934,12 @@ Object.freeze({
23830
23934
  addonId: null,
23831
23935
  access: "view"
23832
23936
  },
23937
+ "pipelineOrchestrator.getPipelineDevicePin": {
23938
+ capName: "pipeline-orchestrator",
23939
+ capScope: "system",
23940
+ addonId: null,
23941
+ access: "view"
23942
+ },
23833
23943
  "pipelineOrchestrator.listAgentSettings": {
23834
23944
  capName: "pipeline-orchestrator",
23835
23945
  capScope: "system",
@@ -23872,19 +23982,19 @@ Object.freeze({
23872
23982
  addonId: null,
23873
23983
  access: "create"
23874
23984
  },
23875
- "pipelineOrchestrator.setAgentAddonDefaults": {
23985
+ "pipelineOrchestrator.setAgentCapabilities": {
23876
23986
  capName: "pipeline-orchestrator",
23877
23987
  capScope: "system",
23878
23988
  addonId: null,
23879
23989
  access: "create"
23880
23990
  },
23881
- "pipelineOrchestrator.setAgentCapabilities": {
23991
+ "pipelineOrchestrator.setAgentDetectWeight": {
23882
23992
  capName: "pipeline-orchestrator",
23883
23993
  capScope: "system",
23884
23994
  addonId: null,
23885
23995
  access: "create"
23886
23996
  },
23887
- "pipelineOrchestrator.setAgentDetectWeight": {
23997
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23888
23998
  capName: "pipeline-orchestrator",
23889
23999
  capScope: "system",
23890
24000
  addonId: null,
@@ -23926,6 +24036,12 @@ Object.freeze({
23926
24036
  addonId: null,
23927
24037
  access: "create"
23928
24038
  },
24039
+ "pipelineOrchestrator.setPipelineDevicePin": {
24040
+ capName: "pipeline-orchestrator",
24041
+ capScope: "system",
24042
+ addonId: null,
24043
+ access: "create"
24044
+ },
23929
24045
  "pipelineOrchestrator.unassignAudio": {
23930
24046
  capName: "pipeline-orchestrator",
23931
24047
  capScope: "system",
@@ -25478,32 +25594,6 @@ Object.freeze({
25478
25594
  "network-access": "ingress",
25479
25595
  "smtp-provider": "email"
25480
25596
  });
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
25597
  var NOTIFIER_ICONS = {
25508
25598
  telegram: {
25509
25599
  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.0",
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",