@camstack/addon-decoder-nodeav 1.1.18 → 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/index.js +213 -106
  2. package/dist/index.mjs +213 -106
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7084,6 +7084,17 @@ var ModelCatalogEntrySchema = object({
7084
7084
  "imagenet",
7085
7085
  "none"
7086
7086
  ]).optional(),
7087
+ /**
7088
+ * The model already applies softmax IN-GRAPH — its raw output is a
7089
+ * probability distribution, not logits. When set, the `softmax`
7090
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7091
+ * probability vector collapses it toward uniform (top-1 score craters far
7092
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7093
+ * the output is raw logits and the postprocessor applies softmax (the normal
7094
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7095
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7096
+ */
7097
+ outputProbabilities: boolean().optional(),
7087
7098
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7088
7099
  /**
7089
7100
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -11097,10 +11108,7 @@ var ConfigUISchemaNullableBridge = custom();
11097
11108
  var InferenceCapabilitiesBridge = custom();
11098
11109
  var ModelAvailabilityListBridge = custom();
11099
11110
  var PipelineRunResultBridge = custom();
11100
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11101
- kind: "mutation",
11102
- auth: "admin"
11103
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11111
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11104
11112
  modelId: string(),
11105
11113
  settings: record(string(), unknown()).readonly()
11106
11114
  }))), method(object({ steps: record(string(), object({
@@ -11160,13 +11168,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11160
11168
  * (inputClasses ≠ null) are skipped and served per-track via
11161
11169
  * pipelineRunner.runDetailSubtree (two-plane design).
11162
11170
  */
11163
- plane: _enum(["full", "frame"]).optional()
11171
+ plane: _enum(["full", "frame"]).optional(),
11172
+ /**
11173
+ * Inference-device selector (Phase 2 multi-device). Format
11174
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11175
+ * Omitted ⇒ the runner's default device (current single-engine
11176
+ * behaviour). Selects WHICH device pool of the node runs the call.
11177
+ */
11178
+ deviceKey: string().optional()
11164
11179
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11165
11180
  engine: PipelineEngineChoiceSchema.optional(),
11166
11181
  steps: array(PipelineStepInputSchema).min(1),
11167
11182
  frames: array(FrameInputSchema).min(1).max(255),
11168
11183
  deviceId: number().optional(),
11169
- sessionId: string().optional()
11184
+ sessionId: string().optional(),
11185
+ /**
11186
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11187
+ * the batch to the Python pool's bench preprocess cache
11188
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11189
+ * preprocessed ONCE and every later inference is a pure-inference cache
11190
+ * hit — the sustained-throughput run measures inference, not
11191
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11192
+ * full preprocess every call, correct). Fresh per sustained run;
11193
+ * released via `uncacheFrame`.
11194
+ */
11195
+ frameId: number().int().nonnegative().optional(),
11196
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11197
+ deviceKey: string().optional()
11170
11198
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11171
11199
  data: _instanceof(Uint8Array),
11172
11200
  width: number().int().positive(),
@@ -11198,8 +11226,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11198
11226
  * - `runtime` — main camera-serving engine (no idle TTL).
11199
11227
  * - `warm-override` — benchmark/test override held in the warm
11200
11228
  * cache; auto-disposed after the idle TTL.
11229
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11230
+ * multi-device, keyed by `deviceKey`) resolved
11231
+ * via `resolveDeviceFactory`. Runs alongside the
11232
+ * `runtime` engine on a DIFFERENT accelerator
11233
+ * (NPU / iGPU / Coral) — this is how the
11234
+ * Engines tab shows all pools running at once.
11201
11235
  */
11202
- kind: _enum(["runtime", "warm-override"]),
11236
+ kind: _enum([
11237
+ "runtime",
11238
+ "warm-override",
11239
+ "device-pool"
11240
+ ]),
11203
11241
  /** Native pid of the underlying Python pool (null when no pool). */
11204
11242
  poolPid: number().nullable(),
11205
11243
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11301,7 +11339,21 @@ var NativeCropResultSchema = object({
11301
11339
  /** Packed rgb (24-bit) pixels of the crop. */
11302
11340
  bytes: _instanceof(Uint8Array),
11303
11341
  width: number().int().positive(),
11304
- height: number().int().positive()
11342
+ height: number().int().positive(),
11343
+ /**
11344
+ * Which source served this crop, so a quality-sensitive consumer (the native
11345
+ * `keyFrame`) can reject a degraded fallback:
11346
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
11347
+ * quality path).
11348
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
11349
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
11350
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
11351
+ *
11352
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
11353
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
11354
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
11355
+ */
11356
+ tier: _enum(["native", "ram-fullframe"]).optional()
11305
11357
  });
11306
11358
  /** Parent detection context passed to `runDetailSubtree` — the crop's
11307
11359
  * originating detection, in FRAME-space coordinates. Reuses
@@ -11541,7 +11593,14 @@ var RunnerCameraConfigSchema = object({
11541
11593
  * camera's detect node differs from its source-owner (P2d, gated by the
11542
11594
  * `remoteSourcingNodes` rollout setting).
11543
11595
  */
11544
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11596
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11597
+ /**
11598
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11599
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11600
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11601
+ * this only selects WHICH device pool of that node runs the session.
11602
+ */
11603
+ deviceKey: string().optional()
11545
11604
  });
11546
11605
  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;
11547
11606
  /**
@@ -11562,6 +11621,19 @@ var RunnerLocalLoadSchema = object({
11562
11621
  avgInferenceTimeMs: number(),
11563
11622
  /** Total queue depth across motion + detection queues. */
11564
11623
  queueDepthTotal: number(),
11624
+ /**
11625
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11626
+ * this runner currently has attached cameras on, so the orchestrator's second
11627
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11628
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11629
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11630
+ */
11631
+ devices: array(object({
11632
+ deviceKey: string(),
11633
+ backend: string(),
11634
+ attachedCameras: number(),
11635
+ queueDepthTotal: number()
11636
+ })).default([]),
11565
11637
  /** Hardware capability flags reported by this node. */
11566
11638
  hardware: object({
11567
11639
  hasGpu: boolean(),
@@ -13037,6 +13109,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13037
13109
  kind: "mutation",
13038
13110
  auth: "admin"
13039
13111
  });
13112
+ 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;
13113
+ new Set(Object.values(DeviceType));
13040
13114
  /**
13041
13115
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13042
13116
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -14740,7 +14814,8 @@ var LinkedDeviceSchema = object({
14740
14814
  deviceId: number(),
14741
14815
  name: string(),
14742
14816
  location: string().nullable(),
14743
- features: array(string())
14817
+ features: array(string()),
14818
+ producesTrackedEvents: boolean().optional()
14744
14819
  });
14745
14820
  var SavedDeviceRowSchema = object({
14746
14821
  /** Numeric id reserved at allocateDeviceId time. */
@@ -16370,6 +16445,7 @@ var TrackSchema = object({
16370
16445
  deviceId: number(),
16371
16446
  className: string(),
16372
16447
  label: string().optional(),
16448
+ producingDeviceName: string().optional(),
16373
16449
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16374
16450
  source: TrackSourceSchema.optional(),
16375
16451
  firstSeen: number(),
@@ -16507,7 +16583,8 @@ var MediaFileKindEnum = _enum([
16507
16583
  "fullFrameBoxed",
16508
16584
  "faceCrop",
16509
16585
  "plateCrop",
16510
- "keyFrame"
16586
+ "keyFrame",
16587
+ "keyFrameSmall"
16511
16588
  ]);
16512
16589
  var MediaFileSchema = object({
16513
16590
  key: string(),
@@ -16836,13 +16913,11 @@ var PipelineTemplateSchema = object({
16836
16913
  createdAt: string(),
16837
16914
  updatedAt: string()
16838
16915
  });
16839
- var AgentAddonConfigSchema = object({
16840
- enabled: boolean(),
16916
+ var DeviceStepConfigSchema = object({
16841
16917
  modelId: string().optional(),
16842
- settings: record(string(), unknown()).readonly()
16918
+ settings: record(string(), unknown()).optional()
16843
16919
  });
16844
16920
  var AgentPipelineSettingsSchema = object({
16845
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
16846
16921
  maxCameras: number().int().nonnegative().nullable().default(null),
16847
16922
  /** Per-node detection weight (relative share for the quota balancer). */
16848
16923
  detectWeight: number().positive().optional(),
@@ -16866,7 +16941,22 @@ var AgentPipelineSettingsSchema = object({
16866
16941
  * it already uses to reach the hub). Set this only when the auto-detected
16867
16942
  * address is wrong (multi-homed host, NAT, custom interface).
16868
16943
  */
16869
- reachableHost: string().optional()
16944
+ reachableHost: string().optional(),
16945
+ /**
16946
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
16947
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
16948
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
16949
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
16950
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
16951
+ * the default model/settings for every camera landing on that accelerator;
16952
+ * a stepId absent ⇒ the step uses that device's format default.
16953
+ */
16954
+ inferenceDevices: record(string(), object({
16955
+ enabled: boolean(),
16956
+ weight: number().positive().optional(),
16957
+ maxSessions: number().int().positive().optional(),
16958
+ steps: record(string(), DeviceStepConfigSchema).optional()
16959
+ })).optional()
16870
16960
  });
16871
16961
  var CameraPipelineForAgentSchema = object({
16872
16962
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16876,14 +16966,13 @@ var CameraPipelineForAgentSchema = object({
16876
16966
  }).nullable()
16877
16967
  });
16878
16968
  var CameraStepOverridePatchSchema = object({
16879
- enabled: boolean().optional(),
16880
16969
  modelId: string().optional(),
16881
16970
  settings: record(string(), unknown()).readonly().optional()
16882
16971
  });
16883
16972
  var CameraPipelineSettingsSchema = object({
16884
16973
  pinnedAgentNodeId: string().optional(),
16885
16974
  stepToggles: record(string(), boolean()).optional(),
16886
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
16975
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
16887
16976
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
16888
16977
  });
16889
16978
  /**
@@ -17097,6 +17186,44 @@ var CameraStatusSchema = object({
17097
17186
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17098
17187
  fetchedAt: number()
17099
17188
  });
17189
+ var NodeInferenceDeviceSchema = object({
17190
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17191
+ key: string(),
17192
+ backend: string(),
17193
+ device: string(),
17194
+ format: _enum(MODEL_FORMATS),
17195
+ /** Whether the node's live probe reports the device as usable right now. */
17196
+ available: boolean(),
17197
+ /**
17198
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17199
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17200
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17201
+ * not a balanced target). An explicit stored value always wins; a stored-only
17202
+ * (unavailable) key keeps its stored value.
17203
+ */
17204
+ enabled: boolean(),
17205
+ /** Relative balancer weight for the enabled device (default 1). */
17206
+ weight: number(),
17207
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17208
+ maxSessions: number().nullable(),
17209
+ /** Object-detection model the executor defaults to for this deviceKey. */
17210
+ defaultModelId: string(),
17211
+ /**
17212
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17213
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17214
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17215
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17216
+ * available per format; this is the stored selection that becomes the
17217
+ * default for EVERY camera landing on this accelerator.
17218
+ */
17219
+ steps: record(string(), DeviceStepConfigSchema).optional()
17220
+ });
17221
+ var NodeInferenceDevicesSchema = object({
17222
+ nodeId: string(),
17223
+ /** False when the node's platform-probe was unreachable (no live device set). */
17224
+ reachable: boolean(),
17225
+ devices: array(NodeInferenceDeviceSchema).readonly()
17226
+ });
17100
17227
  method(object({
17101
17228
  deviceId: number(),
17102
17229
  agentNodeId: string()
@@ -17106,7 +17233,13 @@ method(object({
17106
17233
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
17107
17234
  kind: "mutation",
17108
17235
  auth: "admin"
17109
- }), method(_void(), object({ migrated: number() }), {
17236
+ }), method(object({
17237
+ deviceId: number(),
17238
+ deviceKey: string()
17239
+ }), object({ success: literal(true) }), {
17240
+ kind: "mutation",
17241
+ auth: "admin"
17242
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
17110
17243
  kind: "mutation",
17111
17244
  auth: "admin"
17112
17245
  }), 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({
@@ -17140,13 +17273,7 @@ method(object({
17140
17273
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17141
17274
  nodeId: string(),
17142
17275
  settings: AgentPipelineSettingsSchema
17143
- })).readonly()), method(object({
17144
- agentNodeId: string(),
17145
- defaults: record(string(), AgentAddonConfigSchema)
17146
- }), object({ success: literal(true) }), {
17147
- kind: "mutation",
17148
- auth: "admin"
17149
- }), method(object({ agentNodeId: string() }), object({
17276
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17150
17277
  success: boolean(),
17151
17278
  removed: boolean()
17152
17279
  }), {
@@ -17178,7 +17305,18 @@ method(object({
17178
17305
  }), object({ success: literal(true) }), {
17179
17306
  kind: "mutation",
17180
17307
  auth: "admin"
17181
- }), method(object({ agentNodeId: string() }), object({
17308
+ }), method(object({
17309
+ agentNodeId: string(),
17310
+ inferenceDevices: record(string(), object({
17311
+ enabled: boolean(),
17312
+ weight: number().positive().optional(),
17313
+ maxSessions: number().int().positive().optional(),
17314
+ steps: record(string(), DeviceStepConfigSchema).optional()
17315
+ }))
17316
+ }), object({ success: literal(true) }), {
17317
+ kind: "mutation",
17318
+ auth: "admin"
17319
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17182
17320
  success: literal(true),
17183
17321
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17184
17322
  effectiveModelId: string().nullable(),
@@ -17194,9 +17332,10 @@ method(object({
17194
17332
  }), object({ success: literal(true) }), {
17195
17333
  kind: "mutation",
17196
17334
  auth: "admin"
17197
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17335
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17198
17336
  deviceId: number(),
17199
17337
  agentNodeId: string(),
17338
+ deviceKey: string(),
17200
17339
  addonId: string(),
17201
17340
  patch: CameraStepOverridePatchSchema.nullable()
17202
17341
  }), object({ success: literal(true) }), {
@@ -17233,14 +17372,13 @@ method(object({
17233
17372
  });
17234
17373
  /**
17235
17374
  * server-management — per-NODE singleton capability for a node's ROOT
17236
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17237
- * agents).
17375
+ * package lifecycle (runtime-updatable node packages).
17238
17376
  *
17239
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
17240
- * on agents) carries the whole software stack in its npm dep tree, so ONE
17241
- * version describes the node. Updates install into
17242
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
17243
- * starter (probation boot + auto-rollback to N-1).
17377
+ * Every node role runs the SAME root package (`@camstack/server`), which
17378
+ * carries the whole software stack in its npm dep tree, so ONE version
17379
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17380
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17381
+ * no auto-rollback).
17244
17382
  *
17245
17383
  * Providers:
17246
17384
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17348,7 +17486,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17348
17486
  /** Explicit target version; omitted = latest from the registry. */
17349
17487
  version: string().optional() }), ServerUpdateActionResultSchema, {
17350
17488
  kind: "mutation",
17351
- auth: "admin"
17489
+ auth: "admin",
17490
+ timeoutMs: 16 * 6e4
17352
17491
  }), method(_void(), ServerUpdateActionResultSchema, {
17353
17492
  kind: "mutation",
17354
17493
  auth: "admin"
@@ -18392,22 +18531,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18392
18531
  var RestartAddonResultSchema = unknown();
18393
18532
  var InstallPackageResultSchema = unknown();
18394
18533
  var ReloadPackagesResultSchema = unknown();
18395
- /**
18396
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18397
- * server restarts so the admin UI can react to the `restartingAt`
18398
- * timestamp (shows reconnect overlay). The transition from
18399
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18400
- * `system.restart-completed` event after the new process boots.
18401
- *
18402
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18403
- */
18404
- var UpdateFrameworkPackageResultSchema = object({
18405
- packageName: string(),
18406
- fromVersion: string(),
18407
- toVersion: string(),
18408
- /** Ms-epoch the server scheduled its self-restart. */
18409
- restartingAt: number()
18410
- });
18411
18534
  var BulkUpdateItemStatusSchema = _enum([
18412
18535
  "queued",
18413
18536
  "updating",
@@ -18535,13 +18658,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18535
18658
  }), object({ success: literal(true) }), {
18536
18659
  kind: "mutation",
18537
18660
  auth: "admin"
18538
- }), method(object({
18539
- packageName: string().min(1),
18540
- version: string().optional(),
18541
- deferRestart: boolean().optional()
18542
- }), UpdateFrameworkPackageResultSchema, {
18543
- kind: "mutation",
18544
- auth: "admin"
18545
18661
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18546
18662
  kind: "mutation",
18547
18663
  auth: "admin"
@@ -19407,10 +19523,10 @@ var TopologyCategorySchema = object({
19407
19523
  addons: array(TopologyCategoryAddonSchema).readonly()
19408
19524
  });
19409
19525
  /**
19410
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19411
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19412
- * version visibility for the Server management surface. Nullable: offline
19413
- * rows and pre-phase-2 nodes report none.
19526
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19527
+ * root package for every node role) as reported by its `registerNode`
19528
+ * manifest — version visibility for the Server management surface. Nullable:
19529
+ * offline rows and nodes that never reported one.
19414
19530
  */
19415
19531
  var TopologyRootPackageSchema = object({
19416
19532
  name: string(),
@@ -19798,17 +19914,28 @@ var PlatformScoreSchema = object({
19798
19914
  format: _enum([
19799
19915
  "onnx",
19800
19916
  "coreml",
19801
- "openvino"
19917
+ "openvino",
19918
+ "tflite"
19802
19919
  ]),
19803
19920
  score: number(),
19804
19921
  reason: string(),
19805
19922
  available: boolean()
19806
19923
  });
19924
+ var InferenceDeviceDescriptorSchema = object({
19925
+ key: string(),
19926
+ backend: string(),
19927
+ device: string(),
19928
+ format: ModelFormatSchema,
19929
+ runtime: literal("python"),
19930
+ score: number(),
19931
+ available: boolean()
19932
+ });
19807
19933
  var PlatformCapabilitiesSchema = object({
19808
19934
  hardware: HardwareInfoSchema,
19809
19935
  scores: array(PlatformScoreSchema).readonly(),
19810
19936
  bestScore: PlatformScoreSchema,
19811
- pythonPath: string().nullable()
19937
+ pythonPath: string().nullable(),
19938
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
19812
19939
  });
19813
19940
  var ModelRequirementSchema = object({
19814
19941
  modelId: string(),
@@ -20687,12 +20814,6 @@ Object.freeze({
20687
20814
  addonId: null,
20688
20815
  access: "delete"
20689
20816
  },
20690
- "addons.updateFrameworkPackage": {
20691
- capName: "addons",
20692
- capScope: "system",
20693
- addonId: null,
20694
- access: "create"
20695
- },
20696
20817
  "addons.updatePackage": {
20697
20818
  capName: "addons",
20698
20819
  capScope: "system",
@@ -23465,12 +23586,6 @@ Object.freeze({
23465
23586
  addonId: null,
23466
23587
  access: "view"
23467
23588
  },
23468
- "pipelineExecutor.reprobeEngine": {
23469
- capName: "pipeline-executor",
23470
- capScope: "system",
23471
- addonId: null,
23472
- access: "create"
23473
- },
23474
23589
  "pipelineExecutor.runAudioTest": {
23475
23590
  capName: "pipeline-executor",
23476
23591
  capScope: "system",
@@ -23621,6 +23736,12 @@ Object.freeze({
23621
23736
  addonId: null,
23622
23737
  access: "view"
23623
23738
  },
23739
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23740
+ capName: "pipeline-orchestrator",
23741
+ capScope: "system",
23742
+ addonId: null,
23743
+ access: "view"
23744
+ },
23624
23745
  "pipelineOrchestrator.getPipelineAssignment": {
23625
23746
  capName: "pipeline-orchestrator",
23626
23747
  capScope: "system",
@@ -23633,6 +23754,12 @@ Object.freeze({
23633
23754
  addonId: null,
23634
23755
  access: "view"
23635
23756
  },
23757
+ "pipelineOrchestrator.getPipelineDevicePin": {
23758
+ capName: "pipeline-orchestrator",
23759
+ capScope: "system",
23760
+ addonId: null,
23761
+ access: "view"
23762
+ },
23636
23763
  "pipelineOrchestrator.listAgentSettings": {
23637
23764
  capName: "pipeline-orchestrator",
23638
23765
  capScope: "system",
@@ -23675,19 +23802,19 @@ Object.freeze({
23675
23802
  addonId: null,
23676
23803
  access: "create"
23677
23804
  },
23678
- "pipelineOrchestrator.setAgentAddonDefaults": {
23805
+ "pipelineOrchestrator.setAgentCapabilities": {
23679
23806
  capName: "pipeline-orchestrator",
23680
23807
  capScope: "system",
23681
23808
  addonId: null,
23682
23809
  access: "create"
23683
23810
  },
23684
- "pipelineOrchestrator.setAgentCapabilities": {
23811
+ "pipelineOrchestrator.setAgentDetectWeight": {
23685
23812
  capName: "pipeline-orchestrator",
23686
23813
  capScope: "system",
23687
23814
  addonId: null,
23688
23815
  access: "create"
23689
23816
  },
23690
- "pipelineOrchestrator.setAgentDetectWeight": {
23817
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23691
23818
  capName: "pipeline-orchestrator",
23692
23819
  capScope: "system",
23693
23820
  addonId: null,
@@ -23729,6 +23856,12 @@ Object.freeze({
23729
23856
  addonId: null,
23730
23857
  access: "create"
23731
23858
  },
23859
+ "pipelineOrchestrator.setPipelineDevicePin": {
23860
+ capName: "pipeline-orchestrator",
23861
+ capScope: "system",
23862
+ addonId: null,
23863
+ access: "create"
23864
+ },
23732
23865
  "pipelineOrchestrator.unassignAudio": {
23733
23866
  capName: "pipeline-orchestrator",
23734
23867
  capScope: "system",
@@ -25281,32 +25414,6 @@ Object.freeze({
25281
25414
  "network-access": "ingress",
25282
25415
  "smtp-provider": "email"
25283
25416
  });
25284
- var frameworkSwapPackageSchema = object({
25285
- name: string(),
25286
- stagedPath: string(),
25287
- backupPath: string(),
25288
- toVersion: string(),
25289
- fromVersion: string().nullable()
25290
- });
25291
- object({
25292
- jobId: string(),
25293
- taskId: string(),
25294
- packages: array(frameworkSwapPackageSchema),
25295
- requestedAtMs: number(),
25296
- schemaVersion: literal(1)
25297
- });
25298
- object({
25299
- jobId: string(),
25300
- taskId: string(),
25301
- backups: array(object({
25302
- name: string(),
25303
- backupPath: string(),
25304
- livePath: string()
25305
- })),
25306
- appliedAtMs: number(),
25307
- bootAttempts: number(),
25308
- schemaVersion: literal(1)
25309
- });
25310
25417
  /**
25311
25418
  * Fixed-capacity ring buffer. When full, push() overwrites the oldest entry.
25312
25419
  * drain() returns up to maxCount items in FIFO order and removes them.
package/dist/index.mjs CHANGED
@@ -7080,6 +7080,17 @@ var ModelCatalogEntrySchema = object({
7080
7080
  "imagenet",
7081
7081
  "none"
7082
7082
  ]).optional(),
7083
+ /**
7084
+ * The model already applies softmax IN-GRAPH — its raw output is a
7085
+ * probability distribution, not logits. When set, the `softmax`
7086
+ * postprocessor must NOT re-apply softmax: re-softmaxing an already-normalised
7087
+ * probability vector collapses it toward uniform (top-1 score craters far
7088
+ * below its true value, making every confidence gate meaningless). Absent ⇒
7089
+ * the output is raw logits and the postprocessor applies softmax (the normal
7090
+ * case). Set on the Google AIY Birds `bird-classifier` (softmax baked into the
7091
+ * TF graph). Threaded to the Python pool via `PoolModelConfig.outputProbabilities`.
7092
+ */
7093
+ outputProbabilities: boolean().optional(),
7083
7094
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7084
7095
  /**
7085
7096
  * Per-MODEL postprocessor override. Absent ⇒ the step's own
@@ -11093,10 +11104,7 @@ var ConfigUISchemaNullableBridge = custom();
11093
11104
  var InferenceCapabilitiesBridge = custom();
11094
11105
  var ModelAvailabilityListBridge = custom();
11095
11106
  var PipelineRunResultBridge = custom();
11096
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(_void(), PipelineEngineChoiceSchema, {
11097
- kind: "mutation",
11098
- auth: "admin"
11099
- }), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11107
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
11100
11108
  modelId: string(),
11101
11109
  settings: record(string(), unknown()).readonly()
11102
11110
  }))), method(object({ steps: record(string(), object({
@@ -11156,13 +11164,33 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11156
11164
  * (inputClasses ≠ null) are skipped and served per-track via
11157
11165
  * pipelineRunner.runDetailSubtree (two-plane design).
11158
11166
  */
11159
- plane: _enum(["full", "frame"]).optional()
11167
+ plane: _enum(["full", "frame"]).optional(),
11168
+ /**
11169
+ * Inference-device selector (Phase 2 multi-device). Format
11170
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
11171
+ * Omitted ⇒ the runner's default device (current single-engine
11172
+ * behaviour). Selects WHICH device pool of the node runs the call.
11173
+ */
11174
+ deviceKey: string().optional()
11160
11175
  }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
11161
11176
  engine: PipelineEngineChoiceSchema.optional(),
11162
11177
  steps: array(PipelineStepInputSchema).min(1),
11163
11178
  frames: array(FrameInputSchema).min(1).max(255),
11164
11179
  deviceId: number().optional(),
11165
- sessionId: string().optional()
11180
+ sessionId: string().optional(),
11181
+ /**
11182
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
11183
+ * the batch to the Python pool's bench preprocess cache
11184
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
11185
+ * preprocessed ONCE and every later inference is a pure-inference cache
11186
+ * hit — the sustained-throughput run measures inference, not
11187
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
11188
+ * full preprocess every call, correct). Fresh per sustained run;
11189
+ * released via `uncacheFrame`.
11190
+ */
11191
+ frameId: number().int().nonnegative().optional(),
11192
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
11193
+ deviceKey: string().optional()
11166
11194
  }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
11167
11195
  data: _instanceof(Uint8Array),
11168
11196
  width: number().int().positive(),
@@ -11194,8 +11222,18 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
11194
11222
  * - `runtime` — main camera-serving engine (no idle TTL).
11195
11223
  * - `warm-override` — benchmark/test override held in the warm
11196
11224
  * cache; auto-disposed after the idle TTL.
11225
+ * - `device-pool` — a concurrent per-device pool (Phase 2
11226
+ * multi-device, keyed by `deviceKey`) resolved
11227
+ * via `resolveDeviceFactory`. Runs alongside the
11228
+ * `runtime` engine on a DIFFERENT accelerator
11229
+ * (NPU / iGPU / Coral) — this is how the
11230
+ * Engines tab shows all pools running at once.
11197
11231
  */
11198
- kind: _enum(["runtime", "warm-override"]),
11232
+ kind: _enum([
11233
+ "runtime",
11234
+ "warm-override",
11235
+ "device-pool"
11236
+ ]),
11199
11237
  /** Native pid of the underlying Python pool (null when no pool). */
11200
11238
  poolPid: number().nullable(),
11201
11239
  /** ms since this factory was last used (null when not warm-tracked). */
@@ -11297,7 +11335,21 @@ var NativeCropResultSchema = object({
11297
11335
  /** Packed rgb (24-bit) pixels of the crop. */
11298
11336
  bytes: _instanceof(Uint8Array),
11299
11337
  width: number().int().positive(),
11300
- height: number().int().positive()
11338
+ height: number().int().positive(),
11339
+ /**
11340
+ * Which source served this crop, so a quality-sensitive consumer (the native
11341
+ * `keyFrame`) can reject a degraded fallback:
11342
+ * - `native` — cut from the decode worker's retained NATIVE surface (the
11343
+ * quality path).
11344
+ * - `ram-fullframe` — the native surface MISSED but the request was full-frame,
11345
+ * so the ≤640 RAM `RetainedFrameStore` served it (honest lower-res; legit for
11346
+ * the blank-frame guard / 640-snapshot resolve, NOT for the clean keyFrame).
11347
+ *
11348
+ * OPTIONAL: a pre-tier runner (version skew) omits it — an ABSENT `tier` MUST be
11349
+ * treated as `native` (accepted) by every consumer so mixed-version clusters
11350
+ * keep the pre-tier behaviour. An ROI miss returns `null` (no tier at all).
11351
+ */
11352
+ tier: _enum(["native", "ram-fullframe"]).optional()
11301
11353
  });
11302
11354
  /** Parent detection context passed to `runDetailSubtree` — the crop's
11303
11355
  * originating detection, in FRAME-space coordinates. Reuses
@@ -11537,7 +11589,14 @@ var RunnerCameraConfigSchema = object({
11537
11589
  * camera's detect node differs from its source-owner (P2d, gated by the
11538
11590
  * `remoteSourcingNodes` rollout setting).
11539
11591
  */
11540
- frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" })
11592
+ frameSource: RunnerFrameSourceSchema.default({ kind: "local-broker" }),
11593
+ /**
11594
+ * Inference-device selector for this camera's sessions (Phase 2 multi-device).
11595
+ * Format `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`);
11596
+ * omitted ⇒ the runner's default device. The engine itself stays node-local —
11597
+ * this only selects WHICH device pool of that node runs the session.
11598
+ */
11599
+ deviceKey: string().optional()
11541
11600
  });
11542
11601
  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;
11543
11602
  /**
@@ -11558,6 +11617,19 @@ var RunnerLocalLoadSchema = object({
11558
11617
  avgInferenceTimeMs: number(),
11559
11618
  /** Total queue depth across motion + detection queues. */
11560
11619
  queueDepthTotal: number(),
11620
+ /**
11621
+ * Per-inference-device live load (multi-device C4). One entry per deviceKey
11622
+ * this runner currently has attached cameras on, so the orchestrator's second
11623
+ * `balance()` pass (over a node's devices) weights on real per-pool session
11624
+ * counts. Empty on single-device / pre-multi-device runners. `queueDepthTotal`
11625
+ * per device is 0 until the pool backlog gets a public accessor (follow-up).
11626
+ */
11627
+ devices: array(object({
11628
+ deviceKey: string(),
11629
+ backend: string(),
11630
+ attachedCameras: number(),
11631
+ queueDepthTotal: number()
11632
+ })).default([]),
11561
11633
  /** Hardware capability flags reported by this node. */
11562
11634
  hardware: object({
11563
11635
  hasGpu: boolean(),
@@ -13033,6 +13105,8 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
13033
13105
  kind: "mutation",
13034
13106
  auth: "admin"
13035
13107
  });
13108
+ 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;
13109
+ new Set(Object.values(DeviceType));
13036
13110
  /**
13037
13111
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
13038
13112
  * surface that admin-ui consumes through `useAddonPagesListPages()`.
@@ -14736,7 +14810,8 @@ var LinkedDeviceSchema = object({
14736
14810
  deviceId: number(),
14737
14811
  name: string(),
14738
14812
  location: string().nullable(),
14739
- features: array(string())
14813
+ features: array(string()),
14814
+ producesTrackedEvents: boolean().optional()
14740
14815
  });
14741
14816
  var SavedDeviceRowSchema = object({
14742
14817
  /** Numeric id reserved at allocateDeviceId time. */
@@ -16366,6 +16441,7 @@ var TrackSchema = object({
16366
16441
  deviceId: number(),
16367
16442
  className: string(),
16368
16443
  label: string().optional(),
16444
+ producingDeviceName: string().optional(),
16369
16445
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
16370
16446
  source: TrackSourceSchema.optional(),
16371
16447
  firstSeen: number(),
@@ -16503,7 +16579,8 @@ var MediaFileKindEnum = _enum([
16503
16579
  "fullFrameBoxed",
16504
16580
  "faceCrop",
16505
16581
  "plateCrop",
16506
- "keyFrame"
16582
+ "keyFrame",
16583
+ "keyFrameSmall"
16507
16584
  ]);
16508
16585
  var MediaFileSchema = object({
16509
16586
  key: string(),
@@ -16832,13 +16909,11 @@ var PipelineTemplateSchema = object({
16832
16909
  createdAt: string(),
16833
16910
  updatedAt: string()
16834
16911
  });
16835
- var AgentAddonConfigSchema = object({
16836
- enabled: boolean(),
16912
+ var DeviceStepConfigSchema = object({
16837
16913
  modelId: string().optional(),
16838
- settings: record(string(), unknown()).readonly()
16914
+ settings: record(string(), unknown()).optional()
16839
16915
  });
16840
16916
  var AgentPipelineSettingsSchema = object({
16841
- addonDefaults: record(string(), AgentAddonConfigSchema).readonly(),
16842
16917
  maxCameras: number().int().nonnegative().nullable().default(null),
16843
16918
  /** Per-node detection weight (relative share for the quota balancer). */
16844
16919
  detectWeight: number().positive().optional(),
@@ -16862,7 +16937,22 @@ var AgentPipelineSettingsSchema = object({
16862
16937
  * it already uses to reach the hub). Set this only when the auto-detected
16863
16938
  * address is wrong (multi-homed host, NAT, custom interface).
16864
16939
  */
16865
- reachableHost: string().optional()
16940
+ reachableHost: string().optional(),
16941
+ /**
16942
+ * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
16943
+ * weight, steps}. Absent / all-disabled ⇒ the node's single default
16944
+ * accelerator (safe default); two+ enabled ⇒ the dispatcher balances
16945
+ * detection sessions across them so they run CONCURRENTLY. `steps` is the
16946
+ * per-(node,device) BASE provisioning (`stepId → {modelId?, settings?}`) —
16947
+ * the default model/settings for every camera landing on that accelerator;
16948
+ * a stepId absent ⇒ the step uses that device's format default.
16949
+ */
16950
+ inferenceDevices: record(string(), object({
16951
+ enabled: boolean(),
16952
+ weight: number().positive().optional(),
16953
+ maxSessions: number().int().positive().optional(),
16954
+ steps: record(string(), DeviceStepConfigSchema).optional()
16955
+ })).optional()
16866
16956
  });
16867
16957
  var CameraPipelineForAgentSchema = object({
16868
16958
  steps: array(PipelineStepInputSchema).readonly(),
@@ -16872,14 +16962,13 @@ var CameraPipelineForAgentSchema = object({
16872
16962
  }).nullable()
16873
16963
  });
16874
16964
  var CameraStepOverridePatchSchema = object({
16875
- enabled: boolean().optional(),
16876
16965
  modelId: string().optional(),
16877
16966
  settings: record(string(), unknown()).readonly().optional()
16878
16967
  });
16879
16968
  var CameraPipelineSettingsSchema = object({
16880
16969
  pinnedAgentNodeId: string().optional(),
16881
16970
  stepToggles: record(string(), boolean()).optional(),
16882
- stepOverridesByAgent: record(string(), record(string(), CameraStepOverridePatchSchema)).optional(),
16971
+ stepOverridesByDevice: record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).optional(),
16883
16972
  pipelineByAgent: record(string(), CameraPipelineForAgentSchema).optional()
16884
16973
  });
16885
16974
  /**
@@ -17093,6 +17182,44 @@ var CameraStatusSchema = object({
17093
17182
  /** Unix timestamp (ms) when this snapshot was composed server-side. */
17094
17183
  fetchedAt: number()
17095
17184
  });
17185
+ var NodeInferenceDeviceSchema = object({
17186
+ /** Stable per-node device key, e.g. `openvino:npu`, `edgetpu:usb`, `cpu`. */
17187
+ key: string(),
17188
+ backend: string(),
17189
+ device: string(),
17190
+ format: _enum(MODEL_FORMATS),
17191
+ /** Whether the node's live probe reports the device as usable right now. */
17192
+ available: boolean(),
17193
+ /**
17194
+ * Whether this device participates in dispatch. AUTO default (spec C2):
17195
+ * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
17196
+ * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
17197
+ * not a balanced target). An explicit stored value always wins; a stored-only
17198
+ * (unavailable) key keeps its stored value.
17199
+ */
17200
+ enabled: boolean(),
17201
+ /** Relative balancer weight for the enabled device (default 1). */
17202
+ weight: number(),
17203
+ /** Per-device concurrent-session cap; null = unlimited (multi-device C4). */
17204
+ maxSessions: number().nullable(),
17205
+ /** Object-detection model the executor defaults to for this deviceKey. */
17206
+ defaultModelId: string(),
17207
+ /**
17208
+ * Per-(node,device) BASE provisioning (C7.2/C7.4) — the RAW stored
17209
+ * `stepId → {modelId?, settings?}` map from `inferenceDevices[key].steps`.
17210
+ * Absent/empty ⇒ no base (every step uses its device format default). The
17211
+ * UI cross-references `pipelineExecutor.getSchema` for the models actually
17212
+ * available per format; this is the stored selection that becomes the
17213
+ * default for EVERY camera landing on this accelerator.
17214
+ */
17215
+ steps: record(string(), DeviceStepConfigSchema).optional()
17216
+ });
17217
+ var NodeInferenceDevicesSchema = object({
17218
+ nodeId: string(),
17219
+ /** False when the node's platform-probe was unreachable (no live device set). */
17220
+ reachable: boolean(),
17221
+ devices: array(NodeInferenceDeviceSchema).readonly()
17222
+ });
17096
17223
  method(object({
17097
17224
  deviceId: number(),
17098
17225
  agentNodeId: string()
@@ -17102,7 +17229,13 @@ method(object({
17102
17229
  }), method(object({ deviceId: number() }), object({ success: literal(true) }), {
17103
17230
  kind: "mutation",
17104
17231
  auth: "admin"
17105
- }), method(_void(), object({ migrated: number() }), {
17232
+ }), method(object({
17233
+ deviceId: number(),
17234
+ deviceKey: string()
17235
+ }), object({ success: literal(true) }), {
17236
+ kind: "mutation",
17237
+ auth: "admin"
17238
+ }), method(object({ deviceId: number() }), object({ deviceKey: string().nullable() })), method(_void(), object({ migrated: number() }), {
17106
17239
  kind: "mutation",
17107
17240
  auth: "admin"
17108
17241
  }), 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({
@@ -17136,13 +17269,7 @@ method(object({
17136
17269
  }))), method(object({ agentNodeId: string() }), AgentPipelineSettingsSchema.nullable()), method(_void(), array(object({
17137
17270
  nodeId: string(),
17138
17271
  settings: AgentPipelineSettingsSchema
17139
- })).readonly()), method(object({
17140
- agentNodeId: string(),
17141
- defaults: record(string(), AgentAddonConfigSchema)
17142
- }), object({ success: literal(true) }), {
17143
- kind: "mutation",
17144
- auth: "admin"
17145
- }), method(object({ agentNodeId: string() }), object({
17272
+ })).readonly()), method(object({ agentNodeId: string() }), object({
17146
17273
  success: boolean(),
17147
17274
  removed: boolean()
17148
17275
  }), {
@@ -17174,7 +17301,18 @@ method(object({
17174
17301
  }), object({ success: literal(true) }), {
17175
17302
  kind: "mutation",
17176
17303
  auth: "admin"
17177
- }), method(object({ agentNodeId: string() }), object({
17304
+ }), method(object({
17305
+ agentNodeId: string(),
17306
+ inferenceDevices: record(string(), object({
17307
+ enabled: boolean(),
17308
+ weight: number().positive().optional(),
17309
+ maxSessions: number().int().positive().optional(),
17310
+ steps: record(string(), DeviceStepConfigSchema).optional()
17311
+ }))
17312
+ }), object({ success: literal(true) }), {
17313
+ kind: "mutation",
17314
+ auth: "admin"
17315
+ }), method(object({ nodeId: string() }), NodeInferenceDevicesSchema), method(object({ agentNodeId: string() }), object({
17178
17316
  success: literal(true),
17179
17317
  /** Hardware-aware default detection model now in effect on the node (null when unresolvable). */
17180
17318
  effectiveModelId: string().nullable(),
@@ -17190,9 +17328,10 @@ method(object({
17190
17328
  }), object({ success: literal(true) }), {
17191
17329
  kind: "mutation",
17192
17330
  auth: "admin"
17193
- }), method(object({ deviceId: number() }), record(string(), record(string(), CameraStepOverridePatchSchema)).nullable()), method(object({
17331
+ }), method(object({ deviceId: number() }), record(string(), record(string(), record(string(), CameraStepOverridePatchSchema))).nullable()), method(object({
17194
17332
  deviceId: number(),
17195
17333
  agentNodeId: string(),
17334
+ deviceKey: string(),
17196
17335
  addonId: string(),
17197
17336
  patch: CameraStepOverridePatchSchema.nullable()
17198
17337
  }), object({ success: literal(true) }), {
@@ -17229,14 +17368,13 @@ method(object({
17229
17368
  });
17230
17369
  /**
17231
17370
  * server-management — per-NODE singleton capability for a node's ROOT
17232
- * package lifecycle (runtime-updatable node packages, phase 2: hub + docker
17233
- * agents).
17371
+ * package lifecycle (runtime-updatable node packages).
17234
17372
  *
17235
- * Each node's root package (`@camstack/server` on the hub, `@camstack/agent`
17236
- * on agents) carries the whole software stack in its npm dep tree, so ONE
17237
- * version describes the node. Updates install into
17238
- * `<dataDir>/server-root/versions/<v>/` and apply on restart via the baked
17239
- * starter (probation boot + auto-rollback to N-1).
17373
+ * Every node role runs the SAME root package (`@camstack/server`), which
17374
+ * carries the whole software stack in its npm dep tree, so ONE version
17375
+ * describes the node. Updates stage into `<dataDir>/server-root/` and apply
17376
+ * on restart via the baked starter (single-copy in-place swap — no probation,
17377
+ * no auto-rollback).
17240
17378
  *
17241
17379
  * Providers:
17242
17380
  * - HUB: `ServerUpdateService` behind the `server-provided` mount
@@ -17344,7 +17482,8 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
17344
17482
  /** Explicit target version; omitted = latest from the registry. */
17345
17483
  version: string().optional() }), ServerUpdateActionResultSchema, {
17346
17484
  kind: "mutation",
17347
- auth: "admin"
17485
+ auth: "admin",
17486
+ timeoutMs: 16 * 6e4
17348
17487
  }), method(_void(), ServerUpdateActionResultSchema, {
17349
17488
  kind: "mutation",
17350
17489
  auth: "admin"
@@ -18388,22 +18527,6 @@ var AddonAutoUpdateSchema = ChannelWithInheritSchema;
18388
18527
  var RestartAddonResultSchema = unknown();
18389
18528
  var InstallPackageResultSchema = unknown();
18390
18529
  var ReloadPackagesResultSchema = unknown();
18391
- /**
18392
- * Result of `updateFrameworkPackage`. The cap method returns BEFORE the
18393
- * server restarts so the admin UI can react to the `restartingAt`
18394
- * timestamp (shows reconnect overlay). The transition from
18395
- * `fromVersion` to `toVersion` will be confirmed by a subsequent
18396
- * `system.restart-completed` event after the new process boots.
18397
- *
18398
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
18399
- */
18400
- var UpdateFrameworkPackageResultSchema = object({
18401
- packageName: string(),
18402
- fromVersion: string(),
18403
- toVersion: string(),
18404
- /** Ms-epoch the server scheduled its self-restart. */
18405
- restartingAt: number()
18406
- });
18407
18530
  var BulkUpdateItemStatusSchema = _enum([
18408
18531
  "queued",
18409
18532
  "updating",
@@ -18531,13 +18654,6 @@ method(_void(), array(AddonListItemSchema).readonly()), method(object({
18531
18654
  }), object({ success: literal(true) }), {
18532
18655
  kind: "mutation",
18533
18656
  auth: "admin"
18534
- }), method(object({
18535
- packageName: string().min(1),
18536
- version: string().optional(),
18537
- deferRestart: boolean().optional()
18538
- }), UpdateFrameworkPackageResultSchema, {
18539
- kind: "mutation",
18540
- auth: "admin"
18541
18657
  }), method(object({ name: string() }), array(PackageVersionInfoSchema).readonly()), method(object({ addonId: string() }), RestartAddonResultSchema, {
18542
18658
  kind: "mutation",
18543
18659
  auth: "admin"
@@ -19403,10 +19519,10 @@ var TopologyCategorySchema = object({
19403
19519
  addons: array(TopologyCategoryAddonSchema).readonly()
19404
19520
  });
19405
19521
  /**
19406
- * The node's runtime-updatable ROOT package (`@camstack/server` on the hub,
19407
- * `@camstack/agent` on agents) as reported by its `registerNode` manifest —
19408
- * version visibility for the Server management surface. Nullable: offline
19409
- * rows and pre-phase-2 nodes report none.
19522
+ * The node's runtime-updatable ROOT package (`@camstack/server` the single
19523
+ * root package for every node role) as reported by its `registerNode`
19524
+ * manifest — version visibility for the Server management surface. Nullable:
19525
+ * offline rows and nodes that never reported one.
19410
19526
  */
19411
19527
  var TopologyRootPackageSchema = object({
19412
19528
  name: string(),
@@ -19794,17 +19910,28 @@ var PlatformScoreSchema = object({
19794
19910
  format: _enum([
19795
19911
  "onnx",
19796
19912
  "coreml",
19797
- "openvino"
19913
+ "openvino",
19914
+ "tflite"
19798
19915
  ]),
19799
19916
  score: number(),
19800
19917
  reason: string(),
19801
19918
  available: boolean()
19802
19919
  });
19920
+ var InferenceDeviceDescriptorSchema = object({
19921
+ key: string(),
19922
+ backend: string(),
19923
+ device: string(),
19924
+ format: ModelFormatSchema,
19925
+ runtime: literal("python"),
19926
+ score: number(),
19927
+ available: boolean()
19928
+ });
19803
19929
  var PlatformCapabilitiesSchema = object({
19804
19930
  hardware: HardwareInfoSchema,
19805
19931
  scores: array(PlatformScoreSchema).readonly(),
19806
19932
  bestScore: PlatformScoreSchema,
19807
- pythonPath: string().nullable()
19933
+ pythonPath: string().nullable(),
19934
+ devices: array(InferenceDeviceDescriptorSchema).readonly()
19808
19935
  });
19809
19936
  var ModelRequirementSchema = object({
19810
19937
  modelId: string(),
@@ -20683,12 +20810,6 @@ Object.freeze({
20683
20810
  addonId: null,
20684
20811
  access: "delete"
20685
20812
  },
20686
- "addons.updateFrameworkPackage": {
20687
- capName: "addons",
20688
- capScope: "system",
20689
- addonId: null,
20690
- access: "create"
20691
- },
20692
20813
  "addons.updatePackage": {
20693
20814
  capName: "addons",
20694
20815
  capScope: "system",
@@ -23461,12 +23582,6 @@ Object.freeze({
23461
23582
  addonId: null,
23462
23583
  access: "view"
23463
23584
  },
23464
- "pipelineExecutor.reprobeEngine": {
23465
- capName: "pipeline-executor",
23466
- capScope: "system",
23467
- addonId: null,
23468
- access: "create"
23469
- },
23470
23585
  "pipelineExecutor.runAudioTest": {
23471
23586
  capName: "pipeline-executor",
23472
23587
  capScope: "system",
@@ -23617,6 +23732,12 @@ Object.freeze({
23617
23732
  addonId: null,
23618
23733
  access: "view"
23619
23734
  },
23735
+ "pipelineOrchestrator.getNodeInferenceDevices": {
23736
+ capName: "pipeline-orchestrator",
23737
+ capScope: "system",
23738
+ addonId: null,
23739
+ access: "view"
23740
+ },
23620
23741
  "pipelineOrchestrator.getPipelineAssignment": {
23621
23742
  capName: "pipeline-orchestrator",
23622
23743
  capScope: "system",
@@ -23629,6 +23750,12 @@ Object.freeze({
23629
23750
  addonId: null,
23630
23751
  access: "view"
23631
23752
  },
23753
+ "pipelineOrchestrator.getPipelineDevicePin": {
23754
+ capName: "pipeline-orchestrator",
23755
+ capScope: "system",
23756
+ addonId: null,
23757
+ access: "view"
23758
+ },
23632
23759
  "pipelineOrchestrator.listAgentSettings": {
23633
23760
  capName: "pipeline-orchestrator",
23634
23761
  capScope: "system",
@@ -23671,19 +23798,19 @@ Object.freeze({
23671
23798
  addonId: null,
23672
23799
  access: "create"
23673
23800
  },
23674
- "pipelineOrchestrator.setAgentAddonDefaults": {
23801
+ "pipelineOrchestrator.setAgentCapabilities": {
23675
23802
  capName: "pipeline-orchestrator",
23676
23803
  capScope: "system",
23677
23804
  addonId: null,
23678
23805
  access: "create"
23679
23806
  },
23680
- "pipelineOrchestrator.setAgentCapabilities": {
23807
+ "pipelineOrchestrator.setAgentDetectWeight": {
23681
23808
  capName: "pipeline-orchestrator",
23682
23809
  capScope: "system",
23683
23810
  addonId: null,
23684
23811
  access: "create"
23685
23812
  },
23686
- "pipelineOrchestrator.setAgentDetectWeight": {
23813
+ "pipelineOrchestrator.setAgentInferenceDevices": {
23687
23814
  capName: "pipeline-orchestrator",
23688
23815
  capScope: "system",
23689
23816
  addonId: null,
@@ -23725,6 +23852,12 @@ Object.freeze({
23725
23852
  addonId: null,
23726
23853
  access: "create"
23727
23854
  },
23855
+ "pipelineOrchestrator.setPipelineDevicePin": {
23856
+ capName: "pipeline-orchestrator",
23857
+ capScope: "system",
23858
+ addonId: null,
23859
+ access: "create"
23860
+ },
23728
23861
  "pipelineOrchestrator.unassignAudio": {
23729
23862
  capName: "pipeline-orchestrator",
23730
23863
  capScope: "system",
@@ -25277,32 +25410,6 @@ Object.freeze({
25277
25410
  "network-access": "ingress",
25278
25411
  "smtp-provider": "email"
25279
25412
  });
25280
- var frameworkSwapPackageSchema = object({
25281
- name: string(),
25282
- stagedPath: string(),
25283
- backupPath: string(),
25284
- toVersion: string(),
25285
- fromVersion: string().nullable()
25286
- });
25287
- object({
25288
- jobId: string(),
25289
- taskId: string(),
25290
- packages: array(frameworkSwapPackageSchema),
25291
- requestedAtMs: number(),
25292
- schemaVersion: literal(1)
25293
- });
25294
- object({
25295
- jobId: string(),
25296
- taskId: string(),
25297
- backups: array(object({
25298
- name: string(),
25299
- backupPath: string(),
25300
- livePath: string()
25301
- })),
25302
- appliedAtMs: number(),
25303
- bootAttempts: number(),
25304
- schemaVersion: literal(1)
25305
- });
25306
25413
  /**
25307
25414
  * Fixed-capacity ring buffer. When full, push() overwrites the oldest entry.
25308
25415
  * drain() returns up to maxCount items in FIFO order and removes them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-decoder-nodeav",
3
- "version": "1.1.18",
3
+ "version": "1.2.1",
4
4
  "description": "Standalone in-process node-av decoder addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",