@camstack/addon-pipeline 1.2.293 → 1.2.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/THIRD_PARTY_MODELS.md +233 -0
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/{default-detection-model-DCQlW9fx.mjs → default-detection-model-CAUBVbgK.mjs} +53 -19
  5. package/dist/{default-detection-model-DTKHLLcC.js → default-detection-model-wNEISVA9.js} +58 -18
  6. package/dist/detection-pipeline/index.js +286 -458
  7. package/dist/detection-pipeline/index.mjs +286 -458
  8. package/dist/{dist-Ce8s7XqV.js → dist-BsVcf5wO.js} +1321 -833
  9. package/dist/{dist-C0Fr4hM5.mjs → dist-CuxSNLKW.mjs} +1316 -834
  10. package/dist/motion-wasm/index.js +1 -1
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-Xm7amG2e.mjs → node-Cqb1QiXk.mjs} +1 -1
  13. package/dist/{node-DpOrymbW.js → node-UFk6I2f6.js} +1 -1
  14. package/dist/pipeline-runner/index.js +266 -72
  15. package/dist/pipeline-runner/index.mjs +266 -72
  16. package/dist/{process-memory-CBRjuhyg.js → process-memory-CJV29sPf.js} +1 -1
  17. package/dist/{process-memory-BN5qYGsD.mjs → process-memory-D0Nvs9rr.mjs} +1 -1
  18. package/dist/recorder/index.js +12 -2
  19. package/dist/recorder/index.mjs +12 -2
  20. package/dist/{segment-demux-js-BhMlS_Ow.js → segment-demux-js-DGwmf5hu.js} +1 -1
  21. package/dist/{segment-demux-js-Bhwpj5Wl.mjs → segment-demux-js-DIDWw1iE.mjs} +1 -1
  22. package/dist/stream-broker/_stub.js +2 -2
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DKd7oYba.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B7nBFqva.mjs} +2 -2
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CCIyBvRa.mjs +26 -0
  25. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DoyA71_q.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DeD_UFdb.mjs} +1 -1
  26. package/dist/stream-broker/demux-worker-child.js +1 -1
  27. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  28. package/dist/stream-broker/{hostInit-CCMIHjff.mjs → hostInit-BKlb3qac.mjs} +2 -2
  29. package/dist/stream-broker/index.js +3 -3
  30. package/dist/stream-broker/index.mjs +3 -3
  31. package/dist/stream-broker/remoteEntry.js +1 -1
  32. package/package.json +3 -2
  33. package/python/postprocessors/__init__.py +2 -0
  34. package/python/postprocessors/ssd.py +73 -17
  35. package/python/postprocessors/test_ssd.py +205 -0
  36. package/python/postprocessors/test_yunet.py +292 -0
  37. package/python/postprocessors/testdata/ssdlite_mobiledet_outputs.json +1 -0
  38. package/python/postprocessors/yunet.py +275 -0
  39. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6IHzlLJ_.mjs +0 -26
@@ -5360,7 +5360,7 @@ var ZodIssueCode = {
5360
5360
  var ZodFirstPartyTypeKind;
5361
5361
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5362
5362
  //#endregion
5363
- //#region ../types/dist/sleep-DU7mu4wb.mjs
5363
+ //#region ../types/dist/sleep-BaRu0bS5.mjs
5364
5364
  /**
5365
5365
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5366
5366
  * window to float samples (D455).
@@ -11327,7 +11327,7 @@ var ModelFormatEntrySchema = object({
11327
11327
  /** Multi-file payload (directory members or sibling files). */
11328
11328
  files: array(string()).readonly().optional(),
11329
11329
  /** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
11330
- runtimes: array(_enum(["node", "python"])).readonly().optional()
11330
+ runtimes: array(_enum(["python"])).readonly().optional()
11331
11331
  });
11332
11332
  /**
11333
11333
  * Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
@@ -19852,6 +19852,29 @@ var NC_LEGACY_SYSTEM_EVENT_KINDS = new Set([
19852
19852
  ]);
19853
19853
  NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
19854
19854
  /**
19855
+ * The panel's transitions, as ONE list.
19856
+ *
19857
+ * Named here rather than spelled out at each of the four sites that need them
19858
+ * (the emitter, the intake, the editor group, the combined-notification gate),
19859
+ * because a transition added to the enum and forgotten at one of them is an
19860
+ * alarm state nobody can be notified about. That is exactly what this list
19861
+ * failed to prevent while it held three: `alarm-arming` was the fourth.
19862
+ *
19863
+ * In LIFECYCLE order, which is the order an editor lists them in and the order
19864
+ * a person experiences them — not the order they were added.
19865
+ */
19866
+ var NC_ALARM_SYSTEM_EVENT_KINDS = [
19867
+ "alarm-arm-refused",
19868
+ "alarm-check-failed",
19869
+ "alarm-arming",
19870
+ "alarm-armed",
19871
+ "alarm-pending",
19872
+ "alarm-triggered",
19873
+ "alarm-rearmed",
19874
+ "alarm-sensor-bypass",
19875
+ "alarm-disarmed"
19876
+ ];
19877
+ /**
19855
19878
  * One coherent system-event condition. `kinds` is the required opt-in safety
19856
19879
  * gate; the remaining lists are optional narrowing filters relevant to the
19857
19880
  * selected kinds.
@@ -19918,6 +19941,25 @@ var NcScheduleSchema = object({
19918
19941
  /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
19919
19942
  invert: boolean().optional()
19920
19943
  });
19944
+ /**
19945
+ * How a system-event rule of the `device-events` or `system` family speaks
19946
+ * (D647): at once, per event (`instant`, the only behaviour before D647), or
19947
+ * as a STATE SUMMARY at the end of each window of `schedule`.
19948
+ */
19949
+ var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
19950
+ /**
19951
+ * `summary`: the rule sends NOTHING per event. When a window of `schedule`
19952
+ * CLOSES, it sends one notification with the current state of its kinds
19953
+ * (low batteries, consumables to replace, devices offline, updates
19954
+ * available, ...), after its own filters. Nothing to report sends nothing.
19955
+ * The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
19956
+ * midnight crossing); the send time is each window's END. Refused on the
19957
+ * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
19958
+ */
19959
+ var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
19960
+ mode: literal("summary"),
19961
+ schedule: NcScheduleSchema
19962
+ })]);
19921
19963
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
19922
19964
  var NcPlateMatcherSchema = object({
19923
19965
  /**
@@ -20653,6 +20695,16 @@ var NcRuleInputSchema = object({
20653
20695
  delivery: NcDeliverySchema,
20654
20696
  conditions: NcConditionsSchema.default({}),
20655
20697
  schedule: NcScheduleSchema.optional(),
20698
+ /**
20699
+ * A `device-events` / `system` rule's delivery (D647): absent or `instant`
20700
+ * is a notification per event; `summary` is one state summary per closed
20701
+ * window of its schedule. `.optional()` and never defaulted: absent must
20702
+ * keep meaning what every rule authored before D647 meant, with no
20703
+ * migration. Top-level rather than inside `conditions.systemEvent`, so a
20704
+ * client that does not know it (an older viewer re-sending `conditions`
20705
+ * whole) leaves it standing through the store's patch merge.
20706
+ */
20707
+ systemEventDelivery: NcSystemEventDeliverySchema.optional(),
20656
20708
  /** May be empty when `targetUsers` addresses at least one user — the
20657
20709
  * "at least one addressee" invariant is enforced by the provider, because
20658
20710
  * a cross-field refine here would break `NcRulePatchSchema.partial()`. */
@@ -20881,6 +20933,28 @@ var NcRuleSchema = NcRuleInputSchema.extend({
20881
20933
  */
20882
20934
  disabledTargetIds: array(string()).default([])
20883
20935
  });
20936
+ /** One section of a previewed state summary. `unavailable` = its state could not be read. */
20937
+ var NcStateSummaryPreviewSectionSchema = object({
20938
+ id: string(),
20939
+ status: _enum(["read", "unavailable"]),
20940
+ items: number().int().nonnegative(),
20941
+ reason: string().optional()
20942
+ });
20943
+ /**
20944
+ * The summary a rule would send now. `empty` = nothing to report and every
20945
+ * section read: such a summary is NOT sent. `dropped` counts the open states
20946
+ * the rule's own filters (or a camera mute) left out, by reason.
20947
+ * `excludedGroups` names the kind groups selected that have no state to report
20948
+ * (backups & exports).
20949
+ */
20950
+ var NcStateSummaryPreviewSchema = object({
20951
+ empty: boolean(),
20952
+ count: number().int().nonnegative(),
20953
+ text: string(),
20954
+ sections: array(NcStateSummaryPreviewSectionSchema),
20955
+ dropped: record(string(), number().int().nonnegative()),
20956
+ excludedGroups: array(string())
20957
+ });
20884
20958
  var NcTestResultSchema = object({
20885
20959
  recordId: string(),
20886
20960
  recordKind: _enum([
@@ -20972,6 +21046,13 @@ var NcTemplateVarDescriptorSchema = object({
20972
21046
  kinds: array(NcTemplateVarKindSchema).optional(),
20973
21047
  deliveries: array(NcDeliverySchema).optional(),
20974
21048
  systemEventKinds: array(NcSystemEventKindSchema).optional(),
21049
+ /**
21050
+ * Which system-event DELIVERY has a value for it (D647). Absent = an
21051
+ * instant row only when the descriptor names `systemEventKinds` (a
21052
+ * per-event fact: a summary row has no single device), and both otherwise.
21053
+ * `['summary']` marks the state-summary vocabulary (`{{summaryText}}`, ...).
21054
+ */
21055
+ systemEventDeliveries: array(NcSystemEventDeliveryModeSchema).optional(),
20975
21056
  dynamic: object({
20976
21057
  prefix: literal("count_"),
20977
21058
  from: literal("classes")
@@ -20984,7 +21065,9 @@ var NcTemplatePreviewInputSchema = object({
20984
21065
  field: NcTemplateFieldSchema,
20985
21066
  kind: NcRuleKindSchema.optional(),
20986
21067
  delivery: NcDeliverySchema.optional(),
20987
- systemEventKinds: array(NcSystemEventKindSchema).optional()
21068
+ systemEventKinds: array(NcSystemEventKindSchema).optional(),
21069
+ /** The rule's system-event delivery (D647); absent = instant. */
21070
+ systemEventDelivery: NcSystemEventDeliveryModeSchema.optional()
20988
21071
  }),
20989
21072
  template: object({
20990
21073
  title: string().max(500).optional(),
@@ -21518,6 +21601,10 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
21518
21601
  }), object({ results: array(NcTestResultSchema) }), {
21519
21602
  kind: "mutation",
21520
21603
  auth: "admin"
21604
+ }), method(object({ rule: NcRuleInputSchema }), NcStateSummaryPreviewSchema, {
21605
+ kind: "mutation",
21606
+ auth: "admin",
21607
+ access: "view"
21521
21608
  }), method(object({}), object({
21522
21609
  catalog: array(NcConditionDescriptorSchema),
21523
21610
  taxonomy: NcTaxonomySchema.optional()
@@ -21864,790 +21951,6 @@ var OauthIntegrationDescriptorSchema = object({
21864
21951
  });
21865
21952
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
21866
21953
  /**
21867
- * Reference to the frame's retained NATIVE surface + the parent crop's placement
21868
- * within the frame, so the executor can re-cut a leaf child ROI at native
21869
- * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
21870
- */
21871
- var NativeCropRefSchema = object({
21872
- /** Handle keying the retained native surface (node-pinned to its owner). */
21873
- handle: FrameHandleSchema,
21874
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
21875
- cropFrameSpace: object({
21876
- x: number(),
21877
- y: number(),
21878
- w: number(),
21879
- h: number()
21880
- })
21881
- });
21882
- object({
21883
- crop: object({
21884
- left: number(),
21885
- top: number(),
21886
- width: number().positive(),
21887
- height: number().positive()
21888
- }).optional(),
21889
- content: object({
21890
- width: number().int().positive(),
21891
- height: number().int().positive()
21892
- }),
21893
- fit: _enum(["stretch", "contain"]),
21894
- format: _enum([
21895
- "rgb",
21896
- "gray",
21897
- "jpeg"
21898
- ])
21899
- });
21900
- /**
21901
- * Process-local frame identity. It is serializable so it can ride an in-process
21902
- * capability call, but `registryId` deliberately prevents resolution in any
21903
- * other process or execution group.
21904
- */
21905
- var FrameRefSchema = object({
21906
- registryId: string().min(1),
21907
- id: string().min(1),
21908
- width: number().int().positive(),
21909
- height: number().int().positive(),
21910
- format: _enum(["rgb", "gray"]),
21911
- timestamp: number(),
21912
- capturedAt: number().optional()
21913
- });
21914
- var ModelFormatSchema$1 = _enum([
21915
- "onnx",
21916
- "coreml",
21917
- "openvino",
21918
- "tflite",
21919
- "pt",
21920
- "gguf"
21921
- ]);
21922
- var PipelineSlotSchema = _enum([
21923
- "detector",
21924
- "cropper",
21925
- "classifier",
21926
- "refiner",
21927
- "audio-classifier"
21928
- ]);
21929
- var PipelineEngineChoiceSchema = object({
21930
- runtime: _enum(["node", "python"]),
21931
- backend: string(),
21932
- format: ModelFormatSchema$1,
21933
- device: string().optional()
21934
- });
21935
- var AvailableEngineSchema = object({
21936
- engine: PipelineEngineChoiceSchema,
21937
- devices: array(object({
21938
- id: string(),
21939
- label: string(),
21940
- description: string().optional()
21941
- })).readonly(),
21942
- defaultDevice: string()
21943
- });
21944
- var PipelineDefaultStepSchema = lazy(() => object({
21945
- addonId: string(),
21946
- addonName: string(),
21947
- slot: PipelineSlotSchema,
21948
- inputClasses: array(string()).readonly(),
21949
- outputClasses: array(string()).readonly(),
21950
- enabled: boolean(),
21951
- modelId: string(),
21952
- children: array(PipelineDefaultStepSchema).readonly(),
21953
- group: string().optional(),
21954
- settings: record(string(), unknown()).optional()
21955
- }));
21956
- var PipelineTemplateStepSchema = lazy(() => object({
21957
- addonId: string(),
21958
- enabled: boolean(),
21959
- modelId: string(),
21960
- children: array(PipelineTemplateStepSchema).readonly(),
21961
- settings: record(string(), unknown()).optional()
21962
- }));
21963
- var PipelineTemplateSchema$1 = object({
21964
- id: string(),
21965
- name: string(),
21966
- createdAt: string(),
21967
- updatedAt: string(),
21968
- engine: PipelineEngineChoiceSchema,
21969
- steps: array(PipelineTemplateStepSchema).readonly()
21970
- });
21971
- var PipelineModelOptionSchema = object({
21972
- id: string(),
21973
- name: string(),
21974
- formats: record(string(), object({
21975
- downloaded: boolean(),
21976
- sizeMB: number()
21977
- })),
21978
- group: ModelVariantGroupSchema.optional(),
21979
- legacy: boolean().optional(),
21980
- provider: ModelProviderIdSchema.optional()
21981
- });
21982
- var ConfigFieldBridge = custom();
21983
- var PipelineAddonSchemaSchema = object({
21984
- id: string(),
21985
- name: string(),
21986
- slot: PipelineSlotSchema,
21987
- inputClasses: array(string()).readonly(),
21988
- outputClasses: array(string()).readonly(),
21989
- childSlots: array(PipelineSlotSchema).readonly(),
21990
- models: array(PipelineModelOptionSchema).readonly(),
21991
- defaultModelId: string(),
21992
- defaultModelIdByFormat: record(string(), string()).optional(),
21993
- enabledByDefault: boolean().optional(),
21994
- backfillIntoExistingOverrides: boolean().optional(),
21995
- defaultConfidence: number(),
21996
- group: string().optional(),
21997
- configSchema: array(ConfigFieldBridge).readonly().optional()
21998
- });
21999
- var PipelineSlotSchemaSchema = object({
22000
- id: PipelineSlotSchema,
22001
- label: string(),
22002
- priority: number(),
22003
- parentSlot: PipelineSlotSchema.nullable(),
22004
- addons: array(PipelineAddonSchemaSchema).readonly()
22005
- });
22006
- var PipelineSchemaSchema = object({
22007
- availableEngines: array(AvailableEngineSchema).readonly(),
22008
- selectedEngine: PipelineEngineChoiceSchema,
22009
- slots: array(PipelineSlotSchemaSchema).readonly()
22010
- });
22011
- var EngineProvisioningSchema = object({
22012
- runtimeId: _enum([
22013
- "onnx",
22014
- "openvino",
22015
- "coreml",
22016
- "edgetpu"
22017
- ]).nullable(),
22018
- device: string().nullable(),
22019
- state: _enum([
22020
- "idle",
22021
- "installing",
22022
- "verifying",
22023
- "ready",
22024
- "failed"
22025
- ]),
22026
- progress: number().optional(),
22027
- error: string().optional(),
22028
- nextRetryAt: number().optional(),
22029
- /**
22030
- * Gate A (config-correctness gate at engine change): human-readable
22031
- * config issues surfaced EAGERLY when the node's engine changes — model
22032
- * substitutions ("chose X, running Y") and zero-build steps ("no model
22033
- * has a <format> build"). Additive/optional: informational only, never
22034
- * enforced here — `assertEngineReady` (readiness) still gates inference.
22035
- * Absent/empty when the node-default tree resolves cleanly.
22036
- */
22037
- configIssues: array(string()).optional()
22038
- });
22039
- var PipelineStepInputSchema = lazy(() => object({
22040
- addonId: string(),
22041
- modelId: string().optional(),
22042
- enabled: boolean().default(true),
22043
- children: array(PipelineStepInputSchema).optional(),
22044
- settings: record(string(), unknown()).optional(),
22045
- jumpDeviceKey: string().optional()
22046
- }));
22047
- var ModelSubstitutionSchema = object({
22048
- addonId: string(),
22049
- chosen: string(),
22050
- running: string(),
22051
- format: string()
22052
- });
22053
- var PipelineValidationIssueSchema = object({
22054
- addonId: string(),
22055
- kind: _enum(["unknown-addon", "no-format-build"]),
22056
- detail: string()
22057
- });
22058
- var PipelineValidationResultSchema = object({
22059
- ok: boolean(),
22060
- issues: array(PipelineValidationIssueSchema).readonly(),
22061
- substitutions: array(ModelSubstitutionSchema).readonly(),
22062
- /** The node's `currentEngine.format` this validation ran against. */
22063
- format: string()
22064
- });
22065
- var ReferenceImageEntrySchema = object({
22066
- filename: string(),
22067
- stepIds: array(string()).readonly().optional()
22068
- });
22069
- var ReferenceImageBodySchema = object({
22070
- base64: string(),
22071
- filename: string()
22072
- });
22073
- var ReferenceAudioEntrySchema = object({
22074
- filename: string(),
22075
- sizeKb: number()
22076
- });
22077
- var ReferenceAudioBodySchema = object({ base64: string() });
22078
- var AudioBackendSchema = object({
22079
- id: string(),
22080
- name: string(),
22081
- description: string(),
22082
- available: boolean(),
22083
- /**
22084
- * Raw classifier labels this backend can emit (e.g. YAMNet's
22085
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
22086
- * the benchmark UI to populate the `enabledMicroClasses` filter
22087
- * specific to the selected backend without a separate fetch.
22088
- */
22089
- rawLabels: array(string()).readonly().optional()
22090
- });
22091
- var AudioCapabilitiesSchema = object({
22092
- activeBackend: string(),
22093
- availableBackends: array(AudioBackendSchema).readonly(),
22094
- sampleRate: number(),
22095
- chunkDurationMs: number()
22096
- });
22097
- var DownloadModelResultSchema = object({
22098
- filePath: string(),
22099
- sizeMB: number(),
22100
- durationMs: number()
22101
- });
22102
- /**
22103
- * Wrapper carrying a single test run's result. Replaces the legacy
22104
- * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
22105
- * canonical `AudioResult` from the Phase 6 output rework: one
22106
- * `AudioDetection` per class above `minScore`, top-N candidates in
22107
- * `debug.alternateLabels['audio-classifier']`, per-source timings in
22108
- * `debug.stepTimings`. The outer `success`/`error` fields stay so the
22109
- * benchmark UI can still report a clean failure when the classifier
22110
- * cap isn't available.
22111
- */
22112
- var AudioTestResultSchema = object({
22113
- success: boolean(),
22114
- error: string().optional(),
22115
- frame: custom().optional()
22116
- });
22117
- var PipelineConfigBridge = custom();
22118
- var ConfigUISchemaBridge = custom();
22119
- var ConfigUISchemaNullableBridge = custom();
22120
- var InferenceCapabilitiesBridge = custom();
22121
- var ModelAvailabilityListBridge = custom();
22122
- var PipelineRunResultBridge = custom();
22123
- /**
22124
- * Pipeline executor — detection engine + configuration + inference API.
22125
- *
22126
- * Merged from: pipeline-executor, pipeline-config, inference, detection-config.
22127
- * Implemented by the detection-pipeline addon.
22128
- *
22129
- * Per-device surface (DeviceSettingsContribution + the "is detection
22130
- * enabled for this camera?" toggle) lives on the paired
22131
- * `detection-pipeline` cap (device-scoped, singleton, wrapper
22132
- * defaultActive) — same split pattern used by stream-broker /
22133
- * camera-streams and audio-analyzer / audio-analysis.
22134
- */
22135
- var pipelineExecutorCapability = {
22136
- name: "pipeline-executor",
22137
- scope: "system",
22138
- mode: "singleton",
22139
- methods: {
22140
- getAvailableEngines: method(_void(), array(PipelineEngineChoiceSchema)),
22141
- getSelectedEngine: method(_void(), PipelineEngineChoiceSchema),
22142
- getDefaultSteps: method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)),
22143
- /**
22144
- * Per-node detection-engine provisioning snapshot. Returns the live
22145
- * state of the lazy runtime-provisioning machine on `nodeId`
22146
- * (idle / installing / verifying / ready / failed). The UI pairs this
22147
- * one-shot query with the `pipeline.engine-provisioning` live event
22148
- * (emitted on every transition) to drive a per-node "engine ready?"
22149
- * indicator without polling. Phase 2.
22150
- */
22151
- getEngineProvisioning: method(object({ nodeId: string() }), EngineProvisioningSchema),
22152
- getVideoPipelineSteps: method(_void(), record(string(), object({
22153
- modelId: string(),
22154
- settings: record(string(), unknown()).readonly()
22155
- }))),
22156
- setVideoPipelineSteps: method(object({ steps: record(string(), object({
22157
- modelId: string(),
22158
- settings: record(string(), unknown()).readonly()
22159
- })) }), object({ success: literal(true) }), {
22160
- kind: "mutation",
22161
- auth: "admin"
22162
- }),
22163
- /**
22164
- * Clear THIS node's executor-side PER-DEVICE settings stores (the
22165
- * per-camera step overrides the object-detection root reads via
22166
- * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
22167
- * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
22168
- * dispatches to that node, so the provider method runs ON the target
22169
- * node and receives no `nodeId`.
22170
- *
22171
- * This is the slimmed executor leg of the orchestrator's
22172
- * `resetNodePipelineDefaults` flow (which owns the real reset: node
22173
- * addonDefaults pins + per-camera orchestrator overrides). The legacy
22174
- * `resetToDefault` — which reset a persisted global step-tree seed
22175
- * nothing in the live per-camera path read — was removed together with
22176
- * that seed.
22177
- */
22178
- clearDeviceOverrides: method(object({ nodeId: string() }), object({
22179
- success: literal(true),
22180
- clearedDevices: number()
22181
- }), {
22182
- kind: "mutation",
22183
- auth: "admin"
22184
- }),
22185
- /**
22186
- * Which of THIS node's inference devices the executor currently refuses,
22187
- * and why. `nodeId` is the ROUTING key (stripped by the generated router).
22188
- *
22189
- * The channel that did not exist. Pool health was known only inside the
22190
- * detection addon and was an input to no routing decision anywhere: the
22191
- * per-dispatch capability gate is keyed on model FORMAT and so can never
22192
- * separate `openvino:gpu` from `openvino:npu`, and the orchestrator's live
22193
- * eligibility probe (`platformProbe.getCapabilities`) answers about
22194
- * HARDWARE — which was present throughout. So when the hub's `openvino:gpu`
22195
- * Python worker was SIGABRT'd by the Intel GPU plugin on 2026-08-25, the
22196
- * balancer went on handing that dead pool cameras by rotation for 31 hours:
22197
- * ~370 000 `PoolWorker[w0]: not initialized` lines, every frame lost.
22198
- *
22199
- * Read semantics the caller depends on, and which the provider guarantees:
22200
- * this is a synchronous read of in-memory state. It never probes hardware,
22201
- * never spawns a pool and never throws — an EMPTY `unhealthy` means "asked,
22202
- * nothing is refused", which is what re-admits a device. A read that FAILS
22203
- * (node offline, version skew) must therefore be distinguishable from an
22204
- * empty answer, and it is: it rejects.
22205
- */
22206
- getInferenceDeviceHealth: method(object({ nodeId: string() }), object({ unhealthy: array(object({
22207
- /** `<backend>:<device>`, e.g. `openvino:gpu`. */
22208
- deviceKey: string(),
22209
- /**
22210
- * `failed` — the per-device restart budget is exhausted; no pool
22211
- * will be spawned until an operator re-arms it or the runner
22212
- * respawns. `backoff` — under budget, waiting out the backoff (or
22213
- * a cached pool observed dead and not yet condemned).
22214
- */
22215
- state: _enum(["failed", "backoff"]),
22216
- /** Epoch ms of the death that produced this state. */
22217
- since: number(),
22218
- /** Pool deaths inside the current window. */
22219
- deaths: number(),
22220
- /** The last death's message. */
22221
- lastError: string()
22222
- })).readonly() })),
22223
- /**
22224
- * Re-arm a terminally `failed` inference device on `nodeId`: forget its
22225
- * restart budget so the next dispatch builds a fresh pool.
22226
- *
22227
- * The terminal state is deliberate (the abort it bounds is deterministic —
22228
- * an automatic probation would just respawn Python forever, more slowly),
22229
- * and a terminal state an operator cannot leave is a silent fault. This is
22230
- * the way out. `rearmed:false` means there was nothing to forget.
22231
- */
22232
- rearmInferenceDevice: method(object({
22233
- nodeId: string(),
22234
- deviceKey: string()
22235
- }), object({ rearmed: boolean() }), {
22236
- kind: "mutation",
22237
- auth: "admin"
22238
- }),
22239
- getSchema: method(_void(), PipelineSchemaSchema),
22240
- getGlobalSteps: method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()),
22241
- getGlobalPipelineConfig: method(_void(), PipelineConfigBridge),
22242
- getOrchestratorConfigSchema: method(_void(), ConfigUISchemaBridge),
22243
- /**
22244
- * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
22245
- * node's `currentEngine.format` — resolves `steps` the same way the
22246
- * runtime dispatch path would, and reports what WOULD happen without
22247
- * touching any node-global state. Called by the orchestrator at attach
22248
- * time (`attachOn`), node-pinned to the TARGET node, so config problems
22249
- * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
22250
- * per-frame resolve. `ok` is false iff `issues` is non-empty (both
22251
- * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
22252
- * is informational (a degraded-but-loadable model swap) and never
22253
- * affects `ok`. Never throws.
22254
- */
22255
- validatePipeline: method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
22256
- listTemplates: method(_void(), array(PipelineTemplateSchema$1).readonly()),
22257
- saveTemplate: method(object({
22258
- name: string(),
22259
- steps: array(PipelineTemplateStepSchema).readonly(),
22260
- engine: PipelineEngineChoiceSchema
22261
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
22262
- updateTemplate: method(object({
22263
- id: string(),
22264
- name: string().optional(),
22265
- steps: array(PipelineTemplateStepSchema).readonly().optional()
22266
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
22267
- deleteTemplate: method(object({ id: string() }), _void(), { kind: "mutation" }),
22268
- getCapabilities: method(_void(), InferenceCapabilitiesBridge),
22269
- getAddonModels: method(object({ addonId: string() }), ModelAvailabilityListBridge),
22270
- downloadModel: method(object({
22271
- addonId: string(),
22272
- modelId: string(),
22273
- format: ModelFormatSchema$1
22274
- }), DownloadModelResultSchema, { kind: "mutation" }),
22275
- deleteModel: method(object({
22276
- addonId: string(),
22277
- modelId: string(),
22278
- format: ModelFormatSchema$1
22279
- }), object({ success: literal(true) }), { kind: "mutation" }),
22280
- /**
22281
- * Stateless single-frame execution. Callers (runner, benchmark) pass
22282
- * the complete `engine` + `steps` tree; the executor holds no state
22283
- * about cameras or saved pipelines.
22284
- *
22285
- * `engine` is optional during the migration window to preserve the
22286
- * legacy call shape used by existing benchmark code; once all
22287
- * callers pass it explicitly we make it required.
22288
- *
22289
- * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
22290
- * `referenceImage` must be provided:
22291
- * - `frame`: runtime dispatch path (runner → decoded broker frame).
22292
- * Carries the raw buffer, dimensions, and format; the executor
22293
- * uses it directly without base64 round-tripping.
22294
- * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
22295
- * decoded frame. Both runner and executor are hub-local processes
22296
- * sharing `/dev/shm`, so the executor maps the named segment and
22297
- * reads the pixels back zero-copy — eliminating the ~1.2MB
22298
- * re-serialisation over UDS/MsgPack the `frame` path pays per call.
22299
- * High-risk: the FrameRing is a latest-wins seqlock with no
22300
- * refcount, so a recycled slot yields a null read; the executor
22301
- * then degrades to an empty result and the runner ships pixels via
22302
- * `frame` as the fallback (queue-depth gated on the runner side).
22303
- * - `imageBase64`: one-shot test path (benchmark ImageTab).
22304
- * - `referenceImage`: named file from the reference-image store.
22305
- */
22306
- runPipeline: method(object({
22307
- engine: PipelineEngineChoiceSchema.optional(),
22308
- steps: array(PipelineStepInputSchema).min(1),
22309
- frame: FrameInputSchema.optional(),
22310
- /**
22311
- * Process-local lazy frame. Valid only when caller and provider resolve
22312
- * in the same execution-group process; split/cross-node callers use
22313
- * `frame`/`image` inline compatibility instead.
22314
- */
22315
- frameRef: FrameRefSchema.optional(),
22316
- /**
22317
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
22318
- * the decoded pixels live in. One more member of the one-of
22319
- * frame/frameHandle/image/imageBase64/referenceImage group.
22320
- */
22321
- frameHandle: FrameHandleSchema.optional(),
22322
- imageBase64: string().optional(),
22323
- /**
22324
- * Binary JPEG bytes — preferred over `imageBase64` on internal
22325
- * hops (hub → forked worker via Moleculer MsgPack) because it
22326
- * skips the 33% base64 overhead + the per-call base64 decode on
22327
- * the detection-pipeline worker. Callers can pass either; exactly
22328
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
22329
- */
22330
- image: _instanceof(Uint8Array).optional(),
22331
- referenceImage: string().optional(),
22332
- deviceId: number().optional(),
22333
- sessionId: string().optional(),
22334
- /**
22335
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
22336
- * reference-image, and detail-subtree calls. 'frame' is the live
22337
- * per-frame dispatch: ONLY root-plane steps run; crop children
22338
- * (inputClasses ≠ null) are skipped and served per-track via
22339
- * pipelineRunner.runDetailSubtree (two-plane design).
22340
- */
22341
- plane: _enum(["full", "frame"]).optional(),
22342
- /**
22343
- * Inference-device selector (Phase 2 multi-device). Format
22344
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
22345
- * Omitted ⇒ the runner's default device (current single-engine
22346
- * behaviour). Selects WHICH device pool of the node runs the call.
22347
- */
22348
- deviceKey: string().optional(),
22349
- /**
22350
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
22351
- * when the parent crop was resolved from the frame's retained NATIVE
22352
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
22353
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
22354
- * resolution from that surface — the SAME quality path faces already
22355
- * had — instead of the downscaled parent tile. `handle` keys the native
22356
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
22357
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
22358
- * the executor's crop-normalized child ROI back into frame-normalized
22359
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
22360
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
22361
- * (today's behaviour on the fallback path).
22362
- */
22363
- nativeCropRef: NativeCropRefSchema.optional()
22364
- }), PipelineRunResultBridge, { kind: "mutation" }),
22365
- /**
22366
- * Batched run — N raw frames packed into one cap call. The provider
22367
- * routes the batch through `SharedInferencePool.inferBatch`
22368
- * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
22369
- * envelope cost is amortised N:1 vs N concurrent `runPipeline`
22370
- * calls. Single root step + uniform model assumed; trees with crop
22371
- * children fall back to sequential execution.
22372
- *
22373
- * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
22374
- * N frames in one call to amortise per-call IPC overhead.
22375
- */
22376
- runPipelineBatch: method(object({
22377
- engine: PipelineEngineChoiceSchema.optional(),
22378
- steps: array(PipelineStepInputSchema).min(1),
22379
- frames: array(FrameInputSchema).min(1).max(255),
22380
- deviceId: number().optional(),
22381
- sessionId: string().optional(),
22382
- /**
22383
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
22384
- * the batch to the Python pool's bench preprocess cache
22385
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
22386
- * preprocessed ONCE and every later inference is a pure-inference cache
22387
- * hit — the sustained-throughput run measures inference, not
22388
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
22389
- * full preprocess every call, correct). Fresh per sustained run;
22390
- * released via `uncacheFrame`.
22391
- */
22392
- frameId: number().int().nonnegative().optional(),
22393
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
22394
- deviceKey: string().optional()
22395
- }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
22396
- /**
22397
- * Cache a raw frame inside the Python inference pool's memory.
22398
- * Returns a numeric `frameId` that `inferCached` references —
22399
- * subsequent calls send only 5 bytes through the pipe instead of
22400
- * 1.2MB raw data, eliminating the pipe transfer bottleneck.
22401
- */
22402
- cacheFrameInPool: method(object({
22403
- data: _instanceof(Uint8Array),
22404
- width: number().int().positive(),
22405
- height: number().int().positive(),
22406
- format: _enum([
22407
- "rgb",
22408
- "bgr",
22409
- "gray"
22410
- ])
22411
- }), object({
22412
- frameId: number(),
22413
- width: number(),
22414
- height: number()
22415
- }), { kind: "mutation" }),
22416
- /**
22417
- * Run inference on a previously cached frame. Sends only 5 bytes
22418
- * (model_idx + frameId) through the IPC pipe — eliminates the
22419
- * ~35ms per-call overhead of transferring 1.2MB raw data.
22420
- */
22421
- inferCached: method(object({
22422
- stepId: string(),
22423
- frameId: number().int()
22424
- }), record(string(), unknown()), { kind: "mutation" }),
22425
- /**
22426
- * Release a cached frame from the Python pool's memory.
22427
- */
22428
- uncacheFrame: method(object({ frameId: number().int() }), _void(), { kind: "mutation" }),
22429
- /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
22430
- getEffectiveTuning: method(_void(), object({
22431
- batchMode: string(),
22432
- windowMs: number(),
22433
- maxBatchSize: number(),
22434
- concurrency: number()
22435
- })),
22436
- /**
22437
- * List every EngineFactory currently loaded in this executor's RAM,
22438
- * with the models resident and a coarse "in use" marker derived from
22439
- * ongoing inference activity. Used by the Pipeline page Engines tab.
22440
- */
22441
- listLoadedEngines: method(_void(), array(object({
22442
- engineKey: string(),
22443
- engine: PipelineEngineChoiceSchema,
22444
- modelsLoaded: array(string()).readonly(),
22445
- inUseByCameras: array(number()).readonly(),
22446
- /**
22447
- * Origin of this resident factory.
22448
- * - `runtime` — main camera-serving engine (no idle TTL).
22449
- * - `warm-override` — benchmark/test override held in the warm
22450
- * cache; auto-disposed after the idle TTL.
22451
- * - `device-pool` — a concurrent per-device pool (Phase 2
22452
- * multi-device, keyed by `deviceKey`) resolved
22453
- * via `resolveDeviceFactory`. Runs alongside the
22454
- * `runtime` engine on a DIFFERENT accelerator
22455
- * (NPU / iGPU / Coral) — this is how the
22456
- * Engines tab shows all pools running at once.
22457
- */
22458
- kind: _enum([
22459
- "runtime",
22460
- "warm-override",
22461
- "device-pool"
22462
- ]),
22463
- /** Native pid of the underlying Python pool (null when no pool). */
22464
- poolPid: number().nullable(),
22465
- /** ms since this factory was last used (null when not warm-tracked). */
22466
- idleMs: number().nullable(),
22467
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
22468
- idleTtlMs: number().nullable()
22469
- })).readonly()),
22470
- /** Warm up an engine without running a frame. No-op if already loaded. */
22471
- spinEngine: method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
22472
- kind: "mutation",
22473
- auth: "admin"
22474
- }),
22475
- /**
22476
- * Unload an engine from RAM. `force:true` unloads even when cameras
22477
- * are actively using it (they re-spin on next frame). Default is
22478
- * gated — returns `{success:false, reason}` when in use.
22479
- */
22480
- killEngine: method(object({
22481
- engine: PipelineEngineChoiceSchema,
22482
- force: boolean().optional()
22483
- }), object({
22484
- success: boolean(),
22485
- reason: string().optional()
22486
- }), {
22487
- kind: "mutation",
22488
- auth: "admin"
22489
- }),
22490
- listReferenceImages: method(_void(), array(ReferenceImageEntrySchema).readonly()),
22491
- getReferenceImage: method(object({ filename: string() }), ReferenceImageBodySchema.nullable()),
22492
- getReferenceAudioFiles: method(_void(), array(ReferenceAudioEntrySchema).readonly()),
22493
- getReferenceAudio: method(object({ filename: string() }), ReferenceAudioBodySchema.nullable()),
22494
- getAudioCapabilities: method(_void(), AudioCapabilitiesSchema),
22495
- runAudioTest: method(object({
22496
- addonId: string(),
22497
- modelId: string(),
22498
- filename: string().optional(),
22499
- settings: record(string(), unknown()).optional()
22500
- }), AudioTestResultSchema, { kind: "mutation" }),
22501
- getDetectionConfigSchema: method(_void(), ConfigUISchemaNullableBridge)
22502
- }
22503
- };
22504
- /**
22505
- * Per-stage gating mode applied to the zones a rule references.
22506
- *
22507
- * - `include`: the rule contributes to a **whitelist** for its stage.
22508
- * When at least one `include` rule fires for a stage, only entities
22509
- * inside one of those zones pass that stage.
22510
- * - `exclude`: the rule contributes to a **blacklist** for its stage.
22511
- * Entities inside one of those zones are dropped at that stage.
22512
- *
22513
- * `monitor`-style observation (count without filtering) is not a rule
22514
- * mode — zones without any matching rule are observed naturally by
22515
- * `zone-analytics` (live snapshot + history), so an "I just want to
22516
- * count, not filter" use case needs no rule at all.
22517
- */
22518
- var ZoneRuleModeEnum = _enum(["include", "exclude"]);
22519
- /**
22520
- * Per-consumer rule that references existing zones (geometry) and
22521
- * defines how a specific pipeline stage should treat them. Each
22522
- * consumer addon owns its own `ZoneRule[]` array in its per-device
22523
- * settings:
22524
- *
22525
- * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
22526
- * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
22527
- * - future: notification rules, audio gating, etc.
22528
- *
22529
- * One rule applies to N zones (`zoneIds[]`) so the operator can
22530
- * express "ignore motion in ALL of {garden, street}" with a single
22531
- * rule. `classFilter` narrows the rule to specific object classes —
22532
- * "drop person detections in the street, but keep cars" is one
22533
- * `exclude` rule with `classFilter: ['person']`.
22534
- *
22535
- * `enabled` is a soft toggle — the operator can keep the rule
22536
- * configured but inert without deleting it.
22537
- */
22538
- var ZoneRuleSchema = object({
22539
- /** Stable rule id — survives edits, used by the UI for diffing. */
22540
- id: string(),
22541
- /** Optional human-readable label rendered in the rule editor. */
22542
- name: string().optional(),
22543
- /** Zones this rule targets. The rule's `mode` applies to ALL
22544
- * listed zones (OR-set: a detection in any one of them counts).
22545
- * At least one zone id required — a rule with no targets is a
22546
- * configuration mistake and the form validator rejects it. */
22547
- zoneIds: array(string()).min(1).readonly(),
22548
- mode: ZoneRuleModeEnum,
22549
- /**
22550
- * Class names this rule applies to. Empty / undefined ⇒ rule
22551
- * applies to every class. Class strings match the `macroClass`
22552
- * field on detections (e.g. `person`, `car`, `dog`).
22553
- */
22554
- classFilter: array(string()).readonly().optional(),
22555
- /**
22556
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
22557
- * required to consider an entity "in the zone". Defaults to the
22558
- * consumer's stage default when omitted. Kept for back-compat with
22559
- * existing per-rule overrides; new operators pick the value via
22560
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
22561
- * set, the lower-level engine reads it as a 0–1 fraction.
22562
- */
22563
- overlapThreshold: number().min(0).max(1).optional(),
22564
- /**
22565
- * Operator-friendly version of `overlapThreshold` — the percentage
22566
- * of the detection's bbox that must lie inside the zone for the
22567
- * rule to match. Documented default is 85%; the engine substitutes
22568
- * that when the field is omitted (kept optional so existing rules
22569
- * stored without it stay valid).
22570
- *
22571
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
22572
- * rule, the engine prefers `bboxInclusionPct` because it's the
22573
- * field exposed in the UI. Internally both feed the same gate.
22574
- */
22575
- bboxInclusionPct: number().min(0).max(100).optional(),
22576
- /**
22577
- * When `true` and a detection has a segmentation mask, use the
22578
- * mask for overlap instead of the bbox. Detection-stage only;
22579
- * motion rules ignore this field.
22580
- */
22581
- preferMask: boolean().optional(),
22582
- /**
22583
- * Soft-toggle: `false` disables the rule without deleting it.
22584
- * Defaults to `true` so operators creating a rule via the UI
22585
- * see it active immediately.
22586
- */
22587
- enabled: boolean().default(true)
22588
- });
22589
- array(ZoneRuleSchema).readonly();
22590
- /**
22591
- * Zone — pure geometry + identity. NO filtering behaviour.
22592
- *
22593
- * Zones describe **where** in the frame the operator wants to flag
22594
- * something; consumer-owned {@link ZoneRule} arrays describe **how**
22595
- * each pipeline stage uses them. Splitting the two means a single
22596
- * polygon "Driveway" can simultaneously back a motion-exclude rule,
22597
- * a detection-include rule on `['car']`, and an occupancy aggregate
22598
- * — without three duplicated polygons.
22599
- *
22600
- * Owned by the orchestrator addon (provider) and mirrored into the
22601
- * `zones` device-state slice on every mutation. Consumers
22602
- * (motion-wasm, pipeline-executor, analytics, admin UI) read either
22603
- * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
22604
- * mirror with `onChanged`).
22605
- *
22606
- * Coordinates are normalised fractions of the frame (0–1) so zones
22607
- * survive resolution changes and stream profile switches.
22608
- *
22609
- * `kind` discriminates between full polygons (closed regions used
22610
- * for intrusion / occupancy filters) and tripwires (open 2-point
22611
- * line segments used for cross events). Onboard / firmware-reported
22612
- * zones (Reolink, ONVIF) are out of scope for now — see the deferred
22613
- * task list.
22614
- */
22615
- var ZoneKindEnum = _enum(["polygon", "tripwire"]);
22616
- /** Polygon vertex in fraction-of-frame coordinates (0–1). */
22617
- var PolygonPointSchema = object({
22618
- x: number(),
22619
- y: number()
22620
- });
22621
- /** A camera detection zone — pure geometry/identity. */
22622
- var ZoneSchema = object({
22623
- id: string(),
22624
- name: string(),
22625
- kind: ZoneKindEnum.default("polygon"),
22626
- /** Polygon vertices, fraction of frame (0–1). */
22627
- polygon: array(PolygonPointSchema).readonly(),
22628
- /** Visual color for UI rendering. */
22629
- color: string().default("#3b82f6")
22630
- });
22631
- DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).readonly()), method(object({
22632
- deviceId: number(),
22633
- zone: ZoneSchema
22634
- }), _void(), {
22635
- kind: "mutation",
22636
- auth: "admin"
22637
- }), method(object({
22638
- deviceId: number(),
22639
- zoneId: string()
22640
- }), _void(), {
22641
- kind: "mutation",
22642
- auth: "admin"
22643
- }), method(object({
22644
- deviceId: number(),
22645
- zone: ZoneSchema
22646
- }), _void(), {
22647
- kind: "mutation",
22648
- auth: "admin"
22649
- }), object({ zones: array(ZoneSchema).readonly() });
22650
- /**
22651
21954
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
22652
21955
  * per-frame detections emitted by the pipeline runner into tracked
22653
21956
  * objects, per-kind event collections (motion / object / audio), and
@@ -23218,6 +22521,22 @@ var TrackSchema = object({
23218
22521
  * omit it. Render nothing when absent.
23219
22522
  */
23220
22523
  previewMissReason: string().optional(),
22524
+ /**
22525
+ * The ROOT model that produced this track — the `FrameResult.rootModelId` of
22526
+ * the frame it was BORN on (D646). What a later replay compares against, so
22527
+ * "the original model" is named rather than assumed. A birth fact: never
22528
+ * rewritten by a later frame. **Absent ≠ unknown-model**: a row from before
22529
+ * the column, and a track the camera's own detector produced, carry none.
22530
+ */
22531
+ modelId: string().optional(),
22532
+ /**
22533
+ * Size (px) of the detection frame this track's `positions[].bbox` live in —
22534
+ * the frame it was born on, else the camera's latest analysis frame at
22535
+ * persist time. Divide a position by these to normalise it. Absent when the
22536
+ * size was never known.
22537
+ */
22538
+ frameWidth: number().optional(),
22539
+ frameHeight: number().optional(),
23221
22540
  ...TrackFlagFields,
23222
22541
  ...TrackRetrainFields
23223
22542
  });
@@ -24224,24 +23543,6 @@ var MediaReclaimStatusSchema = object({
24224
23543
  finishedAtMs: number().nullable(),
24225
23544
  error: string().nullable()
24226
23545
  });
24227
- var ReplayFrameInputSchema = object({
24228
- timestamp: number(),
24229
- frame: PipelineRunResultBridge
24230
- });
24231
- var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
24232
- className: string(),
24233
- firstSeenMs: number(),
24234
- lastSeenMs: number(),
24235
- /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
24236
- * frame — a representative box for the diff's `(className, window, IoU)`
24237
- * pairing (`replay-diff.ts`). A replay does not need the full per-frame
24238
- * trajectory production's `Track.positions` keeps. */
24239
- bbox: BoundingBoxSchema,
24240
- /** How many of the input frames this track matched a real detection on
24241
- * (never a coasted/extrapolated frame) — the replay's own signal for "how
24242
- * solid is this track", cheaper than re-deriving it from a trajectory. */
24243
- framesMatched: number().int()
24244
- })).readonly() });
24245
23546
  /**
24246
23547
  * The long-term series a camera produces. A closed union so a typo cannot
24247
23548
  * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
@@ -24472,16 +23773,6 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
24472
23773
  }), RetrainAssistResultSchema, {
24473
23774
  kind: "mutation",
24474
23775
  auth: "admin"
24475
- }), method(object({
24476
- deviceId: number(),
24477
- source: DetectionSourceSchema,
24478
- zones: array(ZoneSchema).readonly().optional(),
24479
- detectionRules: array(ZoneRuleSchema).readonly().optional(),
24480
- zoneMembershipMinOverlap: number().min(0).max(1).optional(),
24481
- frames: array(ReplayFrameInputSchema).min(1)
24482
- }), RunReplayFrameProcessorResultSchema, {
24483
- kind: "mutation",
24484
- auth: "admin"
24485
23776
  }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
24486
23777
  kind: "query",
24487
23778
  auth: "admin"
@@ -24557,6 +23848,674 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
24557
23848
  eventId: string(),
24558
23849
  timestamp: number()
24559
23850
  });
23851
+ /**
23852
+ * Reference to the frame's retained NATIVE surface + the parent crop's placement
23853
+ * within the frame, so the executor can re-cut a leaf child ROI at native
23854
+ * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
23855
+ */
23856
+ var NativeCropRefSchema = object({
23857
+ /** Handle keying the retained native surface (node-pinned to its owner). */
23858
+ handle: FrameHandleSchema,
23859
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
23860
+ cropFrameSpace: object({
23861
+ x: number(),
23862
+ y: number(),
23863
+ w: number(),
23864
+ h: number()
23865
+ })
23866
+ });
23867
+ object({
23868
+ crop: object({
23869
+ left: number(),
23870
+ top: number(),
23871
+ width: number().positive(),
23872
+ height: number().positive()
23873
+ }).optional(),
23874
+ content: object({
23875
+ width: number().int().positive(),
23876
+ height: number().int().positive()
23877
+ }),
23878
+ fit: _enum(["stretch", "contain"]),
23879
+ format: _enum([
23880
+ "rgb",
23881
+ "gray",
23882
+ "jpeg"
23883
+ ])
23884
+ });
23885
+ /**
23886
+ * Process-local frame identity. It is serializable so it can ride an in-process
23887
+ * capability call, but `registryId` deliberately prevents resolution in any
23888
+ * other process or execution group.
23889
+ */
23890
+ var FrameRefSchema = object({
23891
+ registryId: string().min(1),
23892
+ id: string().min(1),
23893
+ width: number().int().positive(),
23894
+ height: number().int().positive(),
23895
+ format: _enum(["rgb", "gray"]),
23896
+ timestamp: number(),
23897
+ capturedAt: number().optional()
23898
+ });
23899
+ var ModelFormatSchema$1 = _enum([
23900
+ "onnx",
23901
+ "coreml",
23902
+ "openvino",
23903
+ "tflite",
23904
+ "pt",
23905
+ "gguf"
23906
+ ]);
23907
+ var PipelineSlotSchema = _enum([
23908
+ "detector",
23909
+ "cropper",
23910
+ "classifier",
23911
+ "refiner",
23912
+ "audio-classifier"
23913
+ ]);
23914
+ var PipelineEngineChoiceSchema = object({
23915
+ runtime: literal("python"),
23916
+ backend: string(),
23917
+ format: ModelFormatSchema$1,
23918
+ device: string().optional()
23919
+ });
23920
+ /**
23921
+ * One pool's resolved provisioning (`getEffectiveTuning`). `numWorkers` is the
23922
+ * knob that lifts a saturated pool (`inference-devices.md`); `concurrency` is
23923
+ * per-worker in-flight requests. `deviceKey` echoes the pool asked about —
23924
+ * `null` for the node-default pool.
23925
+ */
23926
+ var EffectiveTuningSchema = object({
23927
+ deviceKey: string().nullable(),
23928
+ runtime: string(),
23929
+ batchMode: string(),
23930
+ windowMs: number(),
23931
+ maxBatchSize: number(),
23932
+ concurrency: number(),
23933
+ numWorkers: number()
23934
+ });
23935
+ var AvailableEngineSchema = object({
23936
+ engine: PipelineEngineChoiceSchema,
23937
+ devices: array(object({
23938
+ id: string(),
23939
+ label: string(),
23940
+ description: string().optional()
23941
+ })).readonly(),
23942
+ defaultDevice: string()
23943
+ });
23944
+ var PipelineDefaultStepSchema = lazy(() => object({
23945
+ addonId: string(),
23946
+ addonName: string(),
23947
+ slot: PipelineSlotSchema,
23948
+ inputClasses: array(string()).readonly(),
23949
+ outputClasses: array(string()).readonly(),
23950
+ enabled: boolean(),
23951
+ modelId: string(),
23952
+ children: array(PipelineDefaultStepSchema).readonly(),
23953
+ group: string().optional(),
23954
+ settings: record(string(), unknown()).optional()
23955
+ }));
23956
+ var PipelineTemplateStepSchema = lazy(() => object({
23957
+ addonId: string(),
23958
+ enabled: boolean(),
23959
+ modelId: string(),
23960
+ children: array(PipelineTemplateStepSchema).readonly(),
23961
+ settings: record(string(), unknown()).optional()
23962
+ }));
23963
+ var PipelineTemplateSchema$1 = object({
23964
+ id: string(),
23965
+ name: string(),
23966
+ createdAt: string(),
23967
+ updatedAt: string(),
23968
+ engine: PipelineEngineChoiceSchema,
23969
+ steps: array(PipelineTemplateStepSchema).readonly()
23970
+ });
23971
+ var PipelineModelOptionSchema = object({
23972
+ id: string(),
23973
+ name: string(),
23974
+ formats: record(string(), object({
23975
+ downloaded: boolean(),
23976
+ sizeMB: number()
23977
+ })),
23978
+ group: ModelVariantGroupSchema.optional(),
23979
+ legacy: boolean().optional(),
23980
+ provider: ModelProviderIdSchema.optional()
23981
+ });
23982
+ var ConfigFieldBridge = custom();
23983
+ var PipelineAddonSchemaSchema = object({
23984
+ id: string(),
23985
+ name: string(),
23986
+ slot: PipelineSlotSchema,
23987
+ inputClasses: array(string()).readonly(),
23988
+ outputClasses: array(string()).readonly(),
23989
+ childSlots: array(PipelineSlotSchema).readonly(),
23990
+ models: array(PipelineModelOptionSchema).readonly(),
23991
+ defaultModelId: string(),
23992
+ defaultModelIdByFormat: record(string(), string()).optional(),
23993
+ enabledByDefault: boolean().optional(),
23994
+ backfillIntoExistingOverrides: boolean().optional(),
23995
+ defaultConfidence: number(),
23996
+ group: string().optional(),
23997
+ configSchema: array(ConfigFieldBridge).readonly().optional()
23998
+ });
23999
+ var PipelineSlotSchemaSchema = object({
24000
+ id: PipelineSlotSchema,
24001
+ label: string(),
24002
+ priority: number(),
24003
+ parentSlot: PipelineSlotSchema.nullable(),
24004
+ addons: array(PipelineAddonSchemaSchema).readonly()
24005
+ });
24006
+ var PipelineSchemaSchema = object({
24007
+ availableEngines: array(AvailableEngineSchema).readonly(),
24008
+ selectedEngine: PipelineEngineChoiceSchema,
24009
+ slots: array(PipelineSlotSchemaSchema).readonly()
24010
+ });
24011
+ var EngineProvisioningSchema = object({
24012
+ runtimeId: _enum([
24013
+ "onnx",
24014
+ "openvino",
24015
+ "coreml",
24016
+ "edgetpu"
24017
+ ]).nullable(),
24018
+ device: string().nullable(),
24019
+ state: _enum([
24020
+ "idle",
24021
+ "installing",
24022
+ "verifying",
24023
+ "ready",
24024
+ "failed"
24025
+ ]),
24026
+ progress: number().optional(),
24027
+ error: string().optional(),
24028
+ nextRetryAt: number().optional(),
24029
+ /**
24030
+ * Gate A (config-correctness gate at engine change): human-readable
24031
+ * config issues surfaced EAGERLY when the node's engine changes — model
24032
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
24033
+ * has a <format> build"). Additive/optional: informational only, never
24034
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
24035
+ * Absent/empty when the node-default tree resolves cleanly.
24036
+ */
24037
+ configIssues: array(string()).optional()
24038
+ });
24039
+ var PipelineStepInputSchema = lazy(() => object({
24040
+ addonId: string(),
24041
+ modelId: string().optional(),
24042
+ enabled: boolean().default(true),
24043
+ children: array(PipelineStepInputSchema).optional(),
24044
+ settings: record(string(), unknown()).optional(),
24045
+ jumpDeviceKey: string().optional()
24046
+ }));
24047
+ var ModelSubstitutionSchema = object({
24048
+ addonId: string(),
24049
+ chosen: string(),
24050
+ running: string(),
24051
+ format: string()
24052
+ });
24053
+ var PipelineValidationIssueSchema = object({
24054
+ addonId: string(),
24055
+ kind: _enum([
24056
+ "unknown-addon",
24057
+ "no-format-build",
24058
+ "unknown-device"
24059
+ ]),
24060
+ detail: string()
24061
+ });
24062
+ var PipelineValidationResultSchema = object({
24063
+ ok: boolean(),
24064
+ issues: array(PipelineValidationIssueSchema).readonly(),
24065
+ substitutions: array(ModelSubstitutionSchema).readonly(),
24066
+ /** The node's `currentEngine.format` this validation ran against. */
24067
+ format: string()
24068
+ });
24069
+ var ReferenceImageEntrySchema = object({
24070
+ filename: string(),
24071
+ stepIds: array(string()).readonly().optional()
24072
+ });
24073
+ var ReferenceImageBodySchema = object({
24074
+ base64: string(),
24075
+ filename: string()
24076
+ });
24077
+ var ReferenceAudioEntrySchema = object({
24078
+ filename: string(),
24079
+ sizeKb: number()
24080
+ });
24081
+ var ReferenceAudioBodySchema = object({ base64: string() });
24082
+ var AudioBackendSchema = object({
24083
+ id: string(),
24084
+ name: string(),
24085
+ description: string(),
24086
+ available: boolean(),
24087
+ /**
24088
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
24089
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
24090
+ * the benchmark UI to populate the `enabledMicroClasses` filter
24091
+ * specific to the selected backend without a separate fetch.
24092
+ */
24093
+ rawLabels: array(string()).readonly().optional()
24094
+ });
24095
+ var AudioCapabilitiesSchema = object({
24096
+ activeBackend: string(),
24097
+ availableBackends: array(AudioBackendSchema).readonly(),
24098
+ sampleRate: number(),
24099
+ chunkDurationMs: number()
24100
+ });
24101
+ var DownloadModelResultSchema = object({
24102
+ filePath: string(),
24103
+ sizeMB: number(),
24104
+ durationMs: number()
24105
+ });
24106
+ /**
24107
+ * Wrapper carrying a single test run's result. Replaces the legacy
24108
+ * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
24109
+ * canonical `AudioResult` from the Phase 6 output rework: one
24110
+ * `AudioDetection` per class above `minScore`, top-N candidates in
24111
+ * `debug.alternateLabels['audio-classifier']`, per-source timings in
24112
+ * `debug.stepTimings`. The outer `success`/`error` fields stay so the
24113
+ * benchmark UI can still report a clean failure when the classifier
24114
+ * cap isn't available.
24115
+ */
24116
+ var AudioTestResultSchema = object({
24117
+ success: boolean(),
24118
+ error: string().optional(),
24119
+ frame: custom().optional()
24120
+ });
24121
+ var PipelineConfigBridge = custom();
24122
+ var ConfigUISchemaBridge = custom();
24123
+ var ConfigUISchemaNullableBridge = custom();
24124
+ var InferenceCapabilitiesBridge = custom();
24125
+ var ModelAvailabilityListBridge = custom();
24126
+ var PipelineRunResultBridge = custom();
24127
+ /**
24128
+ * Pipeline executor — detection engine + configuration + inference API.
24129
+ *
24130
+ * Merged from: pipeline-executor, pipeline-config, inference, detection-config.
24131
+ * Implemented by the detection-pipeline addon.
24132
+ *
24133
+ * Per-device surface (DeviceSettingsContribution + the "is detection
24134
+ * enabled for this camera?" toggle) lives on the paired
24135
+ * `detection-pipeline` cap (device-scoped, singleton, wrapper
24136
+ * defaultActive) — same split pattern used by stream-broker /
24137
+ * camera-streams and audio-analyzer / audio-analysis.
24138
+ */
24139
+ var pipelineExecutorCapability = {
24140
+ name: "pipeline-executor",
24141
+ scope: "system",
24142
+ mode: "singleton",
24143
+ methods: {
24144
+ getAvailableEngines: method(_void(), array(PipelineEngineChoiceSchema)),
24145
+ getSelectedEngine: method(_void(), PipelineEngineChoiceSchema),
24146
+ getDefaultSteps: method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)),
24147
+ /**
24148
+ * Per-node detection-engine provisioning snapshot. Returns the live
24149
+ * state of the lazy runtime-provisioning machine on `nodeId`
24150
+ * (idle / installing / verifying / ready / failed). The UI pairs this
24151
+ * one-shot query with the `pipeline.engine-provisioning` live event
24152
+ * (emitted on every transition) to drive a per-node "engine ready?"
24153
+ * indicator without polling. Phase 2.
24154
+ */
24155
+ getEngineProvisioning: method(object({ nodeId: string() }), EngineProvisioningSchema),
24156
+ getVideoPipelineSteps: method(_void(), record(string(), object({
24157
+ modelId: string(),
24158
+ settings: record(string(), unknown()).readonly()
24159
+ }))),
24160
+ setVideoPipelineSteps: method(object({ steps: record(string(), object({
24161
+ modelId: string(),
24162
+ settings: record(string(), unknown()).readonly()
24163
+ })) }), object({ success: literal(true) }), {
24164
+ kind: "mutation",
24165
+ auth: "admin"
24166
+ }),
24167
+ /**
24168
+ * Clear THIS node's executor-side PER-DEVICE settings stores (the
24169
+ * per-camera step overrides the object-detection root reads via
24170
+ * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
24171
+ * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
24172
+ * dispatches to that node, so the provider method runs ON the target
24173
+ * node and receives no `nodeId`.
24174
+ *
24175
+ * This is the slimmed executor leg of the orchestrator's
24176
+ * `resetNodePipelineDefaults` flow (which owns the real reset: node
24177
+ * addonDefaults pins + per-camera orchestrator overrides). The legacy
24178
+ * `resetToDefault` — which reset a persisted global step-tree seed
24179
+ * nothing in the live per-camera path read — was removed together with
24180
+ * that seed.
24181
+ */
24182
+ clearDeviceOverrides: method(object({ nodeId: string() }), object({
24183
+ success: literal(true),
24184
+ clearedDevices: number()
24185
+ }), {
24186
+ kind: "mutation",
24187
+ auth: "admin"
24188
+ }),
24189
+ /**
24190
+ * Which of THIS node's inference devices the executor currently refuses,
24191
+ * and why. `nodeId` is the ROUTING key (stripped by the generated router).
24192
+ *
24193
+ * The channel that did not exist. Pool health was known only inside the
24194
+ * detection addon and was an input to no routing decision anywhere: the
24195
+ * per-dispatch capability gate is keyed on model FORMAT and so can never
24196
+ * separate `openvino:gpu` from `openvino:npu`, and the orchestrator's live
24197
+ * eligibility probe (`platformProbe.getCapabilities`) answers about
24198
+ * HARDWARE — which was present throughout. So when the hub's `openvino:gpu`
24199
+ * Python worker was SIGABRT'd by the Intel GPU plugin on 2026-08-25, the
24200
+ * balancer went on handing that dead pool cameras by rotation for 31 hours:
24201
+ * ~370 000 `PoolWorker[w0]: not initialized` lines, every frame lost.
24202
+ *
24203
+ * Read semantics the caller depends on, and which the provider guarantees:
24204
+ * this is a synchronous read of in-memory state. It never probes hardware,
24205
+ * never spawns a pool and never throws — an EMPTY `unhealthy` means "asked,
24206
+ * nothing is refused", which is what re-admits a device. A read that FAILS
24207
+ * (node offline, version skew) must therefore be distinguishable from an
24208
+ * empty answer, and it is: it rejects.
24209
+ */
24210
+ getInferenceDeviceHealth: method(object({ nodeId: string() }), object({ unhealthy: array(object({
24211
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
24212
+ deviceKey: string(),
24213
+ /**
24214
+ * `failed` — the per-device restart budget is exhausted; no pool
24215
+ * will be spawned until an operator re-arms it or the runner
24216
+ * respawns. `backoff` — under budget, waiting out the backoff (or
24217
+ * a cached pool observed dead and not yet condemned).
24218
+ */
24219
+ state: _enum(["failed", "backoff"]),
24220
+ /** Epoch ms of the death that produced this state. */
24221
+ since: number(),
24222
+ /** Pool deaths inside the current window. */
24223
+ deaths: number(),
24224
+ /** The last death's message. */
24225
+ lastError: string()
24226
+ })).readonly() })),
24227
+ /**
24228
+ * Re-arm a terminally `failed` inference device on `nodeId`: forget its
24229
+ * restart budget so the next dispatch builds a fresh pool.
24230
+ *
24231
+ * The terminal state is deliberate (the abort it bounds is deterministic —
24232
+ * an automatic probation would just respawn Python forever, more slowly),
24233
+ * and a terminal state an operator cannot leave is a silent fault. This is
24234
+ * the way out. `rearmed:false` means there was nothing to forget.
24235
+ */
24236
+ rearmInferenceDevice: method(object({
24237
+ nodeId: string(),
24238
+ deviceKey: string()
24239
+ }), object({ rearmed: boolean() }), {
24240
+ kind: "mutation",
24241
+ auth: "admin"
24242
+ }),
24243
+ getSchema: method(_void(), PipelineSchemaSchema),
24244
+ getGlobalSteps: method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()),
24245
+ getGlobalPipelineConfig: method(_void(), PipelineConfigBridge),
24246
+ getOrchestratorConfigSchema: method(_void(), ConfigUISchemaBridge),
24247
+ /**
24248
+ * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
24249
+ * node's `currentEngine.format` — resolves `steps` the same way the
24250
+ * runtime dispatch path would, and reports what WOULD happen without
24251
+ * touching any node-global state. Called by the orchestrator at attach
24252
+ * time (`attachOn`), node-pinned to the TARGET node, so config problems
24253
+ * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
24254
+ * per-frame resolve. `ok` is false iff `issues` is non-empty (both
24255
+ * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
24256
+ * is informational (a degraded-but-loadable model swap) and never
24257
+ * affects `ok`. Never throws.
24258
+ *
24259
+ * `deviceKey` validates against THAT device pool's format instead of the
24260
+ * node default (D646) — the cheap question a pinned replay asks before it
24261
+ * reads any media: can this (node, deviceKey) run exactly this model?
24262
+ */
24263
+ validatePipeline: method(object({
24264
+ steps: array(PipelineStepInputSchema),
24265
+ deviceKey: string().optional()
24266
+ }), PipelineValidationResultSchema),
24267
+ listTemplates: method(_void(), array(PipelineTemplateSchema$1).readonly()),
24268
+ saveTemplate: method(object({
24269
+ name: string(),
24270
+ steps: array(PipelineTemplateStepSchema).readonly(),
24271
+ engine: PipelineEngineChoiceSchema
24272
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
24273
+ updateTemplate: method(object({
24274
+ id: string(),
24275
+ name: string().optional(),
24276
+ steps: array(PipelineTemplateStepSchema).readonly().optional()
24277
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
24278
+ deleteTemplate: method(object({ id: string() }), _void(), { kind: "mutation" }),
24279
+ getCapabilities: method(_void(), InferenceCapabilitiesBridge),
24280
+ getAddonModels: method(object({ addonId: string() }), ModelAvailabilityListBridge),
24281
+ downloadModel: method(object({
24282
+ addonId: string(),
24283
+ modelId: string(),
24284
+ format: ModelFormatSchema$1
24285
+ }), DownloadModelResultSchema, { kind: "mutation" }),
24286
+ deleteModel: method(object({
24287
+ addonId: string(),
24288
+ modelId: string(),
24289
+ format: ModelFormatSchema$1
24290
+ }), object({ success: literal(true) }), { kind: "mutation" }),
24291
+ /**
24292
+ * Stateless single-frame execution. Callers (runner, benchmark) pass
24293
+ * the complete `steps` tree; the executor holds no state about cameras
24294
+ * or saved pipelines. WHICH pool answers is the node's own engine, or the
24295
+ * `deviceKey` pool when one is named.
24296
+ *
24297
+ * There is no `engine` field (D646). It existed for the benchmark and was
24298
+ * honoured by swapping the SHARED provider's engine, factory and executor
24299
+ * for the duration of the call, on a node serving live cameras. A caller
24300
+ * that still sends the key has it stripped here and changes nothing.
24301
+ *
24302
+ * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
24303
+ * `referenceImage` must be provided:
24304
+ * - `frame`: runtime dispatch path (runner → decoded broker frame).
24305
+ * Carries the raw buffer, dimensions, and format; the executor
24306
+ * uses it directly without base64 round-tripping.
24307
+ * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
24308
+ * decoded frame. Both runner and executor are hub-local processes
24309
+ * sharing `/dev/shm`, so the executor maps the named segment and
24310
+ * reads the pixels back zero-copy — eliminating the ~1.2MB
24311
+ * re-serialisation over UDS/MsgPack the `frame` path pays per call.
24312
+ * High-risk: the FrameRing is a latest-wins seqlock with no
24313
+ * refcount, so a recycled slot yields a null read; the executor
24314
+ * then degrades to an empty result and the runner ships pixels via
24315
+ * `frame` as the fallback (queue-depth gated on the runner side).
24316
+ * - `imageBase64`: one-shot test path (benchmark ImageTab).
24317
+ * - `referenceImage`: named file from the reference-image store.
24318
+ */
24319
+ runPipeline: method(object({
24320
+ steps: array(PipelineStepInputSchema).min(1),
24321
+ frame: FrameInputSchema.optional(),
24322
+ /**
24323
+ * Process-local lazy frame. Valid only when caller and provider resolve
24324
+ * in the same execution-group process; split/cross-node callers use
24325
+ * `frame`/`image` inline compatibility instead.
24326
+ */
24327
+ frameRef: FrameRefSchema.optional(),
24328
+ /**
24329
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
24330
+ * the decoded pixels live in. One more member of the one-of
24331
+ * frame/frameHandle/image/imageBase64/referenceImage group.
24332
+ */
24333
+ frameHandle: FrameHandleSchema.optional(),
24334
+ imageBase64: string().optional(),
24335
+ /**
24336
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
24337
+ * hops (hub → forked worker via Moleculer MsgPack) because it
24338
+ * skips the 33% base64 overhead + the per-call base64 decode on
24339
+ * the detection-pipeline worker. Callers can pass either; exactly
24340
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
24341
+ */
24342
+ image: _instanceof(Uint8Array).optional(),
24343
+ referenceImage: string().optional(),
24344
+ deviceId: number().optional(),
24345
+ sessionId: string().optional(),
24346
+ /**
24347
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
24348
+ * reference-image, and detail-subtree calls. 'frame' is the live
24349
+ * per-frame dispatch: ONLY root-plane steps run; crop children
24350
+ * (inputClasses ≠ null) are skipped and served per-track via
24351
+ * pipelineRunner.runDetailSubtree (two-plane design).
24352
+ */
24353
+ plane: _enum(["full", "frame"]).optional(),
24354
+ /**
24355
+ * Inference-device selector (Phase 2 multi-device). Format
24356
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
24357
+ * Omitted ⇒ the runner's default device (current single-engine
24358
+ * behaviour). Selects WHICH device pool of the node runs the call.
24359
+ */
24360
+ deviceKey: string().optional(),
24361
+ /**
24362
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
24363
+ * when the parent crop was resolved from the frame's retained NATIVE
24364
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
24365
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
24366
+ * resolution from that surface — the SAME quality path faces already
24367
+ * had — instead of the downscaled parent tile. `handle` keys the native
24368
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
24369
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
24370
+ * the executor's crop-normalized child ROI back into frame-normalized
24371
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
24372
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
24373
+ * (today's behaviour on the fallback path).
24374
+ */
24375
+ nativeCropRef: NativeCropRefSchema.optional(),
24376
+ /**
24377
+ * Offline REPLAY of stored media for `deviceId` (D56, D646): the
24378
+ * camera's zone gate and overrides apply as live, every executing
24379
+ * step runs EXACTLY the model it names on the `deviceKey` pool it
24380
+ * names or the call refuses (`model-not-servable` /
24381
+ * `device-not-servable`), and nothing reaches the camera's live
24382
+ * trace stream. Absent ⇒ unchanged behaviour.
24383
+ */
24384
+ replay: literal(true).optional()
24385
+ }), PipelineRunResultBridge, { kind: "mutation" }),
24386
+ /**
24387
+ * Batched run — N raw frames packed into one cap call. The provider
24388
+ * routes the batch through `SharedInferencePool.inferBatch`
24389
+ * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
24390
+ * envelope cost is amortised N:1 vs N concurrent `runPipeline`
24391
+ * calls. Single root step + uniform model assumed; trees with crop
24392
+ * children fall back to sequential execution.
24393
+ *
24394
+ * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
24395
+ * N frames in one call to amortise per-call IPC overhead.
24396
+ */
24397
+ runPipelineBatch: method(object({
24398
+ steps: array(PipelineStepInputSchema).min(1),
24399
+ frames: array(FrameInputSchema).min(1).max(255),
24400
+ deviceId: number().optional(),
24401
+ sessionId: string().optional(),
24402
+ /**
24403
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
24404
+ * the batch to the Python pool's bench preprocess cache
24405
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
24406
+ * preprocessed ONCE and every later inference is a pure-inference cache
24407
+ * hit — the sustained-throughput run measures inference, not
24408
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
24409
+ * full preprocess every call, correct). Fresh per sustained run;
24410
+ * released via `uncacheFrame`.
24411
+ */
24412
+ frameId: number().int().nonnegative().optional(),
24413
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
24414
+ deviceKey: string().optional()
24415
+ }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
24416
+ /**
24417
+ * Cache a raw frame inside the Python inference pool's memory.
24418
+ * Returns a numeric `frameId` that `inferCached` references —
24419
+ * subsequent calls send only 5 bytes through the pipe instead of
24420
+ * 1.2MB raw data, eliminating the pipe transfer bottleneck.
24421
+ */
24422
+ cacheFrameInPool: method(object({
24423
+ data: _instanceof(Uint8Array),
24424
+ width: number().int().positive(),
24425
+ height: number().int().positive(),
24426
+ format: _enum([
24427
+ "rgb",
24428
+ "bgr",
24429
+ "gray"
24430
+ ])
24431
+ }), object({
24432
+ frameId: number(),
24433
+ width: number(),
24434
+ height: number()
24435
+ }), { kind: "mutation" }),
24436
+ /**
24437
+ * Run inference on a previously cached frame. Sends only 5 bytes
24438
+ * (model_idx + frameId) through the IPC pipe — eliminates the
24439
+ * ~35ms per-call overhead of transferring 1.2MB raw data.
24440
+ */
24441
+ inferCached: method(object({
24442
+ stepId: string(),
24443
+ frameId: number().int()
24444
+ }), record(string(), unknown()), { kind: "mutation" }),
24445
+ /**
24446
+ * Release a cached frame from the Python pool's memory.
24447
+ */
24448
+ uncacheFrame: method(object({ frameId: number().int() }), _void(), { kind: "mutation" }),
24449
+ /**
24450
+ * The effective provisioning of ONE pool of this node (operator override →
24451
+ * the pool's own runtime row). `deviceKey` names the device pool; absent is
24452
+ * the node-default pool. It answered only for the node default until
24453
+ * D646, so a benchmark on `openvino:npu` recorded the onnx pool's tuning
24454
+ * beside an NPU measurement.
24455
+ */
24456
+ getEffectiveTuning: method(object({ deviceKey: string().optional() }), EffectiveTuningSchema),
24457
+ /**
24458
+ * List every EngineFactory currently loaded in this executor's RAM,
24459
+ * with the models resident and a coarse "in use" marker derived from
24460
+ * ongoing inference activity. Used by the Pipeline page Engines tab.
24461
+ */
24462
+ listLoadedEngines: method(_void(), array(object({
24463
+ engineKey: string(),
24464
+ engine: PipelineEngineChoiceSchema,
24465
+ modelsLoaded: array(string()).readonly(),
24466
+ inUseByCameras: array(number()).readonly(),
24467
+ /**
24468
+ * Origin of this resident factory.
24469
+ * - `runtime` — main camera-serving engine (no idle TTL).
24470
+ * - `device-pool` — a concurrent per-device pool (Phase 2
24471
+ * multi-device, keyed by `deviceKey`) resolved
24472
+ * via `resolveDeviceFactory`. Runs alongside the
24473
+ * `runtime` engine on a DIFFERENT accelerator
24474
+ * (NPU / iGPU / Coral) — this is how the
24475
+ * Engines tab shows all pools running at once.
24476
+ */
24477
+ kind: _enum(["runtime", "device-pool"]),
24478
+ /** Native pid of the underlying Python pool (null when no pool). */
24479
+ poolPid: number().nullable(),
24480
+ /** ms since this factory was last used (null when not warm-tracked). */
24481
+ idleMs: number().nullable(),
24482
+ /** Idle TTL after which the factory self-evicts (null when not applicable). */
24483
+ idleTtlMs: number().nullable()
24484
+ })).readonly()),
24485
+ /** Warm up an engine without running a frame. No-op if already loaded. */
24486
+ spinEngine: method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
24487
+ kind: "mutation",
24488
+ auth: "admin"
24489
+ }),
24490
+ /**
24491
+ * Unload an engine from RAM. `force:true` unloads even when cameras
24492
+ * are actively using it (they re-spin on next frame). Default is
24493
+ * gated — returns `{success:false, reason}` when in use.
24494
+ */
24495
+ killEngine: method(object({
24496
+ engine: PipelineEngineChoiceSchema,
24497
+ force: boolean().optional()
24498
+ }), object({
24499
+ success: boolean(),
24500
+ reason: string().optional()
24501
+ }), {
24502
+ kind: "mutation",
24503
+ auth: "admin"
24504
+ }),
24505
+ listReferenceImages: method(_void(), array(ReferenceImageEntrySchema).readonly()),
24506
+ getReferenceImage: method(object({ filename: string() }), ReferenceImageBodySchema.nullable()),
24507
+ getReferenceAudioFiles: method(_void(), array(ReferenceAudioEntrySchema).readonly()),
24508
+ getReferenceAudio: method(object({ filename: string() }), ReferenceAudioBodySchema.nullable()),
24509
+ getAudioCapabilities: method(_void(), AudioCapabilitiesSchema),
24510
+ runAudioTest: method(object({
24511
+ addonId: string(),
24512
+ modelId: string(),
24513
+ filename: string().optional(),
24514
+ settings: record(string(), unknown()).optional()
24515
+ }), AudioTestResultSchema, { kind: "mutation" }),
24516
+ getDetectionConfigSchema: method(_void(), ConfigUISchemaNullableBridge)
24517
+ }
24518
+ };
24560
24519
  object({
24561
24520
  activeCameras: number(),
24562
24521
  throttledCameras: number(),
@@ -24582,6 +24541,66 @@ var CameraMetricsSchema = object({
24582
24541
  });
24583
24542
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: number() });
24584
24543
  /**
24544
+ * Zone — pure geometry + identity. NO filtering behaviour.
24545
+ *
24546
+ * Zones describe **where** in the frame the operator wants to flag
24547
+ * something; consumer-owned {@link ZoneRule} arrays describe **how**
24548
+ * each pipeline stage uses them. Splitting the two means a single
24549
+ * polygon "Driveway" can simultaneously back a motion-exclude rule,
24550
+ * a detection-include rule on `['car']`, and an occupancy aggregate
24551
+ * — without three duplicated polygons.
24552
+ *
24553
+ * Owned by the orchestrator addon (provider) and mirrored into the
24554
+ * `zones` device-state slice on every mutation. Consumers
24555
+ * (motion-wasm, pipeline-executor, analytics, admin UI) read either
24556
+ * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
24557
+ * mirror with `onChanged`).
24558
+ *
24559
+ * Coordinates are normalised fractions of the frame (0–1) so zones
24560
+ * survive resolution changes and stream profile switches.
24561
+ *
24562
+ * `kind` discriminates between full polygons (closed regions used
24563
+ * for intrusion / occupancy filters) and tripwires (open 2-point
24564
+ * line segments used for cross events). Onboard / firmware-reported
24565
+ * zones (Reolink, ONVIF) are out of scope for now — see the deferred
24566
+ * task list.
24567
+ */
24568
+ var ZoneKindEnum = _enum(["polygon", "tripwire"]);
24569
+ /** Polygon vertex in fraction-of-frame coordinates (0–1). */
24570
+ var PolygonPointSchema = object({
24571
+ x: number(),
24572
+ y: number()
24573
+ });
24574
+ /** A camera detection zone — pure geometry/identity. */
24575
+ var ZoneSchema = object({
24576
+ id: string(),
24577
+ name: string(),
24578
+ kind: ZoneKindEnum.default("polygon"),
24579
+ /** Polygon vertices, fraction of frame (0–1). */
24580
+ polygon: array(PolygonPointSchema).readonly(),
24581
+ /** Visual color for UI rendering. */
24582
+ color: string().default("#3b82f6")
24583
+ });
24584
+ DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).readonly()), method(object({
24585
+ deviceId: number(),
24586
+ zone: ZoneSchema
24587
+ }), _void(), {
24588
+ kind: "mutation",
24589
+ auth: "admin"
24590
+ }), method(object({
24591
+ deviceId: number(),
24592
+ zoneId: string()
24593
+ }), _void(), {
24594
+ kind: "mutation",
24595
+ auth: "admin"
24596
+ }), method(object({
24597
+ deviceId: number(),
24598
+ zone: ZoneSchema
24599
+ }), _void(), {
24600
+ kind: "mutation",
24601
+ auth: "admin"
24602
+ }), object({ zones: array(ZoneSchema).readonly() });
24603
+ /**
24585
24604
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
24586
24605
  * decode worker resolves it against the RETAINED native frame's real pixel dims,
24587
24606
  * so the caller supplies only the detection-res bbox divided by the detection
@@ -24768,7 +24787,17 @@ var DetailResultSchema = object({
24768
24787
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
24769
24788
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
24770
24789
  * consumers MUST prefer this when present (2026-07-22 native-gate fix). */
24771
- nativeFaceShortSidePx: number().optional()
24790
+ nativeFaceShortSidePx: number().optional(),
24791
+ /**
24792
+ * The five facial landmarks of a `face` detail, in the SAME frame space as
24793
+ * `bbox` (D644). Additive and optional: post-analysis stores them beside the
24794
+ * face's source frame so a future alignment can be re-derived exactly; a
24795
+ * runner that predates the field sends none and the face is stored without.
24796
+ */
24797
+ landmarks: array(object({
24798
+ x: number(),
24799
+ y: number()
24800
+ })).optional()
24772
24801
  });
24773
24802
  /**
24774
24803
  * Why an executing node REFUSED a stateless step run (`runStatelessStep`).
@@ -33770,7 +33799,7 @@ var HardwareInfoSchema = object({
33770
33799
  coral: CoralInfoSchema.nullable().optional()
33771
33800
  });
33772
33801
  var PlatformScoreSchema = object({
33773
- runtime: _enum(["node", "python"]),
33802
+ runtime: literal("python"),
33774
33803
  backend: string(),
33775
33804
  format: _enum([
33776
33805
  "onnx",
@@ -33807,7 +33836,7 @@ var ModelRequirementSchema = object({
33807
33836
  });
33808
33837
  var ResolvedInferenceConfigSchema = object({
33809
33838
  modelId: string(),
33810
- runtime: _enum(["node", "python"]),
33839
+ runtime: literal("python"),
33811
33840
  backend: string(),
33812
33841
  format: ModelFormatSchema,
33813
33842
  reason: string()
@@ -36534,6 +36563,92 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
36534
36563
  kind: "mutation",
36535
36564
  auth: "admin"
36536
36565
  });
36566
+ /**
36567
+ * Per-stage gating mode applied to the zones a rule references.
36568
+ *
36569
+ * - `include`: the rule contributes to a **whitelist** for its stage.
36570
+ * When at least one `include` rule fires for a stage, only entities
36571
+ * inside one of those zones pass that stage.
36572
+ * - `exclude`: the rule contributes to a **blacklist** for its stage.
36573
+ * Entities inside one of those zones are dropped at that stage.
36574
+ *
36575
+ * `monitor`-style observation (count without filtering) is not a rule
36576
+ * mode — zones without any matching rule are observed naturally by
36577
+ * `zone-analytics` (live snapshot + history), so an "I just want to
36578
+ * count, not filter" use case needs no rule at all.
36579
+ */
36580
+ var ZoneRuleModeEnum = _enum(["include", "exclude"]);
36581
+ /**
36582
+ * Per-consumer rule that references existing zones (geometry) and
36583
+ * defines how a specific pipeline stage should treat them. Each
36584
+ * consumer addon owns its own `ZoneRule[]` array in its per-device
36585
+ * settings:
36586
+ *
36587
+ * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
36588
+ * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
36589
+ * - future: notification rules, audio gating, etc.
36590
+ *
36591
+ * One rule applies to N zones (`zoneIds[]`) so the operator can
36592
+ * express "ignore motion in ALL of {garden, street}" with a single
36593
+ * rule. `classFilter` narrows the rule to specific object classes —
36594
+ * "drop person detections in the street, but keep cars" is one
36595
+ * `exclude` rule with `classFilter: ['person']`.
36596
+ *
36597
+ * `enabled` is a soft toggle — the operator can keep the rule
36598
+ * configured but inert without deleting it.
36599
+ */
36600
+ var ZoneRuleSchema = object({
36601
+ /** Stable rule id — survives edits, used by the UI for diffing. */
36602
+ id: string(),
36603
+ /** Optional human-readable label rendered in the rule editor. */
36604
+ name: string().optional(),
36605
+ /** Zones this rule targets. The rule's `mode` applies to ALL
36606
+ * listed zones (OR-set: a detection in any one of them counts).
36607
+ * At least one zone id required — a rule with no targets is a
36608
+ * configuration mistake and the form validator rejects it. */
36609
+ zoneIds: array(string()).min(1).readonly(),
36610
+ mode: ZoneRuleModeEnum,
36611
+ /**
36612
+ * Class names this rule applies to. Empty / undefined ⇒ rule
36613
+ * applies to every class. Class strings match the `macroClass`
36614
+ * field on detections (e.g. `person`, `car`, `dog`).
36615
+ */
36616
+ classFilter: array(string()).readonly().optional(),
36617
+ /**
36618
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
36619
+ * required to consider an entity "in the zone". Defaults to the
36620
+ * consumer's stage default when omitted. Kept for back-compat with
36621
+ * existing per-rule overrides; new operators pick the value via
36622
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
36623
+ * set, the lower-level engine reads it as a 0–1 fraction.
36624
+ */
36625
+ overlapThreshold: number().min(0).max(1).optional(),
36626
+ /**
36627
+ * Operator-friendly version of `overlapThreshold` — the percentage
36628
+ * of the detection's bbox that must lie inside the zone for the
36629
+ * rule to match. Documented default is 85%; the engine substitutes
36630
+ * that when the field is omitted (kept optional so existing rules
36631
+ * stored without it stay valid).
36632
+ *
36633
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
36634
+ * rule, the engine prefers `bboxInclusionPct` because it's the
36635
+ * field exposed in the UI. Internally both feed the same gate.
36636
+ */
36637
+ bboxInclusionPct: number().min(0).max(100).optional(),
36638
+ /**
36639
+ * When `true` and a detection has a segmentation mask, use the
36640
+ * mask for overlap instead of the bbox. Detection-stage only;
36641
+ * motion rules ignore this field.
36642
+ */
36643
+ preferMask: boolean().optional(),
36644
+ /**
36645
+ * Soft-toggle: `false` disables the rule without deleting it.
36646
+ * Defaults to `true` so operators creating a rule via the UI
36647
+ * see it active immediately.
36648
+ */
36649
+ enabled: boolean().default(true)
36650
+ });
36651
+ array(ZoneRuleSchema).readonly();
36537
36652
  object({
36538
36653
  /** Whether the script is currently executing. */
36539
36654
  isRunning: boolean(),
@@ -43009,6 +43124,12 @@ Object.freeze({
43009
43124
  addonId: null,
43010
43125
  access: "view"
43011
43126
  },
43127
+ "notificationRules.previewStateSummary": {
43128
+ capName: "notification-rules",
43129
+ capScope: "system",
43130
+ addonId: null,
43131
+ access: "view"
43132
+ },
43012
43133
  "notificationRules.previewTemplate": {
43013
43134
  capName: "notification-rules",
43014
43135
  capScope: "system",
@@ -43585,12 +43706,6 @@ Object.freeze({
43585
43706
  addonId: null,
43586
43707
  access: "create"
43587
43708
  },
43588
- "pipelineAnalytics.runReplayFrameProcessor": {
43589
- capName: "pipeline-analytics",
43590
- capScope: "device",
43591
- addonId: null,
43592
- access: "create"
43593
- },
43594
43709
  "pipelineAnalytics.saveRetrainAnnotations": {
43595
43710
  capName: "pipeline-analytics",
43596
43711
  capScope: "device",
@@ -47688,11 +47803,6 @@ Object.freeze({
47688
47803
  form: "single",
47689
47804
  optional: false
47690
47805
  }],
47691
- "pipelineAnalytics.runReplayFrameProcessor": [{
47692
- name: "deviceId",
47693
- form: "single",
47694
- optional: false
47695
- }],
47696
47806
  "pipelineAnalytics.saveRetrainAnnotations": [{
47697
47807
  name: "deviceId",
47698
47808
  form: "single",
@@ -48722,6 +48832,98 @@ new Map(Object.entries({
48722
48832
  "camera-enabled": null
48723
48833
  }));
48724
48834
  [...NC_SYSTEM_EVENT_FILTER_KEYS];
48835
+ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
48836
+ {
48837
+ id: "device-state",
48838
+ family: "device-events",
48839
+ label: "Device state",
48840
+ kinds: [
48841
+ "device-offline",
48842
+ "device-online",
48843
+ "device-disabled",
48844
+ "device-enabled"
48845
+ ]
48846
+ },
48847
+ {
48848
+ id: "streams",
48849
+ family: "device-events",
48850
+ label: "Streams",
48851
+ kinds: ["stream-offline", "stream-online"]
48852
+ },
48853
+ {
48854
+ id: "battery",
48855
+ family: "device-events",
48856
+ label: "Battery",
48857
+ kinds: ["device-battery-low", "device-battery-normal"]
48858
+ },
48859
+ {
48860
+ id: "consumables",
48861
+ family: "device-events",
48862
+ label: "Consumables",
48863
+ kinds: ["device-consumable-low", "device-consumable-normal"]
48864
+ },
48865
+ {
48866
+ id: "tanks",
48867
+ family: "device-events",
48868
+ label: "Tanks",
48869
+ kinds: [
48870
+ "device-tank-low",
48871
+ "device-tank-full",
48872
+ "device-tank-normal"
48873
+ ]
48874
+ },
48875
+ {
48876
+ id: "detection-blind",
48877
+ family: "device-events",
48878
+ label: "Detecting nothing",
48879
+ kinds: ["detection-blind"]
48880
+ },
48881
+ {
48882
+ id: "nodes",
48883
+ family: "system",
48884
+ label: "Nodes",
48885
+ kinds: [
48886
+ "node-offline",
48887
+ "node-online",
48888
+ "node-inference-unavailable"
48889
+ ]
48890
+ },
48891
+ {
48892
+ id: "addons",
48893
+ family: "system",
48894
+ label: "Addons",
48895
+ kinds: [
48896
+ "addon-crash-loop",
48897
+ "addon-update-available",
48898
+ "addon-updated"
48899
+ ]
48900
+ },
48901
+ {
48902
+ id: "updates",
48903
+ family: "system",
48904
+ label: "Server & container updates",
48905
+ kinds: [
48906
+ "server-update-available",
48907
+ "server-updated",
48908
+ "wrapper-update-available"
48909
+ ]
48910
+ },
48911
+ {
48912
+ id: "backups-exports",
48913
+ family: "system",
48914
+ label: "Backups & exports",
48915
+ kinds: ["backup-completed", "export-completed"]
48916
+ },
48917
+ {
48918
+ id: "alarm",
48919
+ family: "alarm",
48920
+ label: "Alarm",
48921
+ kinds: NC_ALARM_SYSTEM_EVENT_KINDS
48922
+ }
48923
+ ];
48924
+ function systemEventKindsOfFamily(family) {
48925
+ return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((g) => g.family === family).flatMap((g) => g.kinds);
48926
+ }
48725
48927
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
48726
48928
  /**
48727
48929
  * WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
@@ -48796,7 +48998,183 @@ var DEVICE_EVENTS = [
48796
48998
  "detection-blind",
48797
48999
  ...ALARM
48798
49000
  ];
48799
- [...DEVICE_EVENTS], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_BODY_ONLY], [...RULE_TEXT], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...BATTERY, ...CONSUMABLE], [...RULE_TEXT], [...CONSUMABLE], [...RULE_TEXT], [...TANK], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT];
49001
+ /** Every kind a summary may speak about — the two summarisable families. */
49002
+ var SUMMARISABLE = [...systemEventKindsOfFamily("device-events"), ...systemEventKindsOfFamily("system")];
49003
+ /** One per summary SECTION (`NC_STATE_SUMMARY_SECTIONS` in the notification
49004
+ * centre): the section's lines, offered when one of its kinds is selected. */
49005
+ var STATE_SUMMARY_SECTION_VARS = [
49006
+ {
49007
+ name: "summaryDevices",
49008
+ label: "Summary: devices offline / switched off",
49009
+ example: "Garage — offline since 07:12",
49010
+ kinds: [
49011
+ "device-offline",
49012
+ "device-online",
49013
+ "device-disabled",
49014
+ "device-enabled"
49015
+ ]
49016
+ },
49017
+ {
49018
+ name: "summaryStreams",
49019
+ label: "Summary: streams offline",
49020
+ example: "Ingresso (high) — since 06:40",
49021
+ kinds: ["stream-offline", "stream-online"]
49022
+ },
49023
+ {
49024
+ name: "summaryBattery",
49025
+ label: "Summary: low batteries",
49026
+ example: "Sensore porta — 8 %",
49027
+ kinds: BATTERY
49028
+ },
49029
+ {
49030
+ name: "summaryConsumables",
49031
+ label: "Summary: consumables",
49032
+ example: "Robot — filter 10 %",
49033
+ kinds: CONSUMABLE
49034
+ },
49035
+ {
49036
+ name: "summaryTanks",
49037
+ label: "Summary: tanks",
49038
+ example: "Robot — clean water low",
49039
+ kinds: TANK
49040
+ },
49041
+ {
49042
+ name: "summaryDetection",
49043
+ label: "Summary: cameras detecting nothing",
49044
+ example: "Ingresso — since 05:10",
49045
+ kinds: ["detection-blind"]
49046
+ },
49047
+ {
49048
+ name: "summaryNodes",
49049
+ label: "Summary: nodes",
49050
+ example: "little-unraid — offline since 03:00",
49051
+ kinds: [
49052
+ "node-offline",
49053
+ "node-online",
49054
+ "node-inference-unavailable"
49055
+ ]
49056
+ },
49057
+ {
49058
+ name: "summaryAddons",
49059
+ label: "Summary: addons",
49060
+ example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
49061
+ kinds: [
49062
+ "addon-crash-loop",
49063
+ "addon-update-available",
49064
+ "addon-updated"
49065
+ ]
49066
+ },
49067
+ {
49068
+ name: "summaryUpdates",
49069
+ label: "Summary: server & container updates",
49070
+ example: "server 1.2.377 → 1.2.378",
49071
+ kinds: [
49072
+ "server-update-available",
49073
+ "server-updated",
49074
+ "wrapper-update-available"
49075
+ ]
49076
+ }
49077
+ ];
49078
+ var STATE_SUMMARY_VARS = [
49079
+ {
49080
+ name: "summaryText",
49081
+ label: "Summary: every section",
49082
+ description: "One block per section with something to report, one line per item.",
49083
+ example: "Batterie scariche: Sensore porta — 8 %",
49084
+ group: "system",
49085
+ families: ["rule"],
49086
+ fields: [...RULE_TEXT],
49087
+ kinds: ["system"],
49088
+ systemEventKinds: [...SUMMARISABLE],
49089
+ systemEventDeliveries: ["summary"]
49090
+ },
49091
+ {
49092
+ name: "summaryCount",
49093
+ label: "Summary: items reported",
49094
+ example: "3",
49095
+ group: "system",
49096
+ families: ["rule"],
49097
+ fields: [...RULE_TEXT],
49098
+ kinds: ["system"],
49099
+ systemEventKinds: [...SUMMARISABLE],
49100
+ systemEventDeliveries: ["summary"]
49101
+ },
49102
+ ...STATE_SUMMARY_SECTION_VARS.map((v) => ({
49103
+ name: v.name,
49104
+ label: v.label,
49105
+ example: v.example,
49106
+ group: "system",
49107
+ families: ["rule"],
49108
+ fields: [...RULE_TEXT],
49109
+ kinds: ["system"],
49110
+ systemEventKinds: [...v.kinds],
49111
+ systemEventDeliveries: ["summary"]
49112
+ }))
49113
+ ];
49114
+ [
49115
+ [...DEVICE_EVENTS],
49116
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
49117
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
49118
+ [...RULE_TEXT],
49119
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
49120
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
49121
+ [...RULE_TEXT],
49122
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
49123
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
49124
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
49125
+ [...RULE_TEXT],
49126
+ [...RULE_TEXT],
49127
+ [...RULE_TEXT],
49128
+ [...RULE_TEXT],
49129
+ [...RULE_TEXT],
49130
+ [...RULE_TEXT],
49131
+ [...RULE_TEXT],
49132
+ [...RULE_TEXT],
49133
+ [...RULE_BODY_ONLY],
49134
+ [...RULE_TEXT],
49135
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
49136
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
49137
+ ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
49138
+ ([...RULE_TEXT], [...CONSUMABLE]),
49139
+ ([...RULE_TEXT], [...TANK]),
49140
+ [...RULE_TEXT],
49141
+ ([...RULE_TEXT], [...UPDATES]),
49142
+ ([...RULE_TEXT], [...UPDATES]),
49143
+ [...RULE_TEXT],
49144
+ [...RULE_TEXT],
49145
+ [...RULE_TEXT],
49146
+ ([...RULE_TEXT], [...UPDATES]),
49147
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
49148
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
49149
+ ([...RULE_TEXT], [...UPDATES]),
49150
+ ([...RULE_TEXT], [...UPDATES]),
49151
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
49152
+ [...RULE_TEXT],
49153
+ [...RULE_TEXT],
49154
+ [...RULE_TEXT],
49155
+ [...RULE_TEXT],
49156
+ [...RULE_TEXT],
49157
+ [...RULE_TEXT],
49158
+ [...RULE_TEXT],
49159
+ [...RULE_TEXT],
49160
+ [...RULE_TEXT],
49161
+ [...RULE_TEXT],
49162
+ [...RULE_TEXT],
49163
+ [...RULE_TEXT],
49164
+ [...RULE_TEXT],
49165
+ [...RULE_TEXT],
49166
+ [...RULE_TEXT],
49167
+ [...RULE_TEXT],
49168
+ [...RULE_TEXT],
49169
+ [...RULE_TEXT],
49170
+ [...RULE_TEXT],
49171
+ [...RULE_TEXT],
49172
+ [...RULE_TEXT],
49173
+ [...RULE_TEXT],
49174
+ [...RULE_TEXT],
49175
+ [...RULE_TEXT],
49176
+ ...STATE_SUMMARY_VARS
49177
+ ];
48800
49178
  /**
48801
49179
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
48802
49180
  *
@@ -49622,6 +50000,110 @@ function pickNativeLeaseOverride(view) {
49622
50000
  }
49623
50001
  return readNativeLeaseOverride(flat);
49624
50002
  }
50003
+ function snapshot(run) {
50004
+ return {
50005
+ runId: run.runId,
50006
+ state: run.state,
50007
+ startedAtMs: run.startedAtMs,
50008
+ finishedAtMs: run.finishedAtMs,
50009
+ progress: run.progress,
50010
+ result: run.result,
50011
+ error: run.error
50012
+ };
50013
+ }
50014
+ var BackgroundRunRegistry = class {
50015
+ opts;
50016
+ runs = /* @__PURE__ */ new Map();
50017
+ now;
50018
+ constructor(opts) {
50019
+ this.opts = opts;
50020
+ this.now = opts.now ?? Date.now;
50021
+ }
50022
+ /** Runs still going. */
50023
+ get runningCount() {
50024
+ let n = 0;
50025
+ for (const run of this.runs.values()) if (run.state === "running") n += 1;
50026
+ return n;
50027
+ }
50028
+ /**
50029
+ * Register and start `work`. Returns the `running` receipt immediately.
50030
+ * Throws only for a refusal the caller must see (duplicate id, busy) — a
50031
+ * failure of the work itself lands in `status`, never here.
50032
+ */
50033
+ start(runId, work) {
50034
+ const existing = this.runs.get(runId);
50035
+ if (existing !== void 0 && existing.state === "running") throw new Error(`run '${runId}' is already running`);
50036
+ if (this.runningCount >= this.opts.maxConcurrent) throw new Error(`busy: ${this.runningCount}/${this.opts.maxConcurrent} run(s) already in progress — wait for one to finish or abort it`);
50037
+ const run = {
50038
+ runId,
50039
+ state: "running",
50040
+ startedAtMs: this.now(),
50041
+ finishedAtMs: null,
50042
+ progress: null,
50043
+ result: null,
50044
+ error: null,
50045
+ controller: new AbortController()
50046
+ };
50047
+ this.runs.delete(runId);
50048
+ this.runs.set(runId, run);
50049
+ const ctx = {
50050
+ signal: run.controller.signal,
50051
+ report: (progress) => {
50052
+ run.progress = progress;
50053
+ }
50054
+ };
50055
+ const settle = (patch) => {
50056
+ if (patch.error !== void 0) {
50057
+ run.state = "failed";
50058
+ run.error = patch.error;
50059
+ } else {
50060
+ run.state = run.controller.signal.aborted ? "aborted" : "done";
50061
+ run.result = patch.result ?? null;
50062
+ }
50063
+ run.finishedAtMs = this.now();
50064
+ this.prune();
50065
+ this.opts.onSettled?.(snapshot(run));
50066
+ };
50067
+ let pending;
50068
+ try {
50069
+ pending = work(ctx);
50070
+ } catch (err) {
50071
+ pending = Promise.reject(err);
50072
+ }
50073
+ pending.then((result) => settle({ result }), (err) => settle({ error: err instanceof Error ? err.message : String(err) }));
50074
+ return snapshot(run);
50075
+ }
50076
+ status(runId) {
50077
+ const run = this.runs.get(runId);
50078
+ return run === void 0 ? null : snapshot(run);
50079
+ }
50080
+ /** Running runs first (newest start first), then finished ones (newest finish first). */
50081
+ list() {
50082
+ const all = [...this.runs.values()];
50083
+ const running = all.filter((r) => r.state === "running").toSorted((a, b) => b.startedAtMs - a.startedAtMs);
50084
+ const finished = all.filter((r) => r.state !== "running").toSorted((a, b) => (b.finishedAtMs ?? 0) - (a.finishedAtMs ?? 0));
50085
+ return [...running, ...finished].map(snapshot);
50086
+ }
50087
+ /** Signal a running run to stop. `false` for an unknown or already finished run. */
50088
+ abort(runId) {
50089
+ const run = this.runs.get(runId);
50090
+ if (run === void 0 || run.state !== "running") return false;
50091
+ run.controller.abort();
50092
+ return true;
50093
+ }
50094
+ /** Abort every running run (shutdown). */
50095
+ abortAll() {
50096
+ for (const run of this.runs.values()) if (run.state === "running") run.controller.abort();
50097
+ }
50098
+ prune() {
50099
+ const finished = [...this.runs.values()].filter((r) => r.state !== "running").toSorted((a, b) => (a.finishedAtMs ?? 0) - (b.finishedAtMs ?? 0));
50100
+ const excess = finished.length - this.opts.keepFinished;
50101
+ for (let i = 0; i < excess; i++) {
50102
+ const victim = finished[i];
50103
+ if (victim !== void 0) this.runs.delete(victim.runId);
50104
+ }
50105
+ }
50106
+ };
49625
50107
  function hfModelUrl(repo, path) {
49626
50108
  return `https://huggingface.co/${repo}/resolve/main/${path}`;
49627
50109
  }
@@ -50023,9 +50505,9 @@ var RingBuffer = class {
50023
50505
  };
50024
50506
  /**
50025
50507
  * DEVICE-POOL backend → model format — the SSOT for the multi-device inference
50026
- * DESCRIPTOR path (Python per-device pools), distinct from {@link BACKEND_TO_FORMAT}
50027
- * above which is the legacy NODE runtime map (where `coreml → onnx` because
50028
- * onnxruntime-node's CoreML EP loads `.onnx`). Here a device pool runs the
50508
+ * DESCRIPTOR path (Python per-device pools), distinct from the former BACKEND_TO_FORMAT,
50509
+ * which was the removed NODE runtime map (where `coreml → onnx` because
50510
+ * onnxruntime-node's CoreML EP loaded `.onnx`). A device pool runs the
50029
50511
  * backend NATIVELY, so `coreml → coreml` and `edgetpu → tflite`. Consumed by
50030
50512
  * `resolveDeviceEngine` (addon-pipeline) and the orchestrator's
50031
50513
  * `parseDeviceKey`/`getNodeInferenceDevices` — the previously-duplicated
@@ -50323,4 +50805,4 @@ function enumerateInferenceDevices(hw) {
50323
50805
  return out;
50324
50806
  }
50325
50807
  //#endregion
50326
- export { defaultDeviceFor as $, errMsg as $t, PoolMemoryWatchdog as A, literal as An, pickDetailCropConvention as At, VISIT_MERGE_GAP_MS as B, resolveCreateExportSubject as Bt, FailureCounters as C, selectAssignedProfileSlots as Cn, migrateLegacyDeviceSignalConfig as Ct, NativeLeaseAdmissionSchema as D, boolean as Dn, parseProcStatus as Dt, MOTION_CLOSE_AFTER_MS as E, array as En, overlayClusterStepSettings as Et, RECORDING_SOURCE_CAMSTACK_ADDON as F, string as Fn, recordingArchiveCapability as Ft, audioKindId as G, runtimeDevices as Gt, addonWidgetsSourceCapability as H, resolveLocationMode as Ht, RecordingConfigSchema as I, union as In, recordingCapability as It, clipExportFailure as J, streamBrokerCapability as Jt, batteryCapability as K, storageEvictableCapability as Kt, RecordingSignalStatusSchema as L, EventCategory as Ln, recordingExportCapability as Lt, RATE_CONTROL_TIGHT as M, object as Mn, pipelineExecutorCapability as Mt, RECORDING_EXPORT_MAX_READ_BYTES as N, preprocess as Nn, pipelineRunnerCapability as Nt, NativeLeaseSettingsSchema as O, discriminatedUnion as On, pickClusterStepModels as Ot, RECORDING_SOURCE_CAMSTACK as P, record as Pn, rankStreams as Pt, declareLogChannel as Q, webrtcSessionCapability as Qt, RingBuffer as R, recordingSignalCapability as Rt, ExportRecordSchema as S, parseProfileBrokerId as Sn, mayWriteToLocation as St, HF_BASE_URL as T, _enum as Tn, motionDetectionCapability as Tt, audioAnalysisCapability as U, resolvePoolMemoryPolicy as Ut, YAMNET_TO_MACRO as V, resolveEgressDecodeHwAccel as Vt, audioAnalyzerCapability as W, resolveRecordingProfiles as Wt, createHwAccelCache as X, supportedRuntimes as Xt, clipExportFailureOf as Y, stripRetiredBandPreBufferSec as Yt, createLogChannelsProvider as Z, videoclipsCapability as Zt, DEFAULT_NATIVE_LEASE_SETTINGS as _, isEvent as _n, loadContributionCapability as _t, CLIP_BROKER_PACED_RATES as a, BOOT_RECOVERY_BACKOFF_MS as an, egressTransportFromRequest as at, EVENT_PAD_MS as b, nodePin as bn, maskUrlCredentials as bt, CLIP_STREAM_TRANSPORT_HEADER as c, CamProfileSchema as cn, evaluateZoneRules as ct, DEFAULT_AUDIO_ANALYZER_CONFIG as d, audioChunkBytesPerSample as dn, exportSubjectOf as dt, AUDIO_PRESETS as en, deriveBatteryPresence as et, DEFAULT_CLUSTER_STEP_MODELS as f, createEvent as fn, failureContributionCapability as ft, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as g, hydrateSchema as gn, isFirstLevelMacroClass as gt, DEFAULT_EVENTS_BAND_BUFFER_SEC as h, expandAudioChunkToF32le as hn, inferModelProvider as ht, BatteryStatusSchema as i, isSoftwareDecode as in, egressTranscodeSharingKey as it, RATE_CONTROL_RELAXED as j, number as jn, pickNativeLeaseOverride as jt, OpsLogEntrySchema as k, lazy as kn, pickClusterStepSettings as kt, COCO_80_LABELS as l, DeviceFeature as ln, evictionPolicyForMode as lt, DEFAULT_DETAIL_CROP_CONVENTION as m, defineCustomActions as mn, hfModelUrl as mt, AUDIO_BACKEND_CHOICES as n, buildFfmpegArgs as nn, deriveRecordingMode as nt, CLIP_STILL_REASON_CODE_HEADER as o, BaseAddon as on, enumerateInferenceDevices as ot, DEFAULT_CLUSTER_STEP_SETTINGS as p, customAction as pn, gbToBytes as pt, cameraStreamsCapability as q, storageOccupancyCapability as qt, AUDIO_MACRO_LABELS as r, invocationFromEncodeProfile as rn, detectionPipelineCapability as rt, CLIP_STILL_REASON_HEADER as s, CAM_PROFILE_ORDER as sn, evaluateSensorEdge as st, APPLE_SA_TO_MACRO as t, Fmp4BoxSplitter as tn, deriveDetailCropRect as tt, COCO_TO_MACRO as u, DeviceType as un, evictionPolicyOfLocation as ut, DEVICE_BACKEND_TO_FORMAT as v, makeProfileBrokerId as vn, logChannelsCapability as vt, G711_SCALE_CORRECTION_DB as w, sleep as wn, modeMayWrite as wt, EncodeProfileSchema as x, parseJsonUnknown as xn, mayReadLocation as xt, EVENT_OWNER_TYPES as y, makeSourceBrokerId as yn, mapAudioLabelToMacro as yt, VIDEOCLIPS_MAX_OFFER_BYTES as z, resolveClusterStepModelId as zt };
50808
+ export { declareLogChannel as $, webrtcSessionCapability as $t, OpsLogEntrySchema as A, lazy as An, pickClusterStepSettings as At, VIDEOCLIPS_MAX_OFFER_BYTES as B, resolveClusterStepModelId as Bt, ExportRecordSchema as C, parseProfileBrokerId as Cn, mayWriteToLocation as Ct, MOTION_CLOSE_AFTER_MS as D, array as Dn, overlayClusterStepSettings as Dt, HF_BASE_URL as E, _enum as En, motionDetectionCapability as Et, RECORDING_SOURCE_CAMSTACK as F, record as Fn, rankStreams as Ft, audioAnalyzerCapability as G, resolveRecordingProfiles as Gt, YAMNET_TO_MACRO as H, resolveEgressDecodeHwAccel as Ht, RECORDING_SOURCE_CAMSTACK_ADDON as I, string as In, recordingArchiveCapability as It, cameraStreamsCapability as J, storageOccupancyCapability as Jt, audioKindId as K, runtimeDevices as Kt, RecordingConfigSchema as L, union as Ln, recordingCapability as Lt, RATE_CONTROL_RELAXED as M, number as Mn, pickNativeLeaseOverride as Mt, RATE_CONTROL_TIGHT as N, object as Nn, pipelineExecutorCapability as Nt, NativeLeaseAdmissionSchema as O, boolean as On, parseProcStatus as Ot, RECORDING_EXPORT_MAX_READ_BYTES as P, preprocess as Pn, pipelineRunnerCapability as Pt, createLogChannelsProvider as Q, videoclipsCapability as Qt, RecordingSignalStatusSchema as R, EventCategory as Rn, recordingExportCapability as Rt, EncodeProfileSchema as S, parseJsonUnknown as Sn, mayReadLocation as St, G711_SCALE_CORRECTION_DB as T, sleep as Tn, modeMayWrite as Tt, addonWidgetsSourceCapability as U, resolveLocationMode as Ut, VISIT_MERGE_GAP_MS as V, resolveCreateExportSubject as Vt, audioAnalysisCapability as W, resolvePoolMemoryPolicy as Wt, clipExportFailureOf as X, stripRetiredBandPreBufferSec as Xt, clipExportFailure as Y, streamBrokerCapability as Yt, createHwAccelCache as Z, supportedRuntimes as Zt, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as _, hydrateSchema as _n, isFirstLevelMacroClass as _t, BatteryStatusSchema as a, isSoftwareDecode as an, egressTranscodeSharingKey as at, EVENT_OWNER_TYPES as b, makeSourceBrokerId as bn, mapAudioLabelToMacro as bt, CLIP_STILL_REASON_HEADER as c, CAM_PROFILE_ORDER as cn, evaluateSensorEdge as ct, COCO_TO_MACRO as d, DeviceType as dn, evictionPolicyOfLocation as dt, errMsg as en, defaultDeviceFor as et, DEFAULT_AUDIO_ANALYZER_CONFIG as f, audioChunkBytesPerSample as fn, exportSubjectOf as ft, DEFAULT_EVENTS_BAND_BUFFER_SEC as g, expandAudioChunkToF32le as gn, inferModelProvider as gt, DEFAULT_DETAIL_CROP_CONVENTION as h, defineCustomActions as hn, hfModelUrl as ht, BackgroundRunRegistry as i, invocationFromEncodeProfile as in, detectionPipelineCapability as it, PoolMemoryWatchdog as j, literal as jn, pickDetailCropConvention as jt, NativeLeaseSettingsSchema as k, discriminatedUnion as kn, pickClusterStepModels as kt, CLIP_STREAM_TRANSPORT_HEADER as l, CamProfileSchema as ln, evaluateZoneRules as lt, DEFAULT_CLUSTER_STEP_SETTINGS as m, customAction as mn, gbToBytes as mt, AUDIO_BACKEND_CHOICES as n, Fmp4BoxSplitter as nn, deriveDetailCropRect as nt, CLIP_BROKER_PACED_RATES as o, BOOT_RECOVERY_BACKOFF_MS as on, egressTransportFromRequest as ot, DEFAULT_CLUSTER_STEP_MODELS as p, createEvent as pn, failureContributionCapability as pt, batteryCapability as q, storageEvictableCapability as qt, AUDIO_MACRO_LABELS as r, buildFfmpegArgs as rn, deriveRecordingMode as rt, CLIP_STILL_REASON_CODE_HEADER as s, BaseAddon as sn, enumerateInferenceDevices as st, APPLE_SA_TO_MACRO as t, AUDIO_PRESETS as tn, deriveBatteryPresence as tt, COCO_80_LABELS as u, DeviceFeature as un, evictionPolicyForMode as ut, DEFAULT_NATIVE_LEASE_SETTINGS as v, isEvent as vn, loadContributionCapability as vt, FailureCounters as w, selectAssignedProfileSlots as wn, migrateLegacyDeviceSignalConfig as wt, EVENT_PAD_MS as x, nodePin as xn, maskUrlCredentials as xt, DEVICE_BACKEND_TO_FORMAT as y, makeProfileBrokerId as yn, logChannelsCapability as yt, RingBuffer as z, recordingSignalCapability as zt };