@camstack/addon-model-studio 1.1.173 → 1.1.174

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 (18) hide show
  1. package/dist/{MotionZonesSettings-BfCoYgxT.mjs → MotionZonesSettings-DavNeZt1.mjs} +2 -2
  2. package/dist/{PrivacyMaskSettings-Th5IVviI.mjs → PrivacyMaskSettings-bWW0b0eT.mjs} +4 -4
  3. package/dist/{SceneMonitorEditor-BERqxPAZ.mjs → SceneMonitorEditor-DuXCuccO.mjs} +3 -3
  4. package/dist/_stub.js +10 -10
  5. package/dist/{_virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-CP30dIZR.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_model_studio_page-C_y_ArLj.mjs} +3 -3
  6. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DwoBFtDL.mjs +26 -0
  7. package/dist/{hostInit-BZQ7ikZC.mjs → hostInit-nK0BL4EK.mjs} +2 -2
  8. package/dist/model-studio.addon.js +1177 -733
  9. package/dist/model-studio.addon.mjs +1177 -733
  10. package/dist/{player-overlays-B4uXOi-a.mjs → player-overlays-BZLfX48i.mjs} +1 -1
  11. package/dist/remoteEntry.js +1 -1
  12. package/dist/{responsive-CEbpemt_.mjs → responsive-DAticuj8.mjs} +1 -1
  13. package/dist/{scene-monitor-copy-DXDF5cQH.mjs → scene-monitor-copy-CuI6S0HF.mjs} +1 -1
  14. package/dist/{square-B_pecyJV.mjs → square-Dh-K-SIJ.mjs} +1 -1
  15. package/dist/{trash-2-DfAU2tvI.mjs → trash-2-Cuodqp-m.mjs} +1 -1
  16. package/dist/{virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-DRHKHJI4.mjs → virtual_mf-REMOTE_ENTRY_ID___mfe_internal__addon_model_studio_page__remoteEntry_js-mbmirjVC.mjs} +1 -1
  17. package/package.json +1 -1
  18. package/dist/_virtual_mf___mfe_internal__addon_model_studio_page__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-ak-DR-J7.mjs +0 -26
@@ -5884,7 +5884,7 @@ var ZodIssueCode = {
5884
5884
  var ZodFirstPartyTypeKind;
5885
5885
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5886
5886
  //#endregion
5887
- //#region ../types/dist/sleep-DU7mu4wb.mjs
5887
+ //#region ../types/dist/sleep-BaRu0bS5.mjs
5888
5888
  /**
5889
5889
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5890
5890
  * window to float samples (D455).
@@ -10020,7 +10020,7 @@ var ModelFormatEntrySchema = object({
10020
10020
  /** Multi-file payload (directory members or sibling files). */
10021
10021
  files: array(string()).readonly().optional(),
10022
10022
  /** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
10023
- runtimes: array(_enum(["node", "python"])).readonly().optional()
10023
+ runtimes: array(_enum(["python"])).readonly().optional()
10024
10024
  });
10025
10025
  /**
10026
10026
  * Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
@@ -10101,10 +10101,17 @@ var ModelCatalogEntrySchema = object({
10101
10101
  inputChannels: number().int().positive().optional(),
10102
10102
  labels: array(LabelDefinitionSchema).readonly(),
10103
10103
  inputLayout: _enum(["nchw", "nhwc"]).optional(),
10104
+ /**
10105
+ * `'scrfd'` applies InsightFace's SCRFD input contract, `(x - 127.5) / 128`
10106
+ * (upstream `insightface/model_zoo/scrfd.py`), instead of the historical
10107
+ * plain `/255`. Measured against COCO GT: 188 → 196 faces found on the
10108
+ * `scrfd-2.5g` catalog entry (2026-09-26 model-replacement spike, §2.4-2).
10109
+ */
10104
10110
  inputNormalization: _enum([
10105
10111
  "zero-one",
10106
10112
  "imagenet",
10107
- "none"
10113
+ "none",
10114
+ "scrfd"
10108
10115
  ]).optional(),
10109
10116
  /**
10110
10117
  * The model already applies softmax IN-GRAPH — its raw output is a
@@ -10206,7 +10213,8 @@ var ModelConvertMetadataSchema = object({
10206
10213
  inputNormalization: _enum([
10207
10214
  "zero-one",
10208
10215
  "imagenet",
10209
- "none"
10216
+ "none",
10217
+ "scrfd"
10210
10218
  ]).optional(),
10211
10219
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
10212
10220
  outputFormat: _enum([
@@ -17624,6 +17632,9 @@ var NcSystemEventKindSchema = _enum([
17624
17632
  "device-battery-normal",
17625
17633
  "device-consumable-low",
17626
17634
  "device-consumable-normal",
17635
+ "device-tank-low",
17636
+ "device-tank-full",
17637
+ "device-tank-normal",
17627
17638
  "stream-online",
17628
17639
  "stream-offline",
17629
17640
  "node-online",
@@ -17661,6 +17672,29 @@ var NC_LEGACY_SYSTEM_EVENT_KINDS = new Set([
17661
17672
  ]);
17662
17673
  NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
17663
17674
  /**
17675
+ * The panel's transitions, as ONE list.
17676
+ *
17677
+ * Named here rather than spelled out at each of the four sites that need them
17678
+ * (the emitter, the intake, the editor group, the combined-notification gate),
17679
+ * because a transition added to the enum and forgotten at one of them is an
17680
+ * alarm state nobody can be notified about. That is exactly what this list
17681
+ * failed to prevent while it held three: `alarm-arming` was the fourth.
17682
+ *
17683
+ * In LIFECYCLE order, which is the order an editor lists them in and the order
17684
+ * a person experiences them — not the order they were added.
17685
+ */
17686
+ var NC_ALARM_SYSTEM_EVENT_KINDS = [
17687
+ "alarm-arm-refused",
17688
+ "alarm-check-failed",
17689
+ "alarm-arming",
17690
+ "alarm-armed",
17691
+ "alarm-pending",
17692
+ "alarm-triggered",
17693
+ "alarm-rearmed",
17694
+ "alarm-sensor-bypass",
17695
+ "alarm-disarmed"
17696
+ ];
17697
+ /**
17664
17698
  * One coherent system-event condition. `kinds` is the required opt-in safety
17665
17699
  * gate; the remaining lists are optional narrowing filters relevant to the
17666
17700
  * selected kinds.
@@ -17694,7 +17728,22 @@ var NcSystemEventConditionSchema = object({
17694
17728
  */
17695
17729
  excludeDeviceIds: array(number().int()).min(1).optional(),
17696
17730
  nodeIds: array(string().min(1)).min(1).optional(),
17697
- packageNames: array(string().min(1)).min(1).optional()
17731
+ packageNames: array(string().min(1)).min(1).optional(),
17732
+ /**
17733
+ * At or below this remaining-life %, a consumable is LOW for THIS rule
17734
+ * (D642). Absent = {@link NC_CONSUMABLE_LOW_PERCENT_DEFAULT}; at most
17735
+ * {@link NC_CONSUMABLE_LOW_PERCENT_MAX}, so every episode can recover. Scoped to the
17736
+ * consumable kinds (`systemEventFilterApplies`) and to the device-events
17737
+ * family; ignored on every other kind.
17738
+ *
17739
+ * It is a property of the RULE because the operator decided so: a rule at
17740
+ * 25 must be told about a filter at 20 %, a rule at 10 must not. The hub
17741
+ * keeps one episode per (threshold, device, item) — its own hysteresis, its
17742
+ * own reminder — and a rule hears only the episode judged at ITS threshold,
17743
+ * exactly as an occupancy rule hears only the edge of its own count. A
17744
+ * provider's own `replace` verdict is low at every threshold.
17745
+ */
17746
+ consumableLowPercent: number().int().min(1).max(95).optional()
17698
17747
  });
17699
17748
  /** Hard ceiling on a window (24h). A snooze that could not expire would be an
17700
17749
  * outage the operator asked for once and forgot. */
@@ -17712,6 +17761,25 @@ var NcScheduleSchema = object({
17712
17761
  /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
17713
17762
  invert: boolean().optional()
17714
17763
  });
17764
+ /**
17765
+ * How a system-event rule of the `device-events` or `system` family speaks
17766
+ * (D647): at once, per event (`instant`, the only behaviour before D647), or
17767
+ * as a STATE SUMMARY at the end of each window of `schedule`.
17768
+ */
17769
+ var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
17770
+ /**
17771
+ * `summary`: the rule sends NOTHING per event. When a window of `schedule`
17772
+ * CLOSES, it sends one notification with the current state of its kinds
17773
+ * (low batteries, consumables to replace, devices offline, updates
17774
+ * available, ...), after its own filters. Nothing to report sends nothing.
17775
+ * The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
17776
+ * midnight crossing); the send time is each window's END. Refused on the
17777
+ * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
17778
+ */
17779
+ var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
17780
+ mode: literal("summary"),
17781
+ schedule: NcScheduleSchema
17782
+ })]);
17715
17783
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
17716
17784
  var NcPlateMatcherSchema = object({
17717
17785
  /**
@@ -18447,6 +18515,16 @@ var NcRuleInputSchema = object({
18447
18515
  delivery: NcDeliverySchema,
18448
18516
  conditions: NcConditionsSchema.default({}),
18449
18517
  schedule: NcScheduleSchema.optional(),
18518
+ /**
18519
+ * A `device-events` / `system` rule's delivery (D647): absent or `instant`
18520
+ * is a notification per event; `summary` is one state summary per closed
18521
+ * window of its schedule. `.optional()` and never defaulted: absent must
18522
+ * keep meaning what every rule authored before D647 meant, with no
18523
+ * migration. Top-level rather than inside `conditions.systemEvent`, so a
18524
+ * client that does not know it (an older viewer re-sending `conditions`
18525
+ * whole) leaves it standing through the store's patch merge.
18526
+ */
18527
+ systemEventDelivery: NcSystemEventDeliverySchema.optional(),
18450
18528
  /** May be empty when `targetUsers` addresses at least one user — the
18451
18529
  * "at least one addressee" invariant is enforced by the provider, because
18452
18530
  * a cross-field refine here would break `NcRulePatchSchema.partial()`. */
@@ -18675,6 +18753,28 @@ var NcRuleSchema = NcRuleInputSchema.extend({
18675
18753
  */
18676
18754
  disabledTargetIds: array(string()).default([])
18677
18755
  });
18756
+ /** One section of a previewed state summary. `unavailable` = its state could not be read. */
18757
+ var NcStateSummaryPreviewSectionSchema = object({
18758
+ id: string(),
18759
+ status: _enum(["read", "unavailable"]),
18760
+ items: number().int().nonnegative(),
18761
+ reason: string().optional()
18762
+ });
18763
+ /**
18764
+ * The summary a rule would send now. `empty` = nothing to report and every
18765
+ * section read: such a summary is NOT sent. `dropped` counts the open states
18766
+ * the rule's own filters (or a camera mute) left out, by reason.
18767
+ * `excludedGroups` names the kind groups selected that have no state to report
18768
+ * (backups & exports).
18769
+ */
18770
+ var NcStateSummaryPreviewSchema = object({
18771
+ empty: boolean(),
18772
+ count: number().int().nonnegative(),
18773
+ text: string(),
18774
+ sections: array(NcStateSummaryPreviewSectionSchema),
18775
+ dropped: record(string(), number().int().nonnegative()),
18776
+ excludedGroups: array(string())
18777
+ });
18678
18778
  var NcTestResultSchema = object({
18679
18779
  recordId: string(),
18680
18780
  recordKind: _enum([
@@ -18766,6 +18866,13 @@ var NcTemplateVarDescriptorSchema = object({
18766
18866
  kinds: array(NcTemplateVarKindSchema).optional(),
18767
18867
  deliveries: array(NcDeliverySchema).optional(),
18768
18868
  systemEventKinds: array(NcSystemEventKindSchema).optional(),
18869
+ /**
18870
+ * Which system-event DELIVERY has a value for it (D647). Absent = an
18871
+ * instant row only when the descriptor names `systemEventKinds` (a
18872
+ * per-event fact: a summary row has no single device), and both otherwise.
18873
+ * `['summary']` marks the state-summary vocabulary (`{{summaryText}}`, ...).
18874
+ */
18875
+ systemEventDeliveries: array(NcSystemEventDeliveryModeSchema).optional(),
18769
18876
  dynamic: object({
18770
18877
  prefix: literal("count_"),
18771
18878
  from: literal("classes")
@@ -18778,7 +18885,9 @@ var NcTemplatePreviewInputSchema = object({
18778
18885
  field: NcTemplateFieldSchema,
18779
18886
  kind: NcRuleKindSchema.optional(),
18780
18887
  delivery: NcDeliverySchema.optional(),
18781
- systemEventKinds: array(NcSystemEventKindSchema).optional()
18888
+ systemEventKinds: array(NcSystemEventKindSchema).optional(),
18889
+ /** The rule's system-event delivery (D647); absent = instant. */
18890
+ systemEventDelivery: NcSystemEventDeliveryModeSchema.optional()
18782
18891
  }),
18783
18892
  template: object({
18784
18893
  title: string().max(500).optional(),
@@ -19312,6 +19421,10 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
19312
19421
  }), object({ results: array(NcTestResultSchema) }), {
19313
19422
  kind: "mutation",
19314
19423
  auth: "admin"
19424
+ }), method(object({ rule: NcRuleInputSchema }), NcStateSummaryPreviewSchema, {
19425
+ kind: "mutation",
19426
+ auth: "admin",
19427
+ access: "view"
19315
19428
  }), method(object({}), object({
19316
19429
  catalog: array(NcConditionDescriptorSchema),
19317
19430
  taxonomy: NcTaxonomySchema.optional()
@@ -19658,606 +19771,6 @@ var OauthIntegrationDescriptorSchema = object({
19658
19771
  });
19659
19772
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
19660
19773
  /**
19661
- * Reference to the frame's retained NATIVE surface + the parent crop's placement
19662
- * within the frame, so the executor can re-cut a leaf child ROI at native
19663
- * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
19664
- */
19665
- var NativeCropRefSchema = object({
19666
- /** Handle keying the retained native surface (node-pinned to its owner). */
19667
- handle: FrameHandleSchema,
19668
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
19669
- cropFrameSpace: object({
19670
- x: number(),
19671
- y: number(),
19672
- w: number(),
19673
- h: number()
19674
- })
19675
- });
19676
- object({
19677
- crop: object({
19678
- left: number(),
19679
- top: number(),
19680
- width: number().positive(),
19681
- height: number().positive()
19682
- }).optional(),
19683
- content: object({
19684
- width: number().int().positive(),
19685
- height: number().int().positive()
19686
- }),
19687
- fit: _enum(["stretch", "contain"]),
19688
- format: _enum([
19689
- "rgb",
19690
- "gray",
19691
- "jpeg"
19692
- ])
19693
- });
19694
- /**
19695
- * Process-local frame identity. It is serializable so it can ride an in-process
19696
- * capability call, but `registryId` deliberately prevents resolution in any
19697
- * other process or execution group.
19698
- */
19699
- var FrameRefSchema = object({
19700
- registryId: string().min(1),
19701
- id: string().min(1),
19702
- width: number().int().positive(),
19703
- height: number().int().positive(),
19704
- format: _enum(["rgb", "gray"]),
19705
- timestamp: number(),
19706
- capturedAt: number().optional()
19707
- });
19708
- var ModelFormatSchema$1 = _enum([
19709
- "onnx",
19710
- "coreml",
19711
- "openvino",
19712
- "tflite",
19713
- "pt",
19714
- "gguf"
19715
- ]);
19716
- var PipelineSlotSchema = _enum([
19717
- "detector",
19718
- "cropper",
19719
- "classifier",
19720
- "refiner",
19721
- "audio-classifier"
19722
- ]);
19723
- var PipelineEngineChoiceSchema = object({
19724
- runtime: _enum(["node", "python"]),
19725
- backend: string(),
19726
- format: ModelFormatSchema$1,
19727
- device: string().optional()
19728
- });
19729
- var AvailableEngineSchema = object({
19730
- engine: PipelineEngineChoiceSchema,
19731
- devices: array(object({
19732
- id: string(),
19733
- label: string(),
19734
- description: string().optional()
19735
- })).readonly(),
19736
- defaultDevice: string()
19737
- });
19738
- var PipelineDefaultStepSchema = lazy(() => object({
19739
- addonId: string(),
19740
- addonName: string(),
19741
- slot: PipelineSlotSchema,
19742
- inputClasses: array(string()).readonly(),
19743
- outputClasses: array(string()).readonly(),
19744
- enabled: boolean(),
19745
- modelId: string(),
19746
- children: array(PipelineDefaultStepSchema).readonly(),
19747
- group: string().optional(),
19748
- settings: record(string(), unknown()).optional()
19749
- }));
19750
- var PipelineTemplateStepSchema = lazy(() => object({
19751
- addonId: string(),
19752
- enabled: boolean(),
19753
- modelId: string(),
19754
- children: array(PipelineTemplateStepSchema).readonly(),
19755
- settings: record(string(), unknown()).optional()
19756
- }));
19757
- var PipelineTemplateSchema$1 = object({
19758
- id: string(),
19759
- name: string(),
19760
- createdAt: string(),
19761
- updatedAt: string(),
19762
- engine: PipelineEngineChoiceSchema,
19763
- steps: array(PipelineTemplateStepSchema).readonly()
19764
- });
19765
- var PipelineModelOptionSchema = object({
19766
- id: string(),
19767
- name: string(),
19768
- formats: record(string(), object({
19769
- downloaded: boolean(),
19770
- sizeMB: number()
19771
- })),
19772
- group: ModelVariantGroupSchema.optional(),
19773
- legacy: boolean().optional(),
19774
- provider: ModelProviderIdSchema.optional()
19775
- });
19776
- var ConfigFieldBridge = custom();
19777
- var PipelineAddonSchemaSchema = object({
19778
- id: string(),
19779
- name: string(),
19780
- slot: PipelineSlotSchema,
19781
- inputClasses: array(string()).readonly(),
19782
- outputClasses: array(string()).readonly(),
19783
- childSlots: array(PipelineSlotSchema).readonly(),
19784
- models: array(PipelineModelOptionSchema).readonly(),
19785
- defaultModelId: string(),
19786
- defaultModelIdByFormat: record(string(), string()).optional(),
19787
- enabledByDefault: boolean().optional(),
19788
- backfillIntoExistingOverrides: boolean().optional(),
19789
- defaultConfidence: number(),
19790
- group: string().optional(),
19791
- configSchema: array(ConfigFieldBridge).readonly().optional()
19792
- });
19793
- var PipelineSlotSchemaSchema = object({
19794
- id: PipelineSlotSchema,
19795
- label: string(),
19796
- priority: number(),
19797
- parentSlot: PipelineSlotSchema.nullable(),
19798
- addons: array(PipelineAddonSchemaSchema).readonly()
19799
- });
19800
- var PipelineSchemaSchema = object({
19801
- availableEngines: array(AvailableEngineSchema).readonly(),
19802
- selectedEngine: PipelineEngineChoiceSchema,
19803
- slots: array(PipelineSlotSchemaSchema).readonly()
19804
- });
19805
- var EngineProvisioningSchema = object({
19806
- runtimeId: _enum([
19807
- "onnx",
19808
- "openvino",
19809
- "coreml",
19810
- "edgetpu"
19811
- ]).nullable(),
19812
- device: string().nullable(),
19813
- state: _enum([
19814
- "idle",
19815
- "installing",
19816
- "verifying",
19817
- "ready",
19818
- "failed"
19819
- ]),
19820
- progress: number().optional(),
19821
- error: string().optional(),
19822
- nextRetryAt: number().optional(),
19823
- /**
19824
- * Gate A (config-correctness gate at engine change): human-readable
19825
- * config issues surfaced EAGERLY when the node's engine changes — model
19826
- * substitutions ("chose X, running Y") and zero-build steps ("no model
19827
- * has a <format> build"). Additive/optional: informational only, never
19828
- * enforced here — `assertEngineReady` (readiness) still gates inference.
19829
- * Absent/empty when the node-default tree resolves cleanly.
19830
- */
19831
- configIssues: array(string()).optional()
19832
- });
19833
- var PipelineStepInputSchema = lazy(() => object({
19834
- addonId: string(),
19835
- modelId: string().optional(),
19836
- enabled: boolean().default(true),
19837
- children: array(PipelineStepInputSchema).optional(),
19838
- settings: record(string(), unknown()).optional(),
19839
- jumpDeviceKey: string().optional()
19840
- }));
19841
- var ModelSubstitutionSchema = object({
19842
- addonId: string(),
19843
- chosen: string(),
19844
- running: string(),
19845
- format: string()
19846
- });
19847
- var PipelineValidationIssueSchema = object({
19848
- addonId: string(),
19849
- kind: _enum(["unknown-addon", "no-format-build"]),
19850
- detail: string()
19851
- });
19852
- var PipelineValidationResultSchema = object({
19853
- ok: boolean(),
19854
- issues: array(PipelineValidationIssueSchema).readonly(),
19855
- substitutions: array(ModelSubstitutionSchema).readonly(),
19856
- /** The node's `currentEngine.format` this validation ran against. */
19857
- format: string()
19858
- });
19859
- var ReferenceImageEntrySchema = object({
19860
- filename: string(),
19861
- stepIds: array(string()).readonly().optional()
19862
- });
19863
- var ReferenceImageBodySchema = object({
19864
- base64: string(),
19865
- filename: string()
19866
- });
19867
- var ReferenceAudioEntrySchema = object({
19868
- filename: string(),
19869
- sizeKb: number()
19870
- });
19871
- var ReferenceAudioBodySchema = object({ base64: string() });
19872
- var AudioBackendSchema = object({
19873
- id: string(),
19874
- name: string(),
19875
- description: string(),
19876
- available: boolean(),
19877
- /**
19878
- * Raw classifier labels this backend can emit (e.g. YAMNet's
19879
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
19880
- * the benchmark UI to populate the `enabledMicroClasses` filter
19881
- * specific to the selected backend without a separate fetch.
19882
- */
19883
- rawLabels: array(string()).readonly().optional()
19884
- });
19885
- var AudioCapabilitiesSchema = object({
19886
- activeBackend: string(),
19887
- availableBackends: array(AudioBackendSchema).readonly(),
19888
- sampleRate: number(),
19889
- chunkDurationMs: number()
19890
- });
19891
- var DownloadModelResultSchema = object({
19892
- filePath: string(),
19893
- sizeMB: number(),
19894
- durationMs: number()
19895
- });
19896
- /**
19897
- * Wrapper carrying a single test run's result. Replaces the legacy
19898
- * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
19899
- * canonical `AudioResult` from the Phase 6 output rework: one
19900
- * `AudioDetection` per class above `minScore`, top-N candidates in
19901
- * `debug.alternateLabels['audio-classifier']`, per-source timings in
19902
- * `debug.stepTimings`. The outer `success`/`error` fields stay so the
19903
- * benchmark UI can still report a clean failure when the classifier
19904
- * cap isn't available.
19905
- */
19906
- var AudioTestResultSchema = object({
19907
- success: boolean(),
19908
- error: string().optional(),
19909
- frame: custom().optional()
19910
- });
19911
- var PipelineConfigBridge = custom();
19912
- var ConfigUISchemaBridge = custom();
19913
- var ConfigUISchemaNullableBridge = custom();
19914
- var InferenceCapabilitiesBridge = custom();
19915
- var ModelAvailabilityListBridge = custom();
19916
- var PipelineRunResultBridge = custom();
19917
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
19918
- modelId: string(),
19919
- settings: record(string(), unknown()).readonly()
19920
- }))), method(object({ steps: record(string(), object({
19921
- modelId: string(),
19922
- settings: record(string(), unknown()).readonly()
19923
- })) }), object({ success: literal(true) }), {
19924
- kind: "mutation",
19925
- auth: "admin"
19926
- }), method(object({ nodeId: string() }), object({
19927
- success: literal(true),
19928
- clearedDevices: number()
19929
- }), {
19930
- kind: "mutation",
19931
- auth: "admin"
19932
- }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
19933
- /** `<backend>:<device>`, e.g. `openvino:gpu`. */
19934
- deviceKey: string(),
19935
- /**
19936
- * `failed` — the per-device restart budget is exhausted; no pool
19937
- * will be spawned until an operator re-arms it or the runner
19938
- * respawns. `backoff` — under budget, waiting out the backoff (or
19939
- * a cached pool observed dead and not yet condemned).
19940
- */
19941
- state: _enum(["failed", "backoff"]),
19942
- /** Epoch ms of the death that produced this state. */
19943
- since: number(),
19944
- /** Pool deaths inside the current window. */
19945
- deaths: number(),
19946
- /** The last death's message. */
19947
- lastError: string()
19948
- })).readonly() })), method(object({
19949
- nodeId: string(),
19950
- deviceKey: string()
19951
- }), object({ rearmed: boolean() }), {
19952
- kind: "mutation",
19953
- auth: "admin"
19954
- }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({ steps: array(PipelineStepInputSchema) }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
19955
- name: string(),
19956
- steps: array(PipelineTemplateStepSchema).readonly(),
19957
- engine: PipelineEngineChoiceSchema
19958
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
19959
- id: string(),
19960
- name: string().optional(),
19961
- steps: array(PipelineTemplateStepSchema).readonly().optional()
19962
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
19963
- addonId: string(),
19964
- modelId: string(),
19965
- format: ModelFormatSchema$1
19966
- }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
19967
- addonId: string(),
19968
- modelId: string(),
19969
- format: ModelFormatSchema$1
19970
- }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
19971
- engine: PipelineEngineChoiceSchema.optional(),
19972
- steps: array(PipelineStepInputSchema).min(1),
19973
- frame: FrameInputSchema.optional(),
19974
- /**
19975
- * Process-local lazy frame. Valid only when caller and provider resolve
19976
- * in the same execution-group process; split/cross-node callers use
19977
- * `frame`/`image` inline compatibility instead.
19978
- */
19979
- frameRef: FrameRefSchema.optional(),
19980
- /**
19981
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
19982
- * the decoded pixels live in. One more member of the one-of
19983
- * frame/frameHandle/image/imageBase64/referenceImage group.
19984
- */
19985
- frameHandle: FrameHandleSchema.optional(),
19986
- imageBase64: string().optional(),
19987
- /**
19988
- * Binary JPEG bytes — preferred over `imageBase64` on internal
19989
- * hops (hub → forked worker via Moleculer MsgPack) because it
19990
- * skips the 33% base64 overhead + the per-call base64 decode on
19991
- * the detection-pipeline worker. Callers can pass either; exactly
19992
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
19993
- */
19994
- image: _instanceof(Uint8Array).optional(),
19995
- referenceImage: string().optional(),
19996
- deviceId: number().optional(),
19997
- sessionId: string().optional(),
19998
- /**
19999
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
20000
- * reference-image, and detail-subtree calls. 'frame' is the live
20001
- * per-frame dispatch: ONLY root-plane steps run; crop children
20002
- * (inputClasses ≠ null) are skipped and served per-track via
20003
- * pipelineRunner.runDetailSubtree (two-plane design).
20004
- */
20005
- plane: _enum(["full", "frame"]).optional(),
20006
- /**
20007
- * Inference-device selector (Phase 2 multi-device). Format
20008
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
20009
- * Omitted ⇒ the runner's default device (current single-engine
20010
- * behaviour). Selects WHICH device pool of the node runs the call.
20011
- */
20012
- deviceKey: string().optional(),
20013
- /**
20014
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
20015
- * when the parent crop was resolved from the frame's retained NATIVE
20016
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
20017
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
20018
- * resolution from that surface — the SAME quality path faces already
20019
- * had — instead of the downscaled parent tile. `handle` keys the native
20020
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
20021
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
20022
- * the executor's crop-normalized child ROI back into frame-normalized
20023
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
20024
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
20025
- * (today's behaviour on the fallback path).
20026
- */
20027
- nativeCropRef: NativeCropRefSchema.optional()
20028
- }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
20029
- engine: PipelineEngineChoiceSchema.optional(),
20030
- steps: array(PipelineStepInputSchema).min(1),
20031
- frames: array(FrameInputSchema).min(1).max(255),
20032
- deviceId: number().optional(),
20033
- sessionId: string().optional(),
20034
- /**
20035
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
20036
- * the batch to the Python pool's bench preprocess cache
20037
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
20038
- * preprocessed ONCE and every later inference is a pure-inference cache
20039
- * hit — the sustained-throughput run measures inference, not
20040
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
20041
- * full preprocess every call, correct). Fresh per sustained run;
20042
- * released via `uncacheFrame`.
20043
- */
20044
- frameId: number().int().nonnegative().optional(),
20045
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
20046
- deviceKey: string().optional()
20047
- }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
20048
- data: _instanceof(Uint8Array),
20049
- width: number().int().positive(),
20050
- height: number().int().positive(),
20051
- format: _enum([
20052
- "rgb",
20053
- "bgr",
20054
- "gray"
20055
- ])
20056
- }), object({
20057
- frameId: number(),
20058
- width: number(),
20059
- height: number()
20060
- }), { kind: "mutation" }), method(object({
20061
- stepId: string(),
20062
- frameId: number().int()
20063
- }), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number().int() }), _void(), { kind: "mutation" }), method(_void(), object({
20064
- batchMode: string(),
20065
- windowMs: number(),
20066
- maxBatchSize: number(),
20067
- concurrency: number()
20068
- })), method(_void(), array(object({
20069
- engineKey: string(),
20070
- engine: PipelineEngineChoiceSchema,
20071
- modelsLoaded: array(string()).readonly(),
20072
- inUseByCameras: array(number()).readonly(),
20073
- /**
20074
- * Origin of this resident factory.
20075
- * - `runtime` — main camera-serving engine (no idle TTL).
20076
- * - `warm-override` — benchmark/test override held in the warm
20077
- * cache; auto-disposed after the idle TTL.
20078
- * - `device-pool` — a concurrent per-device pool (Phase 2
20079
- * multi-device, keyed by `deviceKey`) resolved
20080
- * via `resolveDeviceFactory`. Runs alongside the
20081
- * `runtime` engine on a DIFFERENT accelerator
20082
- * (NPU / iGPU / Coral) — this is how the
20083
- * Engines tab shows all pools running at once.
20084
- */
20085
- kind: _enum([
20086
- "runtime",
20087
- "warm-override",
20088
- "device-pool"
20089
- ]),
20090
- /** Native pid of the underlying Python pool (null when no pool). */
20091
- poolPid: number().nullable(),
20092
- /** ms since this factory was last used (null when not warm-tracked). */
20093
- idleMs: number().nullable(),
20094
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
20095
- idleTtlMs: number().nullable()
20096
- })).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
20097
- kind: "mutation",
20098
- auth: "admin"
20099
- }), method(object({
20100
- engine: PipelineEngineChoiceSchema,
20101
- force: boolean().optional()
20102
- }), object({
20103
- success: boolean(),
20104
- reason: string().optional()
20105
- }), {
20106
- kind: "mutation",
20107
- auth: "admin"
20108
- }), method(_void(), array(ReferenceImageEntrySchema).readonly()), method(object({ filename: string() }), ReferenceImageBodySchema.nullable()), method(_void(), array(ReferenceAudioEntrySchema).readonly()), method(object({ filename: string() }), ReferenceAudioBodySchema.nullable()), method(_void(), AudioCapabilitiesSchema), method(object({
20109
- addonId: string(),
20110
- modelId: string(),
20111
- filename: string().optional(),
20112
- settings: record(string(), unknown()).optional()
20113
- }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
20114
- /**
20115
- * Per-stage gating mode applied to the zones a rule references.
20116
- *
20117
- * - `include`: the rule contributes to a **whitelist** for its stage.
20118
- * When at least one `include` rule fires for a stage, only entities
20119
- * inside one of those zones pass that stage.
20120
- * - `exclude`: the rule contributes to a **blacklist** for its stage.
20121
- * Entities inside one of those zones are dropped at that stage.
20122
- *
20123
- * `monitor`-style observation (count without filtering) is not a rule
20124
- * mode — zones without any matching rule are observed naturally by
20125
- * `zone-analytics` (live snapshot + history), so an "I just want to
20126
- * count, not filter" use case needs no rule at all.
20127
- */
20128
- var ZoneRuleModeEnum = _enum(["include", "exclude"]);
20129
- /**
20130
- * Per-consumer rule that references existing zones (geometry) and
20131
- * defines how a specific pipeline stage should treat them. Each
20132
- * consumer addon owns its own `ZoneRule[]` array in its per-device
20133
- * settings:
20134
- *
20135
- * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
20136
- * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
20137
- * - future: notification rules, audio gating, etc.
20138
- *
20139
- * One rule applies to N zones (`zoneIds[]`) so the operator can
20140
- * express "ignore motion in ALL of {garden, street}" with a single
20141
- * rule. `classFilter` narrows the rule to specific object classes —
20142
- * "drop person detections in the street, but keep cars" is one
20143
- * `exclude` rule with `classFilter: ['person']`.
20144
- *
20145
- * `enabled` is a soft toggle — the operator can keep the rule
20146
- * configured but inert without deleting it.
20147
- */
20148
- var ZoneRuleSchema = object({
20149
- /** Stable rule id — survives edits, used by the UI for diffing. */
20150
- id: string(),
20151
- /** Optional human-readable label rendered in the rule editor. */
20152
- name: string().optional(),
20153
- /** Zones this rule targets. The rule's `mode` applies to ALL
20154
- * listed zones (OR-set: a detection in any one of them counts).
20155
- * At least one zone id required — a rule with no targets is a
20156
- * configuration mistake and the form validator rejects it. */
20157
- zoneIds: array(string()).min(1).readonly(),
20158
- mode: ZoneRuleModeEnum,
20159
- /**
20160
- * Class names this rule applies to. Empty / undefined ⇒ rule
20161
- * applies to every class. Class strings match the `macroClass`
20162
- * field on detections (e.g. `person`, `car`, `dog`).
20163
- */
20164
- classFilter: array(string()).readonly().optional(),
20165
- /**
20166
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
20167
- * required to consider an entity "in the zone". Defaults to the
20168
- * consumer's stage default when omitted. Kept for back-compat with
20169
- * existing per-rule overrides; new operators pick the value via
20170
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
20171
- * set, the lower-level engine reads it as a 0–1 fraction.
20172
- */
20173
- overlapThreshold: number().min(0).max(1).optional(),
20174
- /**
20175
- * Operator-friendly version of `overlapThreshold` — the percentage
20176
- * of the detection's bbox that must lie inside the zone for the
20177
- * rule to match. Documented default is 85%; the engine substitutes
20178
- * that when the field is omitted (kept optional so existing rules
20179
- * stored without it stay valid).
20180
- *
20181
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
20182
- * rule, the engine prefers `bboxInclusionPct` because it's the
20183
- * field exposed in the UI. Internally both feed the same gate.
20184
- */
20185
- bboxInclusionPct: number().min(0).max(100).optional(),
20186
- /**
20187
- * When `true` and a detection has a segmentation mask, use the
20188
- * mask for overlap instead of the bbox. Detection-stage only;
20189
- * motion rules ignore this field.
20190
- */
20191
- preferMask: boolean().optional(),
20192
- /**
20193
- * Soft-toggle: `false` disables the rule without deleting it.
20194
- * Defaults to `true` so operators creating a rule via the UI
20195
- * see it active immediately.
20196
- */
20197
- enabled: boolean().default(true)
20198
- });
20199
- array(ZoneRuleSchema).readonly();
20200
- /**
20201
- * Zone — pure geometry + identity. NO filtering behaviour.
20202
- *
20203
- * Zones describe **where** in the frame the operator wants to flag
20204
- * something; consumer-owned {@link ZoneRule} arrays describe **how**
20205
- * each pipeline stage uses them. Splitting the two means a single
20206
- * polygon "Driveway" can simultaneously back a motion-exclude rule,
20207
- * a detection-include rule on `['car']`, and an occupancy aggregate
20208
- * — without three duplicated polygons.
20209
- *
20210
- * Owned by the orchestrator addon (provider) and mirrored into the
20211
- * `zones` device-state slice on every mutation. Consumers
20212
- * (motion-wasm, pipeline-executor, analytics, admin UI) read either
20213
- * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
20214
- * mirror with `onChanged`).
20215
- *
20216
- * Coordinates are normalised fractions of the frame (0–1) so zones
20217
- * survive resolution changes and stream profile switches.
20218
- *
20219
- * `kind` discriminates between full polygons (closed regions used
20220
- * for intrusion / occupancy filters) and tripwires (open 2-point
20221
- * line segments used for cross events). Onboard / firmware-reported
20222
- * zones (Reolink, ONVIF) are out of scope for now — see the deferred
20223
- * task list.
20224
- */
20225
- var ZoneKindEnum = _enum(["polygon", "tripwire"]);
20226
- /** Polygon vertex in fraction-of-frame coordinates (0–1). */
20227
- var PolygonPointSchema = object({
20228
- x: number(),
20229
- y: number()
20230
- });
20231
- /** A camera detection zone — pure geometry/identity. */
20232
- var ZoneSchema = object({
20233
- id: string(),
20234
- name: string(),
20235
- kind: ZoneKindEnum.default("polygon"),
20236
- /** Polygon vertices, fraction of frame (0–1). */
20237
- polygon: array(PolygonPointSchema).readonly(),
20238
- /** Visual color for UI rendering. */
20239
- color: string().default("#3b82f6")
20240
- });
20241
- DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).readonly()), method(object({
20242
- deviceId: number(),
20243
- zone: ZoneSchema
20244
- }), _void(), {
20245
- kind: "mutation",
20246
- auth: "admin"
20247
- }), method(object({
20248
- deviceId: number(),
20249
- zoneId: string()
20250
- }), _void(), {
20251
- kind: "mutation",
20252
- auth: "admin"
20253
- }), method(object({
20254
- deviceId: number(),
20255
- zone: ZoneSchema
20256
- }), _void(), {
20257
- kind: "mutation",
20258
- auth: "admin"
20259
- }), object({ zones: array(ZoneSchema).readonly() });
20260
- /**
20261
19774
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
20262
19775
  * per-frame detections emitted by the pipeline runner into tracked
20263
19776
  * objects, per-kind event collections (motion / object / audio), and
@@ -20828,6 +20341,22 @@ var TrackSchema = object({
20828
20341
  * omit it. Render nothing when absent.
20829
20342
  */
20830
20343
  previewMissReason: string().optional(),
20344
+ /**
20345
+ * The ROOT model that produced this track — the `FrameResult.rootModelId` of
20346
+ * the frame it was BORN on (D646). What a later replay compares against, so
20347
+ * "the original model" is named rather than assumed. A birth fact: never
20348
+ * rewritten by a later frame. **Absent ≠ unknown-model**: a row from before
20349
+ * the column, and a track the camera's own detector produced, carry none.
20350
+ */
20351
+ modelId: string().optional(),
20352
+ /**
20353
+ * Size (px) of the detection frame this track's `positions[].bbox` live in —
20354
+ * the frame it was born on, else the camera's latest analysis frame at
20355
+ * persist time. Divide a position by these to normalise it. Absent when the
20356
+ * size was never known.
20357
+ */
20358
+ frameWidth: number().optional(),
20359
+ frameHeight: number().optional(),
20831
20360
  ...TrackFlagFields,
20832
20361
  ...TrackRetrainFields
20833
20362
  });
@@ -21834,24 +21363,6 @@ var MediaReclaimStatusSchema = object({
21834
21363
  finishedAtMs: number().nullable(),
21835
21364
  error: string().nullable()
21836
21365
  });
21837
- var ReplayFrameInputSchema = object({
21838
- timestamp: number(),
21839
- frame: PipelineRunResultBridge
21840
- });
21841
- var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
21842
- className: string(),
21843
- firstSeenMs: number(),
21844
- lastSeenMs: number(),
21845
- /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
21846
- * frame — a representative box for the diff's `(className, window, IoU)`
21847
- * pairing (`replay-diff.ts`). A replay does not need the full per-frame
21848
- * trajectory production's `Track.positions` keeps. */
21849
- bbox: BoundingBoxSchema,
21850
- /** How many of the input frames this track matched a real detection on
21851
- * (never a coasted/extrapolated frame) — the replay's own signal for "how
21852
- * solid is this track", cheaper than re-deriving it from a trajectory. */
21853
- framesMatched: number().int()
21854
- })).readonly() });
21855
21366
  /**
21856
21367
  * The long-term series a camera produces. A closed union so a typo cannot
21857
21368
  * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
@@ -22065,108 +21576,570 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
22065
21576
  }), {
22066
21577
  kind: "mutation",
22067
21578
  auth: "admin"
22068
- }), method(object({ frameId: string() }), object({
22069
- base64: string(),
22070
- width: number().int(),
22071
- height: number().int()
21579
+ }), method(object({ frameId: string() }), object({
21580
+ base64: string(),
21581
+ width: number().int(),
21582
+ height: number().int()
21583
+ }), {
21584
+ kind: "query",
21585
+ auth: "admin"
21586
+ }), method(object({
21587
+ deviceId: number(),
21588
+ trackId: string(),
21589
+ frameId: string(),
21590
+ subject: RetrainAssistSubjectSchema,
21591
+ /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
21592
+ nodeId: string().optional()
21593
+ }), RetrainAssistResultSchema, {
21594
+ kind: "mutation",
21595
+ auth: "admin"
21596
+ }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
21597
+ kind: "query",
21598
+ auth: "admin"
21599
+ }), method(object({
21600
+ deviceId: number(),
21601
+ trackId: string(),
21602
+ frameId: string(),
21603
+ annotations: array(RetrainAnnotationDraftSchema)
21604
+ }), array(RetrainAnnotationSchema).readonly(), {
21605
+ kind: "mutation",
21606
+ auth: "admin"
21607
+ }), method(object({
21608
+ deviceId: number(),
21609
+ trackId: string()
21610
+ }), RetrainTransitionResultSchema, {
21611
+ kind: "mutation",
21612
+ auth: "admin"
21613
+ }), method(object({
21614
+ deviceId: number(),
21615
+ trackId: string()
21616
+ }), RetrainTransitionResultSchema, {
21617
+ kind: "mutation",
21618
+ auth: "admin"
21619
+ }), method(object({ deviceIds: array(number()).optional() }), object({ url: string() }), {
21620
+ kind: "query",
21621
+ auth: "admin"
21622
+ }), method(object({
21623
+ /** Which of the three event tables owns the row. */
21624
+ ownerType: EventOwnerTypeSchema,
21625
+ /** The event's rowid in that table (D474: an event id is a number). */
21626
+ eventId: number().int(),
21627
+ kind: MediaFileKindEnum.optional(),
21628
+ deviceId: number()
21629
+ }), array(MediaFileSchema).readonly()), method(object({
21630
+ trackId: string(),
21631
+ kinds: array(MediaFileKindEnum).optional(),
21632
+ deviceId: number()
21633
+ }), array(MediaFileSchema).readonly()), method(object({
21634
+ trackId: string(),
21635
+ deviceId: number()
21636
+ }), array(MediaFileInfoSchema).readonly()), method(object({
21637
+ ownerType: EventOwnerTypeSchema,
21638
+ eventId: number().int(),
21639
+ deviceId: number()
21640
+ }), array(MediaFileInfoSchema).readonly()), method(object({
21641
+ deviceId: number(),
21642
+ ownerKind: MediaPresenceOwnerKindSchema,
21643
+ ownerIds: array(string()).max(5e3)
21644
+ }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21645
+ kind: "mutation",
21646
+ auth: "admin"
21647
+ }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
21648
+ kind: "mutation",
21649
+ auth: "admin"
21650
+ }), method(object({}), RebuildStatusSchema), object({
21651
+ deviceId: number(),
21652
+ timestamp: number(),
21653
+ frameWidth: number(),
21654
+ frameHeight: number(),
21655
+ detections: array(OverlayDetectionSchema).readonly()
21656
+ }), object({
21657
+ deviceId: number(),
21658
+ trackId: string(),
21659
+ className: string()
21660
+ }), object({
21661
+ deviceId: number(),
21662
+ trackId: string(),
21663
+ className: string(),
21664
+ durationMs: number()
21665
+ }), object({
21666
+ deviceId: number(),
21667
+ kind: EventKindSchema,
21668
+ eventId: string(),
21669
+ timestamp: number()
21670
+ });
21671
+ /**
21672
+ * Reference to the frame's retained NATIVE surface + the parent crop's placement
21673
+ * within the frame, so the executor can re-cut a leaf child ROI at native
21674
+ * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
21675
+ */
21676
+ var NativeCropRefSchema = object({
21677
+ /** Handle keying the retained native surface (node-pinned to its owner). */
21678
+ handle: FrameHandleSchema,
21679
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
21680
+ cropFrameSpace: object({
21681
+ x: number(),
21682
+ y: number(),
21683
+ w: number(),
21684
+ h: number()
21685
+ })
21686
+ });
21687
+ object({
21688
+ crop: object({
21689
+ left: number(),
21690
+ top: number(),
21691
+ width: number().positive(),
21692
+ height: number().positive()
21693
+ }).optional(),
21694
+ content: object({
21695
+ width: number().int().positive(),
21696
+ height: number().int().positive()
21697
+ }),
21698
+ fit: _enum(["stretch", "contain"]),
21699
+ format: _enum([
21700
+ "rgb",
21701
+ "gray",
21702
+ "jpeg"
21703
+ ])
21704
+ });
21705
+ /**
21706
+ * Process-local frame identity. It is serializable so it can ride an in-process
21707
+ * capability call, but `registryId` deliberately prevents resolution in any
21708
+ * other process or execution group.
21709
+ */
21710
+ var FrameRefSchema = object({
21711
+ registryId: string().min(1),
21712
+ id: string().min(1),
21713
+ width: number().int().positive(),
21714
+ height: number().int().positive(),
21715
+ format: _enum(["rgb", "gray"]),
21716
+ timestamp: number(),
21717
+ capturedAt: number().optional()
21718
+ });
21719
+ var ModelFormatSchema$1 = _enum([
21720
+ "onnx",
21721
+ "coreml",
21722
+ "openvino",
21723
+ "tflite",
21724
+ "pt",
21725
+ "gguf"
21726
+ ]);
21727
+ var PipelineSlotSchema = _enum([
21728
+ "detector",
21729
+ "cropper",
21730
+ "classifier",
21731
+ "refiner",
21732
+ "audio-classifier"
21733
+ ]);
21734
+ var PipelineEngineChoiceSchema = object({
21735
+ runtime: literal("python"),
21736
+ backend: string(),
21737
+ format: ModelFormatSchema$1,
21738
+ device: string().optional()
21739
+ });
21740
+ /**
21741
+ * One pool's resolved provisioning (`getEffectiveTuning`). `numWorkers` is the
21742
+ * knob that lifts a saturated pool (`inference-devices.md`); `concurrency` is
21743
+ * per-worker in-flight requests. `deviceKey` echoes the pool asked about —
21744
+ * `null` for the node-default pool.
21745
+ */
21746
+ var EffectiveTuningSchema = object({
21747
+ deviceKey: string().nullable(),
21748
+ runtime: string(),
21749
+ batchMode: string(),
21750
+ windowMs: number(),
21751
+ maxBatchSize: number(),
21752
+ concurrency: number(),
21753
+ numWorkers: number()
21754
+ });
21755
+ var AvailableEngineSchema = object({
21756
+ engine: PipelineEngineChoiceSchema,
21757
+ devices: array(object({
21758
+ id: string(),
21759
+ label: string(),
21760
+ description: string().optional()
21761
+ })).readonly(),
21762
+ defaultDevice: string()
21763
+ });
21764
+ var PipelineDefaultStepSchema = lazy(() => object({
21765
+ addonId: string(),
21766
+ addonName: string(),
21767
+ slot: PipelineSlotSchema,
21768
+ inputClasses: array(string()).readonly(),
21769
+ outputClasses: array(string()).readonly(),
21770
+ enabled: boolean(),
21771
+ modelId: string(),
21772
+ children: array(PipelineDefaultStepSchema).readonly(),
21773
+ group: string().optional(),
21774
+ settings: record(string(), unknown()).optional()
21775
+ }));
21776
+ var PipelineTemplateStepSchema = lazy(() => object({
21777
+ addonId: string(),
21778
+ enabled: boolean(),
21779
+ modelId: string(),
21780
+ children: array(PipelineTemplateStepSchema).readonly(),
21781
+ settings: record(string(), unknown()).optional()
21782
+ }));
21783
+ var PipelineTemplateSchema$1 = object({
21784
+ id: string(),
21785
+ name: string(),
21786
+ createdAt: string(),
21787
+ updatedAt: string(),
21788
+ engine: PipelineEngineChoiceSchema,
21789
+ steps: array(PipelineTemplateStepSchema).readonly()
21790
+ });
21791
+ var PipelineModelOptionSchema = object({
21792
+ id: string(),
21793
+ name: string(),
21794
+ formats: record(string(), object({
21795
+ downloaded: boolean(),
21796
+ sizeMB: number()
21797
+ })),
21798
+ group: ModelVariantGroupSchema.optional(),
21799
+ legacy: boolean().optional(),
21800
+ provider: ModelProviderIdSchema.optional()
21801
+ });
21802
+ var ConfigFieldBridge = custom();
21803
+ var PipelineAddonSchemaSchema = object({
21804
+ id: string(),
21805
+ name: string(),
21806
+ slot: PipelineSlotSchema,
21807
+ inputClasses: array(string()).readonly(),
21808
+ outputClasses: array(string()).readonly(),
21809
+ childSlots: array(PipelineSlotSchema).readonly(),
21810
+ models: array(PipelineModelOptionSchema).readonly(),
21811
+ defaultModelId: string(),
21812
+ defaultModelIdByFormat: record(string(), string()).optional(),
21813
+ enabledByDefault: boolean().optional(),
21814
+ backfillIntoExistingOverrides: boolean().optional(),
21815
+ defaultConfidence: number(),
21816
+ group: string().optional(),
21817
+ configSchema: array(ConfigFieldBridge).readonly().optional()
21818
+ });
21819
+ var PipelineSlotSchemaSchema = object({
21820
+ id: PipelineSlotSchema,
21821
+ label: string(),
21822
+ priority: number(),
21823
+ parentSlot: PipelineSlotSchema.nullable(),
21824
+ addons: array(PipelineAddonSchemaSchema).readonly()
21825
+ });
21826
+ var PipelineSchemaSchema = object({
21827
+ availableEngines: array(AvailableEngineSchema).readonly(),
21828
+ selectedEngine: PipelineEngineChoiceSchema,
21829
+ slots: array(PipelineSlotSchemaSchema).readonly()
21830
+ });
21831
+ var EngineProvisioningSchema = object({
21832
+ runtimeId: _enum([
21833
+ "onnx",
21834
+ "openvino",
21835
+ "coreml",
21836
+ "edgetpu"
21837
+ ]).nullable(),
21838
+ device: string().nullable(),
21839
+ state: _enum([
21840
+ "idle",
21841
+ "installing",
21842
+ "verifying",
21843
+ "ready",
21844
+ "failed"
21845
+ ]),
21846
+ progress: number().optional(),
21847
+ error: string().optional(),
21848
+ nextRetryAt: number().optional(),
21849
+ /**
21850
+ * Gate A (config-correctness gate at engine change): human-readable
21851
+ * config issues surfaced EAGERLY when the node's engine changes — model
21852
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
21853
+ * has a <format> build"). Additive/optional: informational only, never
21854
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
21855
+ * Absent/empty when the node-default tree resolves cleanly.
21856
+ */
21857
+ configIssues: array(string()).optional()
21858
+ });
21859
+ var PipelineStepInputSchema = lazy(() => object({
21860
+ addonId: string(),
21861
+ modelId: string().optional(),
21862
+ enabled: boolean().default(true),
21863
+ children: array(PipelineStepInputSchema).optional(),
21864
+ settings: record(string(), unknown()).optional(),
21865
+ jumpDeviceKey: string().optional()
21866
+ }));
21867
+ var ModelSubstitutionSchema = object({
21868
+ addonId: string(),
21869
+ chosen: string(),
21870
+ running: string(),
21871
+ format: string()
21872
+ });
21873
+ var PipelineValidationIssueSchema = object({
21874
+ addonId: string(),
21875
+ kind: _enum([
21876
+ "unknown-addon",
21877
+ "no-format-build",
21878
+ "unknown-device"
21879
+ ]),
21880
+ detail: string()
21881
+ });
21882
+ var PipelineValidationResultSchema = object({
21883
+ ok: boolean(),
21884
+ issues: array(PipelineValidationIssueSchema).readonly(),
21885
+ substitutions: array(ModelSubstitutionSchema).readonly(),
21886
+ /** The node's `currentEngine.format` this validation ran against. */
21887
+ format: string()
21888
+ });
21889
+ var ReferenceImageEntrySchema = object({
21890
+ filename: string(),
21891
+ stepIds: array(string()).readonly().optional()
21892
+ });
21893
+ var ReferenceImageBodySchema = object({
21894
+ base64: string(),
21895
+ filename: string()
21896
+ });
21897
+ var ReferenceAudioEntrySchema = object({
21898
+ filename: string(),
21899
+ sizeKb: number()
21900
+ });
21901
+ var ReferenceAudioBodySchema = object({ base64: string() });
21902
+ var AudioBackendSchema = object({
21903
+ id: string(),
21904
+ name: string(),
21905
+ description: string(),
21906
+ available: boolean(),
21907
+ /**
21908
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
21909
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
21910
+ * the benchmark UI to populate the `enabledMicroClasses` filter
21911
+ * specific to the selected backend without a separate fetch.
21912
+ */
21913
+ rawLabels: array(string()).readonly().optional()
21914
+ });
21915
+ var AudioCapabilitiesSchema = object({
21916
+ activeBackend: string(),
21917
+ availableBackends: array(AudioBackendSchema).readonly(),
21918
+ sampleRate: number(),
21919
+ chunkDurationMs: number()
21920
+ });
21921
+ var DownloadModelResultSchema = object({
21922
+ filePath: string(),
21923
+ sizeMB: number(),
21924
+ durationMs: number()
21925
+ });
21926
+ /**
21927
+ * Wrapper carrying a single test run's result. Replaces the legacy
21928
+ * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
21929
+ * canonical `AudioResult` from the Phase 6 output rework: one
21930
+ * `AudioDetection` per class above `minScore`, top-N candidates in
21931
+ * `debug.alternateLabels['audio-classifier']`, per-source timings in
21932
+ * `debug.stepTimings`. The outer `success`/`error` fields stay so the
21933
+ * benchmark UI can still report a clean failure when the classifier
21934
+ * cap isn't available.
21935
+ */
21936
+ var AudioTestResultSchema = object({
21937
+ success: boolean(),
21938
+ error: string().optional(),
21939
+ frame: custom().optional()
21940
+ });
21941
+ var PipelineConfigBridge = custom();
21942
+ var ConfigUISchemaBridge = custom();
21943
+ var ConfigUISchemaNullableBridge = custom();
21944
+ var InferenceCapabilitiesBridge = custom();
21945
+ var ModelAvailabilityListBridge = custom();
21946
+ var PipelineRunResultBridge = custom();
21947
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
21948
+ modelId: string(),
21949
+ settings: record(string(), unknown()).readonly()
21950
+ }))), method(object({ steps: record(string(), object({
21951
+ modelId: string(),
21952
+ settings: record(string(), unknown()).readonly()
21953
+ })) }), object({ success: literal(true) }), {
21954
+ kind: "mutation",
21955
+ auth: "admin"
21956
+ }), method(object({ nodeId: string() }), object({
21957
+ success: literal(true),
21958
+ clearedDevices: number()
22072
21959
  }), {
22073
- kind: "query",
22074
- auth: "admin"
22075
- }), method(object({
22076
- deviceId: number(),
22077
- trackId: string(),
22078
- frameId: string(),
22079
- subject: RetrainAssistSubjectSchema,
22080
- /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
22081
- nodeId: string().optional()
22082
- }), RetrainAssistResultSchema, {
22083
- kind: "mutation",
22084
- auth: "admin"
22085
- }), method(object({
22086
- deviceId: number(),
22087
- source: DetectionSourceSchema,
22088
- zones: array(ZoneSchema).readonly().optional(),
22089
- detectionRules: array(ZoneRuleSchema).readonly().optional(),
22090
- zoneMembershipMinOverlap: number().min(0).max(1).optional(),
22091
- frames: array(ReplayFrameInputSchema).min(1)
22092
- }), RunReplayFrameProcessorResultSchema, {
22093
- kind: "mutation",
22094
- auth: "admin"
22095
- }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
22096
- kind: "query",
22097
- auth: "admin"
22098
- }), method(object({
22099
- deviceId: number(),
22100
- trackId: string(),
22101
- frameId: string(),
22102
- annotations: array(RetrainAnnotationDraftSchema)
22103
- }), array(RetrainAnnotationSchema).readonly(), {
22104
21960
  kind: "mutation",
22105
21961
  auth: "admin"
22106
- }), method(object({
22107
- deviceId: number(),
22108
- trackId: string()
22109
- }), RetrainTransitionResultSchema, {
21962
+ }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
21963
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
21964
+ deviceKey: string(),
21965
+ /**
21966
+ * `failed` — the per-device restart budget is exhausted; no pool
21967
+ * will be spawned until an operator re-arms it or the runner
21968
+ * respawns. `backoff` — under budget, waiting out the backoff (or
21969
+ * a cached pool observed dead and not yet condemned).
21970
+ */
21971
+ state: _enum(["failed", "backoff"]),
21972
+ /** Epoch ms of the death that produced this state. */
21973
+ since: number(),
21974
+ /** Pool deaths inside the current window. */
21975
+ deaths: number(),
21976
+ /** The last death's message. */
21977
+ lastError: string()
21978
+ })).readonly() })), method(object({
21979
+ nodeId: string(),
21980
+ deviceKey: string()
21981
+ }), object({ rearmed: boolean() }), {
22110
21982
  kind: "mutation",
22111
21983
  auth: "admin"
22112
- }), method(object({
22113
- deviceId: number(),
22114
- trackId: string()
22115
- }), RetrainTransitionResultSchema, {
21984
+ }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
21985
+ steps: array(PipelineStepInputSchema),
21986
+ deviceKey: string().optional()
21987
+ }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
21988
+ name: string(),
21989
+ steps: array(PipelineTemplateStepSchema).readonly(),
21990
+ engine: PipelineEngineChoiceSchema
21991
+ }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
21992
+ id: string(),
21993
+ name: string().optional(),
21994
+ steps: array(PipelineTemplateStepSchema).readonly().optional()
21995
+ }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
21996
+ addonId: string(),
21997
+ modelId: string(),
21998
+ format: ModelFormatSchema$1
21999
+ }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
22000
+ addonId: string(),
22001
+ modelId: string(),
22002
+ format: ModelFormatSchema$1
22003
+ }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
22004
+ steps: array(PipelineStepInputSchema).min(1),
22005
+ frame: FrameInputSchema.optional(),
22006
+ /**
22007
+ * Process-local lazy frame. Valid only when caller and provider resolve
22008
+ * in the same execution-group process; split/cross-node callers use
22009
+ * `frame`/`image` inline compatibility instead.
22010
+ */
22011
+ frameRef: FrameRefSchema.optional(),
22012
+ /**
22013
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
22014
+ * the decoded pixels live in. One more member of the one-of
22015
+ * frame/frameHandle/image/imageBase64/referenceImage group.
22016
+ */
22017
+ frameHandle: FrameHandleSchema.optional(),
22018
+ imageBase64: string().optional(),
22019
+ /**
22020
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
22021
+ * hops (hub → forked worker via Moleculer MsgPack) because it
22022
+ * skips the 33% base64 overhead + the per-call base64 decode on
22023
+ * the detection-pipeline worker. Callers can pass either; exactly
22024
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
22025
+ */
22026
+ image: _instanceof(Uint8Array).optional(),
22027
+ referenceImage: string().optional(),
22028
+ deviceId: number().optional(),
22029
+ sessionId: string().optional(),
22030
+ /**
22031
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
22032
+ * reference-image, and detail-subtree calls. 'frame' is the live
22033
+ * per-frame dispatch: ONLY root-plane steps run; crop children
22034
+ * (inputClasses ≠ null) are skipped and served per-track via
22035
+ * pipelineRunner.runDetailSubtree (two-plane design).
22036
+ */
22037
+ plane: _enum(["full", "frame"]).optional(),
22038
+ /**
22039
+ * Inference-device selector (Phase 2 multi-device). Format
22040
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
22041
+ * Omitted ⇒ the runner's default device (current single-engine
22042
+ * behaviour). Selects WHICH device pool of the node runs the call.
22043
+ */
22044
+ deviceKey: string().optional(),
22045
+ /**
22046
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
22047
+ * when the parent crop was resolved from the frame's retained NATIVE
22048
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
22049
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
22050
+ * resolution from that surface — the SAME quality path faces already
22051
+ * had — instead of the downscaled parent tile. `handle` keys the native
22052
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
22053
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
22054
+ * the executor's crop-normalized child ROI back into frame-normalized
22055
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
22056
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
22057
+ * (today's behaviour on the fallback path).
22058
+ */
22059
+ nativeCropRef: NativeCropRefSchema.optional(),
22060
+ /**
22061
+ * Offline REPLAY of stored media for `deviceId` (D56, D646): the
22062
+ * camera's zone gate and overrides apply as live, every executing
22063
+ * step runs EXACTLY the model it names on the `deviceKey` pool it
22064
+ * names or the call refuses (`model-not-servable` /
22065
+ * `device-not-servable`), and nothing reaches the camera's live
22066
+ * trace stream. Absent ⇒ unchanged behaviour.
22067
+ */
22068
+ replay: literal(true).optional()
22069
+ }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
22070
+ steps: array(PipelineStepInputSchema).min(1),
22071
+ frames: array(FrameInputSchema).min(1).max(255),
22072
+ deviceId: number().optional(),
22073
+ sessionId: string().optional(),
22074
+ /**
22075
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
22076
+ * the batch to the Python pool's bench preprocess cache
22077
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
22078
+ * preprocessed ONCE and every later inference is a pure-inference cache
22079
+ * hit — the sustained-throughput run measures inference, not
22080
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
22081
+ * full preprocess every call, correct). Fresh per sustained run;
22082
+ * released via `uncacheFrame`.
22083
+ */
22084
+ frameId: number().int().nonnegative().optional(),
22085
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
22086
+ deviceKey: string().optional()
22087
+ }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
22088
+ data: _instanceof(Uint8Array),
22089
+ width: number().int().positive(),
22090
+ height: number().int().positive(),
22091
+ format: _enum([
22092
+ "rgb",
22093
+ "bgr",
22094
+ "gray"
22095
+ ])
22096
+ }), object({
22097
+ frameId: number(),
22098
+ width: number(),
22099
+ height: number()
22100
+ }), { kind: "mutation" }), method(object({
22101
+ stepId: string(),
22102
+ frameId: number().int()
22103
+ }), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number().int() }), _void(), { kind: "mutation" }), method(object({ deviceKey: string().optional() }), EffectiveTuningSchema), method(_void(), array(object({
22104
+ engineKey: string(),
22105
+ engine: PipelineEngineChoiceSchema,
22106
+ modelsLoaded: array(string()).readonly(),
22107
+ inUseByCameras: array(number()).readonly(),
22108
+ /**
22109
+ * Origin of this resident factory.
22110
+ * - `runtime` — main camera-serving engine (no idle TTL).
22111
+ * - `device-pool` — a concurrent per-device pool (Phase 2
22112
+ * multi-device, keyed by `deviceKey`) resolved
22113
+ * via `resolveDeviceFactory`. Runs alongside the
22114
+ * `runtime` engine on a DIFFERENT accelerator
22115
+ * (NPU / iGPU / Coral) — this is how the
22116
+ * Engines tab shows all pools running at once.
22117
+ */
22118
+ kind: _enum(["runtime", "device-pool"]),
22119
+ /** Native pid of the underlying Python pool (null when no pool). */
22120
+ poolPid: number().nullable(),
22121
+ /** ms since this factory was last used (null when not warm-tracked). */
22122
+ idleMs: number().nullable(),
22123
+ /** Idle TTL after which the factory self-evicts (null when not applicable). */
22124
+ idleTtlMs: number().nullable()
22125
+ })).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
22116
22126
  kind: "mutation",
22117
22127
  auth: "admin"
22118
- }), method(object({ deviceIds: array(number()).optional() }), object({ url: string() }), {
22119
- kind: "query",
22120
- auth: "admin"
22121
22128
  }), method(object({
22122
- /** Which of the three event tables owns the row. */
22123
- ownerType: EventOwnerTypeSchema,
22124
- /** The event's rowid in that table (D474: an event id is a number). */
22125
- eventId: number().int(),
22126
- kind: MediaFileKindEnum.optional(),
22127
- deviceId: number()
22128
- }), array(MediaFileSchema).readonly()), method(object({
22129
- trackId: string(),
22130
- kinds: array(MediaFileKindEnum).optional(),
22131
- deviceId: number()
22132
- }), array(MediaFileSchema).readonly()), method(object({
22133
- trackId: string(),
22134
- deviceId: number()
22135
- }), array(MediaFileInfoSchema).readonly()), method(object({
22136
- ownerType: EventOwnerTypeSchema,
22137
- eventId: number().int(),
22138
- deviceId: number()
22139
- }), array(MediaFileInfoSchema).readonly()), method(object({
22140
- deviceId: number(),
22141
- ownerKind: MediaPresenceOwnerKindSchema,
22142
- ownerIds: array(string()).max(5e3)
22143
- }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
22144
- kind: "mutation",
22145
- auth: "admin"
22146
- }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
22129
+ engine: PipelineEngineChoiceSchema,
22130
+ force: boolean().optional()
22131
+ }), object({
22132
+ success: boolean(),
22133
+ reason: string().optional()
22134
+ }), {
22147
22135
  kind: "mutation",
22148
22136
  auth: "admin"
22149
- }), method(object({}), RebuildStatusSchema), object({
22150
- deviceId: number(),
22151
- timestamp: number(),
22152
- frameWidth: number(),
22153
- frameHeight: number(),
22154
- detections: array(OverlayDetectionSchema).readonly()
22155
- }), object({
22156
- deviceId: number(),
22157
- trackId: string(),
22158
- className: string()
22159
- }), object({
22160
- deviceId: number(),
22161
- trackId: string(),
22162
- className: string(),
22163
- durationMs: number()
22164
- }), object({
22165
- deviceId: number(),
22166
- kind: EventKindSchema,
22167
- eventId: string(),
22168
- timestamp: number()
22169
- });
22137
+ }), method(_void(), array(ReferenceImageEntrySchema).readonly()), method(object({ filename: string() }), ReferenceImageBodySchema.nullable()), method(_void(), array(ReferenceAudioEntrySchema).readonly()), method(object({ filename: string() }), ReferenceAudioBodySchema.nullable()), method(_void(), AudioCapabilitiesSchema), method(object({
22138
+ addonId: string(),
22139
+ modelId: string(),
22140
+ filename: string().optional(),
22141
+ settings: record(string(), unknown()).optional()
22142
+ }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
22170
22143
  object({
22171
22144
  activeCameras: number(),
22172
22145
  throttledCameras: number(),
@@ -22192,6 +22165,66 @@ var CameraMetricsSchema = object({
22192
22165
  });
22193
22166
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: number() });
22194
22167
  /**
22168
+ * Zone — pure geometry + identity. NO filtering behaviour.
22169
+ *
22170
+ * Zones describe **where** in the frame the operator wants to flag
22171
+ * something; consumer-owned {@link ZoneRule} arrays describe **how**
22172
+ * each pipeline stage uses them. Splitting the two means a single
22173
+ * polygon "Driveway" can simultaneously back a motion-exclude rule,
22174
+ * a detection-include rule on `['car']`, and an occupancy aggregate
22175
+ * — without three duplicated polygons.
22176
+ *
22177
+ * Owned by the orchestrator addon (provider) and mirrored into the
22178
+ * `zones` device-state slice on every mutation. Consumers
22179
+ * (motion-wasm, pipeline-executor, analytics, admin UI) read either
22180
+ * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
22181
+ * mirror with `onChanged`).
22182
+ *
22183
+ * Coordinates are normalised fractions of the frame (0–1) so zones
22184
+ * survive resolution changes and stream profile switches.
22185
+ *
22186
+ * `kind` discriminates between full polygons (closed regions used
22187
+ * for intrusion / occupancy filters) and tripwires (open 2-point
22188
+ * line segments used for cross events). Onboard / firmware-reported
22189
+ * zones (Reolink, ONVIF) are out of scope for now — see the deferred
22190
+ * task list.
22191
+ */
22192
+ var ZoneKindEnum = _enum(["polygon", "tripwire"]);
22193
+ /** Polygon vertex in fraction-of-frame coordinates (0–1). */
22194
+ var PolygonPointSchema = object({
22195
+ x: number(),
22196
+ y: number()
22197
+ });
22198
+ /** A camera detection zone — pure geometry/identity. */
22199
+ var ZoneSchema = object({
22200
+ id: string(),
22201
+ name: string(),
22202
+ kind: ZoneKindEnum.default("polygon"),
22203
+ /** Polygon vertices, fraction of frame (0–1). */
22204
+ polygon: array(PolygonPointSchema).readonly(),
22205
+ /** Visual color for UI rendering. */
22206
+ color: string().default("#3b82f6")
22207
+ });
22208
+ DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).readonly()), method(object({
22209
+ deviceId: number(),
22210
+ zone: ZoneSchema
22211
+ }), _void(), {
22212
+ kind: "mutation",
22213
+ auth: "admin"
22214
+ }), method(object({
22215
+ deviceId: number(),
22216
+ zoneId: string()
22217
+ }), _void(), {
22218
+ kind: "mutation",
22219
+ auth: "admin"
22220
+ }), method(object({
22221
+ deviceId: number(),
22222
+ zone: ZoneSchema
22223
+ }), _void(), {
22224
+ kind: "mutation",
22225
+ auth: "admin"
22226
+ }), object({ zones: array(ZoneSchema).readonly() });
22227
+ /**
22195
22228
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
22196
22229
  * decode worker resolves it against the RETAINED native frame's real pixel dims,
22197
22230
  * so the caller supplies only the detection-res bbox divided by the detection
@@ -22378,7 +22411,17 @@ var DetailResultSchema = object({
22378
22411
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
22379
22412
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
22380
22413
  * consumers MUST prefer this when present (2026-07-22 native-gate fix). */
22381
- nativeFaceShortSidePx: number().optional()
22414
+ nativeFaceShortSidePx: number().optional(),
22415
+ /**
22416
+ * The five facial landmarks of a `face` detail, in the SAME frame space as
22417
+ * `bbox` (D644). Additive and optional: post-analysis stores them beside the
22418
+ * face's source frame so a future alignment can be re-derived exactly; a
22419
+ * runner that predates the field sends none and the face is stored without.
22420
+ */
22421
+ landmarks: array(object({
22422
+ x: number(),
22423
+ y: number()
22424
+ })).optional()
22382
22425
  });
22383
22426
  /**
22384
22427
  * Why an executing node REFUSED a stateless step run (`runStatelessStep`).
@@ -30703,7 +30746,7 @@ var HardwareInfoSchema = object({
30703
30746
  coral: CoralInfoSchema.nullable().optional()
30704
30747
  });
30705
30748
  var PlatformScoreSchema = object({
30706
- runtime: _enum(["node", "python"]),
30749
+ runtime: literal("python"),
30707
30750
  backend: string(),
30708
30751
  format: _enum([
30709
30752
  "onnx",
@@ -30740,7 +30783,7 @@ var ModelRequirementSchema = object({
30740
30783
  });
30741
30784
  var ResolvedInferenceConfigSchema = object({
30742
30785
  modelId: string(),
30743
- runtime: _enum(["node", "python"]),
30786
+ runtime: literal("python"),
30744
30787
  backend: string(),
30745
30788
  format: ModelFormatSchema,
30746
30789
  reason: string()
@@ -32947,6 +32990,92 @@ DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSche
32947
32990
  kind: "mutation",
32948
32991
  auth: "admin"
32949
32992
  });
32993
+ /**
32994
+ * Per-stage gating mode applied to the zones a rule references.
32995
+ *
32996
+ * - `include`: the rule contributes to a **whitelist** for its stage.
32997
+ * When at least one `include` rule fires for a stage, only entities
32998
+ * inside one of those zones pass that stage.
32999
+ * - `exclude`: the rule contributes to a **blacklist** for its stage.
33000
+ * Entities inside one of those zones are dropped at that stage.
33001
+ *
33002
+ * `monitor`-style observation (count without filtering) is not a rule
33003
+ * mode — zones without any matching rule are observed naturally by
33004
+ * `zone-analytics` (live snapshot + history), so an "I just want to
33005
+ * count, not filter" use case needs no rule at all.
33006
+ */
33007
+ var ZoneRuleModeEnum = _enum(["include", "exclude"]);
33008
+ /**
33009
+ * Per-consumer rule that references existing zones (geometry) and
33010
+ * defines how a specific pipeline stage should treat them. Each
33011
+ * consumer addon owns its own `ZoneRule[]` array in its per-device
33012
+ * settings:
33013
+ *
33014
+ * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
33015
+ * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
33016
+ * - future: notification rules, audio gating, etc.
33017
+ *
33018
+ * One rule applies to N zones (`zoneIds[]`) so the operator can
33019
+ * express "ignore motion in ALL of {garden, street}" with a single
33020
+ * rule. `classFilter` narrows the rule to specific object classes —
33021
+ * "drop person detections in the street, but keep cars" is one
33022
+ * `exclude` rule with `classFilter: ['person']`.
33023
+ *
33024
+ * `enabled` is a soft toggle — the operator can keep the rule
33025
+ * configured but inert without deleting it.
33026
+ */
33027
+ var ZoneRuleSchema = object({
33028
+ /** Stable rule id — survives edits, used by the UI for diffing. */
33029
+ id: string(),
33030
+ /** Optional human-readable label rendered in the rule editor. */
33031
+ name: string().optional(),
33032
+ /** Zones this rule targets. The rule's `mode` applies to ALL
33033
+ * listed zones (OR-set: a detection in any one of them counts).
33034
+ * At least one zone id required — a rule with no targets is a
33035
+ * configuration mistake and the form validator rejects it. */
33036
+ zoneIds: array(string()).min(1).readonly(),
33037
+ mode: ZoneRuleModeEnum,
33038
+ /**
33039
+ * Class names this rule applies to. Empty / undefined ⇒ rule
33040
+ * applies to every class. Class strings match the `macroClass`
33041
+ * field on detections (e.g. `person`, `car`, `dog`).
33042
+ */
33043
+ classFilter: array(string()).readonly().optional(),
33044
+ /**
33045
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
33046
+ * required to consider an entity "in the zone". Defaults to the
33047
+ * consumer's stage default when omitted. Kept for back-compat with
33048
+ * existing per-rule overrides; new operators pick the value via
33049
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
33050
+ * set, the lower-level engine reads it as a 0–1 fraction.
33051
+ */
33052
+ overlapThreshold: number().min(0).max(1).optional(),
33053
+ /**
33054
+ * Operator-friendly version of `overlapThreshold` — the percentage
33055
+ * of the detection's bbox that must lie inside the zone for the
33056
+ * rule to match. Documented default is 85%; the engine substitutes
33057
+ * that when the field is omitted (kept optional so existing rules
33058
+ * stored without it stay valid).
33059
+ *
33060
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
33061
+ * rule, the engine prefers `bboxInclusionPct` because it's the
33062
+ * field exposed in the UI. Internally both feed the same gate.
33063
+ */
33064
+ bboxInclusionPct: number().min(0).max(100).optional(),
33065
+ /**
33066
+ * When `true` and a detection has a segmentation mask, use the
33067
+ * mask for overlap instead of the bbox. Detection-stage only;
33068
+ * motion rules ignore this field.
33069
+ */
33070
+ preferMask: boolean().optional(),
33071
+ /**
33072
+ * Soft-toggle: `false` disables the rule without deleting it.
33073
+ * Defaults to `true` so operators creating a rule via the UI
33074
+ * see it active immediately.
33075
+ */
33076
+ enabled: boolean().default(true)
33077
+ });
33078
+ array(ZoneRuleSchema).readonly();
32950
33079
  object({
32951
33080
  /** Whether the script is currently executing. */
32952
33081
  isRunning: boolean(),
@@ -39299,6 +39428,12 @@ Object.freeze({
39299
39428
  addonId: null,
39300
39429
  access: "view"
39301
39430
  },
39431
+ "notificationRules.previewStateSummary": {
39432
+ capName: "notification-rules",
39433
+ capScope: "system",
39434
+ addonId: null,
39435
+ access: "view"
39436
+ },
39302
39437
  "notificationRules.previewTemplate": {
39303
39438
  capName: "notification-rules",
39304
39439
  capScope: "system",
@@ -39875,12 +40010,6 @@ Object.freeze({
39875
40010
  addonId: null,
39876
40011
  access: "create"
39877
40012
  },
39878
- "pipelineAnalytics.runReplayFrameProcessor": {
39879
- capName: "pipeline-analytics",
39880
- capScope: "device",
39881
- addonId: null,
39882
- access: "create"
39883
- },
39884
40013
  "pipelineAnalytics.saveRetrainAnnotations": {
39885
40014
  capName: "pipeline-analytics",
39886
40015
  capScope: "device",
@@ -43978,11 +44107,6 @@ Object.freeze({
43978
44107
  form: "single",
43979
44108
  optional: false
43980
44109
  }],
43981
- "pipelineAnalytics.runReplayFrameProcessor": [{
43982
- name: "deviceId",
43983
- form: "single",
43984
- optional: false
43985
- }],
43986
44110
  "pipelineAnalytics.saveRetrainAnnotations": [{
43987
44111
  name: "deviceId",
43988
44112
  form: "single",
@@ -44871,12 +44995,55 @@ var NC_AUDIO_DEFAULTS = {
44871
44995
  samplingSeconds: 10
44872
44996
  };
44873
44997
  new Set(["devices", "classes"]);
44874
- /** Every filter, for the callers that must cover all four (guards, tests). */
44998
+ /**
44999
+ * WHICH narrowing filter speaks about WHICH system-event kind — one predicate,
45000
+ * consumed by the rule ENGINE and by every rule EDITOR.
45001
+ *
45002
+ * ── The failure this file exists for ───────────────────────────────────────
45003
+ * `NcSystemEventConditionSchema` carries four optional narrowing lists
45004
+ * (`deviceIds`, `deviceTypes`, `nodeIds`, `packageNames`) beside a required
45005
+ * `kinds` gate. They are not four filters over one vocabulary: each belongs to
45006
+ * a FAMILY of kinds and is meaningless outside it. A node going away has no
45007
+ * device type; a completed export has no package; `stream-offline` deliberately
45008
+ * carries no device type at all (a stream belongs to a camera by construction).
45009
+ *
45010
+ * The engine applied all four to every kind, so a filter from one family failed
45011
+ * a kind from another family CLOSED. The operator's live rule "Eventi di
45012
+ * sistema" selected eleven kinds and set `deviceTypes: ['camera']` — the only
45013
+ * way to say "only my cameras" about the two liveness kinds — and NINE of the
45014
+ * eleven silently stopped being deliverable. Four days of history contained
45015
+ * `device-online` and `device-offline` and nothing else, across a window in
45016
+ * which agents were restarted and addons were updated.
45017
+ *
45018
+ * Both editors already computed this scoping, to decide which filter boxes to
45019
+ * render; the cap's own docblock already promised it ("Narrow a `device-*` kind
45020
+ * to these device TYPES"). Only the engine disagreed, and it disagreed in the
45021
+ * destructive direction. So the rule is stated ONCE, here, and the engine and
45022
+ * the editors are readers of it rather than three independent authors.
45023
+ *
45024
+ * ── The direction ─────────────────────────────────────────────────────────
45025
+ * Out of family ⇒ the filter is IGNORED, never fail-closed. An editor that
45026
+ * hides a box has already told the operator this filter does not speak about
45027
+ * these kinds, so honouring it as an exclusion contradicts the only
45028
+ * explanation the operator was ever given. Inside its family the filter still
45029
+ * narrows, and still fails CLOSED on a subject that cannot produce the identity
45030
+ * it tests (a device the mirror cannot name is not evidence of a camera).
45031
+ */
45032
+ /**
45033
+ * Every filter, for the callers that must cover all of them (guards, tests).
45034
+ *
45035
+ * `consumableLowPercent` (D642) is not a list but it is a filter in exactly
45036
+ * this sense: it belongs to a family of kinds (the consumable pair) and the
45037
+ * engine consults it only there. It differs in one way the readers must know —
45038
+ * its ABSENCE is not "no narrowing" but the default threshold, because a
45039
+ * consumable episode is always judged at SOME threshold.
45040
+ */
44875
45041
  var NC_SYSTEM_EVENT_FILTER_KEYS = [
44876
45042
  "deviceIds",
44877
45043
  "deviceTypes",
44878
45044
  "nodeIds",
44879
- "packageNames"
45045
+ "packageNames",
45046
+ "consumableLowPercent"
44880
45047
  ];
44881
45048
  new Map(Object.entries({
44882
45049
  "device-online": "device-events",
@@ -44887,6 +45054,9 @@ new Map(Object.entries({
44887
45054
  "device-battery-normal": "device-events",
44888
45055
  "device-consumable-low": "device-events",
44889
45056
  "device-consumable-normal": "device-events",
45057
+ "device-tank-low": "device-events",
45058
+ "device-tank-full": "device-events",
45059
+ "device-tank-normal": "device-events",
44890
45060
  "stream-online": "device-events",
44891
45061
  "stream-offline": "device-events",
44892
45062
  "detection-blind": "device-events",
@@ -44916,6 +45086,98 @@ new Map(Object.entries({
44916
45086
  "camera-enabled": null
44917
45087
  }));
44918
45088
  [...NC_SYSTEM_EVENT_FILTER_KEYS];
45089
+ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
45090
+ {
45091
+ id: "device-state",
45092
+ family: "device-events",
45093
+ label: "Device state",
45094
+ kinds: [
45095
+ "device-offline",
45096
+ "device-online",
45097
+ "device-disabled",
45098
+ "device-enabled"
45099
+ ]
45100
+ },
45101
+ {
45102
+ id: "streams",
45103
+ family: "device-events",
45104
+ label: "Streams",
45105
+ kinds: ["stream-offline", "stream-online"]
45106
+ },
45107
+ {
45108
+ id: "battery",
45109
+ family: "device-events",
45110
+ label: "Battery",
45111
+ kinds: ["device-battery-low", "device-battery-normal"]
45112
+ },
45113
+ {
45114
+ id: "consumables",
45115
+ family: "device-events",
45116
+ label: "Consumables",
45117
+ kinds: ["device-consumable-low", "device-consumable-normal"]
45118
+ },
45119
+ {
45120
+ id: "tanks",
45121
+ family: "device-events",
45122
+ label: "Tanks",
45123
+ kinds: [
45124
+ "device-tank-low",
45125
+ "device-tank-full",
45126
+ "device-tank-normal"
45127
+ ]
45128
+ },
45129
+ {
45130
+ id: "detection-blind",
45131
+ family: "device-events",
45132
+ label: "Detecting nothing",
45133
+ kinds: ["detection-blind"]
45134
+ },
45135
+ {
45136
+ id: "nodes",
45137
+ family: "system",
45138
+ label: "Nodes",
45139
+ kinds: [
45140
+ "node-offline",
45141
+ "node-online",
45142
+ "node-inference-unavailable"
45143
+ ]
45144
+ },
45145
+ {
45146
+ id: "addons",
45147
+ family: "system",
45148
+ label: "Addons",
45149
+ kinds: [
45150
+ "addon-crash-loop",
45151
+ "addon-update-available",
45152
+ "addon-updated"
45153
+ ]
45154
+ },
45155
+ {
45156
+ id: "updates",
45157
+ family: "system",
45158
+ label: "Server & container updates",
45159
+ kinds: [
45160
+ "server-update-available",
45161
+ "server-updated",
45162
+ "wrapper-update-available"
45163
+ ]
45164
+ },
45165
+ {
45166
+ id: "backups-exports",
45167
+ family: "system",
45168
+ label: "Backups & exports",
45169
+ kinds: ["backup-completed", "export-completed"]
45170
+ },
45171
+ {
45172
+ id: "alarm",
45173
+ family: "alarm",
45174
+ label: "Alarm",
45175
+ kinds: NC_ALARM_SYSTEM_EVENT_KINDS
45176
+ }
45177
+ ];
45178
+ function systemEventKindsOfFamily(family) {
45179
+ return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((g) => g.family === family).flatMap((g) => g.kinds);
45180
+ }
44919
45181
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
44920
45182
  /**
44921
45183
  * WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
@@ -44954,6 +45216,11 @@ var RULE_BODY_ONLY = ["body"];
44954
45216
  var RULE_TEXT = ["title", "body"];
44955
45217
  var BATTERY = ["device-battery-low", "device-battery-normal"];
44956
45218
  var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
45219
+ var TANK = [
45220
+ "device-tank-low",
45221
+ "device-tank-full",
45222
+ "device-tank-normal"
45223
+ ];
44957
45224
  var UPDATES = [
44958
45225
  "addon-update-available",
44959
45226
  "server-update-available",
@@ -44979,12 +45246,189 @@ var DEVICE_EVENTS = [
44979
45246
  "device-enabled",
44980
45247
  ...BATTERY,
44981
45248
  ...CONSUMABLE,
45249
+ ...TANK,
44982
45250
  "stream-online",
44983
45251
  "stream-offline",
44984
45252
  "detection-blind",
44985
45253
  ...ALARM
44986
45254
  ];
44987
- [...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], [...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];
45255
+ /** Every kind a summary may speak about — the two summarisable families. */
45256
+ var SUMMARISABLE = [...systemEventKindsOfFamily("device-events"), ...systemEventKindsOfFamily("system")];
45257
+ /** One per summary SECTION (`NC_STATE_SUMMARY_SECTIONS` in the notification
45258
+ * centre): the section's lines, offered when one of its kinds is selected. */
45259
+ var STATE_SUMMARY_SECTION_VARS = [
45260
+ {
45261
+ name: "summaryDevices",
45262
+ label: "Summary: devices offline / switched off",
45263
+ example: "Garage — offline since 07:12",
45264
+ kinds: [
45265
+ "device-offline",
45266
+ "device-online",
45267
+ "device-disabled",
45268
+ "device-enabled"
45269
+ ]
45270
+ },
45271
+ {
45272
+ name: "summaryStreams",
45273
+ label: "Summary: streams offline",
45274
+ example: "Ingresso (high) — since 06:40",
45275
+ kinds: ["stream-offline", "stream-online"]
45276
+ },
45277
+ {
45278
+ name: "summaryBattery",
45279
+ label: "Summary: low batteries",
45280
+ example: "Sensore porta — 8 %",
45281
+ kinds: BATTERY
45282
+ },
45283
+ {
45284
+ name: "summaryConsumables",
45285
+ label: "Summary: consumables",
45286
+ example: "Robot — filter 10 %",
45287
+ kinds: CONSUMABLE
45288
+ },
45289
+ {
45290
+ name: "summaryTanks",
45291
+ label: "Summary: tanks",
45292
+ example: "Robot — clean water low",
45293
+ kinds: TANK
45294
+ },
45295
+ {
45296
+ name: "summaryDetection",
45297
+ label: "Summary: cameras detecting nothing",
45298
+ example: "Ingresso — since 05:10",
45299
+ kinds: ["detection-blind"]
45300
+ },
45301
+ {
45302
+ name: "summaryNodes",
45303
+ label: "Summary: nodes",
45304
+ example: "little-unraid — offline since 03:00",
45305
+ kinds: [
45306
+ "node-offline",
45307
+ "node-online",
45308
+ "node-inference-unavailable"
45309
+ ]
45310
+ },
45311
+ {
45312
+ name: "summaryAddons",
45313
+ label: "Summary: addons",
45314
+ example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
45315
+ kinds: [
45316
+ "addon-crash-loop",
45317
+ "addon-update-available",
45318
+ "addon-updated"
45319
+ ]
45320
+ },
45321
+ {
45322
+ name: "summaryUpdates",
45323
+ label: "Summary: server & container updates",
45324
+ example: "server 1.2.377 → 1.2.378",
45325
+ kinds: [
45326
+ "server-update-available",
45327
+ "server-updated",
45328
+ "wrapper-update-available"
45329
+ ]
45330
+ }
45331
+ ];
45332
+ var STATE_SUMMARY_VARS = [
45333
+ {
45334
+ name: "summaryText",
45335
+ label: "Summary: every section",
45336
+ description: "One block per section with something to report, one line per item.",
45337
+ example: "Batterie scariche: Sensore porta — 8 %",
45338
+ group: "system",
45339
+ families: ["rule"],
45340
+ fields: [...RULE_TEXT],
45341
+ kinds: ["system"],
45342
+ systemEventKinds: [...SUMMARISABLE],
45343
+ systemEventDeliveries: ["summary"]
45344
+ },
45345
+ {
45346
+ name: "summaryCount",
45347
+ label: "Summary: items reported",
45348
+ example: "3",
45349
+ group: "system",
45350
+ families: ["rule"],
45351
+ fields: [...RULE_TEXT],
45352
+ kinds: ["system"],
45353
+ systemEventKinds: [...SUMMARISABLE],
45354
+ systemEventDeliveries: ["summary"]
45355
+ },
45356
+ ...STATE_SUMMARY_SECTION_VARS.map((v) => ({
45357
+ name: v.name,
45358
+ label: v.label,
45359
+ example: v.example,
45360
+ group: "system",
45361
+ families: ["rule"],
45362
+ fields: [...RULE_TEXT],
45363
+ kinds: ["system"],
45364
+ systemEventKinds: [...v.kinds],
45365
+ systemEventDeliveries: ["summary"]
45366
+ }))
45367
+ ];
45368
+ [
45369
+ [...DEVICE_EVENTS],
45370
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45371
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45372
+ [...RULE_TEXT],
45373
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45374
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45375
+ [...RULE_TEXT],
45376
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45377
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45378
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45379
+ [...RULE_TEXT],
45380
+ [...RULE_TEXT],
45381
+ [...RULE_TEXT],
45382
+ [...RULE_TEXT],
45383
+ [...RULE_TEXT],
45384
+ [...RULE_TEXT],
45385
+ [...RULE_TEXT],
45386
+ [...RULE_TEXT],
45387
+ [...RULE_BODY_ONLY],
45388
+ [...RULE_TEXT],
45389
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
45390
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
45391
+ ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
45392
+ ([...RULE_TEXT], [...CONSUMABLE]),
45393
+ ([...RULE_TEXT], [...TANK]),
45394
+ [...RULE_TEXT],
45395
+ ([...RULE_TEXT], [...UPDATES]),
45396
+ ([...RULE_TEXT], [...UPDATES]),
45397
+ [...RULE_TEXT],
45398
+ [...RULE_TEXT],
45399
+ [...RULE_TEXT],
45400
+ ([...RULE_TEXT], [...UPDATES]),
45401
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45402
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45403
+ ([...RULE_TEXT], [...UPDATES]),
45404
+ ([...RULE_TEXT], [...UPDATES]),
45405
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45406
+ [...RULE_TEXT],
45407
+ [...RULE_TEXT],
45408
+ [...RULE_TEXT],
45409
+ [...RULE_TEXT],
45410
+ [...RULE_TEXT],
45411
+ [...RULE_TEXT],
45412
+ [...RULE_TEXT],
45413
+ [...RULE_TEXT],
45414
+ [...RULE_TEXT],
45415
+ [...RULE_TEXT],
45416
+ [...RULE_TEXT],
45417
+ [...RULE_TEXT],
45418
+ [...RULE_TEXT],
45419
+ [...RULE_TEXT],
45420
+ [...RULE_TEXT],
45421
+ [...RULE_TEXT],
45422
+ [...RULE_TEXT],
45423
+ [...RULE_TEXT],
45424
+ [...RULE_TEXT],
45425
+ [...RULE_TEXT],
45426
+ [...RULE_TEXT],
45427
+ [...RULE_TEXT],
45428
+ [...RULE_TEXT],
45429
+ [...RULE_TEXT],
45430
+ ...STATE_SUMMARY_VARS
45431
+ ];
44988
45432
  /**
44989
45433
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
44990
45434
  *