@camstack/addon-ai 0.4.123 → 0.4.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/addon.mjs CHANGED
@@ -5551,7 +5551,7 @@ function number(params) {
5551
5551
  return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
5552
5552
  }
5553
5553
  //#endregion
5554
- //#region ../types/dist/sleep-D_4W7xuQ.mjs
5554
+ //#region ../types/dist/sleep-BaRu0bS5.mjs
5555
5555
  /**
5556
5556
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5557
5557
  * window to float samples (D455).
@@ -9665,7 +9665,7 @@ var ModelFormatEntrySchema = object({
9665
9665
  /** Multi-file payload (directory members or sibling files). */
9666
9666
  files: array(string()).readonly().optional(),
9667
9667
  /** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
9668
- runtimes: array(_enum(["node", "python"])).readonly().optional()
9668
+ runtimes: array(_enum(["python"])).readonly().optional()
9669
9669
  });
9670
9670
  /**
9671
9671
  * Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
@@ -9746,10 +9746,17 @@ var ModelCatalogEntrySchema = object({
9746
9746
  inputChannels: number$1().int().positive().optional(),
9747
9747
  labels: array(LabelDefinitionSchema).readonly(),
9748
9748
  inputLayout: _enum(["nchw", "nhwc"]).optional(),
9749
+ /**
9750
+ * `'scrfd'` applies InsightFace's SCRFD input contract, `(x - 127.5) / 128`
9751
+ * (upstream `insightface/model_zoo/scrfd.py`), instead of the historical
9752
+ * plain `/255`. Measured against COCO GT: 188 → 196 faces found on the
9753
+ * `scrfd-2.5g` catalog entry (2026-09-26 model-replacement spike, §2.4-2).
9754
+ */
9749
9755
  inputNormalization: _enum([
9750
9756
  "zero-one",
9751
9757
  "imagenet",
9752
- "none"
9758
+ "none",
9759
+ "scrfd"
9753
9760
  ]).optional(),
9754
9761
  /**
9755
9762
  * The model already applies softmax IN-GRAPH — its raw output is a
@@ -9851,7 +9858,8 @@ var ModelConvertMetadataSchema = object({
9851
9858
  inputNormalization: _enum([
9852
9859
  "zero-one",
9853
9860
  "imagenet",
9854
- "none"
9861
+ "none",
9862
+ "scrfd"
9855
9863
  ]).optional(),
9856
9864
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
9857
9865
  outputFormat: _enum([
@@ -17337,6 +17345,9 @@ var NcSystemEventKindSchema = _enum([
17337
17345
  "device-battery-normal",
17338
17346
  "device-consumable-low",
17339
17347
  "device-consumable-normal",
17348
+ "device-tank-low",
17349
+ "device-tank-full",
17350
+ "device-tank-normal",
17340
17351
  "stream-online",
17341
17352
  "stream-offline",
17342
17353
  "node-online",
@@ -17374,6 +17385,29 @@ var NC_LEGACY_SYSTEM_EVENT_KINDS = new Set([
17374
17385
  ]);
17375
17386
  NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
17376
17387
  /**
17388
+ * The panel's transitions, as ONE list.
17389
+ *
17390
+ * Named here rather than spelled out at each of the four sites that need them
17391
+ * (the emitter, the intake, the editor group, the combined-notification gate),
17392
+ * because a transition added to the enum and forgotten at one of them is an
17393
+ * alarm state nobody can be notified about. That is exactly what this list
17394
+ * failed to prevent while it held three: `alarm-arming` was the fourth.
17395
+ *
17396
+ * In LIFECYCLE order, which is the order an editor lists them in and the order
17397
+ * a person experiences them — not the order they were added.
17398
+ */
17399
+ var NC_ALARM_SYSTEM_EVENT_KINDS = [
17400
+ "alarm-arm-refused",
17401
+ "alarm-check-failed",
17402
+ "alarm-arming",
17403
+ "alarm-armed",
17404
+ "alarm-pending",
17405
+ "alarm-triggered",
17406
+ "alarm-rearmed",
17407
+ "alarm-sensor-bypass",
17408
+ "alarm-disarmed"
17409
+ ];
17410
+ /**
17377
17411
  * One coherent system-event condition. `kinds` is the required opt-in safety
17378
17412
  * gate; the remaining lists are optional narrowing filters relevant to the
17379
17413
  * selected kinds.
@@ -17407,7 +17441,22 @@ var NcSystemEventConditionSchema = object({
17407
17441
  */
17408
17442
  excludeDeviceIds: array(number$1().int()).min(1).optional(),
17409
17443
  nodeIds: array(string().min(1)).min(1).optional(),
17410
- packageNames: array(string().min(1)).min(1).optional()
17444
+ packageNames: array(string().min(1)).min(1).optional(),
17445
+ /**
17446
+ * At or below this remaining-life %, a consumable is LOW for THIS rule
17447
+ * (D642). Absent = {@link NC_CONSUMABLE_LOW_PERCENT_DEFAULT}; at most
17448
+ * {@link NC_CONSUMABLE_LOW_PERCENT_MAX}, so every episode can recover. Scoped to the
17449
+ * consumable kinds (`systemEventFilterApplies`) and to the device-events
17450
+ * family; ignored on every other kind.
17451
+ *
17452
+ * It is a property of the RULE because the operator decided so: a rule at
17453
+ * 25 must be told about a filter at 20 %, a rule at 10 must not. The hub
17454
+ * keeps one episode per (threshold, device, item) — its own hysteresis, its
17455
+ * own reminder — and a rule hears only the episode judged at ITS threshold,
17456
+ * exactly as an occupancy rule hears only the edge of its own count. A
17457
+ * provider's own `replace` verdict is low at every threshold.
17458
+ */
17459
+ consumableLowPercent: number$1().int().min(1).max(95).optional()
17411
17460
  });
17412
17461
  /** Hard ceiling on a window (24h). A snooze that could not expire would be an
17413
17462
  * outage the operator asked for once and forgot. */
@@ -17425,6 +17474,25 @@ var NcScheduleSchema = object({
17425
17474
  /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
17426
17475
  invert: boolean().optional()
17427
17476
  });
17477
+ /**
17478
+ * How a system-event rule of the `device-events` or `system` family speaks
17479
+ * (D647): at once, per event (`instant`, the only behaviour before D647), or
17480
+ * as a STATE SUMMARY at the end of each window of `schedule`.
17481
+ */
17482
+ var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
17483
+ /**
17484
+ * `summary`: the rule sends NOTHING per event. When a window of `schedule`
17485
+ * CLOSES, it sends one notification with the current state of its kinds
17486
+ * (low batteries, consumables to replace, devices offline, updates
17487
+ * available, ...), after its own filters. Nothing to report sends nothing.
17488
+ * The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
17489
+ * midnight crossing); the send time is each window's END. Refused on the
17490
+ * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
17491
+ */
17492
+ var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
17493
+ mode: literal("summary"),
17494
+ schedule: NcScheduleSchema
17495
+ })]);
17428
17496
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
17429
17497
  var NcPlateMatcherSchema = object({
17430
17498
  /**
@@ -17550,6 +17618,16 @@ var NcCrossingSchema = _enum([
17550
17618
  "exit",
17551
17619
  "any"
17552
17620
  ]);
17621
+ /**
17622
+ * Which edge of a BOOLEAN sensor fires a device-event rule (D640).
17623
+ * - `raised` — the sensor went high: water detected, smoke, open, tampered.
17624
+ * - `cleared` — it went back: dry, clear, closed.
17625
+ * - absent — both edges, which is what every rule authored before this did.
17626
+ *
17627
+ * There is no third member for "both": absence already says it, and a value
17628
+ * that means the same as absence is a second way to write one rule.
17629
+ */
17630
+ var NcSensorTransitionSchema = _enum(["raised", "cleared"]);
17553
17631
  /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
17554
17632
  var NcZoneConditionSchema = object({
17555
17633
  ids: array(string().min(1)).min(1),
@@ -17739,6 +17817,20 @@ var NcConditionsSchema = object({
17739
17817
  scene: NcSceneConditionSchema.optional(),
17740
17818
  /** Device scope — absent = all devices. */
17741
17819
  devices: array(number$1()).optional(),
17820
+ /**
17821
+ * Device EXCLUSION (D641) — "every device except these". Judged on the SAME
17822
+ * subject ids {@link devices} is judged on (the camera, the sensor that
17823
+ * fired, a linked set's sensor and row cameras), so a sensor fanned out per
17824
+ * linked camera is excluded exactly where an include would have tested it.
17825
+ * `min(1)`: an empty exclusion excludes nothing and is written as absent.
17826
+ *
17827
+ * Not for `system-event` rules — those carry their own
17828
+ * `systemEvent.excludeDeviceIds` (D636), and the hub refuses this key there.
17829
+ * Editors present the two lists as ONE field (All | Only these | All except
17830
+ * these); the engine evaluates each independently, so a legacy rule
17831
+ * carrying both still means "these, minus those".
17832
+ */
17833
+ devicesExclude: array(number$1()).min(1).optional(),
17742
17834
  /** Detector class names (any overlap with the record's class set). */
17743
17835
  classes: array(string().min(1)).optional(),
17744
17836
  /** Veto classes — any overlap fails the rule. */
@@ -17872,6 +17964,16 @@ var NcConditionsSchema = object({
17872
17964
  */
17873
17965
  sensorKinds: array(string().min(1)).optional(),
17874
17966
  /**
17967
+ * DEVICE-EVENT only (D640). Fire on one edge of a boolean sensor —
17968
+ * {@link NcSensorTransitionSchema}. Which field of the slice means "raised"
17969
+ * is `@camstack/types` → `notification/sensor-raised.ts`. A reading the
17970
+ * engine cannot place (a kind with no raise, an unreadable slice) PASSES:
17971
+ * the condition fails open towards notifying, never towards silence. Editors
17972
+ * offer it only when every chosen {@link sensorKinds} member has a raise
17973
+ * (`sensorTransitionOffered`), and the hub refuses it on any other delivery.
17974
+ */
17975
+ sensorTransition: NcSensorTransitionSchema.optional(),
17976
+ /**
17875
17977
  * PACKAGE-EVENT only. Which package phase fires the rule — `delivered`
17876
17978
  * (a parked parcel appeared), `picked-up` (it departed), or `both`. Fails
17877
17979
  * when the subject's phase does not match (a subject always carries a phase
@@ -18126,6 +18228,16 @@ var NcRuleInputSchema = object({
18126
18228
  delivery: NcDeliverySchema,
18127
18229
  conditions: NcConditionsSchema.default({}),
18128
18230
  schedule: NcScheduleSchema.optional(),
18231
+ /**
18232
+ * A `device-events` / `system` rule's delivery (D647): absent or `instant`
18233
+ * is a notification per event; `summary` is one state summary per closed
18234
+ * window of its schedule. `.optional()` and never defaulted: absent must
18235
+ * keep meaning what every rule authored before D647 meant, with no
18236
+ * migration. Top-level rather than inside `conditions.systemEvent`, so a
18237
+ * client that does not know it (an older viewer re-sending `conditions`
18238
+ * whole) leaves it standing through the store's patch merge.
18239
+ */
18240
+ systemEventDelivery: NcSystemEventDeliverySchema.optional(),
18129
18241
  /** May be empty when `targetUsers` addresses at least one user — the
18130
18242
  * "at least one addressee" invariant is enforced by the provider, because
18131
18243
  * a cross-field refine here would break `NcRulePatchSchema.partial()`. */
@@ -18354,6 +18466,28 @@ var NcRuleSchema = NcRuleInputSchema.extend({
18354
18466
  */
18355
18467
  disabledTargetIds: array(string()).default([])
18356
18468
  });
18469
+ /** One section of a previewed state summary. `unavailable` = its state could not be read. */
18470
+ var NcStateSummaryPreviewSectionSchema = object({
18471
+ id: string(),
18472
+ status: _enum(["read", "unavailable"]),
18473
+ items: number$1().int().nonnegative(),
18474
+ reason: string().optional()
18475
+ });
18476
+ /**
18477
+ * The summary a rule would send now. `empty` = nothing to report and every
18478
+ * section read: such a summary is NOT sent. `dropped` counts the open states
18479
+ * the rule's own filters (or a camera mute) left out, by reason.
18480
+ * `excludedGroups` names the kind groups selected that have no state to report
18481
+ * (backups & exports).
18482
+ */
18483
+ var NcStateSummaryPreviewSchema = object({
18484
+ empty: boolean(),
18485
+ count: number$1().int().nonnegative(),
18486
+ text: string(),
18487
+ sections: array(NcStateSummaryPreviewSectionSchema),
18488
+ dropped: record(string(), number$1().int().nonnegative()),
18489
+ excludedGroups: array(string())
18490
+ });
18357
18491
  var NcTestResultSchema = object({
18358
18492
  recordId: string(),
18359
18493
  recordKind: _enum([
@@ -18371,9 +18505,29 @@ var NcTestResultSchema = object({
18371
18505
  className: string().optional(),
18372
18506
  label: string().optional()
18373
18507
  });
18374
- /** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
18375
- * is the same union; the template-vars spec pins the two together. */
18508
+ /** The rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts` is the
18509
+ * same union; the template-vars spec pins the two together. `mixed` is DERIVED
18510
+ * (a stored system rule whose kinds span families, D637) and never authored. */
18376
18511
  var NcRuleKindSchema = _enum([
18512
+ "detection",
18513
+ "sensor",
18514
+ "occupancy",
18515
+ "sound",
18516
+ "device-events",
18517
+ "system",
18518
+ "alarm",
18519
+ "mixed"
18520
+ ]);
18521
+ /**
18522
+ * The kinds a SERVED template-variable descriptor may name: the five that
18523
+ * existed before D637, frozen. `getTemplateCatalog` ships `kinds` to every
18524
+ * installed viewer, and a viewer at `@camstack/types` 1.2.25x parses them with a
18525
+ * strict five-member enum — one `device-events` in one descriptor fails the
18526
+ * whole catalog parse. So the WIRE says `system`, and the MATCH widens it:
18527
+ * `templateVarKindApplies` reads a `system` descriptor as applying to every
18528
+ * system rule kind (`device-events`, `system`, `alarm`, `mixed`).
18529
+ */
18530
+ var NcTemplateVarKindSchema = _enum([
18377
18531
  "detection",
18378
18532
  "sensor",
18379
18533
  "occupancy",
@@ -18411,7 +18565,7 @@ var NcTemplateVarGroupSchema = _enum([
18411
18565
  *
18412
18566
  * `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
18413
18567
  * it has a value; absent = no restriction on that axis. `systemEventKinds` is
18414
- * read only for a `system` rule. `dynamic` declares a FAMILY of names
18568
+ * read only for a system-event rule (any family, or `mixed`). `dynamic` declares a FAMILY of names
18415
18569
  * (`count_<class>`): the descriptor's `name` is the example member.
18416
18570
  */
18417
18571
  var NcTemplateVarDescriptorSchema = object({
@@ -18422,9 +18576,16 @@ var NcTemplateVarDescriptorSchema = object({
18422
18576
  group: NcTemplateVarGroupSchema,
18423
18577
  families: array(NcTemplateFamilySchema).min(1),
18424
18578
  fields: array(NcTemplateFieldSchema).optional(),
18425
- kinds: array(NcRuleKindSchema).optional(),
18579
+ kinds: array(NcTemplateVarKindSchema).optional(),
18426
18580
  deliveries: array(NcDeliverySchema).optional(),
18427
18581
  systemEventKinds: array(NcSystemEventKindSchema).optional(),
18582
+ /**
18583
+ * Which system-event DELIVERY has a value for it (D647). Absent = an
18584
+ * instant row only when the descriptor names `systemEventKinds` (a
18585
+ * per-event fact: a summary row has no single device), and both otherwise.
18586
+ * `['summary']` marks the state-summary vocabulary (`{{summaryText}}`, ...).
18587
+ */
18588
+ systemEventDeliveries: array(NcSystemEventDeliveryModeSchema).optional(),
18428
18589
  dynamic: object({
18429
18590
  prefix: literal("count_"),
18430
18591
  from: literal("classes")
@@ -18437,7 +18598,9 @@ var NcTemplatePreviewInputSchema = object({
18437
18598
  field: NcTemplateFieldSchema,
18438
18599
  kind: NcRuleKindSchema.optional(),
18439
18600
  delivery: NcDeliverySchema.optional(),
18440
- systemEventKinds: array(NcSystemEventKindSchema).optional()
18601
+ systemEventKinds: array(NcSystemEventKindSchema).optional(),
18602
+ /** The rule's system-event delivery (D647); absent = instant. */
18603
+ systemEventDelivery: NcSystemEventDeliveryModeSchema.optional()
18441
18604
  }),
18442
18605
  template: object({
18443
18606
  title: string().max(500).optional(),
@@ -18470,6 +18633,7 @@ var NcConditionDescriptorSchema = object({
18470
18633
  /** Editor widget the UI renders — never hardcode per-condition forms. */
18471
18634
  valueType: _enum([
18472
18635
  "deviceIdList",
18636
+ "deviceIdExcludeList",
18473
18637
  "stringList",
18474
18638
  "number01",
18475
18639
  "number",
@@ -18480,6 +18644,7 @@ var NcConditionDescriptorSchema = object({
18480
18644
  "plateMatcher",
18481
18645
  "packagePhase",
18482
18646
  "crossingSelect",
18647
+ "sensorTransition",
18483
18648
  "polygonDraw",
18484
18649
  "occupancy",
18485
18650
  "audio",
@@ -18630,6 +18795,28 @@ var NcHistoryFilterSchema = object({
18630
18795
  limit: number$1().int().min(1).max(500).default(100)
18631
18796
  });
18632
18797
  /**
18798
+ * Every rule's last run, in one answer.
18799
+ *
18800
+ * A rule ABSENT from `runs` produced no delivery inside `horizonMs` — how far
18801
+ * back this answer looked, in ms before the hub's now. It is the outbox prune
18802
+ * window (7 days by default, operator-configurable), NARROWED when the read hit
18803
+ * its row bound: the answer never claims a window it did not read. `null` means
18804
+ * the operator keeps the outbox forever AND the read reached its end, so absent
18805
+ * means "never on record". A UI derives its "not in the last N days" from this
18806
+ * value, never from a constant — and only when the call ANSWERED (D315).
18807
+ */
18808
+ var NcRuleLastRunsSchema = object({
18809
+ runs: array(object({
18810
+ ruleId: string(),
18811
+ /** Fire time of the newest row (`createdAt`, epoch ms). */
18812
+ lastAt: number$1(),
18813
+ status: NcHistoryStatusSchema,
18814
+ /** The camera/device of that row (a digest row files under its first camera). */
18815
+ deviceId: number$1()
18816
+ })),
18817
+ horizonMs: number$1().nonnegative().nullable()
18818
+ });
18819
+ /**
18633
18820
  * What a snooze covers. Broader scopes win when several overlap, so one window
18634
18821
  * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
18635
18822
  * summarising the same silence.
@@ -18810,6 +18997,14 @@ var NcAlarmModeCoverageSchema = object({
18810
18997
  /** Ids named by the covering rules. A SUBSET when `allDevices` is true. */
18811
18998
  deviceIds: array(number$1().int()),
18812
18999
  /**
19000
+ * D641: the ids NOT covered although `allDevices` is true — every unscoped
19001
+ * covering rule EXCLUDES them (`conditions.devicesExclude`) and no named rule
19002
+ * covers them. Always `[]` when `allDevices` is false. Excluded by the rules,
19003
+ * which is not the same as skipped by a switch: never in `skippedDevices`.
19004
+ * Defaulted so an answer computed before this field parses as "none".
19005
+ */
19006
+ allDevicesExcept: array(number$1().int()).default([]),
19007
+ /**
18813
19008
  * Devices this mode NAMES but cannot actually arm, each with the switch that
18814
19009
  * excludes it.
18815
19010
  *
@@ -18939,13 +19134,17 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
18939
19134
  }), object({ results: array(NcTestResultSchema) }), {
18940
19135
  kind: "mutation",
18941
19136
  auth: "admin"
19137
+ }), method(object({ rule: NcRuleInputSchema }), NcStateSummaryPreviewSchema, {
19138
+ kind: "mutation",
19139
+ auth: "admin",
19140
+ access: "view"
18942
19141
  }), method(object({}), object({
18943
19142
  catalog: array(NcConditionDescriptorSchema),
18944
19143
  taxonomy: NcTaxonomySchema.optional()
18945
19144
  })), method(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
18946
19145
  kind: "mutation",
18947
19146
  access: "view"
18948
- }), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
19147
+ }), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({}), NcRuleLastRunsSchema, { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
18949
19148
  kind: "mutation",
18950
19149
  caller: "required"
18951
19150
  }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
@@ -19285,606 +19484,6 @@ var OauthIntegrationDescriptorSchema = object({
19285
19484
  });
19286
19485
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
19287
19486
  /**
19288
- * Reference to the frame's retained NATIVE surface + the parent crop's placement
19289
- * within the frame, so the executor can re-cut a leaf child ROI at native
19290
- * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
19291
- */
19292
- var NativeCropRefSchema = object({
19293
- /** Handle keying the retained native surface (node-pinned to its owner). */
19294
- handle: FrameHandleSchema,
19295
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
19296
- cropFrameSpace: object({
19297
- x: number$1(),
19298
- y: number$1(),
19299
- w: number$1(),
19300
- h: number$1()
19301
- })
19302
- });
19303
- object({
19304
- crop: object({
19305
- left: number$1(),
19306
- top: number$1(),
19307
- width: number$1().positive(),
19308
- height: number$1().positive()
19309
- }).optional(),
19310
- content: object({
19311
- width: number$1().int().positive(),
19312
- height: number$1().int().positive()
19313
- }),
19314
- fit: _enum(["stretch", "contain"]),
19315
- format: _enum([
19316
- "rgb",
19317
- "gray",
19318
- "jpeg"
19319
- ])
19320
- });
19321
- /**
19322
- * Process-local frame identity. It is serializable so it can ride an in-process
19323
- * capability call, but `registryId` deliberately prevents resolution in any
19324
- * other process or execution group.
19325
- */
19326
- var FrameRefSchema = object({
19327
- registryId: string().min(1),
19328
- id: string().min(1),
19329
- width: number$1().int().positive(),
19330
- height: number$1().int().positive(),
19331
- format: _enum(["rgb", "gray"]),
19332
- timestamp: number$1(),
19333
- capturedAt: number$1().optional()
19334
- });
19335
- var ModelFormatSchema$1 = _enum([
19336
- "onnx",
19337
- "coreml",
19338
- "openvino",
19339
- "tflite",
19340
- "pt",
19341
- "gguf"
19342
- ]);
19343
- var PipelineSlotSchema = _enum([
19344
- "detector",
19345
- "cropper",
19346
- "classifier",
19347
- "refiner",
19348
- "audio-classifier"
19349
- ]);
19350
- var PipelineEngineChoiceSchema = object({
19351
- runtime: _enum(["node", "python"]),
19352
- backend: string(),
19353
- format: ModelFormatSchema$1,
19354
- device: string().optional()
19355
- });
19356
- var AvailableEngineSchema = object({
19357
- engine: PipelineEngineChoiceSchema,
19358
- devices: array(object({
19359
- id: string(),
19360
- label: string(),
19361
- description: string().optional()
19362
- })).readonly(),
19363
- defaultDevice: string()
19364
- });
19365
- var PipelineDefaultStepSchema = lazy(() => object({
19366
- addonId: string(),
19367
- addonName: string(),
19368
- slot: PipelineSlotSchema,
19369
- inputClasses: array(string()).readonly(),
19370
- outputClasses: array(string()).readonly(),
19371
- enabled: boolean(),
19372
- modelId: string(),
19373
- children: array(PipelineDefaultStepSchema).readonly(),
19374
- group: string().optional(),
19375
- settings: record(string(), unknown()).optional()
19376
- }));
19377
- var PipelineTemplateStepSchema = lazy(() => object({
19378
- addonId: string(),
19379
- enabled: boolean(),
19380
- modelId: string(),
19381
- children: array(PipelineTemplateStepSchema).readonly(),
19382
- settings: record(string(), unknown()).optional()
19383
- }));
19384
- var PipelineTemplateSchema$1 = object({
19385
- id: string(),
19386
- name: string(),
19387
- createdAt: string(),
19388
- updatedAt: string(),
19389
- engine: PipelineEngineChoiceSchema,
19390
- steps: array(PipelineTemplateStepSchema).readonly()
19391
- });
19392
- var PipelineModelOptionSchema = object({
19393
- id: string(),
19394
- name: string(),
19395
- formats: record(string(), object({
19396
- downloaded: boolean(),
19397
- sizeMB: number$1()
19398
- })),
19399
- group: ModelVariantGroupSchema.optional(),
19400
- legacy: boolean().optional(),
19401
- provider: ModelProviderIdSchema.optional()
19402
- });
19403
- var ConfigFieldBridge = custom();
19404
- var PipelineAddonSchemaSchema = object({
19405
- id: string(),
19406
- name: string(),
19407
- slot: PipelineSlotSchema,
19408
- inputClasses: array(string()).readonly(),
19409
- outputClasses: array(string()).readonly(),
19410
- childSlots: array(PipelineSlotSchema).readonly(),
19411
- models: array(PipelineModelOptionSchema).readonly(),
19412
- defaultModelId: string(),
19413
- defaultModelIdByFormat: record(string(), string()).optional(),
19414
- enabledByDefault: boolean().optional(),
19415
- backfillIntoExistingOverrides: boolean().optional(),
19416
- defaultConfidence: number$1(),
19417
- group: string().optional(),
19418
- configSchema: array(ConfigFieldBridge).readonly().optional()
19419
- });
19420
- var PipelineSlotSchemaSchema = object({
19421
- id: PipelineSlotSchema,
19422
- label: string(),
19423
- priority: number$1(),
19424
- parentSlot: PipelineSlotSchema.nullable(),
19425
- addons: array(PipelineAddonSchemaSchema).readonly()
19426
- });
19427
- var PipelineSchemaSchema = object({
19428
- availableEngines: array(AvailableEngineSchema).readonly(),
19429
- selectedEngine: PipelineEngineChoiceSchema,
19430
- slots: array(PipelineSlotSchemaSchema).readonly()
19431
- });
19432
- var EngineProvisioningSchema = object({
19433
- runtimeId: _enum([
19434
- "onnx",
19435
- "openvino",
19436
- "coreml",
19437
- "edgetpu"
19438
- ]).nullable(),
19439
- device: string().nullable(),
19440
- state: _enum([
19441
- "idle",
19442
- "installing",
19443
- "verifying",
19444
- "ready",
19445
- "failed"
19446
- ]),
19447
- progress: number$1().optional(),
19448
- error: string().optional(),
19449
- nextRetryAt: number$1().optional(),
19450
- /**
19451
- * Gate A (config-correctness gate at engine change): human-readable
19452
- * config issues surfaced EAGERLY when the node's engine changes — model
19453
- * substitutions ("chose X, running Y") and zero-build steps ("no model
19454
- * has a <format> build"). Additive/optional: informational only, never
19455
- * enforced here — `assertEngineReady` (readiness) still gates inference.
19456
- * Absent/empty when the node-default tree resolves cleanly.
19457
- */
19458
- configIssues: array(string()).optional()
19459
- });
19460
- var PipelineStepInputSchema = lazy(() => object({
19461
- addonId: string(),
19462
- modelId: string().optional(),
19463
- enabled: boolean().default(true),
19464
- children: array(PipelineStepInputSchema).optional(),
19465
- settings: record(string(), unknown()).optional(),
19466
- jumpDeviceKey: string().optional()
19467
- }));
19468
- var ModelSubstitutionSchema = object({
19469
- addonId: string(),
19470
- chosen: string(),
19471
- running: string(),
19472
- format: string()
19473
- });
19474
- var PipelineValidationIssueSchema = object({
19475
- addonId: string(),
19476
- kind: _enum(["unknown-addon", "no-format-build"]),
19477
- detail: string()
19478
- });
19479
- var PipelineValidationResultSchema = object({
19480
- ok: boolean(),
19481
- issues: array(PipelineValidationIssueSchema).readonly(),
19482
- substitutions: array(ModelSubstitutionSchema).readonly(),
19483
- /** The node's `currentEngine.format` this validation ran against. */
19484
- format: string()
19485
- });
19486
- var ReferenceImageEntrySchema = object({
19487
- filename: string(),
19488
- stepIds: array(string()).readonly().optional()
19489
- });
19490
- var ReferenceImageBodySchema = object({
19491
- base64: string(),
19492
- filename: string()
19493
- });
19494
- var ReferenceAudioEntrySchema = object({
19495
- filename: string(),
19496
- sizeKb: number$1()
19497
- });
19498
- var ReferenceAudioBodySchema = object({ base64: string() });
19499
- var AudioBackendSchema = object({
19500
- id: string(),
19501
- name: string(),
19502
- description: string(),
19503
- available: boolean(),
19504
- /**
19505
- * Raw classifier labels this backend can emit (e.g. YAMNet's
19506
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
19507
- * the benchmark UI to populate the `enabledMicroClasses` filter
19508
- * specific to the selected backend without a separate fetch.
19509
- */
19510
- rawLabels: array(string()).readonly().optional()
19511
- });
19512
- var AudioCapabilitiesSchema = object({
19513
- activeBackend: string(),
19514
- availableBackends: array(AudioBackendSchema).readonly(),
19515
- sampleRate: number$1(),
19516
- chunkDurationMs: number$1()
19517
- });
19518
- var DownloadModelResultSchema = object({
19519
- filePath: string(),
19520
- sizeMB: number$1(),
19521
- durationMs: number$1()
19522
- });
19523
- /**
19524
- * Wrapper carrying a single test run's result. Replaces the legacy
19525
- * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
19526
- * canonical `AudioResult` from the Phase 6 output rework: one
19527
- * `AudioDetection` per class above `minScore`, top-N candidates in
19528
- * `debug.alternateLabels['audio-classifier']`, per-source timings in
19529
- * `debug.stepTimings`. The outer `success`/`error` fields stay so the
19530
- * benchmark UI can still report a clean failure when the classifier
19531
- * cap isn't available.
19532
- */
19533
- var AudioTestResultSchema = object({
19534
- success: boolean(),
19535
- error: string().optional(),
19536
- frame: custom().optional()
19537
- });
19538
- var PipelineConfigBridge = custom();
19539
- var ConfigUISchemaBridge = custom();
19540
- var ConfigUISchemaNullableBridge = custom();
19541
- var InferenceCapabilitiesBridge = custom();
19542
- var ModelAvailabilityListBridge = custom();
19543
- var PipelineRunResultBridge = custom();
19544
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
19545
- modelId: string(),
19546
- settings: record(string(), unknown()).readonly()
19547
- }))), method(object({ steps: record(string(), object({
19548
- modelId: string(),
19549
- settings: record(string(), unknown()).readonly()
19550
- })) }), object({ success: literal(true) }), {
19551
- kind: "mutation",
19552
- auth: "admin"
19553
- }), method(object({ nodeId: string() }), object({
19554
- success: literal(true),
19555
- clearedDevices: number$1()
19556
- }), {
19557
- kind: "mutation",
19558
- auth: "admin"
19559
- }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
19560
- /** `<backend>:<device>`, e.g. `openvino:gpu`. */
19561
- deviceKey: string(),
19562
- /**
19563
- * `failed` — the per-device restart budget is exhausted; no pool
19564
- * will be spawned until an operator re-arms it or the runner
19565
- * respawns. `backoff` — under budget, waiting out the backoff (or
19566
- * a cached pool observed dead and not yet condemned).
19567
- */
19568
- state: _enum(["failed", "backoff"]),
19569
- /** Epoch ms of the death that produced this state. */
19570
- since: number$1(),
19571
- /** Pool deaths inside the current window. */
19572
- deaths: number$1(),
19573
- /** The last death's message. */
19574
- lastError: string()
19575
- })).readonly() })), method(object({
19576
- nodeId: string(),
19577
- deviceKey: string()
19578
- }), object({ rearmed: boolean() }), {
19579
- kind: "mutation",
19580
- auth: "admin"
19581
- }), 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({
19582
- name: string(),
19583
- steps: array(PipelineTemplateStepSchema).readonly(),
19584
- engine: PipelineEngineChoiceSchema
19585
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
19586
- id: string(),
19587
- name: string().optional(),
19588
- steps: array(PipelineTemplateStepSchema).readonly().optional()
19589
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
19590
- addonId: string(),
19591
- modelId: string(),
19592
- format: ModelFormatSchema$1
19593
- }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
19594
- addonId: string(),
19595
- modelId: string(),
19596
- format: ModelFormatSchema$1
19597
- }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
19598
- engine: PipelineEngineChoiceSchema.optional(),
19599
- steps: array(PipelineStepInputSchema).min(1),
19600
- frame: FrameInputSchema.optional(),
19601
- /**
19602
- * Process-local lazy frame. Valid only when caller and provider resolve
19603
- * in the same execution-group process; split/cross-node callers use
19604
- * `frame`/`image` inline compatibility instead.
19605
- */
19606
- frameRef: FrameRefSchema.optional(),
19607
- /**
19608
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
19609
- * the decoded pixels live in. One more member of the one-of
19610
- * frame/frameHandle/image/imageBase64/referenceImage group.
19611
- */
19612
- frameHandle: FrameHandleSchema.optional(),
19613
- imageBase64: string().optional(),
19614
- /**
19615
- * Binary JPEG bytes — preferred over `imageBase64` on internal
19616
- * hops (hub → forked worker via Moleculer MsgPack) because it
19617
- * skips the 33% base64 overhead + the per-call base64 decode on
19618
- * the detection-pipeline worker. Callers can pass either; exactly
19619
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
19620
- */
19621
- image: _instanceof(Uint8Array).optional(),
19622
- referenceImage: string().optional(),
19623
- deviceId: number$1().optional(),
19624
- sessionId: string().optional(),
19625
- /**
19626
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
19627
- * reference-image, and detail-subtree calls. 'frame' is the live
19628
- * per-frame dispatch: ONLY root-plane steps run; crop children
19629
- * (inputClasses ≠ null) are skipped and served per-track via
19630
- * pipelineRunner.runDetailSubtree (two-plane design).
19631
- */
19632
- plane: _enum(["full", "frame"]).optional(),
19633
- /**
19634
- * Inference-device selector (Phase 2 multi-device). Format
19635
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
19636
- * Omitted ⇒ the runner's default device (current single-engine
19637
- * behaviour). Selects WHICH device pool of the node runs the call.
19638
- */
19639
- deviceKey: string().optional(),
19640
- /**
19641
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
19642
- * when the parent crop was resolved from the frame's retained NATIVE
19643
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
19644
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
19645
- * resolution from that surface — the SAME quality path faces already
19646
- * had — instead of the downscaled parent tile. `handle` keys the native
19647
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
19648
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
19649
- * the executor's crop-normalized child ROI back into frame-normalized
19650
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
19651
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
19652
- * (today's behaviour on the fallback path).
19653
- */
19654
- nativeCropRef: NativeCropRefSchema.optional()
19655
- }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
19656
- engine: PipelineEngineChoiceSchema.optional(),
19657
- steps: array(PipelineStepInputSchema).min(1),
19658
- frames: array(FrameInputSchema).min(1).max(255),
19659
- deviceId: number$1().optional(),
19660
- sessionId: string().optional(),
19661
- /**
19662
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
19663
- * the batch to the Python pool's bench preprocess cache
19664
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
19665
- * preprocessed ONCE and every later inference is a pure-inference cache
19666
- * hit — the sustained-throughput run measures inference, not
19667
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
19668
- * full preprocess every call, correct). Fresh per sustained run;
19669
- * released via `uncacheFrame`.
19670
- */
19671
- frameId: number$1().int().nonnegative().optional(),
19672
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
19673
- deviceKey: string().optional()
19674
- }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
19675
- data: _instanceof(Uint8Array),
19676
- width: number$1().int().positive(),
19677
- height: number$1().int().positive(),
19678
- format: _enum([
19679
- "rgb",
19680
- "bgr",
19681
- "gray"
19682
- ])
19683
- }), object({
19684
- frameId: number$1(),
19685
- width: number$1(),
19686
- height: number$1()
19687
- }), { kind: "mutation" }), method(object({
19688
- stepId: string(),
19689
- frameId: number$1().int()
19690
- }), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number$1().int() }), _void(), { kind: "mutation" }), method(_void(), object({
19691
- batchMode: string(),
19692
- windowMs: number$1(),
19693
- maxBatchSize: number$1(),
19694
- concurrency: number$1()
19695
- })), method(_void(), array(object({
19696
- engineKey: string(),
19697
- engine: PipelineEngineChoiceSchema,
19698
- modelsLoaded: array(string()).readonly(),
19699
- inUseByCameras: array(number$1()).readonly(),
19700
- /**
19701
- * Origin of this resident factory.
19702
- * - `runtime` — main camera-serving engine (no idle TTL).
19703
- * - `warm-override` — benchmark/test override held in the warm
19704
- * cache; auto-disposed after the idle TTL.
19705
- * - `device-pool` — a concurrent per-device pool (Phase 2
19706
- * multi-device, keyed by `deviceKey`) resolved
19707
- * via `resolveDeviceFactory`. Runs alongside the
19708
- * `runtime` engine on a DIFFERENT accelerator
19709
- * (NPU / iGPU / Coral) — this is how the
19710
- * Engines tab shows all pools running at once.
19711
- */
19712
- kind: _enum([
19713
- "runtime",
19714
- "warm-override",
19715
- "device-pool"
19716
- ]),
19717
- /** Native pid of the underlying Python pool (null when no pool). */
19718
- poolPid: number$1().nullable(),
19719
- /** ms since this factory was last used (null when not warm-tracked). */
19720
- idleMs: number$1().nullable(),
19721
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
19722
- idleTtlMs: number$1().nullable()
19723
- })).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
19724
- kind: "mutation",
19725
- auth: "admin"
19726
- }), method(object({
19727
- engine: PipelineEngineChoiceSchema,
19728
- force: boolean().optional()
19729
- }), object({
19730
- success: boolean(),
19731
- reason: string().optional()
19732
- }), {
19733
- kind: "mutation",
19734
- auth: "admin"
19735
- }), 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({
19736
- addonId: string(),
19737
- modelId: string(),
19738
- filename: string().optional(),
19739
- settings: record(string(), unknown()).optional()
19740
- }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
19741
- /**
19742
- * Per-stage gating mode applied to the zones a rule references.
19743
- *
19744
- * - `include`: the rule contributes to a **whitelist** for its stage.
19745
- * When at least one `include` rule fires for a stage, only entities
19746
- * inside one of those zones pass that stage.
19747
- * - `exclude`: the rule contributes to a **blacklist** for its stage.
19748
- * Entities inside one of those zones are dropped at that stage.
19749
- *
19750
- * `monitor`-style observation (count without filtering) is not a rule
19751
- * mode — zones without any matching rule are observed naturally by
19752
- * `zone-analytics` (live snapshot + history), so an "I just want to
19753
- * count, not filter" use case needs no rule at all.
19754
- */
19755
- var ZoneRuleModeEnum = _enum(["include", "exclude"]);
19756
- /**
19757
- * Per-consumer rule that references existing zones (geometry) and
19758
- * defines how a specific pipeline stage should treat them. Each
19759
- * consumer addon owns its own `ZoneRule[]` array in its per-device
19760
- * settings:
19761
- *
19762
- * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
19763
- * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
19764
- * - future: notification rules, audio gating, etc.
19765
- *
19766
- * One rule applies to N zones (`zoneIds[]`) so the operator can
19767
- * express "ignore motion in ALL of {garden, street}" with a single
19768
- * rule. `classFilter` narrows the rule to specific object classes —
19769
- * "drop person detections in the street, but keep cars" is one
19770
- * `exclude` rule with `classFilter: ['person']`.
19771
- *
19772
- * `enabled` is a soft toggle — the operator can keep the rule
19773
- * configured but inert without deleting it.
19774
- */
19775
- var ZoneRuleSchema = object({
19776
- /** Stable rule id — survives edits, used by the UI for diffing. */
19777
- id: string(),
19778
- /** Optional human-readable label rendered in the rule editor. */
19779
- name: string().optional(),
19780
- /** Zones this rule targets. The rule's `mode` applies to ALL
19781
- * listed zones (OR-set: a detection in any one of them counts).
19782
- * At least one zone id required — a rule with no targets is a
19783
- * configuration mistake and the form validator rejects it. */
19784
- zoneIds: array(string()).min(1).readonly(),
19785
- mode: ZoneRuleModeEnum,
19786
- /**
19787
- * Class names this rule applies to. Empty / undefined ⇒ rule
19788
- * applies to every class. Class strings match the `macroClass`
19789
- * field on detections (e.g. `person`, `car`, `dog`).
19790
- */
19791
- classFilter: array(string()).readonly().optional(),
19792
- /**
19793
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
19794
- * required to consider an entity "in the zone". Defaults to the
19795
- * consumer's stage default when omitted. Kept for back-compat with
19796
- * existing per-rule overrides; new operators pick the value via
19797
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
19798
- * set, the lower-level engine reads it as a 0–1 fraction.
19799
- */
19800
- overlapThreshold: number$1().min(0).max(1).optional(),
19801
- /**
19802
- * Operator-friendly version of `overlapThreshold` — the percentage
19803
- * of the detection's bbox that must lie inside the zone for the
19804
- * rule to match. Documented default is 85%; the engine substitutes
19805
- * that when the field is omitted (kept optional so existing rules
19806
- * stored without it stay valid).
19807
- *
19808
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
19809
- * rule, the engine prefers `bboxInclusionPct` because it's the
19810
- * field exposed in the UI. Internally both feed the same gate.
19811
- */
19812
- bboxInclusionPct: number$1().min(0).max(100).optional(),
19813
- /**
19814
- * When `true` and a detection has a segmentation mask, use the
19815
- * mask for overlap instead of the bbox. Detection-stage only;
19816
- * motion rules ignore this field.
19817
- */
19818
- preferMask: boolean().optional(),
19819
- /**
19820
- * Soft-toggle: `false` disables the rule without deleting it.
19821
- * Defaults to `true` so operators creating a rule via the UI
19822
- * see it active immediately.
19823
- */
19824
- enabled: boolean().default(true)
19825
- });
19826
- array(ZoneRuleSchema).readonly();
19827
- /**
19828
- * Zone — pure geometry + identity. NO filtering behaviour.
19829
- *
19830
- * Zones describe **where** in the frame the operator wants to flag
19831
- * something; consumer-owned {@link ZoneRule} arrays describe **how**
19832
- * each pipeline stage uses them. Splitting the two means a single
19833
- * polygon "Driveway" can simultaneously back a motion-exclude rule,
19834
- * a detection-include rule on `['car']`, and an occupancy aggregate
19835
- * — without three duplicated polygons.
19836
- *
19837
- * Owned by the orchestrator addon (provider) and mirrored into the
19838
- * `zones` device-state slice on every mutation. Consumers
19839
- * (motion-wasm, pipeline-executor, analytics, admin UI) read either
19840
- * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
19841
- * mirror with `onChanged`).
19842
- *
19843
- * Coordinates are normalised fractions of the frame (0–1) so zones
19844
- * survive resolution changes and stream profile switches.
19845
- *
19846
- * `kind` discriminates between full polygons (closed regions used
19847
- * for intrusion / occupancy filters) and tripwires (open 2-point
19848
- * line segments used for cross events). Onboard / firmware-reported
19849
- * zones (Reolink, ONVIF) are out of scope for now — see the deferred
19850
- * task list.
19851
- */
19852
- var ZoneKindEnum = _enum(["polygon", "tripwire"]);
19853
- /** Polygon vertex in fraction-of-frame coordinates (0–1). */
19854
- var PolygonPointSchema = object({
19855
- x: number$1(),
19856
- y: number$1()
19857
- });
19858
- /** A camera detection zone — pure geometry/identity. */
19859
- var ZoneSchema = object({
19860
- id: string(),
19861
- name: string(),
19862
- kind: ZoneKindEnum.default("polygon"),
19863
- /** Polygon vertices, fraction of frame (0–1). */
19864
- polygon: array(PolygonPointSchema).readonly(),
19865
- /** Visual color for UI rendering. */
19866
- color: string().default("#3b82f6")
19867
- });
19868
- DeviceType.Camera, method(object({ deviceId: number$1() }), array(ZoneSchema).readonly()), method(object({
19869
- deviceId: number$1(),
19870
- zone: ZoneSchema
19871
- }), _void(), {
19872
- kind: "mutation",
19873
- auth: "admin"
19874
- }), method(object({
19875
- deviceId: number$1(),
19876
- zoneId: string()
19877
- }), _void(), {
19878
- kind: "mutation",
19879
- auth: "admin"
19880
- }), method(object({
19881
- deviceId: number$1(),
19882
- zone: ZoneSchema
19883
- }), _void(), {
19884
- kind: "mutation",
19885
- auth: "admin"
19886
- }), object({ zones: array(ZoneSchema).readonly() });
19887
- /**
19888
19487
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
19889
19488
  * per-frame detections emitted by the pipeline runner into tracked
19890
19489
  * objects, per-kind event collections (motion / object / audio), and
@@ -20455,6 +20054,22 @@ var TrackSchema = object({
20455
20054
  * omit it. Render nothing when absent.
20456
20055
  */
20457
20056
  previewMissReason: string().optional(),
20057
+ /**
20058
+ * The ROOT model that produced this track — the `FrameResult.rootModelId` of
20059
+ * the frame it was BORN on (D646). What a later replay compares against, so
20060
+ * "the original model" is named rather than assumed. A birth fact: never
20061
+ * rewritten by a later frame. **Absent ≠ unknown-model**: a row from before
20062
+ * the column, and a track the camera's own detector produced, carry none.
20063
+ */
20064
+ modelId: string().optional(),
20065
+ /**
20066
+ * Size (px) of the detection frame this track's `positions[].bbox` live in —
20067
+ * the frame it was born on, else the camera's latest analysis frame at
20068
+ * persist time. Divide a position by these to normalise it. Absent when the
20069
+ * size was never known.
20070
+ */
20071
+ frameWidth: number$1().optional(),
20072
+ frameHeight: number$1().optional(),
20458
20073
  ...TrackFlagFields,
20459
20074
  ...TrackRetrainFields
20460
20075
  });
@@ -21461,24 +21076,6 @@ var MediaReclaimStatusSchema = object({
21461
21076
  finishedAtMs: number$1().nullable(),
21462
21077
  error: string().nullable()
21463
21078
  });
21464
- var ReplayFrameInputSchema = object({
21465
- timestamp: number$1(),
21466
- frame: PipelineRunResultBridge
21467
- });
21468
- var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
21469
- className: string(),
21470
- firstSeenMs: number$1(),
21471
- lastSeenMs: number$1(),
21472
- /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
21473
- * frame — a representative box for the diff's `(className, window, IoU)`
21474
- * pairing (`replay-diff.ts`). A replay does not need the full per-frame
21475
- * trajectory production's `Track.positions` keeps. */
21476
- bbox: BoundingBoxSchema,
21477
- /** How many of the input frames this track matched a real detection on
21478
- * (never a coasted/extrapolated frame) — the replay's own signal for "how
21479
- * solid is this track", cheaper than re-deriving it from a trajectory. */
21480
- framesMatched: number$1().int()
21481
- })).readonly() });
21482
21079
  /**
21483
21080
  * The long-term series a camera produces. A closed union so a typo cannot
21484
21081
  * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
@@ -21692,108 +21289,570 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
21692
21289
  }), {
21693
21290
  kind: "mutation",
21694
21291
  auth: "admin"
21695
- }), method(object({ frameId: string() }), object({
21696
- base64: string(),
21697
- width: number$1().int(),
21698
- height: number$1().int()
21292
+ }), method(object({ frameId: string() }), object({
21293
+ base64: string(),
21294
+ width: number$1().int(),
21295
+ height: number$1().int()
21296
+ }), {
21297
+ kind: "query",
21298
+ auth: "admin"
21299
+ }), method(object({
21300
+ deviceId: number$1(),
21301
+ trackId: string(),
21302
+ frameId: string(),
21303
+ subject: RetrainAssistSubjectSchema,
21304
+ /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
21305
+ nodeId: string().optional()
21306
+ }), RetrainAssistResultSchema, {
21307
+ kind: "mutation",
21308
+ auth: "admin"
21309
+ }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
21310
+ kind: "query",
21311
+ auth: "admin"
21312
+ }), method(object({
21313
+ deviceId: number$1(),
21314
+ trackId: string(),
21315
+ frameId: string(),
21316
+ annotations: array(RetrainAnnotationDraftSchema)
21317
+ }), array(RetrainAnnotationSchema).readonly(), {
21318
+ kind: "mutation",
21319
+ auth: "admin"
21320
+ }), method(object({
21321
+ deviceId: number$1(),
21322
+ trackId: string()
21323
+ }), RetrainTransitionResultSchema, {
21324
+ kind: "mutation",
21325
+ auth: "admin"
21326
+ }), method(object({
21327
+ deviceId: number$1(),
21328
+ trackId: string()
21329
+ }), RetrainTransitionResultSchema, {
21330
+ kind: "mutation",
21331
+ auth: "admin"
21332
+ }), method(object({ deviceIds: array(number$1()).optional() }), object({ url: string() }), {
21333
+ kind: "query",
21334
+ auth: "admin"
21335
+ }), method(object({
21336
+ /** Which of the three event tables owns the row. */
21337
+ ownerType: EventOwnerTypeSchema,
21338
+ /** The event's rowid in that table (D474: an event id is a number). */
21339
+ eventId: number$1().int(),
21340
+ kind: MediaFileKindEnum.optional(),
21341
+ deviceId: number$1()
21342
+ }), array(MediaFileSchema).readonly()), method(object({
21343
+ trackId: string(),
21344
+ kinds: array(MediaFileKindEnum).optional(),
21345
+ deviceId: number$1()
21346
+ }), array(MediaFileSchema).readonly()), method(object({
21347
+ trackId: string(),
21348
+ deviceId: number$1()
21349
+ }), array(MediaFileInfoSchema).readonly()), method(object({
21350
+ ownerType: EventOwnerTypeSchema,
21351
+ eventId: number$1().int(),
21352
+ deviceId: number$1()
21353
+ }), array(MediaFileInfoSchema).readonly()), method(object({
21354
+ deviceId: number$1(),
21355
+ ownerKind: MediaPresenceOwnerKindSchema,
21356
+ ownerIds: array(string()).max(5e3)
21357
+ }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21358
+ kind: "mutation",
21359
+ auth: "admin"
21360
+ }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
21361
+ kind: "mutation",
21362
+ auth: "admin"
21363
+ }), method(object({}), RebuildStatusSchema), object({
21364
+ deviceId: number$1(),
21365
+ timestamp: number$1(),
21366
+ frameWidth: number$1(),
21367
+ frameHeight: number$1(),
21368
+ detections: array(OverlayDetectionSchema).readonly()
21369
+ }), object({
21370
+ deviceId: number$1(),
21371
+ trackId: string(),
21372
+ className: string()
21373
+ }), object({
21374
+ deviceId: number$1(),
21375
+ trackId: string(),
21376
+ className: string(),
21377
+ durationMs: number$1()
21378
+ }), object({
21379
+ deviceId: number$1(),
21380
+ kind: EventKindSchema,
21381
+ eventId: string(),
21382
+ timestamp: number$1()
21383
+ });
21384
+ /**
21385
+ * Reference to the frame's retained NATIVE surface + the parent crop's placement
21386
+ * within the frame, so the executor can re-cut a leaf child ROI at native
21387
+ * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
21388
+ */
21389
+ var NativeCropRefSchema = object({
21390
+ /** Handle keying the retained native surface (node-pinned to its owner). */
21391
+ handle: FrameHandleSchema,
21392
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
21393
+ cropFrameSpace: object({
21394
+ x: number$1(),
21395
+ y: number$1(),
21396
+ w: number$1(),
21397
+ h: number$1()
21398
+ })
21399
+ });
21400
+ object({
21401
+ crop: object({
21402
+ left: number$1(),
21403
+ top: number$1(),
21404
+ width: number$1().positive(),
21405
+ height: number$1().positive()
21406
+ }).optional(),
21407
+ content: object({
21408
+ width: number$1().int().positive(),
21409
+ height: number$1().int().positive()
21410
+ }),
21411
+ fit: _enum(["stretch", "contain"]),
21412
+ format: _enum([
21413
+ "rgb",
21414
+ "gray",
21415
+ "jpeg"
21416
+ ])
21417
+ });
21418
+ /**
21419
+ * Process-local frame identity. It is serializable so it can ride an in-process
21420
+ * capability call, but `registryId` deliberately prevents resolution in any
21421
+ * other process or execution group.
21422
+ */
21423
+ var FrameRefSchema = object({
21424
+ registryId: string().min(1),
21425
+ id: string().min(1),
21426
+ width: number$1().int().positive(),
21427
+ height: number$1().int().positive(),
21428
+ format: _enum(["rgb", "gray"]),
21429
+ timestamp: number$1(),
21430
+ capturedAt: number$1().optional()
21431
+ });
21432
+ var ModelFormatSchema$1 = _enum([
21433
+ "onnx",
21434
+ "coreml",
21435
+ "openvino",
21436
+ "tflite",
21437
+ "pt",
21438
+ "gguf"
21439
+ ]);
21440
+ var PipelineSlotSchema = _enum([
21441
+ "detector",
21442
+ "cropper",
21443
+ "classifier",
21444
+ "refiner",
21445
+ "audio-classifier"
21446
+ ]);
21447
+ var PipelineEngineChoiceSchema = object({
21448
+ runtime: literal("python"),
21449
+ backend: string(),
21450
+ format: ModelFormatSchema$1,
21451
+ device: string().optional()
21452
+ });
21453
+ /**
21454
+ * One pool's resolved provisioning (`getEffectiveTuning`). `numWorkers` is the
21455
+ * knob that lifts a saturated pool (`inference-devices.md`); `concurrency` is
21456
+ * per-worker in-flight requests. `deviceKey` echoes the pool asked about —
21457
+ * `null` for the node-default pool.
21458
+ */
21459
+ var EffectiveTuningSchema = object({
21460
+ deviceKey: string().nullable(),
21461
+ runtime: string(),
21462
+ batchMode: string(),
21463
+ windowMs: number$1(),
21464
+ maxBatchSize: number$1(),
21465
+ concurrency: number$1(),
21466
+ numWorkers: number$1()
21467
+ });
21468
+ var AvailableEngineSchema = object({
21469
+ engine: PipelineEngineChoiceSchema,
21470
+ devices: array(object({
21471
+ id: string(),
21472
+ label: string(),
21473
+ description: string().optional()
21474
+ })).readonly(),
21475
+ defaultDevice: string()
21476
+ });
21477
+ var PipelineDefaultStepSchema = lazy(() => object({
21478
+ addonId: string(),
21479
+ addonName: string(),
21480
+ slot: PipelineSlotSchema,
21481
+ inputClasses: array(string()).readonly(),
21482
+ outputClasses: array(string()).readonly(),
21483
+ enabled: boolean(),
21484
+ modelId: string(),
21485
+ children: array(PipelineDefaultStepSchema).readonly(),
21486
+ group: string().optional(),
21487
+ settings: record(string(), unknown()).optional()
21488
+ }));
21489
+ var PipelineTemplateStepSchema = lazy(() => object({
21490
+ addonId: string(),
21491
+ enabled: boolean(),
21492
+ modelId: string(),
21493
+ children: array(PipelineTemplateStepSchema).readonly(),
21494
+ settings: record(string(), unknown()).optional()
21495
+ }));
21496
+ var PipelineTemplateSchema$1 = object({
21497
+ id: string(),
21498
+ name: string(),
21499
+ createdAt: string(),
21500
+ updatedAt: string(),
21501
+ engine: PipelineEngineChoiceSchema,
21502
+ steps: array(PipelineTemplateStepSchema).readonly()
21503
+ });
21504
+ var PipelineModelOptionSchema = object({
21505
+ id: string(),
21506
+ name: string(),
21507
+ formats: record(string(), object({
21508
+ downloaded: boolean(),
21509
+ sizeMB: number$1()
21510
+ })),
21511
+ group: ModelVariantGroupSchema.optional(),
21512
+ legacy: boolean().optional(),
21513
+ provider: ModelProviderIdSchema.optional()
21514
+ });
21515
+ var ConfigFieldBridge = custom();
21516
+ var PipelineAddonSchemaSchema = object({
21517
+ id: string(),
21518
+ name: string(),
21519
+ slot: PipelineSlotSchema,
21520
+ inputClasses: array(string()).readonly(),
21521
+ outputClasses: array(string()).readonly(),
21522
+ childSlots: array(PipelineSlotSchema).readonly(),
21523
+ models: array(PipelineModelOptionSchema).readonly(),
21524
+ defaultModelId: string(),
21525
+ defaultModelIdByFormat: record(string(), string()).optional(),
21526
+ enabledByDefault: boolean().optional(),
21527
+ backfillIntoExistingOverrides: boolean().optional(),
21528
+ defaultConfidence: number$1(),
21529
+ group: string().optional(),
21530
+ configSchema: array(ConfigFieldBridge).readonly().optional()
21531
+ });
21532
+ var PipelineSlotSchemaSchema = object({
21533
+ id: PipelineSlotSchema,
21534
+ label: string(),
21535
+ priority: number$1(),
21536
+ parentSlot: PipelineSlotSchema.nullable(),
21537
+ addons: array(PipelineAddonSchemaSchema).readonly()
21538
+ });
21539
+ var PipelineSchemaSchema = object({
21540
+ availableEngines: array(AvailableEngineSchema).readonly(),
21541
+ selectedEngine: PipelineEngineChoiceSchema,
21542
+ slots: array(PipelineSlotSchemaSchema).readonly()
21543
+ });
21544
+ var EngineProvisioningSchema = object({
21545
+ runtimeId: _enum([
21546
+ "onnx",
21547
+ "openvino",
21548
+ "coreml",
21549
+ "edgetpu"
21550
+ ]).nullable(),
21551
+ device: string().nullable(),
21552
+ state: _enum([
21553
+ "idle",
21554
+ "installing",
21555
+ "verifying",
21556
+ "ready",
21557
+ "failed"
21558
+ ]),
21559
+ progress: number$1().optional(),
21560
+ error: string().optional(),
21561
+ nextRetryAt: number$1().optional(),
21562
+ /**
21563
+ * Gate A (config-correctness gate at engine change): human-readable
21564
+ * config issues surfaced EAGERLY when the node's engine changes — model
21565
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
21566
+ * has a <format> build"). Additive/optional: informational only, never
21567
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
21568
+ * Absent/empty when the node-default tree resolves cleanly.
21569
+ */
21570
+ configIssues: array(string()).optional()
21571
+ });
21572
+ var PipelineStepInputSchema = lazy(() => object({
21573
+ addonId: string(),
21574
+ modelId: string().optional(),
21575
+ enabled: boolean().default(true),
21576
+ children: array(PipelineStepInputSchema).optional(),
21577
+ settings: record(string(), unknown()).optional(),
21578
+ jumpDeviceKey: string().optional()
21579
+ }));
21580
+ var ModelSubstitutionSchema = object({
21581
+ addonId: string(),
21582
+ chosen: string(),
21583
+ running: string(),
21584
+ format: string()
21585
+ });
21586
+ var PipelineValidationIssueSchema = object({
21587
+ addonId: string(),
21588
+ kind: _enum([
21589
+ "unknown-addon",
21590
+ "no-format-build",
21591
+ "unknown-device"
21592
+ ]),
21593
+ detail: string()
21594
+ });
21595
+ var PipelineValidationResultSchema = object({
21596
+ ok: boolean(),
21597
+ issues: array(PipelineValidationIssueSchema).readonly(),
21598
+ substitutions: array(ModelSubstitutionSchema).readonly(),
21599
+ /** The node's `currentEngine.format` this validation ran against. */
21600
+ format: string()
21601
+ });
21602
+ var ReferenceImageEntrySchema = object({
21603
+ filename: string(),
21604
+ stepIds: array(string()).readonly().optional()
21605
+ });
21606
+ var ReferenceImageBodySchema = object({
21607
+ base64: string(),
21608
+ filename: string()
21609
+ });
21610
+ var ReferenceAudioEntrySchema = object({
21611
+ filename: string(),
21612
+ sizeKb: number$1()
21613
+ });
21614
+ var ReferenceAudioBodySchema = object({ base64: string() });
21615
+ var AudioBackendSchema = object({
21616
+ id: string(),
21617
+ name: string(),
21618
+ description: string(),
21619
+ available: boolean(),
21620
+ /**
21621
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
21622
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
21623
+ * the benchmark UI to populate the `enabledMicroClasses` filter
21624
+ * specific to the selected backend without a separate fetch.
21625
+ */
21626
+ rawLabels: array(string()).readonly().optional()
21627
+ });
21628
+ var AudioCapabilitiesSchema = object({
21629
+ activeBackend: string(),
21630
+ availableBackends: array(AudioBackendSchema).readonly(),
21631
+ sampleRate: number$1(),
21632
+ chunkDurationMs: number$1()
21633
+ });
21634
+ var DownloadModelResultSchema = object({
21635
+ filePath: string(),
21636
+ sizeMB: number$1(),
21637
+ durationMs: number$1()
21638
+ });
21639
+ /**
21640
+ * Wrapper carrying a single test run's result. Replaces the legacy
21641
+ * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
21642
+ * canonical `AudioResult` from the Phase 6 output rework: one
21643
+ * `AudioDetection` per class above `minScore`, top-N candidates in
21644
+ * `debug.alternateLabels['audio-classifier']`, per-source timings in
21645
+ * `debug.stepTimings`. The outer `success`/`error` fields stay so the
21646
+ * benchmark UI can still report a clean failure when the classifier
21647
+ * cap isn't available.
21648
+ */
21649
+ var AudioTestResultSchema = object({
21650
+ success: boolean(),
21651
+ error: string().optional(),
21652
+ frame: custom().optional()
21653
+ });
21654
+ var PipelineConfigBridge = custom();
21655
+ var ConfigUISchemaBridge = custom();
21656
+ var ConfigUISchemaNullableBridge = custom();
21657
+ var InferenceCapabilitiesBridge = custom();
21658
+ var ModelAvailabilityListBridge = custom();
21659
+ var PipelineRunResultBridge = custom();
21660
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
21661
+ modelId: string(),
21662
+ settings: record(string(), unknown()).readonly()
21663
+ }))), method(object({ steps: record(string(), object({
21664
+ modelId: string(),
21665
+ settings: record(string(), unknown()).readonly()
21666
+ })) }), object({ success: literal(true) }), {
21667
+ kind: "mutation",
21668
+ auth: "admin"
21669
+ }), method(object({ nodeId: string() }), object({
21670
+ success: literal(true),
21671
+ clearedDevices: number$1()
21699
21672
  }), {
21700
- kind: "query",
21701
- auth: "admin"
21702
- }), method(object({
21703
- deviceId: number$1(),
21704
- trackId: string(),
21705
- frameId: string(),
21706
- subject: RetrainAssistSubjectSchema,
21707
- /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
21708
- nodeId: string().optional()
21709
- }), RetrainAssistResultSchema, {
21710
- kind: "mutation",
21711
- auth: "admin"
21712
- }), method(object({
21713
- deviceId: number$1(),
21714
- source: DetectionSourceSchema,
21715
- zones: array(ZoneSchema).readonly().optional(),
21716
- detectionRules: array(ZoneRuleSchema).readonly().optional(),
21717
- zoneMembershipMinOverlap: number$1().min(0).max(1).optional(),
21718
- frames: array(ReplayFrameInputSchema).min(1)
21719
- }), RunReplayFrameProcessorResultSchema, {
21720
- kind: "mutation",
21721
- auth: "admin"
21722
- }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
21723
- kind: "query",
21724
- auth: "admin"
21725
- }), method(object({
21726
- deviceId: number$1(),
21727
- trackId: string(),
21728
- frameId: string(),
21729
- annotations: array(RetrainAnnotationDraftSchema)
21730
- }), array(RetrainAnnotationSchema).readonly(), {
21731
21673
  kind: "mutation",
21732
21674
  auth: "admin"
21733
- }), method(object({
21734
- deviceId: number$1(),
21735
- trackId: string()
21736
- }), RetrainTransitionResultSchema, {
21675
+ }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
21676
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
21677
+ deviceKey: string(),
21678
+ /**
21679
+ * `failed` — the per-device restart budget is exhausted; no pool
21680
+ * will be spawned until an operator re-arms it or the runner
21681
+ * respawns. `backoff` — under budget, waiting out the backoff (or
21682
+ * a cached pool observed dead and not yet condemned).
21683
+ */
21684
+ state: _enum(["failed", "backoff"]),
21685
+ /** Epoch ms of the death that produced this state. */
21686
+ since: number$1(),
21687
+ /** Pool deaths inside the current window. */
21688
+ deaths: number$1(),
21689
+ /** The last death's message. */
21690
+ lastError: string()
21691
+ })).readonly() })), method(object({
21692
+ nodeId: string(),
21693
+ deviceKey: string()
21694
+ }), object({ rearmed: boolean() }), {
21737
21695
  kind: "mutation",
21738
21696
  auth: "admin"
21739
- }), method(object({
21740
- deviceId: number$1(),
21741
- trackId: string()
21742
- }), RetrainTransitionResultSchema, {
21697
+ }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
21698
+ steps: array(PipelineStepInputSchema),
21699
+ deviceKey: string().optional()
21700
+ }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
21701
+ name: string(),
21702
+ steps: array(PipelineTemplateStepSchema).readonly(),
21703
+ engine: PipelineEngineChoiceSchema
21704
+ }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
21705
+ id: string(),
21706
+ name: string().optional(),
21707
+ steps: array(PipelineTemplateStepSchema).readonly().optional()
21708
+ }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
21709
+ addonId: string(),
21710
+ modelId: string(),
21711
+ format: ModelFormatSchema$1
21712
+ }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
21713
+ addonId: string(),
21714
+ modelId: string(),
21715
+ format: ModelFormatSchema$1
21716
+ }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
21717
+ steps: array(PipelineStepInputSchema).min(1),
21718
+ frame: FrameInputSchema.optional(),
21719
+ /**
21720
+ * Process-local lazy frame. Valid only when caller and provider resolve
21721
+ * in the same execution-group process; split/cross-node callers use
21722
+ * `frame`/`image` inline compatibility instead.
21723
+ */
21724
+ frameRef: FrameRefSchema.optional(),
21725
+ /**
21726
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
21727
+ * the decoded pixels live in. One more member of the one-of
21728
+ * frame/frameHandle/image/imageBase64/referenceImage group.
21729
+ */
21730
+ frameHandle: FrameHandleSchema.optional(),
21731
+ imageBase64: string().optional(),
21732
+ /**
21733
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
21734
+ * hops (hub → forked worker via Moleculer MsgPack) because it
21735
+ * skips the 33% base64 overhead + the per-call base64 decode on
21736
+ * the detection-pipeline worker. Callers can pass either; exactly
21737
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
21738
+ */
21739
+ image: _instanceof(Uint8Array).optional(),
21740
+ referenceImage: string().optional(),
21741
+ deviceId: number$1().optional(),
21742
+ sessionId: string().optional(),
21743
+ /**
21744
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
21745
+ * reference-image, and detail-subtree calls. 'frame' is the live
21746
+ * per-frame dispatch: ONLY root-plane steps run; crop children
21747
+ * (inputClasses ≠ null) are skipped and served per-track via
21748
+ * pipelineRunner.runDetailSubtree (two-plane design).
21749
+ */
21750
+ plane: _enum(["full", "frame"]).optional(),
21751
+ /**
21752
+ * Inference-device selector (Phase 2 multi-device). Format
21753
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
21754
+ * Omitted ⇒ the runner's default device (current single-engine
21755
+ * behaviour). Selects WHICH device pool of the node runs the call.
21756
+ */
21757
+ deviceKey: string().optional(),
21758
+ /**
21759
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
21760
+ * when the parent crop was resolved from the frame's retained NATIVE
21761
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
21762
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
21763
+ * resolution from that surface — the SAME quality path faces already
21764
+ * had — instead of the downscaled parent tile. `handle` keys the native
21765
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
21766
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
21767
+ * the executor's crop-normalized child ROI back into frame-normalized
21768
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
21769
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
21770
+ * (today's behaviour on the fallback path).
21771
+ */
21772
+ nativeCropRef: NativeCropRefSchema.optional(),
21773
+ /**
21774
+ * Offline REPLAY of stored media for `deviceId` (D56, D646): the
21775
+ * camera's zone gate and overrides apply as live, every executing
21776
+ * step runs EXACTLY the model it names on the `deviceKey` pool it
21777
+ * names or the call refuses (`model-not-servable` /
21778
+ * `device-not-servable`), and nothing reaches the camera's live
21779
+ * trace stream. Absent ⇒ unchanged behaviour.
21780
+ */
21781
+ replay: literal(true).optional()
21782
+ }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
21783
+ steps: array(PipelineStepInputSchema).min(1),
21784
+ frames: array(FrameInputSchema).min(1).max(255),
21785
+ deviceId: number$1().optional(),
21786
+ sessionId: string().optional(),
21787
+ /**
21788
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
21789
+ * the batch to the Python pool's bench preprocess cache
21790
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
21791
+ * preprocessed ONCE and every later inference is a pure-inference cache
21792
+ * hit — the sustained-throughput run measures inference, not
21793
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
21794
+ * full preprocess every call, correct). Fresh per sustained run;
21795
+ * released via `uncacheFrame`.
21796
+ */
21797
+ frameId: number$1().int().nonnegative().optional(),
21798
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
21799
+ deviceKey: string().optional()
21800
+ }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
21801
+ data: _instanceof(Uint8Array),
21802
+ width: number$1().int().positive(),
21803
+ height: number$1().int().positive(),
21804
+ format: _enum([
21805
+ "rgb",
21806
+ "bgr",
21807
+ "gray"
21808
+ ])
21809
+ }), object({
21810
+ frameId: number$1(),
21811
+ width: number$1(),
21812
+ height: number$1()
21813
+ }), { kind: "mutation" }), method(object({
21814
+ stepId: string(),
21815
+ frameId: number$1().int()
21816
+ }), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number$1().int() }), _void(), { kind: "mutation" }), method(object({ deviceKey: string().optional() }), EffectiveTuningSchema), method(_void(), array(object({
21817
+ engineKey: string(),
21818
+ engine: PipelineEngineChoiceSchema,
21819
+ modelsLoaded: array(string()).readonly(),
21820
+ inUseByCameras: array(number$1()).readonly(),
21821
+ /**
21822
+ * Origin of this resident factory.
21823
+ * - `runtime` — main camera-serving engine (no idle TTL).
21824
+ * - `device-pool` — a concurrent per-device pool (Phase 2
21825
+ * multi-device, keyed by `deviceKey`) resolved
21826
+ * via `resolveDeviceFactory`. Runs alongside the
21827
+ * `runtime` engine on a DIFFERENT accelerator
21828
+ * (NPU / iGPU / Coral) — this is how the
21829
+ * Engines tab shows all pools running at once.
21830
+ */
21831
+ kind: _enum(["runtime", "device-pool"]),
21832
+ /** Native pid of the underlying Python pool (null when no pool). */
21833
+ poolPid: number$1().nullable(),
21834
+ /** ms since this factory was last used (null when not warm-tracked). */
21835
+ idleMs: number$1().nullable(),
21836
+ /** Idle TTL after which the factory self-evicts (null when not applicable). */
21837
+ idleTtlMs: number$1().nullable()
21838
+ })).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
21743
21839
  kind: "mutation",
21744
21840
  auth: "admin"
21745
- }), method(object({ deviceIds: array(number$1()).optional() }), object({ url: string() }), {
21746
- kind: "query",
21747
- auth: "admin"
21748
21841
  }), method(object({
21749
- /** Which of the three event tables owns the row. */
21750
- ownerType: EventOwnerTypeSchema,
21751
- /** The event's rowid in that table (D474: an event id is a number). */
21752
- eventId: number$1().int(),
21753
- kind: MediaFileKindEnum.optional(),
21754
- deviceId: number$1()
21755
- }), array(MediaFileSchema).readonly()), method(object({
21756
- trackId: string(),
21757
- kinds: array(MediaFileKindEnum).optional(),
21758
- deviceId: number$1()
21759
- }), array(MediaFileSchema).readonly()), method(object({
21760
- trackId: string(),
21761
- deviceId: number$1()
21762
- }), array(MediaFileInfoSchema).readonly()), method(object({
21763
- ownerType: EventOwnerTypeSchema,
21764
- eventId: number$1().int(),
21765
- deviceId: number$1()
21766
- }), array(MediaFileInfoSchema).readonly()), method(object({
21767
- deviceId: number$1(),
21768
- ownerKind: MediaPresenceOwnerKindSchema,
21769
- ownerIds: array(string()).max(5e3)
21770
- }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21771
- kind: "mutation",
21772
- auth: "admin"
21773
- }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
21842
+ engine: PipelineEngineChoiceSchema,
21843
+ force: boolean().optional()
21844
+ }), object({
21845
+ success: boolean(),
21846
+ reason: string().optional()
21847
+ }), {
21774
21848
  kind: "mutation",
21775
21849
  auth: "admin"
21776
- }), method(object({}), RebuildStatusSchema), object({
21777
- deviceId: number$1(),
21778
- timestamp: number$1(),
21779
- frameWidth: number$1(),
21780
- frameHeight: number$1(),
21781
- detections: array(OverlayDetectionSchema).readonly()
21782
- }), object({
21783
- deviceId: number$1(),
21784
- trackId: string(),
21785
- className: string()
21786
- }), object({
21787
- deviceId: number$1(),
21788
- trackId: string(),
21789
- className: string(),
21790
- durationMs: number$1()
21791
- }), object({
21792
- deviceId: number$1(),
21793
- kind: EventKindSchema,
21794
- eventId: string(),
21795
- timestamp: number$1()
21796
- });
21850
+ }), 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({
21851
+ addonId: string(),
21852
+ modelId: string(),
21853
+ filename: string().optional(),
21854
+ settings: record(string(), unknown()).optional()
21855
+ }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
21797
21856
  object({
21798
21857
  activeCameras: number$1(),
21799
21858
  throttledCameras: number$1(),
@@ -21819,6 +21878,66 @@ var CameraMetricsSchema = object({
21819
21878
  });
21820
21879
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: number$1() });
21821
21880
  /**
21881
+ * Zone — pure geometry + identity. NO filtering behaviour.
21882
+ *
21883
+ * Zones describe **where** in the frame the operator wants to flag
21884
+ * something; consumer-owned {@link ZoneRule} arrays describe **how**
21885
+ * each pipeline stage uses them. Splitting the two means a single
21886
+ * polygon "Driveway" can simultaneously back a motion-exclude rule,
21887
+ * a detection-include rule on `['car']`, and an occupancy aggregate
21888
+ * — without three duplicated polygons.
21889
+ *
21890
+ * Owned by the orchestrator addon (provider) and mirrored into the
21891
+ * `zones` device-state slice on every mutation. Consumers
21892
+ * (motion-wasm, pipeline-executor, analytics, admin UI) read either
21893
+ * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
21894
+ * mirror with `onChanged`).
21895
+ *
21896
+ * Coordinates are normalised fractions of the frame (0–1) so zones
21897
+ * survive resolution changes and stream profile switches.
21898
+ *
21899
+ * `kind` discriminates between full polygons (closed regions used
21900
+ * for intrusion / occupancy filters) and tripwires (open 2-point
21901
+ * line segments used for cross events). Onboard / firmware-reported
21902
+ * zones (Reolink, ONVIF) are out of scope for now — see the deferred
21903
+ * task list.
21904
+ */
21905
+ var ZoneKindEnum = _enum(["polygon", "tripwire"]);
21906
+ /** Polygon vertex in fraction-of-frame coordinates (0–1). */
21907
+ var PolygonPointSchema = object({
21908
+ x: number$1(),
21909
+ y: number$1()
21910
+ });
21911
+ /** A camera detection zone — pure geometry/identity. */
21912
+ var ZoneSchema = object({
21913
+ id: string(),
21914
+ name: string(),
21915
+ kind: ZoneKindEnum.default("polygon"),
21916
+ /** Polygon vertices, fraction of frame (0–1). */
21917
+ polygon: array(PolygonPointSchema).readonly(),
21918
+ /** Visual color for UI rendering. */
21919
+ color: string().default("#3b82f6")
21920
+ });
21921
+ DeviceType.Camera, method(object({ deviceId: number$1() }), array(ZoneSchema).readonly()), method(object({
21922
+ deviceId: number$1(),
21923
+ zone: ZoneSchema
21924
+ }), _void(), {
21925
+ kind: "mutation",
21926
+ auth: "admin"
21927
+ }), method(object({
21928
+ deviceId: number$1(),
21929
+ zoneId: string()
21930
+ }), _void(), {
21931
+ kind: "mutation",
21932
+ auth: "admin"
21933
+ }), method(object({
21934
+ deviceId: number$1(),
21935
+ zone: ZoneSchema
21936
+ }), _void(), {
21937
+ kind: "mutation",
21938
+ auth: "admin"
21939
+ }), object({ zones: array(ZoneSchema).readonly() });
21940
+ /**
21822
21941
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
21823
21942
  * decode worker resolves it against the RETAINED native frame's real pixel dims,
21824
21943
  * so the caller supplies only the detection-res bbox divided by the detection
@@ -22005,7 +22124,17 @@ var DetailResultSchema = object({
22005
22124
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
22006
22125
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
22007
22126
  * consumers MUST prefer this when present (2026-07-22 native-gate fix). */
22008
- nativeFaceShortSidePx: number$1().optional()
22127
+ nativeFaceShortSidePx: number$1().optional(),
22128
+ /**
22129
+ * The five facial landmarks of a `face` detail, in the SAME frame space as
22130
+ * `bbox` (D644). Additive and optional: post-analysis stores them beside the
22131
+ * face's source frame so a future alignment can be re-derived exactly; a
22132
+ * runner that predates the field sends none and the face is stored without.
22133
+ */
22134
+ landmarks: array(object({
22135
+ x: number$1(),
22136
+ y: number$1()
22137
+ })).optional()
22009
22138
  });
22010
22139
  /**
22011
22140
  * Why an executing node REFUSED a stateless step run (`runStatelessStep`).
@@ -23391,7 +23520,11 @@ function buildDescriptor(capName, kind) {
23391
23520
  icon: legacyIcon(t.iconId)
23392
23521
  };
23393
23522
  }
23394
- Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
23523
+ /**
23524
+ * Sensor / control cap name → static event-kind descriptor. A linked device
23525
+ * contributes one entry per bound cap present in this map.
23526
+ */
23527
+ var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
23395
23528
  /**
23396
23529
  * server-management — per-NODE singleton capability for a node's ROOT
23397
23530
  * package lifecycle (runtime-updatable node packages).
@@ -29534,7 +29667,30 @@ var RenameNodeResultSchema = object({
29534
29667
  nodeId: string(),
29535
29668
  name: string()
29536
29669
  });
29537
- method(_void(), array(TopologyNodeSchema).readonly(), { auth: "admin" }), method(NodeAddonInputSchema, SuccessSchema, {
29670
+ /**
29671
+ * The cheap node list. A consumer's rule for a STORED node id (a rule's
29672
+ * `nodeIds`) that is missing from `nodes`, whatever `complete` says: render it
29673
+ * as a removable chip labelled unknown, never drop it (D49). Even a complete
29674
+ * list omits a node that joined and left between two history captures, so a
29675
+ * missing id means "not known to the hub", never "removed". Only the operator
29676
+ * removes it.
29677
+ *
29678
+ * `complete` changes only the WORDING: `false` = no history read has ever
29679
+ * answered, so the list may be missing offline nodes the hub does know (D315).
29680
+ *
29681
+ * `name` is the history row's name (≤ 30 s behind a rename), or the id when
29682
+ * the history has none.
29683
+ */
29684
+ var NodeDirectorySchema = object({
29685
+ nodes: array(object({
29686
+ id: string(),
29687
+ name: string(),
29688
+ isHub: boolean(),
29689
+ isOnline: boolean()
29690
+ })).readonly(),
29691
+ complete: boolean()
29692
+ });
29693
+ method(_void(), array(TopologyNodeSchema).readonly(), { auth: "admin" }), method(_void(), NodeDirectorySchema, { auth: "admin" }), method(NodeAddonInputSchema, SuccessSchema, {
29538
29694
  kind: "mutation",
29539
29695
  auth: "admin"
29540
29696
  }), method(NodeAddonInputSchema, SuccessSchema, {
@@ -30303,7 +30459,7 @@ var HardwareInfoSchema = object({
30303
30459
  coral: CoralInfoSchema.nullable().optional()
30304
30460
  });
30305
30461
  var PlatformScoreSchema = object({
30306
- runtime: _enum(["node", "python"]),
30462
+ runtime: literal("python"),
30307
30463
  backend: string(),
30308
30464
  format: _enum([
30309
30465
  "onnx",
@@ -30340,7 +30496,7 @@ var ModelRequirementSchema = object({
30340
30496
  });
30341
30497
  var ResolvedInferenceConfigSchema = object({
30342
30498
  modelId: string(),
30343
- runtime: _enum(["node", "python"]),
30499
+ runtime: literal("python"),
30344
30500
  backend: string(),
30345
30501
  format: ModelFormatSchema,
30346
30502
  reason: string()
@@ -32547,6 +32703,92 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), SceneMonitorStatusSc
32547
32703
  kind: "mutation",
32548
32704
  auth: "admin"
32549
32705
  });
32706
+ /**
32707
+ * Per-stage gating mode applied to the zones a rule references.
32708
+ *
32709
+ * - `include`: the rule contributes to a **whitelist** for its stage.
32710
+ * When at least one `include` rule fires for a stage, only entities
32711
+ * inside one of those zones pass that stage.
32712
+ * - `exclude`: the rule contributes to a **blacklist** for its stage.
32713
+ * Entities inside one of those zones are dropped at that stage.
32714
+ *
32715
+ * `monitor`-style observation (count without filtering) is not a rule
32716
+ * mode — zones without any matching rule are observed naturally by
32717
+ * `zone-analytics` (live snapshot + history), so an "I just want to
32718
+ * count, not filter" use case needs no rule at all.
32719
+ */
32720
+ var ZoneRuleModeEnum = _enum(["include", "exclude"]);
32721
+ /**
32722
+ * Per-consumer rule that references existing zones (geometry) and
32723
+ * defines how a specific pipeline stage should treat them. Each
32724
+ * consumer addon owns its own `ZoneRule[]` array in its per-device
32725
+ * settings:
32726
+ *
32727
+ * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
32728
+ * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
32729
+ * - future: notification rules, audio gating, etc.
32730
+ *
32731
+ * One rule applies to N zones (`zoneIds[]`) so the operator can
32732
+ * express "ignore motion in ALL of {garden, street}" with a single
32733
+ * rule. `classFilter` narrows the rule to specific object classes —
32734
+ * "drop person detections in the street, but keep cars" is one
32735
+ * `exclude` rule with `classFilter: ['person']`.
32736
+ *
32737
+ * `enabled` is a soft toggle — the operator can keep the rule
32738
+ * configured but inert without deleting it.
32739
+ */
32740
+ var ZoneRuleSchema = object({
32741
+ /** Stable rule id — survives edits, used by the UI for diffing. */
32742
+ id: string(),
32743
+ /** Optional human-readable label rendered in the rule editor. */
32744
+ name: string().optional(),
32745
+ /** Zones this rule targets. The rule's `mode` applies to ALL
32746
+ * listed zones (OR-set: a detection in any one of them counts).
32747
+ * At least one zone id required — a rule with no targets is a
32748
+ * configuration mistake and the form validator rejects it. */
32749
+ zoneIds: array(string()).min(1).readonly(),
32750
+ mode: ZoneRuleModeEnum,
32751
+ /**
32752
+ * Class names this rule applies to. Empty / undefined ⇒ rule
32753
+ * applies to every class. Class strings match the `macroClass`
32754
+ * field on detections (e.g. `person`, `car`, `dog`).
32755
+ */
32756
+ classFilter: array(string()).readonly().optional(),
32757
+ /**
32758
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
32759
+ * required to consider an entity "in the zone". Defaults to the
32760
+ * consumer's stage default when omitted. Kept for back-compat with
32761
+ * existing per-rule overrides; new operators pick the value via
32762
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
32763
+ * set, the lower-level engine reads it as a 0–1 fraction.
32764
+ */
32765
+ overlapThreshold: number$1().min(0).max(1).optional(),
32766
+ /**
32767
+ * Operator-friendly version of `overlapThreshold` — the percentage
32768
+ * of the detection's bbox that must lie inside the zone for the
32769
+ * rule to match. Documented default is 85%; the engine substitutes
32770
+ * that when the field is omitted (kept optional so existing rules
32771
+ * stored without it stay valid).
32772
+ *
32773
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
32774
+ * rule, the engine prefers `bboxInclusionPct` because it's the
32775
+ * field exposed in the UI. Internally both feed the same gate.
32776
+ */
32777
+ bboxInclusionPct: number$1().min(0).max(100).optional(),
32778
+ /**
32779
+ * When `true` and a detection has a segmentation mask, use the
32780
+ * mask for overlap instead of the bbox. Detection-stage only;
32781
+ * motion rules ignore this field.
32782
+ */
32783
+ preferMask: boolean().optional(),
32784
+ /**
32785
+ * Soft-toggle: `false` disables the rule without deleting it.
32786
+ * Defaults to `true` so operators creating a rule via the UI
32787
+ * see it active immediately.
32788
+ */
32789
+ enabled: boolean().default(true)
32790
+ });
32791
+ array(ZoneRuleSchema).readonly();
32550
32792
  object({
32551
32793
  /** Whether the script is currently executing. */
32552
32794
  isRunning: boolean(),
@@ -33928,6 +34170,42 @@ DeviceType.Camera, method(object({
33928
34170
  package: array(ZoneRuleSchema).readonly()
33929
34171
  });
33930
34172
  /**
34173
+ * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
34174
+ * valutatore puro del suo FRONTE.
34175
+ *
34176
+ * Viveva dentro il builtin virtual-doorbell
34177
+ * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
34178
+ * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
34179
+ * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
34180
+ * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
34181
+ * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
34182
+ * SPOSTA qui e il doorbell la ri-esporta.
34183
+ *
34184
+ * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
34185
+ * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
34186
+ * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
34187
+ * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
34188
+ */
34189
+ /**
34190
+ * Known binary / switch source caps → the boolean slice field whose
34191
+ * false→true rise counts as ACTIVE. Every entry is "fire on active".
34192
+ * Sensors whose "active" reading is not a plain boolean (presence's string
34193
+ * state, connectivity's connected flag) are deliberately excluded — a
34194
+ * reconnect is not a doorbell press, and it is not a recording either.
34195
+ */
34196
+ var SOURCE_CAP_ACTIVE_FIELD = {
34197
+ contact: "entryOpen",
34198
+ binary: "on",
34199
+ switch: "on",
34200
+ motion: "detected",
34201
+ flood: "flooded",
34202
+ gas: "detected",
34203
+ smoke: "detected",
34204
+ "carbon-monoxide": "detected",
34205
+ vibration: "detected",
34206
+ tamper: "tampered"
34207
+ };
34208
+ /**
33931
34209
  * Accessory device helpers — shared across drivers.
33932
34210
  *
33933
34211
  * Many vendor-specific drivers register accessory child devices on
@@ -38659,6 +38937,12 @@ Object.freeze({
38659
38937
  addonId: null,
38660
38938
  access: "create"
38661
38939
  },
38940
+ "nodes.directory": {
38941
+ capName: "nodes",
38942
+ capScope: "system",
38943
+ addonId: null,
38944
+ access: "view"
38945
+ },
38662
38946
  "nodes.executeQuery": {
38663
38947
  capName: "nodes",
38664
38948
  capScope: "system",
@@ -38827,6 +39111,12 @@ Object.freeze({
38827
39111
  addonId: null,
38828
39112
  access: "view"
38829
39113
  },
39114
+ "notificationRules.getRuleLastRuns": {
39115
+ capName: "notification-rules",
39116
+ capScope: "system",
39117
+ addonId: null,
39118
+ access: "view"
39119
+ },
38830
39120
  "notificationRules.getTemplateCatalog": {
38831
39121
  capName: "notification-rules",
38832
39122
  capScope: "system",
@@ -38851,6 +39141,12 @@ Object.freeze({
38851
39141
  addonId: null,
38852
39142
  access: "view"
38853
39143
  },
39144
+ "notificationRules.previewStateSummary": {
39145
+ capName: "notification-rules",
39146
+ capScope: "system",
39147
+ addonId: null,
39148
+ access: "view"
39149
+ },
38854
39150
  "notificationRules.previewTemplate": {
38855
39151
  capName: "notification-rules",
38856
39152
  capScope: "system",
@@ -39427,12 +39723,6 @@ Object.freeze({
39427
39723
  addonId: null,
39428
39724
  access: "create"
39429
39725
  },
39430
- "pipelineAnalytics.runReplayFrameProcessor": {
39431
- capName: "pipeline-analytics",
39432
- capScope: "device",
39433
- addonId: null,
39434
- access: "create"
39435
- },
39436
39726
  "pipelineAnalytics.saveRetrainAnnotations": {
39437
39727
  capName: "pipeline-analytics",
39438
39728
  capScope: "device",
@@ -43530,11 +43820,6 @@ Object.freeze({
43530
43820
  form: "single",
43531
43821
  optional: false
43532
43822
  }],
43533
- "pipelineAnalytics.runReplayFrameProcessor": [{
43534
- name: "deviceId",
43535
- form: "single",
43536
- optional: false
43537
- }],
43538
43823
  "pipelineAnalytics.saveRetrainAnnotations": [{
43539
43824
  name: "deviceId",
43540
43825
  form: "single",
@@ -44423,11 +44708,232 @@ var NC_AUDIO_DEFAULTS = {
44423
44708
  samplingSeconds: 10
44424
44709
  };
44425
44710
  new Set(["devices", "classes"]);
44711
+ /**
44712
+ * WHICH narrowing filter speaks about WHICH system-event kind — one predicate,
44713
+ * consumed by the rule ENGINE and by every rule EDITOR.
44714
+ *
44715
+ * ── The failure this file exists for ───────────────────────────────────────
44716
+ * `NcSystemEventConditionSchema` carries four optional narrowing lists
44717
+ * (`deviceIds`, `deviceTypes`, `nodeIds`, `packageNames`) beside a required
44718
+ * `kinds` gate. They are not four filters over one vocabulary: each belongs to
44719
+ * a FAMILY of kinds and is meaningless outside it. A node going away has no
44720
+ * device type; a completed export has no package; `stream-offline` deliberately
44721
+ * carries no device type at all (a stream belongs to a camera by construction).
44722
+ *
44723
+ * The engine applied all four to every kind, so a filter from one family failed
44724
+ * a kind from another family CLOSED. The operator's live rule "Eventi di
44725
+ * sistema" selected eleven kinds and set `deviceTypes: ['camera']` — the only
44726
+ * way to say "only my cameras" about the two liveness kinds — and NINE of the
44727
+ * eleven silently stopped being deliverable. Four days of history contained
44728
+ * `device-online` and `device-offline` and nothing else, across a window in
44729
+ * which agents were restarted and addons were updated.
44730
+ *
44731
+ * Both editors already computed this scoping, to decide which filter boxes to
44732
+ * render; the cap's own docblock already promised it ("Narrow a `device-*` kind
44733
+ * to these device TYPES"). Only the engine disagreed, and it disagreed in the
44734
+ * destructive direction. So the rule is stated ONCE, here, and the engine and
44735
+ * the editors are readers of it rather than three independent authors.
44736
+ *
44737
+ * ── The direction ─────────────────────────────────────────────────────────
44738
+ * Out of family ⇒ the filter is IGNORED, never fail-closed. An editor that
44739
+ * hides a box has already told the operator this filter does not speak about
44740
+ * these kinds, so honouring it as an exclusion contradicts the only
44741
+ * explanation the operator was ever given. Inside its family the filter still
44742
+ * narrows, and still fails CLOSED on a subject that cannot produce the identity
44743
+ * it tests (a device the mirror cannot name is not evidence of a camera).
44744
+ */
44745
+ /**
44746
+ * Every filter, for the callers that must cover all of them (guards, tests).
44747
+ *
44748
+ * `consumableLowPercent` (D642) is not a list but it is a filter in exactly
44749
+ * this sense: it belongs to a family of kinds (the consumable pair) and the
44750
+ * engine consults it only there. It differs in one way the readers must know —
44751
+ * its ABSENCE is not "no narrowing" but the default threshold, because a
44752
+ * consumable episode is always judged at SOME threshold.
44753
+ */
44754
+ var NC_SYSTEM_EVENT_FILTER_KEYS = [
44755
+ "deviceIds",
44756
+ "deviceTypes",
44757
+ "nodeIds",
44758
+ "packageNames",
44759
+ "consumableLowPercent"
44760
+ ];
44761
+ new Map(Object.entries({
44762
+ "device-online": "device-events",
44763
+ "device-offline": "device-events",
44764
+ "device-disabled": "device-events",
44765
+ "device-enabled": "device-events",
44766
+ "device-battery-low": "device-events",
44767
+ "device-battery-normal": "device-events",
44768
+ "device-consumable-low": "device-events",
44769
+ "device-consumable-normal": "device-events",
44770
+ "device-tank-low": "device-events",
44771
+ "device-tank-full": "device-events",
44772
+ "device-tank-normal": "device-events",
44773
+ "stream-online": "device-events",
44774
+ "stream-offline": "device-events",
44775
+ "detection-blind": "device-events",
44776
+ "node-online": "system",
44777
+ "node-offline": "system",
44778
+ "node-inference-unavailable": "system",
44779
+ "addon-crash-loop": "system",
44780
+ "addon-update-available": "system",
44781
+ "addon-updated": "system",
44782
+ "server-update-available": "system",
44783
+ "server-updated": "system",
44784
+ "wrapper-update-available": "system",
44785
+ "backup-completed": "system",
44786
+ "export-completed": "system",
44787
+ "alarm-triggered": "alarm",
44788
+ "alarm-armed": "alarm",
44789
+ "alarm-disarmed": "alarm",
44790
+ "alarm-arming": "alarm",
44791
+ "alarm-arm-refused": "alarm",
44792
+ "alarm-pending": "alarm",
44793
+ "alarm-rearmed": "alarm",
44794
+ "alarm-sensor-bypass": "alarm",
44795
+ "alarm-check-failed": "alarm",
44796
+ "camera-online": null,
44797
+ "camera-offline": null,
44798
+ "camera-disabled": null,
44799
+ "camera-enabled": null
44800
+ }));
44801
+ [...NC_SYSTEM_EVENT_FILTER_KEYS];
44802
+ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
44803
+ {
44804
+ id: "device-state",
44805
+ family: "device-events",
44806
+ label: "Device state",
44807
+ kinds: [
44808
+ "device-offline",
44809
+ "device-online",
44810
+ "device-disabled",
44811
+ "device-enabled"
44812
+ ]
44813
+ },
44814
+ {
44815
+ id: "streams",
44816
+ family: "device-events",
44817
+ label: "Streams",
44818
+ kinds: ["stream-offline", "stream-online"]
44819
+ },
44820
+ {
44821
+ id: "battery",
44822
+ family: "device-events",
44823
+ label: "Battery",
44824
+ kinds: ["device-battery-low", "device-battery-normal"]
44825
+ },
44826
+ {
44827
+ id: "consumables",
44828
+ family: "device-events",
44829
+ label: "Consumables",
44830
+ kinds: ["device-consumable-low", "device-consumable-normal"]
44831
+ },
44832
+ {
44833
+ id: "tanks",
44834
+ family: "device-events",
44835
+ label: "Tanks",
44836
+ kinds: [
44837
+ "device-tank-low",
44838
+ "device-tank-full",
44839
+ "device-tank-normal"
44840
+ ]
44841
+ },
44842
+ {
44843
+ id: "detection-blind",
44844
+ family: "device-events",
44845
+ label: "Detecting nothing",
44846
+ kinds: ["detection-blind"]
44847
+ },
44848
+ {
44849
+ id: "nodes",
44850
+ family: "system",
44851
+ label: "Nodes",
44852
+ kinds: [
44853
+ "node-offline",
44854
+ "node-online",
44855
+ "node-inference-unavailable"
44856
+ ]
44857
+ },
44858
+ {
44859
+ id: "addons",
44860
+ family: "system",
44861
+ label: "Addons",
44862
+ kinds: [
44863
+ "addon-crash-loop",
44864
+ "addon-update-available",
44865
+ "addon-updated"
44866
+ ]
44867
+ },
44868
+ {
44869
+ id: "updates",
44870
+ family: "system",
44871
+ label: "Server & container updates",
44872
+ kinds: [
44873
+ "server-update-available",
44874
+ "server-updated",
44875
+ "wrapper-update-available"
44876
+ ]
44877
+ },
44878
+ {
44879
+ id: "backups-exports",
44880
+ family: "system",
44881
+ label: "Backups & exports",
44882
+ kinds: ["backup-completed", "export-completed"]
44883
+ },
44884
+ {
44885
+ id: "alarm",
44886
+ family: "alarm",
44887
+ label: "Alarm",
44888
+ kinds: NC_ALARM_SYSTEM_EVENT_KINDS
44889
+ }
44890
+ ];
44891
+ function systemEventKindsOfFamily(family) {
44892
+ return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((g) => g.family === family).flatMap((g) => g.kinds);
44893
+ }
44426
44894
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
44895
+ /**
44896
+ * WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
44897
+ * persists (D640 "a sensor rule fires on the raise, the clear, or both").
44898
+ *
44899
+ * A device-event rule over a flood sensor notified on BOTH edges: water
44900
+ * detected, and back to dry. `NcConditions.sensorTransition` lets the rule
44901
+ * name one edge; this file answers, for one `SensorEvent`, which edge it is.
44902
+ *
44903
+ * NOT A SECOND TABLE. The "which boolean of this cap counts as HIGH" question
44904
+ * already has one answer, `SOURCE_CAP_ACTIVE_FIELD`
44905
+ * (`catalogs/sensor-active-state.ts`, the doorbell's and the recorder's edge
44906
+ * table), and a sensor event names its kind, not its cap. So the table here is
44907
+ * DERIVED: event kind → cap (`EVENT_KIND_BY_CAP`) → that cap's active field.
44908
+ * Only kinds whose taxonomy category is `sensor` take part — a switch has an
44909
+ * `on` boolean too, but "it triggers / it clears" is not a sentence about a
44910
+ * light, and offering it there would be a knob with a misleading label.
44911
+ * `presence` is a WORD (`home` / `not_home` / a zone), not a boolean, and is
44912
+ * absent from the source table, so it is absent here.
44913
+ *
44914
+ * `undefined` = never guessed: a kind with no raise (a doorbell press, a
44915
+ * button, a cover), or a slice whose field is missing or not a boolean. The
44916
+ * engine then does NOT filter — a flood alert must not go silent because one
44917
+ * read came back unreadable (the same stance as the alarm's `sensorEdgeOf`).
44918
+ *
44919
+ * Pure, RN-safe: the viewer imports the support rule from here.
44920
+ */
44921
+ /** Event kind → the boolean field of its cap slice that means "raised". */
44922
+ var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.entries(EVENT_KIND_BY_CAP).flatMap(([capName, descriptor]) => {
44923
+ const field = SOURCE_CAP_ACTIVE_FIELD[capName];
44924
+ if (field === void 0 || descriptor.category !== "sensor") return [];
44925
+ return [[descriptor.kind, field]];
44926
+ })));
44927
+ Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
44427
44928
  var RULE_BODY_ONLY = ["body"];
44428
44929
  var RULE_TEXT = ["title", "body"];
44429
44930
  var BATTERY = ["device-battery-low", "device-battery-normal"];
44430
44931
  var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
44932
+ var TANK = [
44933
+ "device-tank-low",
44934
+ "device-tank-full",
44935
+ "device-tank-normal"
44936
+ ];
44431
44937
  var UPDATES = [
44432
44938
  "addon-update-available",
44433
44939
  "server-update-available",
@@ -44453,12 +44959,189 @@ var DEVICE_EVENTS = [
44453
44959
  "device-enabled",
44454
44960
  ...BATTERY,
44455
44961
  ...CONSUMABLE,
44962
+ ...TANK,
44456
44963
  "stream-online",
44457
44964
  "stream-offline",
44458
44965
  "detection-blind",
44459
44966
  ...ALARM
44460
44967
  ];
44461
- [...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];
44968
+ /** Every kind a summary may speak about — the two summarisable families. */
44969
+ var SUMMARISABLE = [...systemEventKindsOfFamily("device-events"), ...systemEventKindsOfFamily("system")];
44970
+ /** One per summary SECTION (`NC_STATE_SUMMARY_SECTIONS` in the notification
44971
+ * centre): the section's lines, offered when one of its kinds is selected. */
44972
+ var STATE_SUMMARY_SECTION_VARS = [
44973
+ {
44974
+ name: "summaryDevices",
44975
+ label: "Summary: devices offline / switched off",
44976
+ example: "Garage — offline since 07:12",
44977
+ kinds: [
44978
+ "device-offline",
44979
+ "device-online",
44980
+ "device-disabled",
44981
+ "device-enabled"
44982
+ ]
44983
+ },
44984
+ {
44985
+ name: "summaryStreams",
44986
+ label: "Summary: streams offline",
44987
+ example: "Ingresso (high) — since 06:40",
44988
+ kinds: ["stream-offline", "stream-online"]
44989
+ },
44990
+ {
44991
+ name: "summaryBattery",
44992
+ label: "Summary: low batteries",
44993
+ example: "Sensore porta — 8 %",
44994
+ kinds: BATTERY
44995
+ },
44996
+ {
44997
+ name: "summaryConsumables",
44998
+ label: "Summary: consumables",
44999
+ example: "Robot — filter 10 %",
45000
+ kinds: CONSUMABLE
45001
+ },
45002
+ {
45003
+ name: "summaryTanks",
45004
+ label: "Summary: tanks",
45005
+ example: "Robot — clean water low",
45006
+ kinds: TANK
45007
+ },
45008
+ {
45009
+ name: "summaryDetection",
45010
+ label: "Summary: cameras detecting nothing",
45011
+ example: "Ingresso — since 05:10",
45012
+ kinds: ["detection-blind"]
45013
+ },
45014
+ {
45015
+ name: "summaryNodes",
45016
+ label: "Summary: nodes",
45017
+ example: "little-unraid — offline since 03:00",
45018
+ kinds: [
45019
+ "node-offline",
45020
+ "node-online",
45021
+ "node-inference-unavailable"
45022
+ ]
45023
+ },
45024
+ {
45025
+ name: "summaryAddons",
45026
+ label: "Summary: addons",
45027
+ example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
45028
+ kinds: [
45029
+ "addon-crash-loop",
45030
+ "addon-update-available",
45031
+ "addon-updated"
45032
+ ]
45033
+ },
45034
+ {
45035
+ name: "summaryUpdates",
45036
+ label: "Summary: server & container updates",
45037
+ example: "server 1.2.377 → 1.2.378",
45038
+ kinds: [
45039
+ "server-update-available",
45040
+ "server-updated",
45041
+ "wrapper-update-available"
45042
+ ]
45043
+ }
45044
+ ];
45045
+ var STATE_SUMMARY_VARS = [
45046
+ {
45047
+ name: "summaryText",
45048
+ label: "Summary: every section",
45049
+ description: "One block per section with something to report, one line per item.",
45050
+ example: "Batterie scariche: Sensore porta — 8 %",
45051
+ group: "system",
45052
+ families: ["rule"],
45053
+ fields: [...RULE_TEXT],
45054
+ kinds: ["system"],
45055
+ systemEventKinds: [...SUMMARISABLE],
45056
+ systemEventDeliveries: ["summary"]
45057
+ },
45058
+ {
45059
+ name: "summaryCount",
45060
+ label: "Summary: items reported",
45061
+ example: "3",
45062
+ group: "system",
45063
+ families: ["rule"],
45064
+ fields: [...RULE_TEXT],
45065
+ kinds: ["system"],
45066
+ systemEventKinds: [...SUMMARISABLE],
45067
+ systemEventDeliveries: ["summary"]
45068
+ },
45069
+ ...STATE_SUMMARY_SECTION_VARS.map((v) => ({
45070
+ name: v.name,
45071
+ label: v.label,
45072
+ example: v.example,
45073
+ group: "system",
45074
+ families: ["rule"],
45075
+ fields: [...RULE_TEXT],
45076
+ kinds: ["system"],
45077
+ systemEventKinds: [...v.kinds],
45078
+ systemEventDeliveries: ["summary"]
45079
+ }))
45080
+ ];
45081
+ [
45082
+ [...DEVICE_EVENTS],
45083
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45084
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45085
+ [...RULE_TEXT],
45086
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45087
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45088
+ [...RULE_TEXT],
45089
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45090
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45091
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45092
+ [...RULE_TEXT],
45093
+ [...RULE_TEXT],
45094
+ [...RULE_TEXT],
45095
+ [...RULE_TEXT],
45096
+ [...RULE_TEXT],
45097
+ [...RULE_TEXT],
45098
+ [...RULE_TEXT],
45099
+ [...RULE_TEXT],
45100
+ [...RULE_BODY_ONLY],
45101
+ [...RULE_TEXT],
45102
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
45103
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
45104
+ ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
45105
+ ([...RULE_TEXT], [...CONSUMABLE]),
45106
+ ([...RULE_TEXT], [...TANK]),
45107
+ [...RULE_TEXT],
45108
+ ([...RULE_TEXT], [...UPDATES]),
45109
+ ([...RULE_TEXT], [...UPDATES]),
45110
+ [...RULE_TEXT],
45111
+ [...RULE_TEXT],
45112
+ [...RULE_TEXT],
45113
+ ([...RULE_TEXT], [...UPDATES]),
45114
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45115
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45116
+ ([...RULE_TEXT], [...UPDATES]),
45117
+ ([...RULE_TEXT], [...UPDATES]),
45118
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45119
+ [...RULE_TEXT],
45120
+ [...RULE_TEXT],
45121
+ [...RULE_TEXT],
45122
+ [...RULE_TEXT],
45123
+ [...RULE_TEXT],
45124
+ [...RULE_TEXT],
45125
+ [...RULE_TEXT],
45126
+ [...RULE_TEXT],
45127
+ [...RULE_TEXT],
45128
+ [...RULE_TEXT],
45129
+ [...RULE_TEXT],
45130
+ [...RULE_TEXT],
45131
+ [...RULE_TEXT],
45132
+ [...RULE_TEXT],
45133
+ [...RULE_TEXT],
45134
+ [...RULE_TEXT],
45135
+ [...RULE_TEXT],
45136
+ [...RULE_TEXT],
45137
+ [...RULE_TEXT],
45138
+ [...RULE_TEXT],
45139
+ [...RULE_TEXT],
45140
+ [...RULE_TEXT],
45141
+ [...RULE_TEXT],
45142
+ [...RULE_TEXT],
45143
+ ...STATE_SUMMARY_VARS
45144
+ ];
44462
45145
  /**
44463
45146
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
44464
45147
  *