@camstack/addon-provider-homematic 1.1.21 → 1.1.23

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.
package/dist/addon.mjs CHANGED
@@ -9,6 +9,550 @@ import { join } from "path";
9
9
  var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
10
10
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
11
11
  //#endregion
12
+ //#region ../types/dist/event-category-H4AVePnn.mjs
13
+ var EventCategory = /* @__PURE__ */ function(EventCategory) {
14
+ EventCategory["SystemBoot"] = "system.boot";
15
+ EventCategory["SystemAddonsReady"] = "system.addons-ready";
16
+ EventCategory["SystemRestarting"] = "system.restarting";
17
+ /**
18
+ * Fired exactly once after the hub finishes booting following a
19
+ * restart that was triggered by `RestartCoordinator` (today: framework
20
+ * live-update). Payload is the marker that was on disk at boot
21
+ * (`PendingRestartMarkerPayload`); admin UI listens for it to display a
22
+ * success toast describing what changed.
23
+ *
24
+ * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
25
+ */
26
+ EventCategory["SystemRestartCompleted"] = "system.restart-completed";
27
+ /**
28
+ * Readiness transition for a capability provider. Every producer emits
29
+ * this event on `onInitialize` completion, `onDestroy`, and
30
+ * `$node.reconnect`; every consumer that needs to gate on a cross-process
31
+ * cap subscribes via the kernel's readiness module (`awaitReady` /
32
+ * `onReadyState`) instead of polling. Payload is
33
+ * `SystemReadyStatePayload` — see event-bus.ts.
34
+ */
35
+ EventCategory["SystemReadyState"] = "system.ready-state";
36
+ EventCategory["AddonStarted"] = "addon.started";
37
+ EventCategory["AddonStopped"] = "addon.stopped";
38
+ EventCategory["AddonRestarted"] = "addon.restarted";
39
+ EventCategory["AddonUpdated"] = "addon.updated";
40
+ EventCategory["AddonInstalled"] = "addon.installed";
41
+ EventCategory["AddonUninstalled"] = "addon.uninstalled";
42
+ EventCategory["AddonCrashed"] = "addon.crashed";
43
+ EventCategory["AddonError"] = "addon.error";
44
+ EventCategory["AddonPageReady"] = "addon.page-ready";
45
+ EventCategory["AddonWidgetReady"] = "addon.widget-ready";
46
+ /**
47
+ * Addon failed to load (import or initialize). Emitted by the kernel's
48
+ * AddonHealthMonitor only AFTER the boot grace period ends — failures
49
+ * during the first 5 minutes are silently retried without alerting
50
+ * (slow-starting addons must have time to come up). Post-grace, this
51
+ * event is emitted exactly once per failure-streak; AlertCenter
52
+ * consumes it to create a persistent operator-visible alert.
53
+ *
54
+ * Payload: `{ packageName, addonId?, error: { message, stack }, retryCount, nextRetryAt }`.
55
+ */
56
+ EventCategory["AddonLoadFailed"] = "addon.load-failed";
57
+ /**
58
+ * Addon recovered from a previous failure. Emitted when an addon
59
+ * transitions from `failed` back to `healthy` (typically via the
60
+ * monitor's auto-retry loop, or after manual `addons.retryLoad`).
61
+ * AlertCenter dismisses the corresponding `AddonLoadFailed` alert
62
+ * on this event.
63
+ */
64
+ EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
65
+ /**
66
+ * Monitor scheduled the next retry for a failed addon. Transient —
67
+ * surfaced to the UI for live-updating the "next retry in Ns"
68
+ * countdown on the Addons page row, NOT persisted as an alert.
69
+ */
70
+ EventCategory["AddonRetryScheduled"] = "addon.retry-scheduled";
71
+ /**
72
+ * Monitor is attempting to reload a failed addon NOW. UI uses this
73
+ * to show a spinner during the retry attempt. Same transient nature
74
+ * as AddonRetryScheduled.
75
+ */
76
+ EventCategory["AddonRetryAttempting"] = "addon.retry-attempting";
77
+ EventCategory["DeviceRegistered"] = "device.registered";
78
+ EventCategory["DeviceUnregistered"] = "device.unregistered";
79
+ EventCategory["DeviceEnabled"] = "device.enabled";
80
+ EventCategory["DeviceDisabled"] = "device.disabled";
81
+ EventCategory["DeviceSettingsUpdated"] = "device.settings-updated";
82
+ /**
83
+ * Emitted when the set of native capability providers bound to a device
84
+ * changes — e.g. an addon registers a new native cap via
85
+ * `DeviceContext.registerNativeCap`, or all native bindings for a device
86
+ * are cleared on removal. Hub consumers re-resolve device-proxy routes
87
+ * when this fires.
88
+ */
89
+ EventCategory["DeviceBindingsChanged"] = "device.bindings-changed";
90
+ /**
91
+ * Emitted when the operator-organisational meta surface changes
92
+ * (`name` / `location` / `disabled`). Payload: `{deviceId, field,
93
+ * value}`. Live consumers (UI device list, alert center) react
94
+ * without polling. Distinct from `DeviceSettingsUpdated` which
95
+ * fires on hardware-config changes (host/port/credentials/etc).
96
+ */
97
+ EventCategory["DeviceMetaChanged"] = "device.meta-changed";
98
+ /**
99
+ * Emitted by `BaseDevice.updateSourceInfo()` after a successful patch
100
+ * to the device's upstream-system identity / rendering envelope. The
101
+ * full new SourceInfo travels in the payload so cross-process listeners
102
+ * (UI, export adapters) don't need to re-read the meta blob.
103
+ *
104
+ * Payload: `{deviceId, sourceInfo}`.
105
+ *
106
+ * Distinct from `DeviceMetaChanged` which covers the operator-edited
107
+ * surface (`name` / `location` / `disabled`). The two share the
108
+ * persistence layer (both ride on `device-manager.setMetadata` for
109
+ * SourceInfo, or `setName`/`setLocation`/`setDisabled` for the meta
110
+ * fields) but consumers care about different subsets.
111
+ */
112
+ EventCategory["DeviceSourceInfoChanged"] = "device.source-info-changed";
113
+ /**
114
+ * Emitted by DeviceStreamWiringService after a successful
115
+ * `stream-broker.registerDeviceStreams` call. Payload includes
116
+ * deviceId — consumers look up the full registered device info via
117
+ * `brokerManager.getRegisteredDevice(deviceId)`.
118
+ *
119
+ * Replaces the legacy `StreamRouterService.onDeviceRegistered` callback.
120
+ */
121
+ EventCategory["DeviceStreamsRegistered"] = "device.streams-registered";
122
+ /**
123
+ * Device-level "fully provisioned" signal — the EXPORT trigger. Emitted once
124
+ * a device's persisted export-relevant shape (its `DeviceFeature` set) is
125
+ * established or changes, carrying `{deviceId, fingerprint, generation}`.
126
+ * Export adapters (Alexa / HAP) react to THIS instead of the chatty per-cap
127
+ * `DeviceBindingsChanged`, turning a boot's incomplete→complete trickle into
128
+ * one clean delta. `fingerprint` is `canonicalDeviceFingerprint(shape)`.
129
+ *
130
+ * Spec: docs/superpowers/specs/2026-06-01-alexa-hap-export-reconciler-design.md
131
+ */
132
+ EventCategory["DeviceProvisioned"] = "device.provisioned";
133
+ /**
134
+ * Fires once when a device's initial feature-probe completes successfully
135
+ * (lastProbedAt 0→>0); exported shape is now stable. Telemetry (may be
136
+ * dropped) — consumers MUST also gate on the device record's `probed` flag.
137
+ */
138
+ EventCategory["DeviceReady"] = "device.ready";
139
+ EventCategory["IntegrationEnabled"] = "integration.enabled";
140
+ EventCategory["IntegrationDisabled"] = "integration.disabled";
141
+ EventCategory["IntegrationDeleted"] = "integration.deleted";
142
+ /** Emitted when a broker connection's status flips (connected /
143
+ * disconnected / error). Carries `{ brokerId, status, error? }`. */
144
+ EventCategory["BrokerStatusChanged"] = "broker.status-changed";
145
+ /** Emitted for every subscription-matched message routed by a
146
+ * broker provider. Carries `{ brokerId, subscriptionId, key, payload }`.
147
+ * Consumers filter by `brokerId` + `subscriptionId` in the handler. */
148
+ EventCategory["BrokerMessage"] = "broker.message";
149
+ EventCategory["ProviderStarted"] = "provider.started";
150
+ EventCategory["ProviderStopped"] = "provider.stopped";
151
+ EventCategory["ProcessCrashed"] = "process.crashed";
152
+ EventCategory["ProcessRestartScheduled"] = "process.restart_scheduled";
153
+ EventCategory["ProcessRestarted"] = "process.restarted";
154
+ EventCategory["RecordingStarted"] = "recording.started";
155
+ EventCategory["RecordingStopped"] = "recording.stopped";
156
+ EventCategory["RecordingError"] = "recording.error";
157
+ EventCategory["RecordingHealthDegraded"] = "recording.health.degraded";
158
+ EventCategory["RecordingStorageCritical"] = "recording.storage.critical";
159
+ EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
160
+ EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
161
+ EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
162
+ /** Runner-sampled scrub thumbnail (~1/5 s/camera). Telemetry (D8): a lost
163
+ * thumb is a scrub gap the recorder's keyframe backfill covers. */
164
+ EventCategory["RecordingThumbSampled"] = "recording.thumb-sampled";
165
+ EventCategory["DetectionEvent"] = "detection.event";
166
+ EventCategory["SessionTrackNew"] = "session.track.new";
167
+ EventCategory["SessionTrackExpired"] = "session.track.expired";
168
+ EventCategory["BenchmarkProgress"] = "benchmark.progress";
169
+ EventCategory["PlatformProbePhase"] = "platform-probe.phase";
170
+ EventCategory["PipelineProgress"] = "pipeline.progress";
171
+ /** Per-frame execution trace emitted by the pipeline executor for live observability. */
172
+ EventCategory["PipelineTrace"] = "pipeline.trace";
173
+ /**
174
+ * Raw inference output emitted by `addon-pipeline-runner` after running the
175
+ * detection pipeline on a frame. Carries the `FrameResult` (with
176
+ * `detections[]`, `width`/`height`, timing debug) — never the frame
177
+ * buffer itself. Hub-side consumers (analysis pipeline, class filters,
178
+ * notifications) subscribe to this and re-emit `detection.result` after
179
+ * post-processing.
180
+ */
181
+ EventCategory["PipelineInferenceResult"] = "pipeline.inference-result";
182
+ /**
183
+ * Camera lifecycle event emitted by `addon-pipeline-orchestrator` when it
184
+ * assigns or unassigns a camera to/from an agent. Carries no frame data;
185
+ * pure observability for UI dashboards and metrics consumers.
186
+ */
187
+ EventCategory["PipelineCameraAssigned"] = "pipeline.camera-assigned";
188
+ EventCategory["PipelineCameraUnassigned"] = "pipeline.camera-unassigned";
189
+ /**
190
+ * Per-camera pipeline config was mutated by the orchestrator
191
+ * (3-level settings change via `setAgentAddonDefaults` /
192
+ * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
193
+ * pipeline-scoped `applyDeviceSettingsPatch`). Orchestrator
194
+ * subscribes to its own emission to hot-reload the assigned runner
195
+ * via `attachCamera` so the next frame executes against the new
196
+ * engine/steps without waiting for a rebalance or the next
197
+ * `DeviceStreamsRegistered` cycle.
198
+ */
199
+ EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
200
+ /**
201
+ * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
202
+ * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
203
+ * capabilities — at boot, on agent online/offline, and on an ingest-node
204
+ * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
205
+ * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
206
+ * cross-process `getIngestOwner` query (push the authority's decision instead
207
+ * of polling it on the hot path). Idempotent state — re-emitted on every
208
+ * topology change, so a dropped event self-heals on the next one (plus the
209
+ * broker's long backstop reconcile query).
210
+ */
211
+ EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
212
+ /**
213
+ * Periodic snapshot of per-node pipeline-runner load
214
+ * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
215
+ * subscribe instead of polling `pipelineRunner.getLocalLoad`.
216
+ * `nodeId` carried in the payload + on `event.source.nodeId`.
217
+ */
218
+ EventCategory["PipelineRunnerLoadSnapshot"] = "pipeline.runner-load-snapshot";
219
+ /**
220
+ * Periodic snapshot of per-camera pipeline metrics (`CameraMetrics`
221
+ * + `deviceId` + `nodeId`). Emitted ~1Hz by every runner for each
222
+ * attached camera. UI subscribes to drive overlay phase / fps /
223
+ * inference time without polling `getCameraMetrics`.
224
+ */
225
+ EventCategory["PipelineCameraMetricsSnapshot"] = "pipeline.camera-metrics-snapshot";
226
+ /**
227
+ * Periodic snapshot of stream-broker per-broker statistics (input
228
+ * fps, decoded fps, bitrate, codec). Emitted ~1Hz by every
229
+ * stream-broker process for each active broker so the UI can drive
230
+ * the Stream / Cluster dashboards without polling
231
+ * `streamBroker.listAllProfileSlots` and friends.
232
+ */
233
+ EventCategory["StreamBrokerMetricsSnapshot"] = "stream-broker.metrics-snapshot";
234
+ /**
235
+ * Cap event fired by `stream-broker` when a profile slot enters
236
+ * "demanded" state — a cam stream has been assigned and at least one
237
+ * consumer (RTSP restream, decoded subscriber, WebRTC session, …) is
238
+ * present. Camera-provider addons (Reolink Baichuan push, …)
239
+ * subscribe to this category to start their underlying transport
240
+ * lazily. Payload: `{ deviceId, camStreamId, profile }`.
241
+ */
242
+ EventCategory["StreamBrokerOnCamStreamDemand"] = "stream-broker.onCamStreamDemand";
243
+ /**
244
+ * Cap event fired by `stream-broker` when the last consumer leaves a
245
+ * previously-demanded cam stream. Providers tear down their
246
+ * underlying transport on receipt. Payload: `{ deviceId, camStreamId }`.
247
+ */
248
+ EventCategory["StreamBrokerOnCamStreamIdle"] = "stream-broker.onCamStreamIdle";
249
+ /**
250
+ * Cap event fired by `stream-broker` when a broker fails to dial a
251
+ * managed-loopback source (today: `pull-rfc4571`) and the publisher
252
+ * needs to refresh the cached URL. The lib's TCP server idle-tears-down
253
+ * on its own schedule, so a re-publish with a fresh `host:port` is the
254
+ * only way to keep the broker dialable. Camera providers (Reolink
255
+ * Baichuan native, …) subscribe and respond by re-running their publish
256
+ * pipeline.
257
+ * Payload: `{ deviceId, camStreamId, brokerId }`.
258
+ */
259
+ EventCategory["StreamBrokerOnRequestStreamSourceRefresh"] = "stream-broker.onRequestStreamSourceRefresh";
260
+ /**
261
+ * A camera provider changed a device's stream parameters (codec /
262
+ * resolution / bitrate / a stream added or removed). A LOW-LATENCY NUDGE
263
+ * for the stream-broker's catalog reconcile: it carries NO authoritative
264
+ * data — the broker re-PULLS that device's `stream-catalog` cap on receipt
265
+ * (the 30s reconcile poll is the backstop if this nudge is dropped). Emitted
266
+ * by providers from `stream-params.setProfile`. Payload: `{ deviceId }`.
267
+ */
268
+ EventCategory["StreamParamsChanged"] = "stream-params.changed";
269
+ /**
270
+ * Generic per-device runtime-state change. Fired by `device-manager`
271
+ * whenever a persisted slice in any cap's `runtimeState` shape
272
+ * mutates. Payload: `{deviceId, capName, slice}`. Subscribers are
273
+ * the `deviceState` cap router (cross-process listeners) and the
274
+ * deviceProxy reactive bindings (`device.state.<capName>.value`).
275
+ * Cap-specific events (`battery.onStatusChanged`, …) still fire
276
+ * — they're authoritative for callers that want a typed payload
277
+ * without filtering on `capName`.
278
+ */
279
+ EventCategory["DeviceStateChanged"] = "device.state-changed";
280
+ /**
281
+ * Cap event fired by every device that registers the `battery`
282
+ * capability. Mirrors the cap definition's `onStatusChanged`. Carries
283
+ * `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,
284
+ * snapshot wrapper, UI) react to charge/sleep transitions without
285
+ * polling `batteryCapability.getStatus`.
286
+ */
287
+ EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
288
+ /**
289
+ * Emitted by the battery cap provider WHEN `wakeForStream` enters the
290
+ * "wake in progress" window — between the Baichuan wake-up issue and
291
+ * the camera's first dialed-back RTP packet. The stream-broker
292
+ * manager subscribes to flip the per-broker placeholder reason to
293
+ * `'waking'` so viewers see a labelled "WAKING UP" tile instead of
294
+ * the generic `'reconnecting'` frame. Carries `{ deviceId }`. The
295
+ * complementary "wake complete" signal is the existing
296
+ * `BatteryOnStatusChanged { sleeping: false }` event.
297
+ */
298
+ EventCategory["BatteryOnWakeStarted"] = "battery.onWakeStarted";
299
+ /**
300
+ * Cap event fired by every device that registers the `doorbell`
301
+ * capability. Mirrors `doorbellCapability.events.onPressed`. Carries
302
+ * `{ deviceId, timestamp }`. Operators consuming the UI subscribe
303
+ * here to render transient ring toasts and a "Recent presses" row
304
+ * on the device detail page.
305
+ */
306
+ EventCategory["DoorbellOnPressed"] = "doorbell.onPressed";
307
+ /**
308
+ * Cap event fired by every device that registers the `event-emitter`
309
+ * capability. Mirrors `eventEmitterCapability.events.onEvent`. Carries
310
+ * `{ deviceId, eventType, data, timestamp, seq }` — the device's EXACT
311
+ * declared event verbatim (NO normalization). Subscribers (UI event
312
+ * stream, advanced-notifier rules) react to fired events without
313
+ * holding a cap reference.
314
+ */
315
+ EventCategory["EventEmitted"] = "event-emitter.event";
316
+ /**
317
+ * Periodic snapshot of the per-node detection-pipeline engine
318
+ * registry (loaded engines, models resident, in-use cameras, idle
319
+ * TTL). Emitted ~0.2Hz (every 5 s) by every detection-pipeline
320
+ * process. The Engines tab subscribes to drive its inventory view
321
+ * without polling `pipelineExecutor.listLoadedEngines`.
322
+ */
323
+ EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
324
+ /**
325
+ * Per-node detection-engine runtime-provisioning transition. Emitted by
326
+ * the detection-pipeline provider on every state change of its lazy
327
+ * engine-provisioning machine (idle → installing → verifying → ready,
328
+ * or → failed with a `nextRetryAt`). Payload is the
329
+ * `EngineProvisioningState` snapshot; `event.source.nodeId` carries the
330
+ * node. The Pipeline page subscribes to drive a live "installing
331
+ * OpenVINO… / ready" indicator per node without polling
332
+ * `pipelineExecutor.getEngineProvisioning`. Telemetry-grade (D8): the UI
333
+ * also reads the cap snapshot on mount / reconnect. Phase 2.
334
+ */
335
+ EventCategory["PipelineEngineProvisioning"] = "pipeline.engine-provisioning";
336
+ /**
337
+ * Cluster topology snapshot. Carries the same payload returned by
338
+ * `nodes.topology` (every reachable node + addons + processes).
339
+ * Emitted by the hub on any agent / addon lifecycle change
340
+ * (debounced) plus a periodic safety net. Replaces UI polling on
341
+ * `nodes.topology` — admin-ui dashboards subscribe to drive the
342
+ * cluster view directly from the event payload.
343
+ */
344
+ EventCategory["ClusterTopologySnapshot"] = "cluster.topology-snapshot";
345
+ /**
346
+ * Periodic per-node system metrics snapshot (CPU / memory / GPU /
347
+ * disk / network). Emitted ~0.2 Hz by the metrics-provider addon
348
+ * for each node. Drives the dashboard SystemStatus / ProcessResources
349
+ * widgets without polling `metricsProvider.getCurrent`.
350
+ */
351
+ EventCategory["MetricsNodeResourcesSnapshot"] = "metrics.node-resources-snapshot";
352
+ /**
353
+ * Periodic per-node process-tree snapshot (camstack-related pids
354
+ * with ghost / managed / root classification). Emitted ~0.2 Hz by
355
+ * the metrics-provider addon. Drives the Cluster → Processes tab
356
+ * without polling `metricsProvider.listNodeProcesses`.
357
+ */
358
+ EventCategory["MetricsNodeProcessesSnapshot"] = "metrics.node-processes-snapshot";
359
+ /**
360
+ * Capability binding change event emitted by `addon-pipeline-orchestrator`
361
+ * when a user changes which addon implements a cap on a node. Subscribed
362
+ * by every kernel process to update its local `preferredProviderRegistry`
363
+ * so future capability lookups respect the new binding.
364
+ */
365
+ /**
366
+ * A capability binding was changed for a node — addon X now provides
367
+ * capability `cap` on node `nodeId`. Lives under the generic
368
+ * `capability.*` namespace because capability bindings are a kernel-
369
+ * level concept used by many addons, not strictly pipeline-scoped.
370
+ */
371
+ EventCategory["CapabilityBindingChanged"] = "capability.binding-changed";
372
+ EventCategory["ModelDownloadProgress"] = "model.download.progress";
373
+ EventCategory["AgentRegistered"] = "agent.registered";
374
+ EventCategory["AgentUnregistered"] = "agent.unregistered";
375
+ EventCategory["AgentOnline"] = "agent.online";
376
+ EventCategory["AgentOffline"] = "agent.offline";
377
+ /** Forked worker process (e.g. hub/pipeline) connected to the broker. */
378
+ EventCategory["WorkerOnline"] = "worker.online";
379
+ /** Forked worker process disconnected from the broker. */
380
+ EventCategory["WorkerOffline"] = "worker.offline";
381
+ EventCategory["AgentTaskDispatched"] = "agent.task.dispatched";
382
+ EventCategory["AgentTaskAssigned"] = "agent.task.assigned";
383
+ EventCategory["AgentTrpcConnected"] = "agent.trpc.connected";
384
+ EventCategory["AgentWsConnected"] = "agent.ws.connected";
385
+ EventCategory["AgentWsDisconnected"] = "agent.ws.disconnected";
386
+ EventCategory["AgentBackupActivated"] = "agent.backup.activated";
387
+ EventCategory["OrchestrationSettingsUpdated"] = "orchestration.settings-updated";
388
+ /**
389
+ * Per-agent hwaccel preference changed (user override set, cleared, or
390
+ * re-probed). Observability event — decoders pull the current pref at
391
+ * `createSession`, so running sessions keep their current backend until
392
+ * they rotate naturally (camera add/remove, stream restart). Future
393
+ * work can wire a listener in stream-broker that force-rotates live
394
+ * sessions; for now this event powers logs + admin-UI toast feedback.
395
+ */
396
+ EventCategory["PipelineAgentHwaccelChanged"] = "pipeline.agent-hwaccel-changed";
397
+ EventCategory["MotionAnalysis"] = "detection.motion-analysis";
398
+ /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */
399
+ EventCategory["MotionZonesRaw"] = "detection.motion-zones-raw";
400
+ /**
401
+ * Per-camera motion phase transition (`watching ↔ active`) emitted
402
+ * by the runner. Mirrors the `motion.onMotionChanged` cap event
403
+ * surface — payload `MotionOnMotionChangedPayload` carries
404
+ * `{deviceId, detected, timestamp, source, regions?}`. Subscribers
405
+ * include addons that need to react to motion state without
406
+ * polling the runtime-state mirror.
407
+ */
408
+ EventCategory["MotionOnMotionChanged"] = "motion.on-motion-changed";
409
+ EventCategory["DetectionResult"] = "detection.result";
410
+ EventCategory["DetectionRaw"] = "detection.raw";
411
+ EventCategory["DetectionCameraNative"] = "detection.camera-native";
412
+ /**
413
+ * Canonical per-chunk live audio pipeline output. Payload is
414
+ * `PipelineAudioInferenceResultPayload` carrying a full `AudioResult`
415
+ * (level + detections + debug). Lives on the pipeline.* namespace
416
+ * alongside `pipeline.inference-result` (video) for symmetry.
417
+ */
418
+ EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
419
+ EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
420
+ EventCategory["ProviderMotion"] = "provider.motion";
421
+ EventCategory["ProviderDetection"] = "provider.detection";
422
+ EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
423
+ EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
424
+ EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
425
+ /**
426
+ * Unified track-lifecycle event: ONE category carrying `phase:
427
+ * 'start' | 'update' | 'end'` with a rich, typed
428
+ * `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
429
+ * `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
430
+ * pair — a consumer subscribes once and branches on `phase`.
431
+ */
432
+ EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
433
+ /**
434
+ * @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
435
+ * (`phase:'start'`). Kept as a soft alias — no known subscribers.
436
+ */
437
+ EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
438
+ /**
439
+ * @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
440
+ * (`phase:'end'`). Kept as a soft alias — no known subscribers.
441
+ */
442
+ EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
443
+ EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
444
+ EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
445
+ /**
446
+ * Fired by `addon-post-analysis` when a parked (stationary) object appears
447
+ * (a track was promoted to a stationary-registry entry) or departs (the
448
+ * object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
449
+ * the dedicated "Stationary" section — never the live event feed. Payload:
450
+ * `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
451
+ */
452
+ EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
453
+ /**
454
+ * Fired by `addon-post-analysis` whenever a gallery face row changes:
455
+ * `kind:'buffered'` a new detected face was persisted, `'assigned'` /
456
+ * `'unassigned'` its identity link changed, `'deleted'` the row was
457
+ * removed. Telemetry semantics (D8): a lost event only delays a refresh,
458
+ * never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
459
+ */
460
+ EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
461
+ /**
462
+ * Fired by `addon-post-analysis` whenever a gallery plate row changes:
463
+ * `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
464
+ * `'assigned'` / `'unassigned'` its vehicle link changed
465
+ * (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
466
+ * semantics (D8): a lost event only delays a refresh, never a correctness
467
+ * issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
468
+ */
469
+ EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
470
+ EventCategory["FrigateLiveEvent"] = "frigate.live-event";
471
+ EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
472
+ /**
473
+ * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
474
+ * `stream.offline` fires after STREAM_STALE_TIMEOUT_MS without an encoded
475
+ * packet on an active broker. `stream.online` fires on first packet
476
+ * after a stale gap (or on initial first packet). Payload includes
477
+ * the assigned camStreamId as `profileKey`.
478
+ */
479
+ EventCategory["StreamOnline"] = "stream.online";
480
+ EventCategory["StreamOffline"] = "stream.offline";
481
+ EventCategory["NetworkTunnelStarted"] = "network.tunnel.started";
482
+ EventCategory["NetworkTunnelStopped"] = "network.tunnel.stopped";
483
+ /** Fired by the `local-network` cap when the host's interface set
484
+ * changes (new IP from DHCP, VPN connect, docker bridge added). */
485
+ EventCategory["LocalNetworkChanged"] = "network.local.changed";
486
+ /**
487
+ * Fired by a `mesh-network` provider (Tailscale, …) when its
488
+ * mesh-reachable host changes (join / leave / MagicDNS or 100.x IP
489
+ * change). Lets `local-network` fold the mesh endpoint into its
490
+ * connection-endpoint list without a cross-package import. Payload
491
+ * carries the preferred host (`host: ''` = no longer reachable),
492
+ * the hub port, and the scheme. Telemetry-grade (D8): consumers
493
+ * pull-reconcile from the provider's `getStatus` on reconnect. */
494
+ EventCategory["MeshNetworkChanged"] = "network.mesh.changed";
495
+ EventCategory["BackupCompleted"] = "backup.completed";
496
+ EventCategory["BackupRestored"] = "backup.restored";
497
+ EventCategory["NotificationDispatched"] = "notification.dispatched";
498
+ EventCategory["NotificationFailed"] = "notification.failed";
499
+ EventCategory["DeviceUpdated"] = "device.updated";
500
+ /**
501
+ * Transport-level connectivity. Emitted by the device driver when
502
+ * the underlying control socket actually connects / disconnects
503
+ * (Baichuan TCP, ONVIF probe response, RTSP DESCRIBE, …) — NOT for
504
+ * power-state transitions on a battery camera. For battery wake /
505
+ * doze cycles see `DeviceAwake` / `DeviceSleeping`.
506
+ */
507
+ EventCategory["DeviceOnline"] = "device.online";
508
+ /**
509
+ * Stream-broker watchdog — emitted when no encoded packet has been
510
+ * received for STREAM_STALE_TIMEOUT_MS on an active broker (rtsp or
511
+ * push). Paired with DeviceOnline which fires on first packet after
512
+ * a stale gap. Payload: DeviceStreamHealthPayload.
513
+ */
514
+ EventCategory["DeviceOffline"] = "device.offline";
515
+ /**
516
+ * Battery cam woke up — physical power-state transition reported
517
+ * by the device firmware. Distinct from `DeviceOnline` so a UI panel
518
+ * watching power state doesn't flap on every UDP socket reconnect.
519
+ */
520
+ EventCategory["DeviceAwake"] = "device.awake";
521
+ /**
522
+ * Battery cam went to sleep. See `DeviceAwake`.
523
+ */
524
+ EventCategory["DeviceSleeping"] = "device.sleeping";
525
+ EventCategory["RetentionCleanup"] = "retention.cleanup";
526
+ /**
527
+ * Legacy bulk-update progress snapshot (payload `BulkUpdateState`). No longer
528
+ * emitted — F3 removed the coordinator that produced it; "Update all" now runs
529
+ * as one lifecycle engine job (`AddonsJobProgress`/`AddonsJobLog`). Retained
530
+ * (with `BulkUpdateState`) only to avoid regenerating the event maps; removed
531
+ * in F4 once live bulk progress is re-implemented over the engine events.
532
+ */
533
+ EventCategory["AddonsBulkUpdateProgress"] = "addons.bulk-update-progress";
534
+ EventCategory["AddonsJobProgress"] = "addons.job-progress";
535
+ EventCategory["AddonsJobLog"] = "addons.job-log";
536
+ /**
537
+ * A container's child visibility toggled (hidden/shown). Emitted by the
538
+ * `accessories` cap when a child device is hidden or revealed.
539
+ * Payload: `{ deviceId, childDeviceId, hidden }`.
540
+ */
541
+ EventCategory["AccessoriesChildVisibilityChanged"] = "accessories.onChildVisibilityChanged";
542
+ /**
543
+ * A container's child set changed (children added/removed/reordered).
544
+ * Payload: `{ deviceId, childDeviceIds, hiddenChildIds }`.
545
+ */
546
+ EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
547
+ /**
548
+ * Progress update from a running model conversion job.
549
+ * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
550
+ * Emitted by `addon-model-studio` on the converting node.
551
+ */
552
+ EventCategory["ModelConvertProgress"] = "model-convert.progress";
553
+ return EventCategory;
554
+ }({});
555
+ //#endregion
12
556
  //#region ../../node_modules/zod/v4/core/core.js
13
557
  var _a$1;
14
558
  function $constructor(name, initializer, params) {
@@ -4300,6 +4844,14 @@ function object(shape, params) {
4300
4844
  ...normalizeParams(params)
4301
4845
  });
4302
4846
  }
4847
+ function looseObject(shape, params) {
4848
+ return new ZodObject({
4849
+ type: "object",
4850
+ shape,
4851
+ catchall: unknown(),
4852
+ ...normalizeParams(params)
4853
+ });
4854
+ }
4303
4855
  var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
4304
4856
  $ZodUnion.init(inst, def);
4305
4857
  ZodType.init(inst, def);
@@ -4651,506 +5203,6 @@ function preprocess(fn, schema) {
4651
5203
  out: schema
4652
5204
  });
4653
5205
  }
4654
- //#endregion
4655
- //#region ../types/dist/sleep-Baang_XW.mjs
4656
- var EventCategory = /* @__PURE__ */ function(EventCategory) {
4657
- EventCategory["SystemBoot"] = "system.boot";
4658
- EventCategory["SystemAddonsReady"] = "system.addons-ready";
4659
- EventCategory["SystemRestarting"] = "system.restarting";
4660
- /**
4661
- * Fired exactly once after the hub finishes booting following a
4662
- * restart that was triggered by `RestartCoordinator` (today: framework
4663
- * live-update). Payload is the marker that was on disk at boot
4664
- * (`PendingRestartMarkerPayload`); admin UI listens for it to display a
4665
- * success toast describing what changed.
4666
- *
4667
- * Spec: docs/superpowers/specs/2026-05-14-framework-live-update-design.md
4668
- */
4669
- EventCategory["SystemRestartCompleted"] = "system.restart-completed";
4670
- /**
4671
- * Readiness transition for a capability provider. Every producer emits
4672
- * this event on `onInitialize` completion, `onDestroy`, and
4673
- * `$node.reconnect`; every consumer that needs to gate on a cross-process
4674
- * cap subscribes via the kernel's readiness module (`awaitReady` /
4675
- * `onReadyState`) instead of polling. Payload is
4676
- * `SystemReadyStatePayload` — see event-bus.ts.
4677
- */
4678
- EventCategory["SystemReadyState"] = "system.ready-state";
4679
- EventCategory["AddonStarted"] = "addon.started";
4680
- EventCategory["AddonStopped"] = "addon.stopped";
4681
- EventCategory["AddonRestarted"] = "addon.restarted";
4682
- EventCategory["AddonUpdated"] = "addon.updated";
4683
- EventCategory["AddonInstalled"] = "addon.installed";
4684
- EventCategory["AddonUninstalled"] = "addon.uninstalled";
4685
- EventCategory["AddonCrashed"] = "addon.crashed";
4686
- EventCategory["AddonError"] = "addon.error";
4687
- EventCategory["AddonPageReady"] = "addon.page-ready";
4688
- EventCategory["AddonWidgetReady"] = "addon.widget-ready";
4689
- /**
4690
- * Addon failed to load (import or initialize). Emitted by the kernel's
4691
- * AddonHealthMonitor only AFTER the boot grace period ends — failures
4692
- * during the first 5 minutes are silently retried without alerting
4693
- * (slow-starting addons must have time to come up). Post-grace, this
4694
- * event is emitted exactly once per failure-streak; AlertCenter
4695
- * consumes it to create a persistent operator-visible alert.
4696
- *
4697
- * Payload: `{ packageName, addonId?, error: { message, stack }, retryCount, nextRetryAt }`.
4698
- */
4699
- EventCategory["AddonLoadFailed"] = "addon.load-failed";
4700
- /**
4701
- * Addon recovered from a previous failure. Emitted when an addon
4702
- * transitions from `failed` back to `healthy` (typically via the
4703
- * monitor's auto-retry loop, or after manual `addons.retryLoad`).
4704
- * AlertCenter dismisses the corresponding `AddonLoadFailed` alert
4705
- * on this event.
4706
- */
4707
- EventCategory["AddonLoadRecovered"] = "addon.load-recovered";
4708
- /**
4709
- * Monitor scheduled the next retry for a failed addon. Transient —
4710
- * surfaced to the UI for live-updating the "next retry in Ns"
4711
- * countdown on the Addons page row, NOT persisted as an alert.
4712
- */
4713
- EventCategory["AddonRetryScheduled"] = "addon.retry-scheduled";
4714
- /**
4715
- * Monitor is attempting to reload a failed addon NOW. UI uses this
4716
- * to show a spinner during the retry attempt. Same transient nature
4717
- * as AddonRetryScheduled.
4718
- */
4719
- EventCategory["AddonRetryAttempting"] = "addon.retry-attempting";
4720
- EventCategory["DeviceRegistered"] = "device.registered";
4721
- EventCategory["DeviceUnregistered"] = "device.unregistered";
4722
- EventCategory["DeviceEnabled"] = "device.enabled";
4723
- EventCategory["DeviceDisabled"] = "device.disabled";
4724
- EventCategory["DeviceSettingsUpdated"] = "device.settings-updated";
4725
- /**
4726
- * Emitted when the set of native capability providers bound to a device
4727
- * changes — e.g. an addon registers a new native cap via
4728
- * `DeviceContext.registerNativeCap`, or all native bindings for a device
4729
- * are cleared on removal. Hub consumers re-resolve device-proxy routes
4730
- * when this fires.
4731
- */
4732
- EventCategory["DeviceBindingsChanged"] = "device.bindings-changed";
4733
- /**
4734
- * Emitted when the operator-organisational meta surface changes
4735
- * (`name` / `location` / `disabled`). Payload: `{deviceId, field,
4736
- * value}`. Live consumers (UI device list, alert center) react
4737
- * without polling. Distinct from `DeviceSettingsUpdated` which
4738
- * fires on hardware-config changes (host/port/credentials/etc).
4739
- */
4740
- EventCategory["DeviceMetaChanged"] = "device.meta-changed";
4741
- /**
4742
- * Emitted by `BaseDevice.updateSourceInfo()` after a successful patch
4743
- * to the device's upstream-system identity / rendering envelope. The
4744
- * full new SourceInfo travels in the payload so cross-process listeners
4745
- * (UI, export adapters) don't need to re-read the meta blob.
4746
- *
4747
- * Payload: `{deviceId, sourceInfo}`.
4748
- *
4749
- * Distinct from `DeviceMetaChanged` which covers the operator-edited
4750
- * surface (`name` / `location` / `disabled`). The two share the
4751
- * persistence layer (both ride on `device-manager.setMetadata` for
4752
- * SourceInfo, or `setName`/`setLocation`/`setDisabled` for the meta
4753
- * fields) but consumers care about different subsets.
4754
- */
4755
- EventCategory["DeviceSourceInfoChanged"] = "device.source-info-changed";
4756
- /**
4757
- * Emitted by DeviceStreamWiringService after a successful
4758
- * `stream-broker.registerDeviceStreams` call. Payload includes
4759
- * deviceId — consumers look up the full registered device info via
4760
- * `brokerManager.getRegisteredDevice(deviceId)`.
4761
- *
4762
- * Replaces the legacy `StreamRouterService.onDeviceRegistered` callback.
4763
- */
4764
- EventCategory["DeviceStreamsRegistered"] = "device.streams-registered";
4765
- /**
4766
- * Device-level "fully provisioned" signal — the EXPORT trigger. Emitted once
4767
- * a device's persisted export-relevant shape (its `DeviceFeature` set) is
4768
- * established or changes, carrying `{deviceId, fingerprint, generation}`.
4769
- * Export adapters (Alexa / HAP) react to THIS instead of the chatty per-cap
4770
- * `DeviceBindingsChanged`, turning a boot's incomplete→complete trickle into
4771
- * one clean delta. `fingerprint` is `canonicalDeviceFingerprint(shape)`.
4772
- *
4773
- * Spec: docs/superpowers/specs/2026-06-01-alexa-hap-export-reconciler-design.md
4774
- */
4775
- EventCategory["DeviceProvisioned"] = "device.provisioned";
4776
- /**
4777
- * Fires once when a device's initial feature-probe completes successfully
4778
- * (lastProbedAt 0→>0); exported shape is now stable. Telemetry (may be
4779
- * dropped) — consumers MUST also gate on the device record's `probed` flag.
4780
- */
4781
- EventCategory["DeviceReady"] = "device.ready";
4782
- EventCategory["IntegrationEnabled"] = "integration.enabled";
4783
- EventCategory["IntegrationDisabled"] = "integration.disabled";
4784
- EventCategory["IntegrationDeleted"] = "integration.deleted";
4785
- /** Emitted when a broker connection's status flips (connected /
4786
- * disconnected / error). Carries `{ brokerId, status, error? }`. */
4787
- EventCategory["BrokerStatusChanged"] = "broker.status-changed";
4788
- /** Emitted for every subscription-matched message routed by a
4789
- * broker provider. Carries `{ brokerId, subscriptionId, key, payload }`.
4790
- * Consumers filter by `brokerId` + `subscriptionId` in the handler. */
4791
- EventCategory["BrokerMessage"] = "broker.message";
4792
- EventCategory["ProviderStarted"] = "provider.started";
4793
- EventCategory["ProviderStopped"] = "provider.stopped";
4794
- EventCategory["ProcessCrashed"] = "process.crashed";
4795
- EventCategory["ProcessRestartScheduled"] = "process.restart_scheduled";
4796
- EventCategory["ProcessRestarted"] = "process.restarted";
4797
- EventCategory["RecordingStarted"] = "recording.started";
4798
- EventCategory["RecordingStopped"] = "recording.stopped";
4799
- EventCategory["RecordingError"] = "recording.error";
4800
- EventCategory["RecordingHealthDegraded"] = "recording.health.degraded";
4801
- EventCategory["RecordingStorageCritical"] = "recording.storage.critical";
4802
- EventCategory["RecordingSegmentWritten"] = "recording.segment.written";
4803
- EventCategory["RecordingPolicyFallback"] = "recording.policy.fallback";
4804
- EventCategory["RecordingRetentionCompleted"] = "recording.retention.completed";
4805
- EventCategory["DetectionEvent"] = "detection.event";
4806
- EventCategory["SessionTrackNew"] = "session.track.new";
4807
- EventCategory["SessionTrackExpired"] = "session.track.expired";
4808
- EventCategory["BenchmarkProgress"] = "benchmark.progress";
4809
- EventCategory["PlatformProbePhase"] = "platform-probe.phase";
4810
- EventCategory["PipelineProgress"] = "pipeline.progress";
4811
- /** Per-frame execution trace emitted by the pipeline executor for live observability. */
4812
- EventCategory["PipelineTrace"] = "pipeline.trace";
4813
- /**
4814
- * Raw inference output emitted by `addon-pipeline-runner` after running the
4815
- * detection pipeline on a frame. Carries the `FrameResult` (with
4816
- * `detections[]`, `width`/`height`, timing debug) — never the frame
4817
- * buffer itself. Hub-side consumers (analysis pipeline, class filters,
4818
- * notifications) subscribe to this and re-emit `detection.result` after
4819
- * post-processing.
4820
- */
4821
- EventCategory["PipelineInferenceResult"] = "pipeline.inference-result";
4822
- /**
4823
- * Camera lifecycle event emitted by `addon-pipeline-orchestrator` when it
4824
- * assigns or unassigns a camera to/from an agent. Carries no frame data;
4825
- * pure observability for UI dashboards and metrics consumers.
4826
- */
4827
- EventCategory["PipelineCameraAssigned"] = "pipeline.camera-assigned";
4828
- EventCategory["PipelineCameraUnassigned"] = "pipeline.camera-unassigned";
4829
- /**
4830
- * Per-camera pipeline config was mutated by the orchestrator
4831
- * (3-level settings change via `setAgentAddonDefaults` /
4832
- * `setCameraStepToggle` / `setCameraPipelineForAgent` or a
4833
- * pipeline-scoped `applyDeviceSettingsPatch`). Orchestrator
4834
- * subscribes to its own emission to hot-reload the assigned runner
4835
- * via `attachCamera` so the next frame executes against the new
4836
- * engine/steps without waiting for a rebalance or the next
4837
- * `DeviceStreamsRegistered` cycle.
4838
- */
4839
- EventCategory["PipelineCameraUpdated"] = "pipeline.camera-updated";
4840
- /**
4841
- * The cluster camera-source OWNER changed (`clusterRoles.ingestNode`).
4842
- * Emitted by addon-pipeline-orchestrator whenever it (re)derives node
4843
- * capabilities — at boot, on agent online/offline, and on an ingest-node
4844
- * flip. Carries the resolved `ownerNodeId`. The stream-broker consumes it to
4845
- * keep its ingest-owner-gate decision current WITHOUT a per-`ensureBroker`
4846
- * cross-process `getIngestOwner` query (push the authority's decision instead
4847
- * of polling it on the hot path). Idempotent state — re-emitted on every
4848
- * topology change, so a dropped event self-heals on the next one (plus the
4849
- * broker's long backstop reconcile query).
4850
- */
4851
- EventCategory["PipelineIngestOwnerChanged"] = "pipeline.ingest-owner-changed";
4852
- /**
4853
- * Periodic snapshot of per-node pipeline-runner load
4854
- * (`RunnerLocalLoad`). Emitted ~1Hz by every runner so UI dashboards
4855
- * subscribe instead of polling `pipelineRunner.getLocalLoad`.
4856
- * `nodeId` carried in the payload + on `event.source.nodeId`.
4857
- */
4858
- EventCategory["PipelineRunnerLoadSnapshot"] = "pipeline.runner-load-snapshot";
4859
- /**
4860
- * Periodic snapshot of per-camera pipeline metrics (`CameraMetrics`
4861
- * + `deviceId` + `nodeId`). Emitted ~1Hz by every runner for each
4862
- * attached camera. UI subscribes to drive overlay phase / fps /
4863
- * inference time without polling `getCameraMetrics`.
4864
- */
4865
- EventCategory["PipelineCameraMetricsSnapshot"] = "pipeline.camera-metrics-snapshot";
4866
- /**
4867
- * Periodic snapshot of stream-broker per-broker statistics (input
4868
- * fps, decoded fps, bitrate, codec). Emitted ~1Hz by every
4869
- * stream-broker process for each active broker so the UI can drive
4870
- * the Stream / Cluster dashboards without polling
4871
- * `streamBroker.listAllProfileSlots` and friends.
4872
- */
4873
- EventCategory["StreamBrokerMetricsSnapshot"] = "stream-broker.metrics-snapshot";
4874
- /**
4875
- * Cap event fired by `stream-broker` when a profile slot enters
4876
- * "demanded" state — a cam stream has been assigned and at least one
4877
- * consumer (RTSP restream, decoded subscriber, WebRTC session, …) is
4878
- * present. Camera-provider addons (Reolink Baichuan push, …)
4879
- * subscribe to this category to start their underlying transport
4880
- * lazily. Payload: `{ deviceId, camStreamId, profile }`.
4881
- */
4882
- EventCategory["StreamBrokerOnCamStreamDemand"] = "stream-broker.onCamStreamDemand";
4883
- /**
4884
- * Cap event fired by `stream-broker` when the last consumer leaves a
4885
- * previously-demanded cam stream. Providers tear down their
4886
- * underlying transport on receipt. Payload: `{ deviceId, camStreamId }`.
4887
- */
4888
- EventCategory["StreamBrokerOnCamStreamIdle"] = "stream-broker.onCamStreamIdle";
4889
- /**
4890
- * Cap event fired by `stream-broker` when a broker fails to dial a
4891
- * managed-loopback source (today: `pull-rfc4571`) and the publisher
4892
- * needs to refresh the cached URL. The lib's TCP server idle-tears-down
4893
- * on its own schedule, so a re-publish with a fresh `host:port` is the
4894
- * only way to keep the broker dialable. Camera providers (Reolink
4895
- * Baichuan native, …) subscribe and respond by re-running their publish
4896
- * pipeline.
4897
- * Payload: `{ deviceId, camStreamId, brokerId }`.
4898
- */
4899
- EventCategory["StreamBrokerOnRequestStreamSourceRefresh"] = "stream-broker.onRequestStreamSourceRefresh";
4900
- /**
4901
- * A camera provider changed a device's stream parameters (codec /
4902
- * resolution / bitrate / a stream added or removed). A LOW-LATENCY NUDGE
4903
- * for the stream-broker's catalog reconcile: it carries NO authoritative
4904
- * data — the broker re-PULLS that device's `stream-catalog` cap on receipt
4905
- * (the 30s reconcile poll is the backstop if this nudge is dropped). Emitted
4906
- * by providers from `stream-params.setProfile`. Payload: `{ deviceId }`.
4907
- */
4908
- EventCategory["StreamParamsChanged"] = "stream-params.changed";
4909
- /**
4910
- * Generic per-device runtime-state change. Fired by `device-manager`
4911
- * whenever a persisted slice in any cap's `runtimeState` shape
4912
- * mutates. Payload: `{deviceId, capName, slice}`. Subscribers are
4913
- * the `deviceState` cap router (cross-process listeners) and the
4914
- * deviceProxy reactive bindings (`device.state.<capName>.value`).
4915
- * Cap-specific events (`battery.onStatusChanged`, …) still fire
4916
- * — they're authoritative for callers that want a typed payload
4917
- * without filtering on `capName`.
4918
- */
4919
- EventCategory["DeviceStateChanged"] = "device.state-changed";
4920
- /**
4921
- * Cap event fired by every device that registers the `battery`
4922
- * capability. Mirrors the cap definition's `onStatusChanged`. Carries
4923
- * `{ deviceId, status: BatteryStatus }`. Subscribers (alert center,
4924
- * snapshot wrapper, UI) react to charge/sleep transitions without
4925
- * polling `batteryCapability.getStatus`.
4926
- */
4927
- EventCategory["BatteryOnStatusChanged"] = "battery.onStatusChanged";
4928
- /**
4929
- * Emitted by the battery cap provider WHEN `wakeForStream` enters the
4930
- * "wake in progress" window — between the Baichuan wake-up issue and
4931
- * the camera's first dialed-back RTP packet. The stream-broker
4932
- * manager subscribes to flip the per-broker placeholder reason to
4933
- * `'waking'` so viewers see a labelled "WAKING UP" tile instead of
4934
- * the generic `'reconnecting'` frame. Carries `{ deviceId }`. The
4935
- * complementary "wake complete" signal is the existing
4936
- * `BatteryOnStatusChanged { sleeping: false }` event.
4937
- */
4938
- EventCategory["BatteryOnWakeStarted"] = "battery.onWakeStarted";
4939
- /**
4940
- * Cap event fired by every device that registers the `doorbell`
4941
- * capability. Mirrors `doorbellCapability.events.onPressed`. Carries
4942
- * `{ deviceId, timestamp }`. Operators consuming the UI subscribe
4943
- * here to render transient ring toasts and a "Recent presses" row
4944
- * on the device detail page.
4945
- */
4946
- EventCategory["DoorbellOnPressed"] = "doorbell.onPressed";
4947
- /**
4948
- * Cap event fired by every device that registers the `event-emitter`
4949
- * capability. Mirrors `eventEmitterCapability.events.onEvent`. Carries
4950
- * `{ deviceId, eventType, data, timestamp, seq }` — the device's EXACT
4951
- * declared event verbatim (NO normalization). Subscribers (UI event
4952
- * stream, advanced-notifier rules) react to fired events without
4953
- * holding a cap reference.
4954
- */
4955
- EventCategory["EventEmitted"] = "event-emitter.event";
4956
- /**
4957
- * Periodic snapshot of the per-node detection-pipeline engine
4958
- * registry (loaded engines, models resident, in-use cameras, idle
4959
- * TTL). Emitted ~0.2Hz (every 5 s) by every detection-pipeline
4960
- * process. The Engines tab subscribes to drive its inventory view
4961
- * without polling `pipelineExecutor.listLoadedEngines`.
4962
- */
4963
- EventCategory["PipelineEngineMetricsSnapshot"] = "pipeline.engine-metrics-snapshot";
4964
- /**
4965
- * Per-node detection-engine runtime-provisioning transition. Emitted by
4966
- * the detection-pipeline provider on every state change of its lazy
4967
- * engine-provisioning machine (idle → installing → verifying → ready,
4968
- * or → failed with a `nextRetryAt`). Payload is the
4969
- * `EngineProvisioningState` snapshot; `event.source.nodeId` carries the
4970
- * node. The Pipeline page subscribes to drive a live "installing
4971
- * OpenVINO… / ready" indicator per node without polling
4972
- * `pipelineExecutor.getEngineProvisioning`. Telemetry-grade (D8): the UI
4973
- * also reads the cap snapshot on mount / reconnect. Phase 2.
4974
- */
4975
- EventCategory["PipelineEngineProvisioning"] = "pipeline.engine-provisioning";
4976
- /**
4977
- * Cluster topology snapshot. Carries the same payload returned by
4978
- * `nodes.topology` (every reachable node + addons + processes).
4979
- * Emitted by the hub on any agent / addon lifecycle change
4980
- * (debounced) plus a periodic safety net. Replaces UI polling on
4981
- * `nodes.topology` — admin-ui dashboards subscribe to drive the
4982
- * cluster view directly from the event payload.
4983
- */
4984
- EventCategory["ClusterTopologySnapshot"] = "cluster.topology-snapshot";
4985
- /**
4986
- * Periodic per-node system metrics snapshot (CPU / memory / GPU /
4987
- * disk / network). Emitted ~0.2 Hz by the metrics-provider addon
4988
- * for each node. Drives the dashboard SystemStatus / ProcessResources
4989
- * widgets without polling `metricsProvider.getCurrent`.
4990
- */
4991
- EventCategory["MetricsNodeResourcesSnapshot"] = "metrics.node-resources-snapshot";
4992
- /**
4993
- * Periodic per-node process-tree snapshot (camstack-related pids
4994
- * with ghost / managed / root classification). Emitted ~0.2 Hz by
4995
- * the metrics-provider addon. Drives the Cluster → Processes tab
4996
- * without polling `metricsProvider.listNodeProcesses`.
4997
- */
4998
- EventCategory["MetricsNodeProcessesSnapshot"] = "metrics.node-processes-snapshot";
4999
- /**
5000
- * Capability binding change event emitted by `addon-pipeline-orchestrator`
5001
- * when a user changes which addon implements a cap on a node. Subscribed
5002
- * by every kernel process to update its local `preferredProviderRegistry`
5003
- * so future capability lookups respect the new binding.
5004
- */
5005
- /**
5006
- * A capability binding was changed for a node — addon X now provides
5007
- * capability `cap` on node `nodeId`. Lives under the generic
5008
- * `capability.*` namespace because capability bindings are a kernel-
5009
- * level concept used by many addons, not strictly pipeline-scoped.
5010
- */
5011
- EventCategory["CapabilityBindingChanged"] = "capability.binding-changed";
5012
- EventCategory["ModelDownloadProgress"] = "model.download.progress";
5013
- EventCategory["AgentRegistered"] = "agent.registered";
5014
- EventCategory["AgentUnregistered"] = "agent.unregistered";
5015
- EventCategory["AgentOnline"] = "agent.online";
5016
- EventCategory["AgentOffline"] = "agent.offline";
5017
- /** Forked worker process (e.g. hub/pipeline) connected to the broker. */
5018
- EventCategory["WorkerOnline"] = "worker.online";
5019
- /** Forked worker process disconnected from the broker. */
5020
- EventCategory["WorkerOffline"] = "worker.offline";
5021
- EventCategory["AgentTaskDispatched"] = "agent.task.dispatched";
5022
- EventCategory["AgentTaskAssigned"] = "agent.task.assigned";
5023
- EventCategory["AgentTrpcConnected"] = "agent.trpc.connected";
5024
- EventCategory["AgentWsConnected"] = "agent.ws.connected";
5025
- EventCategory["AgentWsDisconnected"] = "agent.ws.disconnected";
5026
- EventCategory["AgentBackupActivated"] = "agent.backup.activated";
5027
- EventCategory["OrchestrationSettingsUpdated"] = "orchestration.settings-updated";
5028
- /**
5029
- * Per-agent hwaccel preference changed (user override set, cleared, or
5030
- * re-probed). Observability event — decoders pull the current pref at
5031
- * `createSession`, so running sessions keep their current backend until
5032
- * they rotate naturally (camera add/remove, stream restart). Future
5033
- * work can wire a listener in stream-broker that force-rotates live
5034
- * sessions; for now this event powers logs + admin-UI toast feedback.
5035
- */
5036
- EventCategory["PipelineAgentHwaccelChanged"] = "pipeline.agent-hwaccel-changed";
5037
- EventCategory["MotionAnalysis"] = "detection.motion-analysis";
5038
- /** All raw motion zones from CCL before minArea filter — for UI debug overlay. */
5039
- EventCategory["MotionZonesRaw"] = "detection.motion-zones-raw";
5040
- /**
5041
- * Per-camera motion phase transition (`watching ↔ active`) emitted
5042
- * by the runner. Mirrors the `motion.onMotionChanged` cap event
5043
- * surface — payload `MotionOnMotionChangedPayload` carries
5044
- * `{deviceId, detected, timestamp, source, regions?}`. Subscribers
5045
- * include addons that need to react to motion state without
5046
- * polling the runtime-state mirror.
5047
- */
5048
- EventCategory["MotionOnMotionChanged"] = "motion.on-motion-changed";
5049
- EventCategory["DetectionResult"] = "detection.result";
5050
- EventCategory["DetectionRaw"] = "detection.raw";
5051
- EventCategory["DetectionCameraNative"] = "detection.camera-native";
5052
- /**
5053
- * Canonical per-chunk live audio pipeline output. Payload is
5054
- * `PipelineAudioInferenceResultPayload` carrying a full `AudioResult`
5055
- * (level + detections + debug). Lives on the pipeline.* namespace
5056
- * alongside `pipeline.inference-result` (video) for symmetry.
5057
- */
5058
- EventCategory["PipelineAudioInferenceResult"] = "pipeline.audio-inference-result";
5059
- EventCategory["DetectionPhaseTransition"] = "detection.phase-transition";
5060
- EventCategory["ProviderMotion"] = "provider.motion";
5061
- EventCategory["ProviderDetection"] = "provider.detection";
5062
- EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
5063
- EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
5064
- EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
5065
- EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
5066
- EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
5067
- EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
5068
- EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
5069
- EventCategory["FrigateLiveEvent"] = "frigate.live-event";
5070
- EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
5071
- /**
5072
- * Stream-broker health watchdog. Per-broker (deviceId/profile) emission.
5073
- * `stream.offline` fires after STREAM_STALE_TIMEOUT_MS without an encoded
5074
- * packet on an active broker. `stream.online` fires on first packet
5075
- * after a stale gap (or on initial first packet). Payload includes
5076
- * the assigned camStreamId as `profileKey`.
5077
- */
5078
- EventCategory["StreamOnline"] = "stream.online";
5079
- EventCategory["StreamOffline"] = "stream.offline";
5080
- EventCategory["NetworkTunnelStarted"] = "network.tunnel.started";
5081
- EventCategory["NetworkTunnelStopped"] = "network.tunnel.stopped";
5082
- /** Fired by the `local-network` cap when the host's interface set
5083
- * changes (new IP from DHCP, VPN connect, docker bridge added). */
5084
- EventCategory["LocalNetworkChanged"] = "network.local.changed";
5085
- /**
5086
- * Fired by a `mesh-network` provider (Tailscale, …) when its
5087
- * mesh-reachable host changes (join / leave / MagicDNS or 100.x IP
5088
- * change). Lets `local-network` fold the mesh endpoint into its
5089
- * connection-endpoint list without a cross-package import. Payload
5090
- * carries the preferred host (`host: ''` = no longer reachable),
5091
- * the hub port, and the scheme. Telemetry-grade (D8): consumers
5092
- * pull-reconcile from the provider's `getStatus` on reconnect. */
5093
- EventCategory["MeshNetworkChanged"] = "network.mesh.changed";
5094
- EventCategory["BackupCompleted"] = "backup.completed";
5095
- EventCategory["BackupRestored"] = "backup.restored";
5096
- EventCategory["NotificationDispatched"] = "notification.dispatched";
5097
- EventCategory["NotificationFailed"] = "notification.failed";
5098
- EventCategory["DeviceUpdated"] = "device.updated";
5099
- /**
5100
- * Transport-level connectivity. Emitted by the device driver when
5101
- * the underlying control socket actually connects / disconnects
5102
- * (Baichuan TCP, ONVIF probe response, RTSP DESCRIBE, …) — NOT for
5103
- * power-state transitions on a battery camera. For battery wake /
5104
- * doze cycles see `DeviceAwake` / `DeviceSleeping`.
5105
- */
5106
- EventCategory["DeviceOnline"] = "device.online";
5107
- /**
5108
- * Stream-broker watchdog — emitted when no encoded packet has been
5109
- * received for STREAM_STALE_TIMEOUT_MS on an active broker (rtsp or
5110
- * push). Paired with DeviceOnline which fires on first packet after
5111
- * a stale gap. Payload: DeviceStreamHealthPayload.
5112
- */
5113
- EventCategory["DeviceOffline"] = "device.offline";
5114
- /**
5115
- * Battery cam woke up — physical power-state transition reported
5116
- * by the device firmware. Distinct from `DeviceOnline` so a UI panel
5117
- * watching power state doesn't flap on every UDP socket reconnect.
5118
- */
5119
- EventCategory["DeviceAwake"] = "device.awake";
5120
- /**
5121
- * Battery cam went to sleep. See `DeviceAwake`.
5122
- */
5123
- EventCategory["DeviceSleeping"] = "device.sleeping";
5124
- EventCategory["RetentionCleanup"] = "retention.cleanup";
5125
- /**
5126
- * Legacy bulk-update progress snapshot (payload `BulkUpdateState`). No longer
5127
- * emitted — F3 removed the coordinator that produced it; "Update all" now runs
5128
- * as one lifecycle engine job (`AddonsJobProgress`/`AddonsJobLog`). Retained
5129
- * (with `BulkUpdateState`) only to avoid regenerating the event maps; removed
5130
- * in F4 once live bulk progress is re-implemented over the engine events.
5131
- */
5132
- EventCategory["AddonsBulkUpdateProgress"] = "addons.bulk-update-progress";
5133
- EventCategory["AddonsJobProgress"] = "addons.job-progress";
5134
- EventCategory["AddonsJobLog"] = "addons.job-log";
5135
- /**
5136
- * A container's child visibility toggled (hidden/shown). Emitted by the
5137
- * `accessories` cap when a child device is hidden or revealed.
5138
- * Payload: `{ deviceId, childDeviceId, hidden }`.
5139
- */
5140
- EventCategory["AccessoriesChildVisibilityChanged"] = "accessories.onChildVisibilityChanged";
5141
- /**
5142
- * A container's child set changed (children added/removed/reordered).
5143
- * Payload: `{ deviceId, childDeviceIds, hiddenChildIds }`.
5144
- */
5145
- EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
5146
- /**
5147
- * Progress update from a running model conversion job.
5148
- * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
5149
- * Emitted by `addon-model-studio` on the converting node.
5150
- */
5151
- EventCategory["ModelConvertProgress"] = "model-convert.progress";
5152
- return EventCategory;
5153
- }({});
5154
5206
  Object.fromEntries([
5155
5207
  {
5156
5208
  id: "overview",
@@ -6910,7 +6962,8 @@ var MODEL_FORMATS = [
6910
6962
  "coreml",
6911
6963
  "openvino",
6912
6964
  "tflite",
6913
- "pt"
6965
+ "pt",
6966
+ "gguf"
6914
6967
  ];
6915
6968
  /**
6916
6969
  * Multi-file format payload.
@@ -6954,7 +7007,8 @@ var ModelFormatsSchema = object({
6954
7007
  coreml: ModelFormatEntrySchema.optional(),
6955
7008
  openvino: ModelFormatEntrySchema.optional(),
6956
7009
  tflite: ModelFormatEntrySchema.optional(),
6957
- pt: ModelFormatEntrySchema.optional()
7010
+ pt: ModelFormatEntrySchema.optional(),
7011
+ gguf: ModelFormatEntrySchema.optional()
6958
7012
  });
6959
7013
  /**
6960
7014
  * Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
@@ -6995,6 +7049,15 @@ var ModelCatalogEntrySchema = object({
6995
7049
  width: number(),
6996
7050
  height: number()
6997
7051
  }),
7052
+ /**
7053
+ * Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
7054
+ * every detector / classifier / embedder. Set to 1 for a grayscale CTC text
7055
+ * recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
7056
+ * feeds a single-channel, EasyOCR-normalized tensor instead of the default
7057
+ * 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
7058
+ * Python inference pool.
7059
+ */
7060
+ inputChannels: number().int().positive().optional(),
6998
7061
  labels: array(LabelDefinitionSchema).readonly(),
6999
7062
  inputLayout: _enum(["nchw", "nhwc"]).optional(),
7000
7063
  inputNormalization: _enum([
@@ -7004,6 +7067,16 @@ var ModelCatalogEntrySchema = object({
7004
7067
  ]).optional(),
7005
7068
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
7006
7069
  /**
7070
+ * Per-MODEL postprocessor override. Absent ⇒ the step's own
7071
+ * `StepDefinition.postprocessor` applies (the normal case — every model in a
7072
+ * step shares its decode). Set it when a step hosts models with DIFFERENT raw
7073
+ * output layouts under one slot: e.g. object-detection is `'yolo'` by default,
7074
+ * but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
7075
+ * 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
7076
+ * by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
7077
+ */
7078
+ postprocessor: custom().optional(),
7079
+ /**
7007
7080
  * When true, the executor produces a landmark-aligned crop (similarity warp
7008
7081
  * onto the canonical template) before this step runs, instead of a plain
7009
7082
  * axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
@@ -10736,10 +10809,23 @@ var deviceDiscoveryCapability = {
10736
10809
  }
10737
10810
  };
10738
10811
  /**
10739
- * Doorbell button cap. Installed on a `DeviceType.Button` accessory
10740
- * with `role: DeviceRole.Doorbell`. Emits an `onPressed` event every
10741
- * time the firmware pushes a ring; status tracks the last press and
10742
- * a pressCount since start (diagnostic).
10812
+ * Doorbell button cap. Two kinds of providers coexist behind this cap
10813
+ * name (same pattern as `snapshot`):
10814
+ *
10815
+ * - **Native** providers: registered per-device by device-driver
10816
+ * addons via `ctx.registerNativeCap` — either on a
10817
+ * `DeviceType.Button` accessory with `role: DeviceRole.Doorbell`,
10818
+ * or directly on the camera (Reolink registers at camera level).
10819
+ * Emits an `onPressed` event every time the firmware pushes a
10820
+ * ring; status tracks the last press and a pressCount since start
10821
+ * (diagnostic).
10822
+ *
10823
+ * - **Wrapper** provider: the `virtual-doorbell` system builtin
10824
+ * (`@camstack/system/builtins/doorbell`). Turns ANY binary-ish
10825
+ * device (contact / switch / event-emitter …) into a doorbell for
10826
+ * a camera. `defaultActive: false` — the operator explicitly binds
10827
+ * it per camera in the device-bindings UI, then picks the source
10828
+ * device + trigger in the per-device settings.
10743
10829
  *
10744
10830
  * The DeviceEventPropagator re-emits `onPressed` on the camera parent
10745
10831
  * — subscribers listening at the camera level receive ring events
@@ -10760,7 +10846,10 @@ var doorbellCapability = {
10760
10846
  scope: "device",
10761
10847
  deviceNative: true,
10762
10848
  mode: "singleton",
10763
- deviceTypes: [DeviceType.Button],
10849
+ kind: "wrapper",
10850
+ defaultActive: false,
10851
+ deviceTypes: [DeviceType.Button, DeviceType.Camera],
10852
+ exposesDeviceSettings: true,
10764
10853
  methods: {},
10765
10854
  events: {
10766
10855
  /**
@@ -11710,7 +11799,8 @@ var ModelFormatSchema$1 = _enum([
11710
11799
  "coreml",
11711
11800
  "openvino",
11712
11801
  "tflite",
11713
- "pt"
11802
+ "pt",
11803
+ "gguf"
11714
11804
  ]);
11715
11805
  var PipelineSlotSchema = _enum([
11716
11806
  "detector",
@@ -11804,7 +11894,8 @@ var EngineProvisioningSchema = object({
11804
11894
  runtimeId: _enum([
11805
11895
  "onnx",
11806
11896
  "openvino",
11807
- "coreml"
11897
+ "coreml",
11898
+ "edgetpu"
11808
11899
  ]).nullable(),
11809
11900
  device: string().nullable(),
11810
11901
  state: _enum([
@@ -13445,6 +13536,167 @@ var ptzAutotrackCapability = {
13445
13536
  runtimeState: PtzAutotrackRuntimeStateSchema
13446
13537
  };
13447
13538
  /**
13539
+ * scene-monitor — device-scoped reference-region state cap. An operator marks
13540
+ * a rect ROI on a camera frame and names one or more states; the engine
13541
+ * (pipeline-analytics, designated post-processing node) reports which state is
13542
+ * active on an ongoing basis. Two operator-selectable check modes share every-
13543
+ * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
13544
+ * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
13545
+ *
13546
+ * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
13547
+ * derives the device-detail contribution; the provider carries NO hand-written
13548
+ * settings-contribution methods. `status.kind:'push'` — the engine pushes on
13549
+ * every hysteresis flip / availability change; consumers never poll.
13550
+ */
13551
+ /** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
13552
+ * be added without a wire break (matching falls back to any-condition refs). */
13553
+ var SceneConditionSchema = string();
13554
+ /** One captured reference — condition-tagged, model-version-gated. `embedding`
13555
+ * is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
13556
+ var SceneReferenceSchema = object({
13557
+ embedding: array(number()),
13558
+ modelId: string(),
13559
+ condition: SceneConditionSchema,
13560
+ capturedAt: number(),
13561
+ thumbnailMediaId: string().optional()
13562
+ });
13563
+ var SceneMonitorStateSchema = object({
13564
+ id: string(),
13565
+ label: string(),
13566
+ references: array(SceneReferenceSchema),
13567
+ textPrompts: array(string()).optional(),
13568
+ referenceCount: number(),
13569
+ currentlyMatched: boolean()
13570
+ });
13571
+ /** Per-mode comparator params in a discriminated union so an `llm` scene never
13572
+ * carries similarity knobs and vice-versa. */
13573
+ var SceneCheckSchema = discriminatedUnion("mode", [object({
13574
+ mode: literal("similarity"),
13575
+ threshold: number().min(0).max(1),
13576
+ hysteresisCount: number().int().positive()
13577
+ }), object({
13578
+ mode: literal("llm"),
13579
+ prompt: string(),
13580
+ profileId: string().optional(),
13581
+ hysteresisCount: number().int().positive()
13582
+ })]);
13583
+ var SceneMonitorSchema = object({
13584
+ id: string(),
13585
+ label: string(),
13586
+ roi: MaskRectShapeSchema,
13587
+ enabled: boolean(),
13588
+ triggerMode: _enum([
13589
+ "periodic",
13590
+ "on-motion",
13591
+ "both"
13592
+ ]),
13593
+ checkIntervalSec: number().optional(),
13594
+ check: SceneCheckSchema,
13595
+ states: array(SceneMonitorStateSchema),
13596
+ currentStateId: string().nullable(),
13597
+ lastCheckedAt: number().nullable(),
13598
+ lastConfidence: number().nullable(),
13599
+ currentCondition: SceneConditionSchema.nullable(),
13600
+ availability: _enum(["ok", "unavailable"]),
13601
+ unavailableReason: string().nullable()
13602
+ });
13603
+ var SceneMonitorStatusSchema = object({
13604
+ monitors: array(SceneMonitorSchema),
13605
+ lastFetchedAt: number()
13606
+ });
13607
+ var sceneMonitorCapability = {
13608
+ name: "scene-monitor",
13609
+ scope: "device",
13610
+ mode: "singleton",
13611
+ kind: "wrapper",
13612
+ defaultActive: true,
13613
+ deviceTypes: [DeviceType.Camera],
13614
+ deviceConfig: { ui: {
13615
+ kind: "widget",
13616
+ widgetId: "host/scene-monitor-editor",
13617
+ tab: "scenes",
13618
+ label: "Scenes"
13619
+ } },
13620
+ methods: {
13621
+ listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
13622
+ createScene: method(object({
13623
+ deviceId: number(),
13624
+ label: string(),
13625
+ roi: MaskRectShapeSchema,
13626
+ check: SceneCheckSchema,
13627
+ triggerMode: _enum([
13628
+ "periodic",
13629
+ "on-motion",
13630
+ "both"
13631
+ ]).optional(),
13632
+ checkIntervalSec: number().optional()
13633
+ }), SceneMonitorSchema, {
13634
+ kind: "mutation",
13635
+ auth: "admin"
13636
+ }),
13637
+ updateScene: method(object({
13638
+ deviceId: number(),
13639
+ monitorId: string(),
13640
+ patch: object({
13641
+ label: string().optional(),
13642
+ roi: MaskRectShapeSchema.optional(),
13643
+ enabled: boolean().optional(),
13644
+ triggerMode: _enum([
13645
+ "periodic",
13646
+ "on-motion",
13647
+ "both"
13648
+ ]).optional(),
13649
+ checkIntervalSec: number().optional(),
13650
+ check: SceneCheckSchema.optional()
13651
+ })
13652
+ }), _void(), {
13653
+ kind: "mutation",
13654
+ auth: "admin"
13655
+ }),
13656
+ deleteScene: method(object({
13657
+ deviceId: number(),
13658
+ monitorId: string()
13659
+ }), _void(), {
13660
+ kind: "mutation",
13661
+ auth: "admin"
13662
+ }),
13663
+ captureReference: method(object({
13664
+ deviceId: number(),
13665
+ monitorId: string(),
13666
+ stateId: string().optional(),
13667
+ label: string().optional(),
13668
+ condition: SceneConditionSchema.optional()
13669
+ }), object({
13670
+ stateId: string(),
13671
+ referenceCount: number()
13672
+ }), {
13673
+ kind: "mutation",
13674
+ auth: "admin"
13675
+ }),
13676
+ deleteReference: method(object({
13677
+ deviceId: number(),
13678
+ monitorId: string(),
13679
+ stateId: string(),
13680
+ index: number()
13681
+ }), _void(), {
13682
+ kind: "mutation",
13683
+ auth: "admin"
13684
+ }),
13685
+ recheckNow: method(object({
13686
+ deviceId: number(),
13687
+ monitorId: string()
13688
+ }), _void(), {
13689
+ kind: "mutation",
13690
+ auth: "admin"
13691
+ })
13692
+ },
13693
+ status: {
13694
+ schema: SceneMonitorStatusSchema,
13695
+ kind: "push"
13696
+ },
13697
+ runtimeState: SceneMonitorStatusSchema
13698
+ };
13699
+ /**
13448
13700
  * Script-runner cap. Models HA `script.*` entities on
13449
13701
  * `DeviceType.Script`. A Script is a pre-recorded action sequence
13450
13702
  * that can be invoked imperatively — optionally with a variables
@@ -14176,6 +14428,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
14176
14428
  * with the per-track detail panel and live overlay. */
14177
14429
  trackIds: array(string()).readonly()
14178
14430
  });
14431
+ /**
14432
+ * A parked ("stationary") object surfaced alongside occupancy — an object that
14433
+ * settled and stopped moving. It is NO LONGER a tracked object (the tracker was
14434
+ * told to forget it so it stops re-spawning tracks/events), but it IS still
14435
+ * physically present, so it keeps counting toward `frame` occupancy and is
14436
+ * listed here so the UI can show it in a dedicated "Stationary" section instead
14437
+ * of flooding the live event feed.
14438
+ */
14439
+ var StationaryObjectSchema = object({
14440
+ id: string(),
14441
+ className: string(),
14442
+ bbox: object({
14443
+ x: number(),
14444
+ y: number(),
14445
+ w: number(),
14446
+ h: number()
14447
+ }),
14448
+ frameWidth: number().int().nonnegative(),
14449
+ frameHeight: number().int().nonnegative(),
14450
+ /** When the source track was first seen. */
14451
+ firstSeenAt: number().int(),
14452
+ /** When the object was recognised as parked (promotion time). */
14453
+ becameStationaryAt: number().int(),
14454
+ /** Last frame a detection confirmed the object is still there. */
14455
+ lastConfirmedAt: number().int(),
14456
+ /** Enrichment label carried from the source track (identity / plate). */
14457
+ label: string().optional(),
14458
+ /** Native-resolution key-frame media key for the parked object's best image. */
14459
+ keyFrameMediaKey: string().optional()
14460
+ });
14179
14461
  var CameraOccupancySnapshotSchema = object({
14180
14462
  /** Frame timestamp of the inference result that produced this snapshot. */
14181
14463
  ts: number().int(),
@@ -14184,10 +14466,15 @@ var CameraOccupancySnapshotSchema = object({
14184
14466
  frameHeight: number().int().nonnegative(),
14185
14467
  /** Per-zone breakdown — one entry per defined zone (user + onboard). */
14186
14468
  zones: array(ZoneScopeBreakdownSchema).readonly(),
14187
- /** Frame-wide aggregate (everywhere, regardless of zone membership). */
14469
+ /** Frame-wide aggregate (everywhere, regardless of zone membership).
14470
+ * INCLUDES currently-confirmed stationary objects (they are still present). */
14188
14471
  frame: PerScopeBreakdownSchema,
14189
14472
  /** Detections that landed outside every zone. Empty when no zones defined. */
14190
- unzoned: PerScopeBreakdownSchema
14473
+ unzoned: PerScopeBreakdownSchema,
14474
+ /** Parked objects on this camera (additive — absent on legacy snapshots).
14475
+ * Surfaced separately so the UI shows them in a dedicated section rather
14476
+ * than as repeated tracks/events. */
14477
+ stationaryObjects: array(StationaryObjectSchema).readonly().optional()
14191
14478
  });
14192
14479
  /**
14193
14480
  * Time-series resolution. The history methods return one bucket per
@@ -14422,6 +14709,7 @@ var DEVICE_LOCAL_STATE_CAPS = {
14422
14709
  pressureSensor: pressureSensorCapability,
14423
14710
  privacyMask: privacyMaskCapability,
14424
14711
  ptzAutotrack: ptzAutotrackCapability,
14712
+ sceneMonitor: sceneMonitorCapability,
14425
14713
  scriptRunner: scriptRunnerCapability,
14426
14714
  smoke: smokeCapability,
14427
14715
  streamParams: streamParamsCapability,
@@ -15707,7 +15995,12 @@ var NotificationRuleConditionsSchema = object({
15707
15995
  clipDescription: object({
15708
15996
  text: string().min(1),
15709
15997
  minSimilarity: number().min(0).max(1)
15710
- }).optional()
15998
+ }).optional(),
15999
+ /** Match events whose recognized-entity label (face identity name or plate
16000
+ * vehicle name, propagated onto `event.data.label`) is one of these values.
16001
+ * Empty/absent → unaffected (back-compat). Enables "notify me when <named
16002
+ * vehicle/person> is seen". */
16003
+ labels: array(string()).readonly().optional()
15711
16004
  });
15712
16005
  var NotificationRuleTemplateSchema = object({
15713
16006
  title: string(),
@@ -15965,11 +16258,19 @@ method(object({
15965
16258
  * login page needs NO change.
15966
16259
  *
15967
16260
  * - `widget` — a Module-Federation widget the login page mounts (via
15968
- * `loadRemoteBundle`) for an in-page ceremony. Covers the passkey
15969
- * login ceremony, which must run `@simplewebauthn/browser` INSIDE the
15970
- * addon bundle. The referenced widget also declares `preAuth: true` in
15971
- * its `addon-widgets-source` catalog entry. `auth.listLoginMethods`
15972
- * stamps a public `bundleUrl` from `addonId` + `bundle`.
16261
+ * `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
16262
+ * stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
16263
+ * mechanism kept for future use; no shipped addon uses it on the login
16264
+ * page (the passkey ceremony below runs natively in the shell instead).
16265
+ *
16266
+ * - `passkey` — a declarative WebAuthn ceremony the shell renders
16267
+ * natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
16268
+ * a remotely-loaded bundle). Carries the addon's effective `rpId` /
16269
+ * `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
16270
+ * can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
16271
+ * fetching any remote code pre-auth. Contribution stays unconditional —
16272
+ * enrollment state is never leaked pre-auth; visibility is a shell
16273
+ * decision.
15973
16274
  *
15974
16275
  * Every contribution carries a `stage`:
15975
16276
  * - `primary` — shown on the first credentials screen (OIDC /
@@ -15983,30 +16284,45 @@ method(object({
15983
16284
  */
15984
16285
  /** When a login method renders in the two-phase login flow. */
15985
16286
  var LoginStageEnum = _enum(["primary", "second-factor"]);
15986
- /** One login-method contribution — redirect button OR pre-auth widget. */
15987
- var LoginMethodContributionSchema = discriminatedUnion("kind", [object({
15988
- kind: literal("redirect"),
15989
- /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
15990
- id: string(),
15991
- /** Operator-facing button label. */
15992
- label: string(),
15993
- /** lucide-react icon name. */
15994
- icon: string().optional(),
15995
- /** Addon-owned HTTP route the button navigates to (GET). */
15996
- startUrl: string(),
15997
- stage: LoginStageEnum
15998
- }), object({
15999
- kind: literal("widget"),
16000
- /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
16001
- id: string(),
16002
- /** Owning addon id — drives the public bundle URL + the MF namespace. */
16003
- addonId: string(),
16004
- /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
16005
- bundle: string(),
16006
- /** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
16007
- remote: WidgetRemoteSchema,
16008
- stage: LoginStageEnum
16009
- })]);
16287
+ /** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
16288
+ var LoginMethodContributionSchema = discriminatedUnion("kind", [
16289
+ object({
16290
+ kind: literal("redirect"),
16291
+ /** Stable id within the login-method set (e.g. `auth-oidc/google`). */
16292
+ id: string(),
16293
+ /** Operator-facing button label. */
16294
+ label: string(),
16295
+ /** lucide-react icon name. */
16296
+ icon: string().optional(),
16297
+ /** Addon-owned HTTP route the button navigates to (GET). */
16298
+ startUrl: string(),
16299
+ stage: LoginStageEnum
16300
+ }),
16301
+ object({
16302
+ kind: literal("widget"),
16303
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
16304
+ id: string(),
16305
+ /** Owning addon id — drives the public bundle URL + the MF namespace. */
16306
+ addonId: string(),
16307
+ /** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
16308
+ bundle: string(),
16309
+ /** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
16310
+ remote: WidgetRemoteSchema,
16311
+ stage: LoginStageEnum
16312
+ }),
16313
+ object({
16314
+ kind: literal("passkey"),
16315
+ /** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
16316
+ id: string(),
16317
+ /** Operator-facing button label. */
16318
+ label: string(),
16319
+ stage: LoginStageEnum,
16320
+ /** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
16321
+ rpId: string(),
16322
+ /** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
16323
+ origin: string().nullable()
16324
+ })
16325
+ ]);
16010
16326
  method(_void(), array(LoginMethodContributionSchema).readonly());
16011
16327
  /**
16012
16328
  * Orchestrator-side destination metadata. The orchestrator computes
@@ -16951,6 +17267,14 @@ var ConfigEntrySchema = object({
16951
17267
  value: unknown(),
16952
17268
  description: string().optional()
16953
17269
  });
17270
+ var DeviceLinkModeSchema = _enum(["auto", "manual"]);
17271
+ /** One resolved linked device — the compact projection consumers need. */
17272
+ var LinkedDeviceSchema = object({
17273
+ deviceId: number(),
17274
+ name: string(),
17275
+ location: string().nullable(),
17276
+ features: array(string())
17277
+ });
16954
17278
  var SavedDeviceRowSchema = object({
16955
17279
  /** Numeric id reserved at allocateDeviceId time. */
16956
17280
  id: number(),
@@ -17172,7 +17496,10 @@ method(object({
17172
17496
  }), _void(), {
17173
17497
  kind: "mutation",
17174
17498
  auth: "admin"
17175
- }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17499
+ }), method(object({ addonId: string() }), array(SavedDeviceRowSchema)), method(object({ addonId: string().optional() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), DeviceInfoSchema.nullable()), method(object({ parentDeviceId: number() }), array(DeviceInfoSchema)), method(object({ deviceId: number() }), object({
17500
+ mode: DeviceLinkModeSchema,
17501
+ devices: array(LinkedDeviceSchema)
17502
+ })), method(object({ deviceId: number() }), array(StreamSourceEntrySchema$1)), method(object({ deviceId: number() }), array(ConfigEntrySchema)), method(object({ deviceId: number() }), ConfigUISchemaOutput), method(object({
17176
17503
  deviceId: number(),
17177
17504
  values: record(string(), unknown())
17178
17505
  }), object({ success: literal(true) }), {
@@ -17900,14 +18227,14 @@ var TargetKindCapsSchema = object({
17900
18227
  * the union is large and not meant for runtime validation here; the exported
17901
18228
  * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
17902
18229
  */
17903
- var ConfigSchemaPassthrough = unknown();
18230
+ var ConfigSchemaPassthrough$1 = unknown();
17904
18231
  var TargetKindSchema = object({
17905
18232
  kind: string(),
17906
18233
  label: string(),
17907
18234
  icon: string(),
17908
18235
  /** Stamped by each provider so the concat-fanned catalog stays routable. */
17909
18236
  addonId: string(),
17910
- configSchema: ConfigSchemaPassthrough,
18237
+ configSchema: ConfigSchemaPassthrough$1,
17911
18238
  supportsDiscovery: boolean(),
17912
18239
  caps: TargetKindCapsSchema
17913
18240
  });
@@ -17960,6 +18287,298 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
17960
18287
  enabled: boolean()
17961
18288
  }), _void(), { kind: "mutation" });
17962
18289
  /**
18290
+ * Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
18291
+ * surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
18292
+ * caps stay wire-compatible without a circular cap→cap import.
18293
+ *
18294
+ * Errors are a discriminated-union RESULT, never thrown: the shape survives
18295
+ * every transport tier structurally, and failed calls still write usage rows.
18296
+ * Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
18297
+ */
18298
+ var LlmUsageSchema = object({
18299
+ inputTokens: number(),
18300
+ outputTokens: number()
18301
+ });
18302
+ var LlmErrorCodeSchema = _enum([
18303
+ "timeout",
18304
+ "rate-limited",
18305
+ "auth",
18306
+ "refusal",
18307
+ "bad-request",
18308
+ "unavailable",
18309
+ "no-profile",
18310
+ "budget-exceeded",
18311
+ "adapter-error"
18312
+ ]);
18313
+ var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
18314
+ ok: literal(true),
18315
+ text: string(),
18316
+ model: string(),
18317
+ usage: LlmUsageSchema,
18318
+ truncated: boolean(),
18319
+ latencyMs: number()
18320
+ }), object({
18321
+ ok: literal(false),
18322
+ code: LlmErrorCodeSchema,
18323
+ message: string(),
18324
+ retryAfterMs: number().optional()
18325
+ })]);
18326
+ /**
18327
+ * `Uint8Array` is the sanctioned binary convention — superjson + the UDS
18328
+ * MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
18329
+ * notification-output.cap.ts:27-31 precedents).
18330
+ */
18331
+ var LlmImageSchema = object({
18332
+ bytes: _instanceof(Uint8Array),
18333
+ mimeType: string()
18334
+ });
18335
+ var LlmGenerateBaseInputSchema = object({
18336
+ /** Collection routing (the notification-output posture). */
18337
+ addonId: string().optional(),
18338
+ /** Explicit profile; else the resolution chain (spec §3). */
18339
+ profileId: string().optional(),
18340
+ /** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
18341
+ consumer: string(),
18342
+ system: string().optional(),
18343
+ /** v1: single-turn. `messages[]` is a v2 additive field. */
18344
+ prompt: string(),
18345
+ /** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
18346
+ jsonSchema: record(string(), unknown()).optional(),
18347
+ /** Per-call override of the profile default. */
18348
+ maxTokens: number().int().positive().optional(),
18349
+ temperature: number().optional()
18350
+ });
18351
+ /**
18352
+ * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
18353
+ * on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
18354
+ * a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
18355
+ * cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
18356
+ * this only through the `llm` cap's methods.
18357
+ *
18358
+ * One running llama-server child per node in v1 (models are RAM-heavy).
18359
+ * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
18360
+ * watchdog — operator decision #3).
18361
+ */
18362
+ var ManagedModelRefSchema = discriminatedUnion("kind", [
18363
+ object({
18364
+ kind: literal("catalog"),
18365
+ catalogId: string()
18366
+ }),
18367
+ object({
18368
+ kind: literal("url"),
18369
+ url: string(),
18370
+ sha256: string().optional()
18371
+ }),
18372
+ object({
18373
+ kind: literal("path"),
18374
+ path: string()
18375
+ })
18376
+ ]);
18377
+ var ManagedRuntimeConfigSchema = object({
18378
+ /** WHERE the runtime lives — hub or any agent. */
18379
+ nodeId: string(),
18380
+ /** Closed for v1; 'ollama' is a v2 candidate. */
18381
+ engine: _enum(["llama-cpp"]),
18382
+ model: ManagedModelRefSchema,
18383
+ contextSize: number().int().default(4096),
18384
+ /** 0 = CPU-only. */
18385
+ gpuLayers: number().int().default(0),
18386
+ /** Default: cpus-2, clamped ≥1 (resolved node-side). */
18387
+ threads: number().int().optional(),
18388
+ /** Concurrent slots. */
18389
+ parallel: number().int().default(1),
18390
+ /** Else lazy: first generate boots it. */
18391
+ autoStart: boolean().default(false),
18392
+ /** 0 = never; frees RAM after quiet periods. */
18393
+ idleStopMinutes: number().int().default(30)
18394
+ });
18395
+ var LlmRuntimeStatusSchema = object({
18396
+ /** Status is ALWAYS node-qualified. */
18397
+ nodeId: string(),
18398
+ state: _enum([
18399
+ "stopped",
18400
+ "downloading",
18401
+ "starting",
18402
+ "ready",
18403
+ "crashed",
18404
+ "failed"
18405
+ ]),
18406
+ pid: number().optional(),
18407
+ port: number().optional(),
18408
+ modelPath: string().optional(),
18409
+ modelId: string().optional(),
18410
+ downloadProgress: number().min(0).max(1).optional(),
18411
+ lastError: string().optional(),
18412
+ crashesInWindow: number(),
18413
+ /** Child RSS (sampled best-effort). */
18414
+ memoryBytes: number().optional(),
18415
+ vramBytes: number().optional()
18416
+ });
18417
+ var LlmNodeModelSchema = object({
18418
+ file: string(),
18419
+ sizeBytes: number(),
18420
+ catalogId: string().optional(),
18421
+ installedAt: number().optional()
18422
+ });
18423
+ var LlmRuntimeDiskUsageSchema = object({
18424
+ nodeId: string(),
18425
+ modelsBytes: number(),
18426
+ freeBytes: number().optional()
18427
+ });
18428
+ method(LlmGenerateBaseInputSchema.extend({
18429
+ images: array(LlmImageSchema).optional(),
18430
+ runtime: ManagedRuntimeConfigSchema,
18431
+ /** The managed profile's timeout, threaded by the hub provider. */
18432
+ timeoutMs: number().int().positive().optional()
18433
+ }), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
18434
+ kind: "mutation",
18435
+ auth: "admin"
18436
+ }), method(object({}), _void(), {
18437
+ kind: "mutation",
18438
+ auth: "admin"
18439
+ }), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
18440
+ kind: "mutation",
18441
+ auth: "admin"
18442
+ }), method(object({ file: string() }), _void(), {
18443
+ kind: "mutation",
18444
+ auth: "admin"
18445
+ }), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
18446
+ /**
18447
+ * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
18448
+ * methods concat-fan across providers; single-row methods route to ONE
18449
+ * provider by the `addonId` in the call input (the notification-output
18450
+ * posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
18451
+ * (hub-placed); the cap stays open for future providers.
18452
+ *
18453
+ * Profiles are ROWS (data), not addons: one row = one usable model endpoint.
18454
+ * `apiKey` is a password field — providers REDACT it on read and merge on
18455
+ * write; a stored key NEVER round-trips to a client.
18456
+ */
18457
+ var LlmProfileKindSchema = _enum([
18458
+ "openai-compatible",
18459
+ "openai",
18460
+ "anthropic",
18461
+ "google",
18462
+ "managed-local"
18463
+ ]);
18464
+ var LlmProfileSchema = object({
18465
+ id: string(),
18466
+ name: string(),
18467
+ kind: LlmProfileKindSchema,
18468
+ /** Stamped by the provider — keeps the fanned catalog routable. */
18469
+ addonId: string(),
18470
+ enabled: boolean(),
18471
+ /** Vendor model id, or the managed runtime's loaded model. */
18472
+ model: string(),
18473
+ /** Required for openai-compatible; override for cloud kinds. */
18474
+ baseUrl: string().optional(),
18475
+ /** ConfigUISchema type:'password' — never round-trips (spec §5). */
18476
+ apiKey: string().optional(),
18477
+ supportsVision: boolean(),
18478
+ temperature: number().min(0).max(2).optional(),
18479
+ maxTokens: number().int().positive().optional(),
18480
+ timeoutMs: number().int().positive().default(6e4),
18481
+ extraHeaders: record(string(), string()).optional(),
18482
+ /** kind === 'managed-local' only (spec §4). */
18483
+ runtime: ManagedRuntimeConfigSchema.optional()
18484
+ });
18485
+ /** ConfigUISchema tree passed through untyped on the wire (the
18486
+ * notification-output `ConfigSchemaPassthrough` precedent at
18487
+ * notification-output.cap.ts:151); the exported TS type re-tightens it. */
18488
+ var ConfigSchemaPassthrough = unknown();
18489
+ var LlmProfileKindDescriptorSchema = object({
18490
+ kind: LlmProfileKindSchema,
18491
+ label: string(),
18492
+ icon: string(),
18493
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
18494
+ addonId: string(),
18495
+ configSchema: ConfigSchemaPassthrough
18496
+ });
18497
+ var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
18498
+ var LlmDefaultSchema = object({
18499
+ selector: LlmDefaultSelectorSchema,
18500
+ profileId: string()
18501
+ });
18502
+ /** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
18503
+ var LlmUsageRollupSchema = object({
18504
+ day: string(),
18505
+ consumer: string(),
18506
+ profileId: string(),
18507
+ calls: number(),
18508
+ okCalls: number(),
18509
+ errorCalls: number(),
18510
+ inputTokens: number(),
18511
+ outputTokens: number(),
18512
+ avgLatencyMs: number()
18513
+ });
18514
+ /** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
18515
+ var ManagedModelCatalogEntrySchema = object({
18516
+ id: string(),
18517
+ label: string(),
18518
+ family: string(),
18519
+ purpose: _enum(["text", "vision"]),
18520
+ url: string(),
18521
+ sha256: string(),
18522
+ sizeBytes: number(),
18523
+ quantization: string(),
18524
+ /** Load-time guidance shown in the picker. */
18525
+ minRamBytes: number(),
18526
+ contextSizeDefault: number().int(),
18527
+ /** Vision models: companion projector file. */
18528
+ mmprojUrl: string().optional()
18529
+ });
18530
+ var LlmRuntimeNodeSchema = object({
18531
+ nodeId: string(),
18532
+ reachable: boolean(),
18533
+ status: LlmRuntimeStatusSchema.optional(),
18534
+ disk: LlmRuntimeDiskUsageSchema.optional(),
18535
+ error: string().optional()
18536
+ });
18537
+ var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
18538
+ var ProfileRefInputSchema = object({
18539
+ addonId: string(),
18540
+ profileId: string()
18541
+ });
18542
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
18543
+ kind: "mutation",
18544
+ auth: "admin"
18545
+ }), method(ProfileRefInputSchema, _void(), {
18546
+ kind: "mutation",
18547
+ auth: "admin"
18548
+ }), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
18549
+ kind: "mutation",
18550
+ auth: "admin"
18551
+ }), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
18552
+ selector: LlmDefaultSelectorSchema,
18553
+ profileId: string().nullable()
18554
+ }), _void(), {
18555
+ kind: "mutation",
18556
+ auth: "admin"
18557
+ }), method(object({
18558
+ since: number().optional(),
18559
+ until: number().optional(),
18560
+ consumer: string().optional(),
18561
+ profileId: string().optional()
18562
+ }), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
18563
+ nodeId: string(),
18564
+ model: ManagedModelRefSchema
18565
+ }), _void(), {
18566
+ kind: "mutation",
18567
+ auth: "admin"
18568
+ }), method(object({
18569
+ nodeId: string(),
18570
+ file: string()
18571
+ }), _void(), {
18572
+ kind: "mutation",
18573
+ auth: "admin"
18574
+ }), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
18575
+ kind: "mutation",
18576
+ auth: "admin"
18577
+ }), method(ProfileRefInputSchema, _void(), {
18578
+ kind: "mutation",
18579
+ auth: "admin"
18580
+ });
18581
+ /**
17963
18582
  * Zod schemas for persisted record types.
17964
18583
  *
17965
18584
  * These schemas serve as the single source of truth for types that are
@@ -18149,6 +18768,61 @@ var EventKindSchema = _enum([
18149
18768
  "object",
18150
18769
  "audio"
18151
18770
  ]);
18771
+ /**
18772
+ * Spatial filter for `listTracks` — the rect + polygon variants of the shared
18773
+ * MaskShape vocabulary (see `mask-shape.ts`). Coordinates are NORMALIZED 0..1
18774
+ * of the camera frame (top-left origin), matching the drawing-plane editor.
18775
+ */
18776
+ var TrackZoneFilterSchema = discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
18777
+ /** Closed icon vocabulary so clients render a known glyph per kind. */
18778
+ var EventKindIconSchema = _enum([
18779
+ "motion",
18780
+ "audio",
18781
+ "person",
18782
+ "vehicle",
18783
+ "animal",
18784
+ "door",
18785
+ "pir",
18786
+ "smoke",
18787
+ "water",
18788
+ "button",
18789
+ "generic"
18790
+ ]);
18791
+ var EventKindCategorySchema = _enum([
18792
+ "motion",
18793
+ "audio",
18794
+ "detection",
18795
+ "sensor",
18796
+ "custom"
18797
+ ]);
18798
+ var EventKindDescriptorSchema = object({
18799
+ /** Stable kind id (e.g. 'motion', 'person', 'contact'). */
18800
+ kind: string(),
18801
+ label: string(),
18802
+ /** Hex color for timeline/legend rendering. */
18803
+ color: string(),
18804
+ icon: EventKindIconSchema,
18805
+ category: EventKindCategorySchema,
18806
+ /** Which cap + device contributes this kind. For built-ins the camera
18807
+ * itself; for sensor kinds the LINKED source device. */
18808
+ source: object({
18809
+ capName: string(),
18810
+ deviceId: number()
18811
+ })
18812
+ });
18813
+ var SensorEventSchema = object({
18814
+ id: string(),
18815
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
18816
+ * yields N rows, one per camera). */
18817
+ deviceId: number(),
18818
+ /** The linked sensor device whose state changed. */
18819
+ sourceDeviceId: number(),
18820
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
18821
+ kind: string(),
18822
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
18823
+ value: record(string(), unknown()).nullable(),
18824
+ timestamp: number()
18825
+ });
18152
18826
  var TrackPositionSchema = object({
18153
18827
  x: number(),
18154
18828
  y: number(),
@@ -18161,6 +18835,46 @@ var TrackSnapshotSchema = object({
18161
18835
  /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
18162
18836
  mediaKey: string()
18163
18837
  });
18838
+ /**
18839
+ * Normalized 0..1 trajectory envelope (min/max over every position bbox,
18840
+ * divided by the track's detection-frame dims), computed at persist time.
18841
+ * Absent when the frame dims were unknown when the track was persisted
18842
+ * (legacy rows / dims-less sources) and on active (in-RAM) tracks.
18843
+ */
18844
+ var TrackEnvelopeSchema = object({
18845
+ minX: number(),
18846
+ minY: number(),
18847
+ maxX: number(),
18848
+ maxY: number()
18849
+ });
18850
+ /**
18851
+ * Row projection for track list queries. `full` (default) returns the
18852
+ * complete Track including the frame-rate `positions[]` history and the
18853
+ * `snapshots[]` references — megabytes across a page of tracks. `slim`
18854
+ * keeps every scalar the list surfaces actually render (ids, class(es),
18855
+ * label / audioLabels / importance enrichment, firstSeen/lastSeen, state,
18856
+ * zonesVisited, bestEventId, envelope) and returns `positions` /
18857
+ * `snapshots` as EMPTY arrays — detail views re-fetch the full row via
18858
+ * `getTrack`. Mirrors the event-store `projection` convention
18859
+ * (`getObjectEvents` et al.).
18860
+ */
18861
+ var TrackProjectionSchema = _enum(["full", "slim"]);
18862
+ /**
18863
+ * One audio-classification label heard on the track's camera while the
18864
+ * track was alive, aggregated per label. An "episode" is one persisted
18865
+ * audio event (the confident-classification path: score ≥ the device's
18866
+ * `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
18867
+ * one 32 ms inference chunk, so counts stay human-scaled.
18868
+ */
18869
+ var TrackAudioLabelSchema = object({
18870
+ label: string(),
18871
+ /** Highest classification score observed across the label's episodes. */
18872
+ peakScore: number(),
18873
+ /** Number of coalesced audio-event episodes carrying this label. */
18874
+ count: number(),
18875
+ firstAt: number(),
18876
+ lastAt: number()
18877
+ });
18164
18878
  var TrackSchema = object({
18165
18879
  trackId: string(),
18166
18880
  deviceId: number(),
@@ -18175,6 +18889,10 @@ var TrackSchema = object({
18175
18889
  snapshots: array(TrackSnapshotSchema).readonly(),
18176
18890
  /** Deduplicated zones the track has entered at least once. */
18177
18891
  zonesVisited: array(string()).readonly(),
18892
+ /** Deduplicated set of detector classes observed for this track over its
18893
+ * life (a track may be reclassified, e.g. person→vehicle). Absent on
18894
+ * legacy rows written before class accumulation shipped. */
18895
+ classes: array(string()).readonly().optional(),
18178
18896
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
18179
18897
  totalDistance: number(),
18180
18898
  state: TrackStateSchema,
@@ -18188,7 +18906,15 @@ var TrackSchema = object({
18188
18906
  bestEventId: string().optional(),
18189
18907
  /** Tag of the importance sub-signal that dominated the score
18190
18908
  * (identity|dwell|proximity|class|confidence|travel|zone). */
18191
- importanceReason: string().optional()
18909
+ importanceReason: string().optional(),
18910
+ /** Audio-classification labels heard on the camera during the track's
18911
+ * life (score ≥ device `classificationMinScore`), aggregated per label.
18912
+ * Absent on legacy rows / tracks with no confident audio. */
18913
+ audioLabels: array(TrackAudioLabelSchema).readonly().optional(),
18914
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
18915
+ * Populated from the persisted envelope columns on historical reads;
18916
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
18917
+ envelope: TrackEnvelopeSchema.optional()
18192
18918
  });
18193
18919
  var BaseEventFields = {
18194
18920
  id: string(),
@@ -18298,11 +19024,13 @@ var MediaFileSchema = object({
18298
19024
  sizeBytes: number(),
18299
19025
  timestamp: number()
18300
19026
  });
19027
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
19028
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
18301
19029
  var DeviceEventQueryInput = object({
18302
19030
  deviceId: number(),
18303
19031
  since: number().optional(),
18304
19032
  until: number().optional(),
18305
- limit: number().int().min(1).max(5e3).default(1e3),
19033
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
18306
19034
  /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
18307
19035
  * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
18308
19036
  * exact behaviour. Callers may omit this field — the store defaults to
@@ -18310,6 +19038,27 @@ var DeviceEventQueryInput = object({
18310
19038
  projection: _enum(["full", "slim"]).optional()
18311
19039
  });
18312
19040
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
19041
+ var RecentTracksQueryInput = object({
19042
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
19043
+ deviceIds: array(number()),
19044
+ /** Window lower bound on `lastSeen` (inclusive). */
19045
+ since: number().optional(),
19046
+ /** Window upper bound on `lastSeen` (inclusive). */
19047
+ until: number().optional(),
19048
+ /** Page size. Default 200, max 1000. */
19049
+ limit: number().int().min(1).max(1e3).default(200),
19050
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
19051
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
19052
+ cursor: string().optional(),
19053
+ /** See {@link TrackProjectionSchema}. Default `full`. */
19054
+ projection: TrackProjectionSchema.optional()
19055
+ });
19056
+ var RecentTracksPageSchema = object({
19057
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
19058
+ tracks: array(TrackSchema).readonly(),
19059
+ /** Cursor for the next page, or null when this page is the last. */
19060
+ nextCursor: string().nullable()
19061
+ });
18313
19062
  var KeyEventQueryInput = object({
18314
19063
  deviceId: number(),
18315
19064
  /** Window lower bound (track firstSeen ≥ since). */
@@ -18336,7 +19085,7 @@ var KeyEventSchema = object({
18336
19085
  /** Track lifetime in ms (lastSeen - firstSeen). */
18337
19086
  windowMs: number().optional()
18338
19087
  });
18339
- var TrackedDetectionSchema = object({
19088
+ object({
18340
19089
  trackId: string(),
18341
19090
  className: string(),
18342
19091
  confidence: number(),
@@ -18344,6 +19093,23 @@ var TrackedDetectionSchema = object({
18344
19093
  zones: array(string()).readonly(),
18345
19094
  state: TrackStateSchema
18346
19095
  });
19096
+ var OverlayDetectionSchema = looseObject({
19097
+ id: string(),
19098
+ kind: _enum(["first-level", "detail"]),
19099
+ macroClass: string(),
19100
+ score: number(),
19101
+ bbox: object({
19102
+ x: number(),
19103
+ y: number(),
19104
+ width: number(),
19105
+ height: number()
19106
+ }),
19107
+ labels: array(looseObject({
19108
+ label: string(),
19109
+ score: number()
19110
+ })).readonly(),
19111
+ parentId: string().optional()
19112
+ });
18347
19113
  var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
18348
19114
  var SearchObjectEventsInput = object({
18349
19115
  text: string(),
@@ -18354,6 +19120,20 @@ var SearchObjectEventsInput = object({
18354
19120
  limit: number().default(50),
18355
19121
  minScore: number().min(0).max(1).default(.2)
18356
19122
  });
19123
+ var TrackCascadeCountsSchema = object({
19124
+ /** Persisted track roots deleted (authoritative). */
19125
+ tracks: number().int(),
19126
+ /** Object events removed with their tracks (best-effort; see note above). */
19127
+ events: number().int(),
19128
+ /** Track/face/plate-owned media removed (best-effort). Never identity media. */
19129
+ media: number().int(),
19130
+ /** Unassigned (non-enrolled) face reads removed (best-effort). */
19131
+ faces: number().int(),
19132
+ /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19133
+ plates: number().int(),
19134
+ /** Per-track CLIP search vectors removed (best-effort). */
19135
+ embeddings: number().int()
19136
+ });
18357
19137
  DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
18358
19138
  deviceId: number(),
18359
19139
  trackId: string()
@@ -18361,11 +19141,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18361
19141
  deviceId: number(),
18362
19142
  since: number().optional(),
18363
19143
  until: number().optional(),
18364
- limit: number().optional()
18365
- }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
19144
+ limit: number().optional(),
19145
+ /** Spatial filter — only tracks whose trajectory intersects the zone
19146
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
19147
+ * envelope columns, then precisely tested per position. Tracks with
19148
+ * an unknown envelope (no frame dims at persist time) always match. */
19149
+ zone: TrackZoneFilterSchema.optional(),
19150
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
19151
+ * compatible — omitting the field keeps today's exact behaviour). */
19152
+ projection: TrackProjectionSchema.optional()
19153
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number() }), _void(), {
18366
19154
  kind: "mutation",
18367
19155
  auth: "admin"
18368
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19156
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number() }), array(EventKindDescriptorSchema).readonly()), method(object({
19157
+ deviceId: number(),
19158
+ since: number().optional(),
19159
+ until: number().optional(),
19160
+ kinds: array(string()).optional(),
19161
+ limit: number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19162
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
18369
19163
  deviceId: number(),
18370
19164
  since: number(),
18371
19165
  until: number(),
@@ -18385,6 +19179,24 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18385
19179
  }), {
18386
19180
  kind: "mutation",
18387
19181
  auth: "admin"
19182
+ }), method(object({
19183
+ deviceId: number(),
19184
+ cutoffMs: number()
19185
+ }), TrackCascadeCountsSchema, {
19186
+ kind: "mutation",
19187
+ auth: "admin"
19188
+ }), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
19189
+ kind: "mutation",
19190
+ auth: "admin"
19191
+ }), method(object({
19192
+ deviceId: number(),
19193
+ trackIds: array(string()).min(1)
19194
+ }), object({
19195
+ deleted: number().int(),
19196
+ failed: array(string()).readonly()
19197
+ }), {
19198
+ kind: "mutation",
19199
+ auth: "admin"
18388
19200
  }), method(object({
18389
19201
  eventId: string(),
18390
19202
  kind: MediaFileKindEnum.optional()
@@ -18393,7 +19205,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18393
19205
  timestamp: number(),
18394
19206
  frameWidth: number(),
18395
19207
  frameHeight: number(),
18396
- detections: array(TrackedDetectionSchema).readonly()
19208
+ detections: array(OverlayDetectionSchema).readonly()
18397
19209
  }), object({
18398
19210
  deviceId: number(),
18399
19211
  trackId: string(),
@@ -19709,7 +20521,15 @@ DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), ar
19709
20521
  */
19710
20522
  disableIpv6: boolean().optional(),
19711
20523
  /** Subscriber attribution. See `createSession` for the contract. */
19712
- consumerAttribution: BrokerConsumerAttributionSchema.optional()
20524
+ consumerAttribution: BrokerConsumerAttributionSchema.optional(),
20525
+ /**
20526
+ * Client-side stream hints (viewport, downlink) — same contract as
20527
+ * `createSession.hints`. Client-offer viewers (the RN viewer's
20528
+ * `WebrtcLiveSession`) negotiate via `handleOffer`, so without this
20529
+ * field their hints were silently stripped by the schema and the
20530
+ * adaptive ladder started blind on the client's display size.
20531
+ */
20532
+ hints: webrtcClientHintsSchema.optional()
19713
20533
  }), object({
19714
20534
  sessionId: string(),
19715
20535
  sdpAnswer: string()
@@ -21162,6 +21982,24 @@ DeviceType.Camera, method(object({
21162
21982
  deviceId: number(),
21163
21983
  status: OsdStatusSchema
21164
21984
  });
21985
+ var VehicleSchema = object({
21986
+ id: string(),
21987
+ name: string(),
21988
+ sampleCount: number().int().nonnegative(),
21989
+ coverMediaKey: string().optional(),
21990
+ /** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
21991
+ coverBase64: string().optional()
21992
+ });
21993
+ var VehicleSampleInfoSchema = object({
21994
+ id: string(),
21995
+ /** The plate text this sample enrolled (self-labeling — no embedding). */
21996
+ text: string(),
21997
+ /** OCR confidence of the enrolled read (0..1). */
21998
+ score: number(),
21999
+ addedAt: number().int(),
22000
+ deviceId: number().int().optional(),
22001
+ base64: string().optional()
22002
+ });
21165
22003
  var PlateInfoSchema = object({
21166
22004
  plateId: string(),
21167
22005
  deviceId: number().int(),
@@ -21173,6 +22011,16 @@ var PlateInfoSchema = object({
21173
22011
  score: number(),
21174
22012
  /** True when the text was manually corrected (exempt from retention). */
21175
22013
  corrected: boolean(),
22014
+ /** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
22015
+ recognizedVehicleId: string().optional(),
22016
+ /** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
22017
+ vehicleName: string().optional(),
22018
+ /** True once the read was assigned to a vehicle (enrolled as a sample). */
22019
+ assigned: boolean(),
22020
+ /** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
22021
+ plateBbox: BoundingBoxSchema.optional(),
22022
+ /** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
22023
+ keyFrameMediaKey: string().optional(),
21176
22024
  base64: string().optional()
21177
22025
  });
21178
22026
  var MediaFileLiteSchema = object({
@@ -21212,6 +22060,48 @@ method(object({
21212
22060
  }), method(object({ plateId: string() }), _void(), {
21213
22061
  kind: "mutation",
21214
22062
  auth: "admin"
22063
+ }), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
22064
+ kind: "mutation",
22065
+ auth: "admin"
22066
+ }), method(object({
22067
+ id: string(),
22068
+ name: string().min(1)
22069
+ }), _void(), {
22070
+ kind: "mutation",
22071
+ auth: "admin"
22072
+ }), method(object({ id: string() }), _void(), {
22073
+ kind: "mutation",
22074
+ auth: "admin"
22075
+ }), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
22076
+ vehicleId: string(),
22077
+ sampleId: string()
22078
+ }), _void(), {
22079
+ kind: "mutation",
22080
+ auth: "admin"
22081
+ }), method(object({
22082
+ plateId: string(),
22083
+ vehicleId: string()
22084
+ }), _void(), {
22085
+ kind: "mutation",
22086
+ auth: "admin"
22087
+ }), method(object({ plateId: string() }), _void(), {
22088
+ kind: "mutation",
22089
+ auth: "admin"
22090
+ }), method(object({
22091
+ plateIds: array(string()).min(1),
22092
+ vehicleId: string()
22093
+ }), object({
22094
+ assigned: number().int(),
22095
+ failed: array(string()).readonly()
22096
+ }), {
22097
+ kind: "mutation",
22098
+ auth: "admin"
22099
+ }), method(object({ plateIds: array(string()).min(1) }), object({
22100
+ unassigned: number().int(),
22101
+ failed: array(string()).readonly()
22102
+ }), {
22103
+ kind: "mutation",
22104
+ auth: "admin"
21215
22105
  });
21216
22106
  var ModelFormatSchema = _enum(MODEL_FORMATS);
21217
22107
  var HwAccelBackendInputSchema = _enum([
@@ -21290,6 +22180,10 @@ var GpuInfoSchema = object({
21290
22180
  memoryMB: number().optional()
21291
22181
  });
21292
22182
  var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
22183
+ var CoralInfoSchema = object({
22184
+ type: literal("coral-edgetpu"),
22185
+ bus: string().optional()
22186
+ });
21293
22187
  var HardwareInfoSchema = object({
21294
22188
  platform: HardwarePlatformSchema,
21295
22189
  arch: HardwareArchSchema,
@@ -21298,7 +22192,8 @@ var HardwareInfoSchema = object({
21298
22192
  totalRAM_MB: number(),
21299
22193
  availableRAM_MB: number(),
21300
22194
  gpu: GpuInfoSchema.nullable(),
21301
- npu: NpuInfoSchema.nullable()
22195
+ npu: NpuInfoSchema.nullable(),
22196
+ coral: CoralInfoSchema.nullable().optional()
21302
22197
  });
21303
22198
  var PlatformScoreSchema = object({
21304
22199
  runtime: _enum(["node", "python"]),
@@ -21465,7 +22360,12 @@ var RecordingStorageUsageSchema = object({
21465
22360
  /**
21466
22361
  * Result of locating footage at a wall-clock instant for one device/profile.
21467
22362
  * `segment` carries the covering segment's window; `gap` reports the forward
21468
- * nearest covered edge (`null` past the end of footage / when none exists).
22363
+ * nearest covered edge (`null` past the end of footage / when none exists)
22364
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
22365
+ * footage behind the epoch; `null` when none — optional so older providers
22366
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
22367
+ * small inter-segment cracks (durMs under-covers the span to the next
22368
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
21469
22369
  */
21470
22370
  var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
21471
22371
  kind: literal("segment"),
@@ -21474,7 +22374,8 @@ var LocateSegmentResultSchema = discriminatedUnion("kind", [object({
21474
22374
  bytes: number()
21475
22375
  }), object({
21476
22376
  kind: literal("gap"),
21477
- nearestEdgeMs: number().nullable()
22377
+ nearestEdgeMs: number().nullable(),
22378
+ prevEndMs: number().nullable().optional()
21478
22379
  })]);
21479
22380
  /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
21480
22381
  var ReadSegmentBytesResultSchema = object({ data: _instanceof(Uint8Array) });
@@ -23043,6 +23944,12 @@ Object.freeze({
23043
23944
  addonId: null,
23044
23945
  access: "view"
23045
23946
  },
23947
+ "deviceManager.getLinkedDevices": {
23948
+ capName: "device-manager",
23949
+ capScope: "system",
23950
+ addonId: null,
23951
+ access: "view"
23952
+ },
23046
23953
  "deviceManager.getRoleDisplayDefaults": {
23047
23954
  capName: "device-manager",
23048
23955
  capScope: "system",
@@ -23757,6 +24664,168 @@ Object.freeze({
23757
24664
  addonId: null,
23758
24665
  access: "create"
23759
24666
  },
24667
+ "llm.deleteModel": {
24668
+ capName: "llm",
24669
+ capScope: "system",
24670
+ addonId: null,
24671
+ access: "delete"
24672
+ },
24673
+ "llm.deleteProfile": {
24674
+ capName: "llm",
24675
+ capScope: "system",
24676
+ addonId: null,
24677
+ access: "delete"
24678
+ },
24679
+ "llm.generate": {
24680
+ capName: "llm",
24681
+ capScope: "system",
24682
+ addonId: null,
24683
+ access: "create"
24684
+ },
24685
+ "llm.generateVision": {
24686
+ capName: "llm",
24687
+ capScope: "system",
24688
+ addonId: null,
24689
+ access: "create"
24690
+ },
24691
+ "llm.getDefaults": {
24692
+ capName: "llm",
24693
+ capScope: "system",
24694
+ addonId: null,
24695
+ access: "view"
24696
+ },
24697
+ "llm.getRuntimeStatus": {
24698
+ capName: "llm",
24699
+ capScope: "system",
24700
+ addonId: null,
24701
+ access: "view"
24702
+ },
24703
+ "llm.getUsage": {
24704
+ capName: "llm",
24705
+ capScope: "system",
24706
+ addonId: null,
24707
+ access: "view"
24708
+ },
24709
+ "llm.installModel": {
24710
+ capName: "llm",
24711
+ capScope: "system",
24712
+ addonId: null,
24713
+ access: "create"
24714
+ },
24715
+ "llm.listModelCatalog": {
24716
+ capName: "llm",
24717
+ capScope: "system",
24718
+ addonId: null,
24719
+ access: "view"
24720
+ },
24721
+ "llm.listModels": {
24722
+ capName: "llm",
24723
+ capScope: "system",
24724
+ addonId: null,
24725
+ access: "view"
24726
+ },
24727
+ "llm.listNodeModels": {
24728
+ capName: "llm",
24729
+ capScope: "system",
24730
+ addonId: null,
24731
+ access: "view"
24732
+ },
24733
+ "llm.listProfileKinds": {
24734
+ capName: "llm",
24735
+ capScope: "system",
24736
+ addonId: null,
24737
+ access: "view"
24738
+ },
24739
+ "llm.listProfiles": {
24740
+ capName: "llm",
24741
+ capScope: "system",
24742
+ addonId: null,
24743
+ access: "view"
24744
+ },
24745
+ "llm.listRuntimeNodes": {
24746
+ capName: "llm",
24747
+ capScope: "system",
24748
+ addonId: null,
24749
+ access: "view"
24750
+ },
24751
+ "llm.setDefault": {
24752
+ capName: "llm",
24753
+ capScope: "system",
24754
+ addonId: null,
24755
+ access: "create"
24756
+ },
24757
+ "llm.startRuntime": {
24758
+ capName: "llm",
24759
+ capScope: "system",
24760
+ addonId: null,
24761
+ access: "create"
24762
+ },
24763
+ "llm.stopRuntime": {
24764
+ capName: "llm",
24765
+ capScope: "system",
24766
+ addonId: null,
24767
+ access: "create"
24768
+ },
24769
+ "llm.testProfile": {
24770
+ capName: "llm",
24771
+ capScope: "system",
24772
+ addonId: null,
24773
+ access: "create"
24774
+ },
24775
+ "llm.upsertProfile": {
24776
+ capName: "llm",
24777
+ capScope: "system",
24778
+ addonId: null,
24779
+ access: "create"
24780
+ },
24781
+ "llmRuntime.complete": {
24782
+ capName: "llm-runtime",
24783
+ capScope: "system",
24784
+ addonId: null,
24785
+ access: "create"
24786
+ },
24787
+ "llmRuntime.deleteModel": {
24788
+ capName: "llm-runtime",
24789
+ capScope: "system",
24790
+ addonId: null,
24791
+ access: "delete"
24792
+ },
24793
+ "llmRuntime.ensureStarted": {
24794
+ capName: "llm-runtime",
24795
+ capScope: "system",
24796
+ addonId: null,
24797
+ access: "create"
24798
+ },
24799
+ "llmRuntime.getDiskUsage": {
24800
+ capName: "llm-runtime",
24801
+ capScope: "system",
24802
+ addonId: null,
24803
+ access: "view"
24804
+ },
24805
+ "llmRuntime.installModel": {
24806
+ capName: "llm-runtime",
24807
+ capScope: "system",
24808
+ addonId: null,
24809
+ access: "create"
24810
+ },
24811
+ "llmRuntime.listLocalModels": {
24812
+ capName: "llm-runtime",
24813
+ capScope: "system",
24814
+ addonId: null,
24815
+ access: "view"
24816
+ },
24817
+ "llmRuntime.status": {
24818
+ capName: "llm-runtime",
24819
+ capScope: "system",
24820
+ addonId: null,
24821
+ access: "view"
24822
+ },
24823
+ "llmRuntime.stop": {
24824
+ capName: "llm-runtime",
24825
+ capScope: "system",
24826
+ addonId: null,
24827
+ access: "create"
24828
+ },
23760
24829
  "localNetwork.getAllowedAddresses": {
23761
24830
  capName: "local-network",
23762
24831
  capScope: "system",
@@ -24387,6 +25456,12 @@ Object.freeze({
24387
25456
  addonId: null,
24388
25457
  access: "delete"
24389
25458
  },
25459
+ "pipelineAnalytics.deleteTracks": {
25460
+ capName: "pipeline-analytics",
25461
+ capScope: "device",
25462
+ addonId: null,
25463
+ access: "delete"
25464
+ },
24390
25465
  "pipelineAnalytics.getActiveTracks": {
24391
25466
  capName: "pipeline-analytics",
24392
25467
  capScope: "device",
@@ -24429,6 +25504,12 @@ Object.freeze({
24429
25504
  addonId: null,
24430
25505
  access: "view"
24431
25506
  },
25507
+ "pipelineAnalytics.getSensorEvents": {
25508
+ capName: "pipeline-analytics",
25509
+ capScope: "device",
25510
+ addonId: null,
25511
+ access: "view"
25512
+ },
24432
25513
  "pipelineAnalytics.getTrack": {
24433
25514
  capName: "pipeline-analytics",
24434
25515
  capScope: "device",
@@ -24441,6 +25522,18 @@ Object.freeze({
24441
25522
  addonId: null,
24442
25523
  access: "view"
24443
25524
  },
25525
+ "pipelineAnalytics.listEventKinds": {
25526
+ capName: "pipeline-analytics",
25527
+ capScope: "device",
25528
+ addonId: null,
25529
+ access: "view"
25530
+ },
25531
+ "pipelineAnalytics.listRecentTracks": {
25532
+ capName: "pipeline-analytics",
25533
+ capScope: "device",
25534
+ addonId: null,
25535
+ access: "view"
25536
+ },
24444
25537
  "pipelineAnalytics.listTracks": {
24445
25538
  capName: "pipeline-analytics",
24446
25539
  capScope: "device",
@@ -24453,12 +25546,24 @@ Object.freeze({
24453
25546
  addonId: null,
24454
25547
  access: "create"
24455
25548
  },
25549
+ "pipelineAnalytics.pruneTracksBefore": {
25550
+ capName: "pipeline-analytics",
25551
+ capScope: "device",
25552
+ addonId: null,
25553
+ access: "create"
25554
+ },
24456
25555
  "pipelineAnalytics.searchObjectEvents": {
24457
25556
  capName: "pipeline-analytics",
24458
25557
  capScope: "device",
24459
25558
  addonId: null,
24460
25559
  access: "view"
24461
25560
  },
25561
+ "pipelineAnalytics.wipeAllAnalytics": {
25562
+ capName: "pipeline-analytics",
25563
+ capScope: "device",
25564
+ addonId: null,
25565
+ access: "delete"
25566
+ },
24462
25567
  "pipelineExecutor.cacheFrameInPool": {
24463
25568
  capName: "pipeline-executor",
24464
25569
  capScope: "system",
@@ -24963,18 +26068,42 @@ Object.freeze({
24963
26068
  addonId: null,
24964
26069
  access: "create"
24965
26070
  },
26071
+ "plateGallery.assignPlate": {
26072
+ capName: "plate-gallery",
26073
+ capScope: "system",
26074
+ addonId: null,
26075
+ access: "create"
26076
+ },
26077
+ "plateGallery.assignPlates": {
26078
+ capName: "plate-gallery",
26079
+ capScope: "system",
26080
+ addonId: null,
26081
+ access: "create"
26082
+ },
24966
26083
  "plateGallery.correctPlateText": {
24967
26084
  capName: "plate-gallery",
24968
26085
  capScope: "system",
24969
26086
  addonId: null,
24970
26087
  access: "create"
24971
26088
  },
26089
+ "plateGallery.createVehicle": {
26090
+ capName: "plate-gallery",
26091
+ capScope: "system",
26092
+ addonId: null,
26093
+ access: "create"
26094
+ },
24972
26095
  "plateGallery.deletePlate": {
24973
26096
  capName: "plate-gallery",
24974
26097
  capScope: "system",
24975
26098
  addonId: null,
24976
26099
  access: "delete"
24977
26100
  },
26101
+ "plateGallery.deleteVehicle": {
26102
+ capName: "plate-gallery",
26103
+ capScope: "system",
26104
+ addonId: null,
26105
+ access: "delete"
26106
+ },
24978
26107
  "plateGallery.getPlateByTrack": {
24979
26108
  capName: "plate-gallery",
24980
26109
  capScope: "system",
@@ -24993,6 +26122,30 @@ Object.freeze({
24993
26122
  addonId: null,
24994
26123
  access: "view"
24995
26124
  },
26125
+ "plateGallery.listVehicles": {
26126
+ capName: "plate-gallery",
26127
+ capScope: "system",
26128
+ addonId: null,
26129
+ access: "view"
26130
+ },
26131
+ "plateGallery.listVehicleSamples": {
26132
+ capName: "plate-gallery",
26133
+ capScope: "system",
26134
+ addonId: null,
26135
+ access: "view"
26136
+ },
26137
+ "plateGallery.removeVehicleSample": {
26138
+ capName: "plate-gallery",
26139
+ capScope: "system",
26140
+ addonId: null,
26141
+ access: "delete"
26142
+ },
26143
+ "plateGallery.renameVehicle": {
26144
+ capName: "plate-gallery",
26145
+ capScope: "system",
26146
+ addonId: null,
26147
+ access: "create"
26148
+ },
24996
26149
  "plateGallery.searchPlates": {
24997
26150
  capName: "plate-gallery",
24998
26151
  capScope: "system",
@@ -25005,6 +26158,18 @@ Object.freeze({
25005
26158
  addonId: null,
25006
26159
  access: "view"
25007
26160
  },
26161
+ "plateGallery.unassignPlate": {
26162
+ capName: "plate-gallery",
26163
+ capScope: "system",
26164
+ addonId: null,
26165
+ access: "create"
26166
+ },
26167
+ "plateGallery.unassignPlates": {
26168
+ capName: "plate-gallery",
26169
+ capScope: "system",
26170
+ addonId: null,
26171
+ access: "create"
26172
+ },
25008
26173
  "platformProbe.getCapabilities": {
25009
26174
  capName: "platform-probe",
25010
26175
  capScope: "system",
@@ -25197,6 +26362,48 @@ Object.freeze({
25197
26362
  addonId: null,
25198
26363
  access: "create"
25199
26364
  },
26365
+ "sceneMonitor.captureReference": {
26366
+ capName: "scene-monitor",
26367
+ capScope: "device",
26368
+ addonId: null,
26369
+ access: "create"
26370
+ },
26371
+ "sceneMonitor.createScene": {
26372
+ capName: "scene-monitor",
26373
+ capScope: "device",
26374
+ addonId: null,
26375
+ access: "create"
26376
+ },
26377
+ "sceneMonitor.deleteReference": {
26378
+ capName: "scene-monitor",
26379
+ capScope: "device",
26380
+ addonId: null,
26381
+ access: "delete"
26382
+ },
26383
+ "sceneMonitor.deleteScene": {
26384
+ capName: "scene-monitor",
26385
+ capScope: "device",
26386
+ addonId: null,
26387
+ access: "delete"
26388
+ },
26389
+ "sceneMonitor.listScenes": {
26390
+ capName: "scene-monitor",
26391
+ capScope: "device",
26392
+ addonId: null,
26393
+ access: "view"
26394
+ },
26395
+ "sceneMonitor.recheckNow": {
26396
+ capName: "scene-monitor",
26397
+ capScope: "device",
26398
+ addonId: null,
26399
+ access: "create"
26400
+ },
26401
+ "sceneMonitor.updateScene": {
26402
+ capName: "scene-monitor",
26403
+ capScope: "device",
26404
+ addonId: null,
26405
+ access: "create"
26406
+ },
25200
26407
  "scriptRunner.run": {
25201
26408
  capName: "script-runner",
25202
26409
  capScope: "device",
@@ -26281,6 +27488,7 @@ Object.freeze({
26281
27488
  Object.freeze({
26282
27489
  "broker": "broker",
26283
27490
  "device-export": "device-export",
27491
+ "llm": "ai",
26284
27492
  "mesh-network": "mesh",
26285
27493
  "mqtt-broker": "broker",
26286
27494
  "network-access": "ingress",
@@ -52223,4 +53431,4 @@ var HomematicProviderAddon = class extends BaseDeviceProvider {
52223
53431
  }
52224
53432
  };
52225
53433
  //#endregion
52226
- export { motionCapability as C, createEvent as D, EventCategory as E, HomematicProviderAddon, object as O, lockControlCapability as S, DeviceType as T, toHomematicOptions as _, decodeClimate as a, coverCapability as b, toHmClimateMode as c, mapHmChannel as d, mapHmDeviceChannels as f, homematicConfigSchema as g, homematicAddonConfigSchema as h, registerTemperatureCap as i, string as k, deriveHmEntitySpecs as l, buildConnectionFormSchema as m, registerHumidityCap as n, decodeCover as o, homematicFacades as p, registerPowerMeterCap as r, decodeLock as s, registerAmbientLightCap as t, inferHmDeviceType as u, BaseDevice as v, switchCapability as w, eventEmitterCapability as x, climateControlCapability as y };
53434
+ export { motionCapability as C, object as D, createEvent as E, HomematicProviderAddon, string as O, lockControlCapability as S, DeviceType as T, toHomematicOptions as _, decodeClimate as a, coverCapability as b, toHmClimateMode as c, mapHmChannel as d, mapHmDeviceChannels as f, homematicConfigSchema as g, homematicAddonConfigSchema as h, registerTemperatureCap as i, EventCategory as k, deriveHmEntitySpecs as l, buildConnectionFormSchema as m, registerHumidityCap as n, decodeCover as o, homematicFacades as p, registerPowerMeterCap as r, decodeLock as s, registerAmbientLightCap as t, inferHmDeviceType as u, BaseDevice as v, switchCapability as w, eventEmitterCapability as x, climateControlCapability as y };