@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.js CHANGED
@@ -5525,7 +5525,7 @@ function number(params) {
5525
5525
  return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
5526
5526
  }
5527
5527
  //#endregion
5528
- //#region ../types/dist/sleep-D_4W7xuQ.mjs
5528
+ //#region ../types/dist/sleep-BaRu0bS5.mjs
5529
5529
  /**
5530
5530
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5531
5531
  * window to float samples (D455).
@@ -9639,7 +9639,7 @@ var ModelFormatEntrySchema = object({
9639
9639
  /** Multi-file payload (directory members or sibling files). */
9640
9640
  files: array(string()).readonly().optional(),
9641
9641
  /** Runtime(s) that can use this format. If omitted, inferred from ModelFormat key */
9642
- runtimes: array(_enum(["node", "python"])).readonly().optional()
9642
+ runtimes: array(_enum(["python"])).readonly().optional()
9643
9643
  });
9644
9644
  /**
9645
9645
  * Extra file that must be downloaded alongside the model (e.g., labels JSON, dict.txt).
@@ -9720,10 +9720,17 @@ var ModelCatalogEntrySchema = object({
9720
9720
  inputChannels: number$1().int().positive().optional(),
9721
9721
  labels: array(LabelDefinitionSchema).readonly(),
9722
9722
  inputLayout: _enum(["nchw", "nhwc"]).optional(),
9723
+ /**
9724
+ * `'scrfd'` applies InsightFace's SCRFD input contract, `(x - 127.5) / 128`
9725
+ * (upstream `insightface/model_zoo/scrfd.py`), instead of the historical
9726
+ * plain `/255`. Measured against COCO GT: 188 → 196 faces found on the
9727
+ * `scrfd-2.5g` catalog entry (2026-09-26 model-replacement spike, §2.4-2).
9728
+ */
9723
9729
  inputNormalization: _enum([
9724
9730
  "zero-one",
9725
9731
  "imagenet",
9726
- "none"
9732
+ "none",
9733
+ "scrfd"
9727
9734
  ]).optional(),
9728
9735
  /**
9729
9736
  * The model already applies softmax IN-GRAPH — its raw output is a
@@ -9825,7 +9832,8 @@ var ModelConvertMetadataSchema = object({
9825
9832
  inputNormalization: _enum([
9826
9833
  "zero-one",
9827
9834
  "imagenet",
9828
- "none"
9835
+ "none",
9836
+ "scrfd"
9829
9837
  ]).optional(),
9830
9838
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
9831
9839
  outputFormat: _enum([
@@ -17311,6 +17319,9 @@ var NcSystemEventKindSchema = _enum([
17311
17319
  "device-battery-normal",
17312
17320
  "device-consumable-low",
17313
17321
  "device-consumable-normal",
17322
+ "device-tank-low",
17323
+ "device-tank-full",
17324
+ "device-tank-normal",
17314
17325
  "stream-online",
17315
17326
  "stream-offline",
17316
17327
  "node-online",
@@ -17348,6 +17359,29 @@ var NC_LEGACY_SYSTEM_EVENT_KINDS = new Set([
17348
17359
  ]);
17349
17360
  NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
17350
17361
  /**
17362
+ * The panel's transitions, as ONE list.
17363
+ *
17364
+ * Named here rather than spelled out at each of the four sites that need them
17365
+ * (the emitter, the intake, the editor group, the combined-notification gate),
17366
+ * because a transition added to the enum and forgotten at one of them is an
17367
+ * alarm state nobody can be notified about. That is exactly what this list
17368
+ * failed to prevent while it held three: `alarm-arming` was the fourth.
17369
+ *
17370
+ * In LIFECYCLE order, which is the order an editor lists them in and the order
17371
+ * a person experiences them — not the order they were added.
17372
+ */
17373
+ var NC_ALARM_SYSTEM_EVENT_KINDS = [
17374
+ "alarm-arm-refused",
17375
+ "alarm-check-failed",
17376
+ "alarm-arming",
17377
+ "alarm-armed",
17378
+ "alarm-pending",
17379
+ "alarm-triggered",
17380
+ "alarm-rearmed",
17381
+ "alarm-sensor-bypass",
17382
+ "alarm-disarmed"
17383
+ ];
17384
+ /**
17351
17385
  * One coherent system-event condition. `kinds` is the required opt-in safety
17352
17386
  * gate; the remaining lists are optional narrowing filters relevant to the
17353
17387
  * selected kinds.
@@ -17381,7 +17415,22 @@ var NcSystemEventConditionSchema = object({
17381
17415
  */
17382
17416
  excludeDeviceIds: array(number$1().int()).min(1).optional(),
17383
17417
  nodeIds: array(string().min(1)).min(1).optional(),
17384
- packageNames: array(string().min(1)).min(1).optional()
17418
+ packageNames: array(string().min(1)).min(1).optional(),
17419
+ /**
17420
+ * At or below this remaining-life %, a consumable is LOW for THIS rule
17421
+ * (D642). Absent = {@link NC_CONSUMABLE_LOW_PERCENT_DEFAULT}; at most
17422
+ * {@link NC_CONSUMABLE_LOW_PERCENT_MAX}, so every episode can recover. Scoped to the
17423
+ * consumable kinds (`systemEventFilterApplies`) and to the device-events
17424
+ * family; ignored on every other kind.
17425
+ *
17426
+ * It is a property of the RULE because the operator decided so: a rule at
17427
+ * 25 must be told about a filter at 20 %, a rule at 10 must not. The hub
17428
+ * keeps one episode per (threshold, device, item) — its own hysteresis, its
17429
+ * own reminder — and a rule hears only the episode judged at ITS threshold,
17430
+ * exactly as an occupancy rule hears only the edge of its own count. A
17431
+ * provider's own `replace` verdict is low at every threshold.
17432
+ */
17433
+ consumableLowPercent: number$1().int().min(1).max(95).optional()
17385
17434
  });
17386
17435
  /** Hard ceiling on a window (24h). A snooze that could not expire would be an
17387
17436
  * outage the operator asked for once and forgot. */
@@ -17399,6 +17448,25 @@ var NcScheduleSchema = object({
17399
17448
  /** Active OUTSIDE the windows (e.g. "only outside business hours"). */
17400
17449
  invert: boolean().optional()
17401
17450
  });
17451
+ /**
17452
+ * How a system-event rule of the `device-events` or `system` family speaks
17453
+ * (D647): at once, per event (`instant`, the only behaviour before D647), or
17454
+ * as a STATE SUMMARY at the end of each window of `schedule`.
17455
+ */
17456
+ var NcSystemEventDeliveryModeSchema = _enum(["instant", "summary"]);
17457
+ /**
17458
+ * `summary`: the rule sends NOTHING per event. When a window of `schedule`
17459
+ * CLOSES, it sends one notification with the current state of its kinds
17460
+ * (low batteries, consumables to replace, devices offline, updates
17461
+ * available, ...), after its own filters. Nothing to report sends nothing.
17462
+ * The schedule is the ordinary `NcSchedule` (weekly windows, IANA timezone,
17463
+ * midnight crossing); the send time is each window's END. Refused on the
17464
+ * `alarm` family, which stays instant (`systemEventDeliveryProblems`).
17465
+ */
17466
+ var NcSystemEventDeliverySchema = discriminatedUnion("mode", [object({ mode: literal("instant") }), object({
17467
+ mode: literal("summary"),
17468
+ schedule: NcScheduleSchema
17469
+ })]);
17402
17470
  /** Fuzzy plate matcher — OCR noise makes exact match useless (spec row 12/13). */
17403
17471
  var NcPlateMatcherSchema = object({
17404
17472
  /**
@@ -17524,6 +17592,16 @@ var NcCrossingSchema = _enum([
17524
17592
  "exit",
17525
17593
  "any"
17526
17594
  ]);
17595
+ /**
17596
+ * Which edge of a BOOLEAN sensor fires a device-event rule (D640).
17597
+ * - `raised` — the sensor went high: water detected, smoke, open, tampered.
17598
+ * - `cleared` — it went back: dry, clear, closed.
17599
+ * - absent — both edges, which is what every rule authored before this did.
17600
+ *
17601
+ * There is no third member for "both": absence already says it, and a value
17602
+ * that means the same as absence is a second way to write one rule.
17603
+ */
17604
+ var NcSensorTransitionSchema = _enum(["raised", "cleared"]);
17527
17605
  /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
17528
17606
  var NcZoneConditionSchema = object({
17529
17607
  ids: array(string().min(1)).min(1),
@@ -17713,6 +17791,20 @@ var NcConditionsSchema = object({
17713
17791
  scene: NcSceneConditionSchema.optional(),
17714
17792
  /** Device scope — absent = all devices. */
17715
17793
  devices: array(number$1()).optional(),
17794
+ /**
17795
+ * Device EXCLUSION (D641) — "every device except these". Judged on the SAME
17796
+ * subject ids {@link devices} is judged on (the camera, the sensor that
17797
+ * fired, a linked set's sensor and row cameras), so a sensor fanned out per
17798
+ * linked camera is excluded exactly where an include would have tested it.
17799
+ * `min(1)`: an empty exclusion excludes nothing and is written as absent.
17800
+ *
17801
+ * Not for `system-event` rules — those carry their own
17802
+ * `systemEvent.excludeDeviceIds` (D636), and the hub refuses this key there.
17803
+ * Editors present the two lists as ONE field (All | Only these | All except
17804
+ * these); the engine evaluates each independently, so a legacy rule
17805
+ * carrying both still means "these, minus those".
17806
+ */
17807
+ devicesExclude: array(number$1()).min(1).optional(),
17716
17808
  /** Detector class names (any overlap with the record's class set). */
17717
17809
  classes: array(string().min(1)).optional(),
17718
17810
  /** Veto classes — any overlap fails the rule. */
@@ -17846,6 +17938,16 @@ var NcConditionsSchema = object({
17846
17938
  */
17847
17939
  sensorKinds: array(string().min(1)).optional(),
17848
17940
  /**
17941
+ * DEVICE-EVENT only (D640). Fire on one edge of a boolean sensor —
17942
+ * {@link NcSensorTransitionSchema}. Which field of the slice means "raised"
17943
+ * is `@camstack/types` → `notification/sensor-raised.ts`. A reading the
17944
+ * engine cannot place (a kind with no raise, an unreadable slice) PASSES:
17945
+ * the condition fails open towards notifying, never towards silence. Editors
17946
+ * offer it only when every chosen {@link sensorKinds} member has a raise
17947
+ * (`sensorTransitionOffered`), and the hub refuses it on any other delivery.
17948
+ */
17949
+ sensorTransition: NcSensorTransitionSchema.optional(),
17950
+ /**
17849
17951
  * PACKAGE-EVENT only. Which package phase fires the rule — `delivered`
17850
17952
  * (a parked parcel appeared), `picked-up` (it departed), or `both`. Fails
17851
17953
  * when the subject's phase does not match (a subject always carries a phase
@@ -18100,6 +18202,16 @@ var NcRuleInputSchema = object({
18100
18202
  delivery: NcDeliverySchema,
18101
18203
  conditions: NcConditionsSchema.default({}),
18102
18204
  schedule: NcScheduleSchema.optional(),
18205
+ /**
18206
+ * A `device-events` / `system` rule's delivery (D647): absent or `instant`
18207
+ * is a notification per event; `summary` is one state summary per closed
18208
+ * window of its schedule. `.optional()` and never defaulted: absent must
18209
+ * keep meaning what every rule authored before D647 meant, with no
18210
+ * migration. Top-level rather than inside `conditions.systemEvent`, so a
18211
+ * client that does not know it (an older viewer re-sending `conditions`
18212
+ * whole) leaves it standing through the store's patch merge.
18213
+ */
18214
+ systemEventDelivery: NcSystemEventDeliverySchema.optional(),
18103
18215
  /** May be empty when `targetUsers` addresses at least one user — the
18104
18216
  * "at least one addressee" invariant is enforced by the provider, because
18105
18217
  * a cross-field refine here would break `NcRulePatchSchema.partial()`. */
@@ -18328,6 +18440,28 @@ var NcRuleSchema = NcRuleInputSchema.extend({
18328
18440
  */
18329
18441
  disabledTargetIds: array(string()).default([])
18330
18442
  });
18443
+ /** One section of a previewed state summary. `unavailable` = its state could not be read. */
18444
+ var NcStateSummaryPreviewSectionSchema = object({
18445
+ id: string(),
18446
+ status: _enum(["read", "unavailable"]),
18447
+ items: number$1().int().nonnegative(),
18448
+ reason: string().optional()
18449
+ });
18450
+ /**
18451
+ * The summary a rule would send now. `empty` = nothing to report and every
18452
+ * section read: such a summary is NOT sent. `dropped` counts the open states
18453
+ * the rule's own filters (or a camera mute) left out, by reason.
18454
+ * `excludedGroups` names the kind groups selected that have no state to report
18455
+ * (backups & exports).
18456
+ */
18457
+ var NcStateSummaryPreviewSchema = object({
18458
+ empty: boolean(),
18459
+ count: number$1().int().nonnegative(),
18460
+ text: string(),
18461
+ sections: array(NcStateSummaryPreviewSectionSchema),
18462
+ dropped: record(string(), number$1().int().nonnegative()),
18463
+ excludedGroups: array(string())
18464
+ });
18331
18465
  var NcTestResultSchema = object({
18332
18466
  recordId: string(),
18333
18467
  recordKind: _enum([
@@ -18345,9 +18479,29 @@ var NcTestResultSchema = object({
18345
18479
  className: string().optional(),
18346
18480
  label: string().optional()
18347
18481
  });
18348
- /** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
18349
- * is the same union; the template-vars spec pins the two together. */
18482
+ /** The rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts` is the
18483
+ * same union; the template-vars spec pins the two together. `mixed` is DERIVED
18484
+ * (a stored system rule whose kinds span families, D637) and never authored. */
18350
18485
  var NcRuleKindSchema = _enum([
18486
+ "detection",
18487
+ "sensor",
18488
+ "occupancy",
18489
+ "sound",
18490
+ "device-events",
18491
+ "system",
18492
+ "alarm",
18493
+ "mixed"
18494
+ ]);
18495
+ /**
18496
+ * The kinds a SERVED template-variable descriptor may name: the five that
18497
+ * existed before D637, frozen. `getTemplateCatalog` ships `kinds` to every
18498
+ * installed viewer, and a viewer at `@camstack/types` 1.2.25x parses them with a
18499
+ * strict five-member enum — one `device-events` in one descriptor fails the
18500
+ * whole catalog parse. So the WIRE says `system`, and the MATCH widens it:
18501
+ * `templateVarKindApplies` reads a `system` descriptor as applying to every
18502
+ * system rule kind (`device-events`, `system`, `alarm`, `mixed`).
18503
+ */
18504
+ var NcTemplateVarKindSchema = _enum([
18351
18505
  "detection",
18352
18506
  "sensor",
18353
18507
  "occupancy",
@@ -18385,7 +18539,7 @@ var NcTemplateVarGroupSchema = _enum([
18385
18539
  *
18386
18540
  * `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
18387
18541
  * it has a value; absent = no restriction on that axis. `systemEventKinds` is
18388
- * read only for a `system` rule. `dynamic` declares a FAMILY of names
18542
+ * read only for a system-event rule (any family, or `mixed`). `dynamic` declares a FAMILY of names
18389
18543
  * (`count_<class>`): the descriptor's `name` is the example member.
18390
18544
  */
18391
18545
  var NcTemplateVarDescriptorSchema = object({
@@ -18396,9 +18550,16 @@ var NcTemplateVarDescriptorSchema = object({
18396
18550
  group: NcTemplateVarGroupSchema,
18397
18551
  families: array(NcTemplateFamilySchema).min(1),
18398
18552
  fields: array(NcTemplateFieldSchema).optional(),
18399
- kinds: array(NcRuleKindSchema).optional(),
18553
+ kinds: array(NcTemplateVarKindSchema).optional(),
18400
18554
  deliveries: array(NcDeliverySchema).optional(),
18401
18555
  systemEventKinds: array(NcSystemEventKindSchema).optional(),
18556
+ /**
18557
+ * Which system-event DELIVERY has a value for it (D647). Absent = an
18558
+ * instant row only when the descriptor names `systemEventKinds` (a
18559
+ * per-event fact: a summary row has no single device), and both otherwise.
18560
+ * `['summary']` marks the state-summary vocabulary (`{{summaryText}}`, ...).
18561
+ */
18562
+ systemEventDeliveries: array(NcSystemEventDeliveryModeSchema).optional(),
18402
18563
  dynamic: object({
18403
18564
  prefix: literal("count_"),
18404
18565
  from: literal("classes")
@@ -18411,7 +18572,9 @@ var NcTemplatePreviewInputSchema = object({
18411
18572
  field: NcTemplateFieldSchema,
18412
18573
  kind: NcRuleKindSchema.optional(),
18413
18574
  delivery: NcDeliverySchema.optional(),
18414
- systemEventKinds: array(NcSystemEventKindSchema).optional()
18575
+ systemEventKinds: array(NcSystemEventKindSchema).optional(),
18576
+ /** The rule's system-event delivery (D647); absent = instant. */
18577
+ systemEventDelivery: NcSystemEventDeliveryModeSchema.optional()
18415
18578
  }),
18416
18579
  template: object({
18417
18580
  title: string().max(500).optional(),
@@ -18444,6 +18607,7 @@ var NcConditionDescriptorSchema = object({
18444
18607
  /** Editor widget the UI renders — never hardcode per-condition forms. */
18445
18608
  valueType: _enum([
18446
18609
  "deviceIdList",
18610
+ "deviceIdExcludeList",
18447
18611
  "stringList",
18448
18612
  "number01",
18449
18613
  "number",
@@ -18454,6 +18618,7 @@ var NcConditionDescriptorSchema = object({
18454
18618
  "plateMatcher",
18455
18619
  "packagePhase",
18456
18620
  "crossingSelect",
18621
+ "sensorTransition",
18457
18622
  "polygonDraw",
18458
18623
  "occupancy",
18459
18624
  "audio",
@@ -18604,6 +18769,28 @@ var NcHistoryFilterSchema = object({
18604
18769
  limit: number$1().int().min(1).max(500).default(100)
18605
18770
  });
18606
18771
  /**
18772
+ * Every rule's last run, in one answer.
18773
+ *
18774
+ * A rule ABSENT from `runs` produced no delivery inside `horizonMs` — how far
18775
+ * back this answer looked, in ms before the hub's now. It is the outbox prune
18776
+ * window (7 days by default, operator-configurable), NARROWED when the read hit
18777
+ * its row bound: the answer never claims a window it did not read. `null` means
18778
+ * the operator keeps the outbox forever AND the read reached its end, so absent
18779
+ * means "never on record". A UI derives its "not in the last N days" from this
18780
+ * value, never from a constant — and only when the call ANSWERED (D315).
18781
+ */
18782
+ var NcRuleLastRunsSchema = object({
18783
+ runs: array(object({
18784
+ ruleId: string(),
18785
+ /** Fire time of the newest row (`createdAt`, epoch ms). */
18786
+ lastAt: number$1(),
18787
+ status: NcHistoryStatusSchema,
18788
+ /** The camera/device of that row (a digest row files under its first camera). */
18789
+ deviceId: number$1()
18790
+ })),
18791
+ horizonMs: number$1().nonnegative().nullable()
18792
+ });
18793
+ /**
18607
18794
  * What a snooze covers. Broader scopes win when several overlap, so one window
18608
18795
  * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
18609
18796
  * summarising the same silence.
@@ -18784,6 +18971,14 @@ var NcAlarmModeCoverageSchema = object({
18784
18971
  /** Ids named by the covering rules. A SUBSET when `allDevices` is true. */
18785
18972
  deviceIds: array(number$1().int()),
18786
18973
  /**
18974
+ * D641: the ids NOT covered although `allDevices` is true — every unscoped
18975
+ * covering rule EXCLUDES them (`conditions.devicesExclude`) and no named rule
18976
+ * covers them. Always `[]` when `allDevices` is false. Excluded by the rules,
18977
+ * which is not the same as skipped by a switch: never in `skippedDevices`.
18978
+ * Defaulted so an answer computed before this field parses as "none".
18979
+ */
18980
+ allDevicesExcept: array(number$1().int()).default([]),
18981
+ /**
18787
18982
  * Devices this mode NAMES but cannot actually arm, each with the switch that
18788
18983
  * excludes it.
18789
18984
  *
@@ -18913,13 +19108,17 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
18913
19108
  }), object({ results: array(NcTestResultSchema) }), {
18914
19109
  kind: "mutation",
18915
19110
  auth: "admin"
19111
+ }), method(object({ rule: NcRuleInputSchema }), NcStateSummaryPreviewSchema, {
19112
+ kind: "mutation",
19113
+ auth: "admin",
19114
+ access: "view"
18916
19115
  }), method(object({}), object({
18917
19116
  catalog: array(NcConditionDescriptorSchema),
18918
19117
  taxonomy: NcTaxonomySchema.optional()
18919
19118
  })), method(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
18920
19119
  kind: "mutation",
18921
19120
  access: "view"
18922
- }), 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 }), {
19121
+ }), 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 }), {
18923
19122
  kind: "mutation",
18924
19123
  caller: "required"
18925
19124
  }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
@@ -19259,606 +19458,6 @@ var OauthIntegrationDescriptorSchema = object({
19259
19458
  });
19260
19459
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
19261
19460
  /**
19262
- * Reference to the frame's retained NATIVE surface + the parent crop's placement
19263
- * within the frame, so the executor can re-cut a leaf child ROI at native
19264
- * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
19265
- */
19266
- var NativeCropRefSchema = object({
19267
- /** Handle keying the retained native surface (node-pinned to its owner). */
19268
- handle: FrameHandleSchema,
19269
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
19270
- cropFrameSpace: object({
19271
- x: number$1(),
19272
- y: number$1(),
19273
- w: number$1(),
19274
- h: number$1()
19275
- })
19276
- });
19277
- object({
19278
- crop: object({
19279
- left: number$1(),
19280
- top: number$1(),
19281
- width: number$1().positive(),
19282
- height: number$1().positive()
19283
- }).optional(),
19284
- content: object({
19285
- width: number$1().int().positive(),
19286
- height: number$1().int().positive()
19287
- }),
19288
- fit: _enum(["stretch", "contain"]),
19289
- format: _enum([
19290
- "rgb",
19291
- "gray",
19292
- "jpeg"
19293
- ])
19294
- });
19295
- /**
19296
- * Process-local frame identity. It is serializable so it can ride an in-process
19297
- * capability call, but `registryId` deliberately prevents resolution in any
19298
- * other process or execution group.
19299
- */
19300
- var FrameRefSchema = object({
19301
- registryId: string().min(1),
19302
- id: string().min(1),
19303
- width: number$1().int().positive(),
19304
- height: number$1().int().positive(),
19305
- format: _enum(["rgb", "gray"]),
19306
- timestamp: number$1(),
19307
- capturedAt: number$1().optional()
19308
- });
19309
- var ModelFormatSchema$1 = _enum([
19310
- "onnx",
19311
- "coreml",
19312
- "openvino",
19313
- "tflite",
19314
- "pt",
19315
- "gguf"
19316
- ]);
19317
- var PipelineSlotSchema = _enum([
19318
- "detector",
19319
- "cropper",
19320
- "classifier",
19321
- "refiner",
19322
- "audio-classifier"
19323
- ]);
19324
- var PipelineEngineChoiceSchema = object({
19325
- runtime: _enum(["node", "python"]),
19326
- backend: string(),
19327
- format: ModelFormatSchema$1,
19328
- device: string().optional()
19329
- });
19330
- var AvailableEngineSchema = object({
19331
- engine: PipelineEngineChoiceSchema,
19332
- devices: array(object({
19333
- id: string(),
19334
- label: string(),
19335
- description: string().optional()
19336
- })).readonly(),
19337
- defaultDevice: string()
19338
- });
19339
- var PipelineDefaultStepSchema = lazy(() => object({
19340
- addonId: string(),
19341
- addonName: string(),
19342
- slot: PipelineSlotSchema,
19343
- inputClasses: array(string()).readonly(),
19344
- outputClasses: array(string()).readonly(),
19345
- enabled: boolean(),
19346
- modelId: string(),
19347
- children: array(PipelineDefaultStepSchema).readonly(),
19348
- group: string().optional(),
19349
- settings: record(string(), unknown()).optional()
19350
- }));
19351
- var PipelineTemplateStepSchema = lazy(() => object({
19352
- addonId: string(),
19353
- enabled: boolean(),
19354
- modelId: string(),
19355
- children: array(PipelineTemplateStepSchema).readonly(),
19356
- settings: record(string(), unknown()).optional()
19357
- }));
19358
- var PipelineTemplateSchema$1 = object({
19359
- id: string(),
19360
- name: string(),
19361
- createdAt: string(),
19362
- updatedAt: string(),
19363
- engine: PipelineEngineChoiceSchema,
19364
- steps: array(PipelineTemplateStepSchema).readonly()
19365
- });
19366
- var PipelineModelOptionSchema = object({
19367
- id: string(),
19368
- name: string(),
19369
- formats: record(string(), object({
19370
- downloaded: boolean(),
19371
- sizeMB: number$1()
19372
- })),
19373
- group: ModelVariantGroupSchema.optional(),
19374
- legacy: boolean().optional(),
19375
- provider: ModelProviderIdSchema.optional()
19376
- });
19377
- var ConfigFieldBridge = custom();
19378
- var PipelineAddonSchemaSchema = object({
19379
- id: string(),
19380
- name: string(),
19381
- slot: PipelineSlotSchema,
19382
- inputClasses: array(string()).readonly(),
19383
- outputClasses: array(string()).readonly(),
19384
- childSlots: array(PipelineSlotSchema).readonly(),
19385
- models: array(PipelineModelOptionSchema).readonly(),
19386
- defaultModelId: string(),
19387
- defaultModelIdByFormat: record(string(), string()).optional(),
19388
- enabledByDefault: boolean().optional(),
19389
- backfillIntoExistingOverrides: boolean().optional(),
19390
- defaultConfidence: number$1(),
19391
- group: string().optional(),
19392
- configSchema: array(ConfigFieldBridge).readonly().optional()
19393
- });
19394
- var PipelineSlotSchemaSchema = object({
19395
- id: PipelineSlotSchema,
19396
- label: string(),
19397
- priority: number$1(),
19398
- parentSlot: PipelineSlotSchema.nullable(),
19399
- addons: array(PipelineAddonSchemaSchema).readonly()
19400
- });
19401
- var PipelineSchemaSchema = object({
19402
- availableEngines: array(AvailableEngineSchema).readonly(),
19403
- selectedEngine: PipelineEngineChoiceSchema,
19404
- slots: array(PipelineSlotSchemaSchema).readonly()
19405
- });
19406
- var EngineProvisioningSchema = object({
19407
- runtimeId: _enum([
19408
- "onnx",
19409
- "openvino",
19410
- "coreml",
19411
- "edgetpu"
19412
- ]).nullable(),
19413
- device: string().nullable(),
19414
- state: _enum([
19415
- "idle",
19416
- "installing",
19417
- "verifying",
19418
- "ready",
19419
- "failed"
19420
- ]),
19421
- progress: number$1().optional(),
19422
- error: string().optional(),
19423
- nextRetryAt: number$1().optional(),
19424
- /**
19425
- * Gate A (config-correctness gate at engine change): human-readable
19426
- * config issues surfaced EAGERLY when the node's engine changes — model
19427
- * substitutions ("chose X, running Y") and zero-build steps ("no model
19428
- * has a <format> build"). Additive/optional: informational only, never
19429
- * enforced here — `assertEngineReady` (readiness) still gates inference.
19430
- * Absent/empty when the node-default tree resolves cleanly.
19431
- */
19432
- configIssues: array(string()).optional()
19433
- });
19434
- var PipelineStepInputSchema = lazy(() => object({
19435
- addonId: string(),
19436
- modelId: string().optional(),
19437
- enabled: boolean().default(true),
19438
- children: array(PipelineStepInputSchema).optional(),
19439
- settings: record(string(), unknown()).optional(),
19440
- jumpDeviceKey: string().optional()
19441
- }));
19442
- var ModelSubstitutionSchema = object({
19443
- addonId: string(),
19444
- chosen: string(),
19445
- running: string(),
19446
- format: string()
19447
- });
19448
- var PipelineValidationIssueSchema = object({
19449
- addonId: string(),
19450
- kind: _enum(["unknown-addon", "no-format-build"]),
19451
- detail: string()
19452
- });
19453
- var PipelineValidationResultSchema = object({
19454
- ok: boolean(),
19455
- issues: array(PipelineValidationIssueSchema).readonly(),
19456
- substitutions: array(ModelSubstitutionSchema).readonly(),
19457
- /** The node's `currentEngine.format` this validation ran against. */
19458
- format: string()
19459
- });
19460
- var ReferenceImageEntrySchema = object({
19461
- filename: string(),
19462
- stepIds: array(string()).readonly().optional()
19463
- });
19464
- var ReferenceImageBodySchema = object({
19465
- base64: string(),
19466
- filename: string()
19467
- });
19468
- var ReferenceAudioEntrySchema = object({
19469
- filename: string(),
19470
- sizeKb: number$1()
19471
- });
19472
- var ReferenceAudioBodySchema = object({ base64: string() });
19473
- var AudioBackendSchema = object({
19474
- id: string(),
19475
- name: string(),
19476
- description: string(),
19477
- available: boolean(),
19478
- /**
19479
- * Raw classifier labels this backend can emit (e.g. YAMNet's
19480
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
19481
- * the benchmark UI to populate the `enabledMicroClasses` filter
19482
- * specific to the selected backend without a separate fetch.
19483
- */
19484
- rawLabels: array(string()).readonly().optional()
19485
- });
19486
- var AudioCapabilitiesSchema = object({
19487
- activeBackend: string(),
19488
- availableBackends: array(AudioBackendSchema).readonly(),
19489
- sampleRate: number$1(),
19490
- chunkDurationMs: number$1()
19491
- });
19492
- var DownloadModelResultSchema = object({
19493
- filePath: string(),
19494
- sizeMB: number$1(),
19495
- durationMs: number$1()
19496
- });
19497
- /**
19498
- * Wrapper carrying a single test run's result. Replaces the legacy
19499
- * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
19500
- * canonical `AudioResult` from the Phase 6 output rework: one
19501
- * `AudioDetection` per class above `minScore`, top-N candidates in
19502
- * `debug.alternateLabels['audio-classifier']`, per-source timings in
19503
- * `debug.stepTimings`. The outer `success`/`error` fields stay so the
19504
- * benchmark UI can still report a clean failure when the classifier
19505
- * cap isn't available.
19506
- */
19507
- var AudioTestResultSchema = object({
19508
- success: boolean(),
19509
- error: string().optional(),
19510
- frame: custom().optional()
19511
- });
19512
- var PipelineConfigBridge = custom();
19513
- var ConfigUISchemaBridge = custom();
19514
- var ConfigUISchemaNullableBridge = custom();
19515
- var InferenceCapabilitiesBridge = custom();
19516
- var ModelAvailabilityListBridge = custom();
19517
- var PipelineRunResultBridge = custom();
19518
- method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
19519
- modelId: string(),
19520
- settings: record(string(), unknown()).readonly()
19521
- }))), method(object({ steps: record(string(), object({
19522
- modelId: string(),
19523
- settings: record(string(), unknown()).readonly()
19524
- })) }), object({ success: literal(true) }), {
19525
- kind: "mutation",
19526
- auth: "admin"
19527
- }), method(object({ nodeId: string() }), object({
19528
- success: literal(true),
19529
- clearedDevices: number$1()
19530
- }), {
19531
- kind: "mutation",
19532
- auth: "admin"
19533
- }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
19534
- /** `<backend>:<device>`, e.g. `openvino:gpu`. */
19535
- deviceKey: string(),
19536
- /**
19537
- * `failed` — the per-device restart budget is exhausted; no pool
19538
- * will be spawned until an operator re-arms it or the runner
19539
- * respawns. `backoff` — under budget, waiting out the backoff (or
19540
- * a cached pool observed dead and not yet condemned).
19541
- */
19542
- state: _enum(["failed", "backoff"]),
19543
- /** Epoch ms of the death that produced this state. */
19544
- since: number$1(),
19545
- /** Pool deaths inside the current window. */
19546
- deaths: number$1(),
19547
- /** The last death's message. */
19548
- lastError: string()
19549
- })).readonly() })), method(object({
19550
- nodeId: string(),
19551
- deviceKey: string()
19552
- }), object({ rearmed: boolean() }), {
19553
- kind: "mutation",
19554
- auth: "admin"
19555
- }), 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({
19556
- name: string(),
19557
- steps: array(PipelineTemplateStepSchema).readonly(),
19558
- engine: PipelineEngineChoiceSchema
19559
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
19560
- id: string(),
19561
- name: string().optional(),
19562
- steps: array(PipelineTemplateStepSchema).readonly().optional()
19563
- }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
19564
- addonId: string(),
19565
- modelId: string(),
19566
- format: ModelFormatSchema$1
19567
- }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
19568
- addonId: string(),
19569
- modelId: string(),
19570
- format: ModelFormatSchema$1
19571
- }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
19572
- engine: PipelineEngineChoiceSchema.optional(),
19573
- steps: array(PipelineStepInputSchema).min(1),
19574
- frame: FrameInputSchema.optional(),
19575
- /**
19576
- * Process-local lazy frame. Valid only when caller and provider resolve
19577
- * in the same execution-group process; split/cross-node callers use
19578
- * `frame`/`image` inline compatibility instead.
19579
- */
19580
- frameRef: FrameRefSchema.optional(),
19581
- /**
19582
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
19583
- * the decoded pixels live in. One more member of the one-of
19584
- * frame/frameHandle/image/imageBase64/referenceImage group.
19585
- */
19586
- frameHandle: FrameHandleSchema.optional(),
19587
- imageBase64: string().optional(),
19588
- /**
19589
- * Binary JPEG bytes — preferred over `imageBase64` on internal
19590
- * hops (hub → forked worker via Moleculer MsgPack) because it
19591
- * skips the 33% base64 overhead + the per-call base64 decode on
19592
- * the detection-pipeline worker. Callers can pass either; exactly
19593
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
19594
- */
19595
- image: _instanceof(Uint8Array).optional(),
19596
- referenceImage: string().optional(),
19597
- deviceId: number$1().optional(),
19598
- sessionId: string().optional(),
19599
- /**
19600
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
19601
- * reference-image, and detail-subtree calls. 'frame' is the live
19602
- * per-frame dispatch: ONLY root-plane steps run; crop children
19603
- * (inputClasses ≠ null) are skipped and served per-track via
19604
- * pipelineRunner.runDetailSubtree (two-plane design).
19605
- */
19606
- plane: _enum(["full", "frame"]).optional(),
19607
- /**
19608
- * Inference-device selector (Phase 2 multi-device). Format
19609
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
19610
- * Omitted ⇒ the runner's default device (current single-engine
19611
- * behaviour). Selects WHICH device pool of the node runs the call.
19612
- */
19613
- deviceKey: string().optional(),
19614
- /**
19615
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
19616
- * when the parent crop was resolved from the frame's retained NATIVE
19617
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
19618
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
19619
- * resolution from that surface — the SAME quality path faces already
19620
- * had — instead of the downscaled parent tile. `handle` keys the native
19621
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
19622
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
19623
- * the executor's crop-normalized child ROI back into frame-normalized
19624
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
19625
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
19626
- * (today's behaviour on the fallback path).
19627
- */
19628
- nativeCropRef: NativeCropRefSchema.optional()
19629
- }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
19630
- engine: PipelineEngineChoiceSchema.optional(),
19631
- steps: array(PipelineStepInputSchema).min(1),
19632
- frames: array(FrameInputSchema).min(1).max(255),
19633
- deviceId: number$1().optional(),
19634
- sessionId: string().optional(),
19635
- /**
19636
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
19637
- * the batch to the Python pool's bench preprocess cache
19638
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
19639
- * preprocessed ONCE and every later inference is a pure-inference cache
19640
- * hit — the sustained-throughput run measures inference, not
19641
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
19642
- * full preprocess every call, correct). Fresh per sustained run;
19643
- * released via `uncacheFrame`.
19644
- */
19645
- frameId: number$1().int().nonnegative().optional(),
19646
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
19647
- deviceKey: string().optional()
19648
- }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
19649
- data: _instanceof(Uint8Array),
19650
- width: number$1().int().positive(),
19651
- height: number$1().int().positive(),
19652
- format: _enum([
19653
- "rgb",
19654
- "bgr",
19655
- "gray"
19656
- ])
19657
- }), object({
19658
- frameId: number$1(),
19659
- width: number$1(),
19660
- height: number$1()
19661
- }), { kind: "mutation" }), method(object({
19662
- stepId: string(),
19663
- frameId: number$1().int()
19664
- }), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number$1().int() }), _void(), { kind: "mutation" }), method(_void(), object({
19665
- batchMode: string(),
19666
- windowMs: number$1(),
19667
- maxBatchSize: number$1(),
19668
- concurrency: number$1()
19669
- })), method(_void(), array(object({
19670
- engineKey: string(),
19671
- engine: PipelineEngineChoiceSchema,
19672
- modelsLoaded: array(string()).readonly(),
19673
- inUseByCameras: array(number$1()).readonly(),
19674
- /**
19675
- * Origin of this resident factory.
19676
- * - `runtime` — main camera-serving engine (no idle TTL).
19677
- * - `warm-override` — benchmark/test override held in the warm
19678
- * cache; auto-disposed after the idle TTL.
19679
- * - `device-pool` — a concurrent per-device pool (Phase 2
19680
- * multi-device, keyed by `deviceKey`) resolved
19681
- * via `resolveDeviceFactory`. Runs alongside the
19682
- * `runtime` engine on a DIFFERENT accelerator
19683
- * (NPU / iGPU / Coral) — this is how the
19684
- * Engines tab shows all pools running at once.
19685
- */
19686
- kind: _enum([
19687
- "runtime",
19688
- "warm-override",
19689
- "device-pool"
19690
- ]),
19691
- /** Native pid of the underlying Python pool (null when no pool). */
19692
- poolPid: number$1().nullable(),
19693
- /** ms since this factory was last used (null when not warm-tracked). */
19694
- idleMs: number$1().nullable(),
19695
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
19696
- idleTtlMs: number$1().nullable()
19697
- })).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
19698
- kind: "mutation",
19699
- auth: "admin"
19700
- }), method(object({
19701
- engine: PipelineEngineChoiceSchema,
19702
- force: boolean().optional()
19703
- }), object({
19704
- success: boolean(),
19705
- reason: string().optional()
19706
- }), {
19707
- kind: "mutation",
19708
- auth: "admin"
19709
- }), 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({
19710
- addonId: string(),
19711
- modelId: string(),
19712
- filename: string().optional(),
19713
- settings: record(string(), unknown()).optional()
19714
- }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
19715
- /**
19716
- * Per-stage gating mode applied to the zones a rule references.
19717
- *
19718
- * - `include`: the rule contributes to a **whitelist** for its stage.
19719
- * When at least one `include` rule fires for a stage, only entities
19720
- * inside one of those zones pass that stage.
19721
- * - `exclude`: the rule contributes to a **blacklist** for its stage.
19722
- * Entities inside one of those zones are dropped at that stage.
19723
- *
19724
- * `monitor`-style observation (count without filtering) is not a rule
19725
- * mode — zones without any matching rule are observed naturally by
19726
- * `zone-analytics` (live snapshot + history), so an "I just want to
19727
- * count, not filter" use case needs no rule at all.
19728
- */
19729
- var ZoneRuleModeEnum = _enum(["include", "exclude"]);
19730
- /**
19731
- * Per-consumer rule that references existing zones (geometry) and
19732
- * defines how a specific pipeline stage should treat them. Each
19733
- * consumer addon owns its own `ZoneRule[]` array in its per-device
19734
- * settings:
19735
- *
19736
- * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
19737
- * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
19738
- * - future: notification rules, audio gating, etc.
19739
- *
19740
- * One rule applies to N zones (`zoneIds[]`) so the operator can
19741
- * express "ignore motion in ALL of {garden, street}" with a single
19742
- * rule. `classFilter` narrows the rule to specific object classes —
19743
- * "drop person detections in the street, but keep cars" is one
19744
- * `exclude` rule with `classFilter: ['person']`.
19745
- *
19746
- * `enabled` is a soft toggle — the operator can keep the rule
19747
- * configured but inert without deleting it.
19748
- */
19749
- var ZoneRuleSchema = object({
19750
- /** Stable rule id — survives edits, used by the UI for diffing. */
19751
- id: string(),
19752
- /** Optional human-readable label rendered in the rule editor. */
19753
- name: string().optional(),
19754
- /** Zones this rule targets. The rule's `mode` applies to ALL
19755
- * listed zones (OR-set: a detection in any one of them counts).
19756
- * At least one zone id required — a rule with no targets is a
19757
- * configuration mistake and the form validator rejects it. */
19758
- zoneIds: array(string()).min(1).readonly(),
19759
- mode: ZoneRuleModeEnum,
19760
- /**
19761
- * Class names this rule applies to. Empty / undefined ⇒ rule
19762
- * applies to every class. Class strings match the `macroClass`
19763
- * field on detections (e.g. `person`, `car`, `dog`).
19764
- */
19765
- classFilter: array(string()).readonly().optional(),
19766
- /**
19767
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
19768
- * required to consider an entity "in the zone". Defaults to the
19769
- * consumer's stage default when omitted. Kept for back-compat with
19770
- * existing per-rule overrides; new operators pick the value via
19771
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
19772
- * set, the lower-level engine reads it as a 0–1 fraction.
19773
- */
19774
- overlapThreshold: number$1().min(0).max(1).optional(),
19775
- /**
19776
- * Operator-friendly version of `overlapThreshold` — the percentage
19777
- * of the detection's bbox that must lie inside the zone for the
19778
- * rule to match. Documented default is 85%; the engine substitutes
19779
- * that when the field is omitted (kept optional so existing rules
19780
- * stored without it stay valid).
19781
- *
19782
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
19783
- * rule, the engine prefers `bboxInclusionPct` because it's the
19784
- * field exposed in the UI. Internally both feed the same gate.
19785
- */
19786
- bboxInclusionPct: number$1().min(0).max(100).optional(),
19787
- /**
19788
- * When `true` and a detection has a segmentation mask, use the
19789
- * mask for overlap instead of the bbox. Detection-stage only;
19790
- * motion rules ignore this field.
19791
- */
19792
- preferMask: boolean().optional(),
19793
- /**
19794
- * Soft-toggle: `false` disables the rule without deleting it.
19795
- * Defaults to `true` so operators creating a rule via the UI
19796
- * see it active immediately.
19797
- */
19798
- enabled: boolean().default(true)
19799
- });
19800
- array(ZoneRuleSchema).readonly();
19801
- /**
19802
- * Zone — pure geometry + identity. NO filtering behaviour.
19803
- *
19804
- * Zones describe **where** in the frame the operator wants to flag
19805
- * something; consumer-owned {@link ZoneRule} arrays describe **how**
19806
- * each pipeline stage uses them. Splitting the two means a single
19807
- * polygon "Driveway" can simultaneously back a motion-exclude rule,
19808
- * a detection-include rule on `['car']`, and an occupancy aggregate
19809
- * — without three duplicated polygons.
19810
- *
19811
- * Owned by the orchestrator addon (provider) and mirrored into the
19812
- * `zones` device-state slice on every mutation. Consumers
19813
- * (motion-wasm, pipeline-executor, analytics, admin UI) read either
19814
- * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
19815
- * mirror with `onChanged`).
19816
- *
19817
- * Coordinates are normalised fractions of the frame (0–1) so zones
19818
- * survive resolution changes and stream profile switches.
19819
- *
19820
- * `kind` discriminates between full polygons (closed regions used
19821
- * for intrusion / occupancy filters) and tripwires (open 2-point
19822
- * line segments used for cross events). Onboard / firmware-reported
19823
- * zones (Reolink, ONVIF) are out of scope for now — see the deferred
19824
- * task list.
19825
- */
19826
- var ZoneKindEnum = _enum(["polygon", "tripwire"]);
19827
- /** Polygon vertex in fraction-of-frame coordinates (0–1). */
19828
- var PolygonPointSchema = object({
19829
- x: number$1(),
19830
- y: number$1()
19831
- });
19832
- /** A camera detection zone — pure geometry/identity. */
19833
- var ZoneSchema = object({
19834
- id: string(),
19835
- name: string(),
19836
- kind: ZoneKindEnum.default("polygon"),
19837
- /** Polygon vertices, fraction of frame (0–1). */
19838
- polygon: array(PolygonPointSchema).readonly(),
19839
- /** Visual color for UI rendering. */
19840
- color: string().default("#3b82f6")
19841
- });
19842
- DeviceType.Camera, method(object({ deviceId: number$1() }), array(ZoneSchema).readonly()), method(object({
19843
- deviceId: number$1(),
19844
- zone: ZoneSchema
19845
- }), _void(), {
19846
- kind: "mutation",
19847
- auth: "admin"
19848
- }), method(object({
19849
- deviceId: number$1(),
19850
- zoneId: string()
19851
- }), _void(), {
19852
- kind: "mutation",
19853
- auth: "admin"
19854
- }), method(object({
19855
- deviceId: number$1(),
19856
- zone: ZoneSchema
19857
- }), _void(), {
19858
- kind: "mutation",
19859
- auth: "admin"
19860
- }), object({ zones: array(ZoneSchema).readonly() });
19861
- /**
19862
19461
  * pipeline-analytics — device-scoped wrapper cap. Refines raw
19863
19462
  * per-frame detections emitted by the pipeline runner into tracked
19864
19463
  * objects, per-kind event collections (motion / object / audio), and
@@ -20429,6 +20028,22 @@ var TrackSchema = object({
20429
20028
  * omit it. Render nothing when absent.
20430
20029
  */
20431
20030
  previewMissReason: string().optional(),
20031
+ /**
20032
+ * The ROOT model that produced this track — the `FrameResult.rootModelId` of
20033
+ * the frame it was BORN on (D646). What a later replay compares against, so
20034
+ * "the original model" is named rather than assumed. A birth fact: never
20035
+ * rewritten by a later frame. **Absent ≠ unknown-model**: a row from before
20036
+ * the column, and a track the camera's own detector produced, carry none.
20037
+ */
20038
+ modelId: string().optional(),
20039
+ /**
20040
+ * Size (px) of the detection frame this track's `positions[].bbox` live in —
20041
+ * the frame it was born on, else the camera's latest analysis frame at
20042
+ * persist time. Divide a position by these to normalise it. Absent when the
20043
+ * size was never known.
20044
+ */
20045
+ frameWidth: number$1().optional(),
20046
+ frameHeight: number$1().optional(),
20432
20047
  ...TrackFlagFields,
20433
20048
  ...TrackRetrainFields
20434
20049
  });
@@ -21435,24 +21050,6 @@ var MediaReclaimStatusSchema = object({
21435
21050
  finishedAtMs: number$1().nullable(),
21436
21051
  error: string().nullable()
21437
21052
  });
21438
- var ReplayFrameInputSchema = object({
21439
- timestamp: number$1(),
21440
- frame: PipelineRunResultBridge
21441
- });
21442
- var RunReplayFrameProcessorResultSchema = object({ tracks: array(object({
21443
- className: string(),
21444
- firstSeenMs: number$1(),
21445
- lastSeenMs: number$1(),
21446
- /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
21447
- * frame — a representative box for the diff's `(className, window, IoU)`
21448
- * pairing (`replay-diff.ts`). A replay does not need the full per-frame
21449
- * trajectory production's `Track.positions` keeps. */
21450
- bbox: BoundingBoxSchema,
21451
- /** How many of the input frames this track matched a real detection on
21452
- * (never a coasted/extrapolated frame) — the replay's own signal for "how
21453
- * solid is this track", cheaper than re-deriving it from a trajectory. */
21454
- framesMatched: number$1().int()
21455
- })).readonly() });
21456
21053
  /**
21457
21054
  * The long-term series a camera produces. A closed union so a typo cannot
21458
21055
  * invent one, and the ONE declaration of it — the addon's `AnalyticsLtsSeries`
@@ -21666,108 +21263,570 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
21666
21263
  }), {
21667
21264
  kind: "mutation",
21668
21265
  auth: "admin"
21669
- }), method(object({ frameId: string() }), object({
21670
- base64: string(),
21671
- width: number$1().int(),
21672
- height: number$1().int()
21266
+ }), method(object({ frameId: string() }), object({
21267
+ base64: string(),
21268
+ width: number$1().int(),
21269
+ height: number$1().int()
21270
+ }), {
21271
+ kind: "query",
21272
+ auth: "admin"
21273
+ }), method(object({
21274
+ deviceId: number$1(),
21275
+ trackId: string(),
21276
+ frameId: string(),
21277
+ subject: RetrainAssistSubjectSchema,
21278
+ /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
21279
+ nodeId: string().optional()
21280
+ }), RetrainAssistResultSchema, {
21281
+ kind: "mutation",
21282
+ auth: "admin"
21283
+ }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
21284
+ kind: "query",
21285
+ auth: "admin"
21286
+ }), method(object({
21287
+ deviceId: number$1(),
21288
+ trackId: string(),
21289
+ frameId: string(),
21290
+ annotations: array(RetrainAnnotationDraftSchema)
21291
+ }), array(RetrainAnnotationSchema).readonly(), {
21292
+ kind: "mutation",
21293
+ auth: "admin"
21294
+ }), method(object({
21295
+ deviceId: number$1(),
21296
+ trackId: string()
21297
+ }), RetrainTransitionResultSchema, {
21298
+ kind: "mutation",
21299
+ auth: "admin"
21300
+ }), method(object({
21301
+ deviceId: number$1(),
21302
+ trackId: string()
21303
+ }), RetrainTransitionResultSchema, {
21304
+ kind: "mutation",
21305
+ auth: "admin"
21306
+ }), method(object({ deviceIds: array(number$1()).optional() }), object({ url: string() }), {
21307
+ kind: "query",
21308
+ auth: "admin"
21309
+ }), method(object({
21310
+ /** Which of the three event tables owns the row. */
21311
+ ownerType: EventOwnerTypeSchema,
21312
+ /** The event's rowid in that table (D474: an event id is a number). */
21313
+ eventId: number$1().int(),
21314
+ kind: MediaFileKindEnum.optional(),
21315
+ deviceId: number$1()
21316
+ }), array(MediaFileSchema).readonly()), method(object({
21317
+ trackId: string(),
21318
+ kinds: array(MediaFileKindEnum).optional(),
21319
+ deviceId: number$1()
21320
+ }), array(MediaFileSchema).readonly()), method(object({
21321
+ trackId: string(),
21322
+ deviceId: number$1()
21323
+ }), array(MediaFileInfoSchema).readonly()), method(object({
21324
+ ownerType: EventOwnerTypeSchema,
21325
+ eventId: number$1().int(),
21326
+ deviceId: number$1()
21327
+ }), array(MediaFileInfoSchema).readonly()), method(object({
21328
+ deviceId: number$1(),
21329
+ ownerKind: MediaPresenceOwnerKindSchema,
21330
+ ownerIds: array(string()).max(5e3)
21331
+ }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21332
+ kind: "mutation",
21333
+ auth: "admin"
21334
+ }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
21335
+ kind: "mutation",
21336
+ auth: "admin"
21337
+ }), method(object({}), RebuildStatusSchema), object({
21338
+ deviceId: number$1(),
21339
+ timestamp: number$1(),
21340
+ frameWidth: number$1(),
21341
+ frameHeight: number$1(),
21342
+ detections: array(OverlayDetectionSchema).readonly()
21343
+ }), object({
21344
+ deviceId: number$1(),
21345
+ trackId: string(),
21346
+ className: string()
21347
+ }), object({
21348
+ deviceId: number$1(),
21349
+ trackId: string(),
21350
+ className: string(),
21351
+ durationMs: number$1()
21352
+ }), object({
21353
+ deviceId: number$1(),
21354
+ kind: EventKindSchema,
21355
+ eventId: string(),
21356
+ timestamp: number$1()
21357
+ });
21358
+ /**
21359
+ * Reference to the frame's retained NATIVE surface + the parent crop's placement
21360
+ * within the frame, so the executor can re-cut a leaf child ROI at native
21361
+ * resolution on the detail plane. See the `runPipeline` `nativeCropRef` field.
21362
+ */
21363
+ var NativeCropRefSchema = object({
21364
+ /** Handle keying the retained native surface (node-pinned to its owner). */
21365
+ handle: FrameHandleSchema,
21366
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
21367
+ cropFrameSpace: object({
21368
+ x: number$1(),
21369
+ y: number$1(),
21370
+ w: number$1(),
21371
+ h: number$1()
21372
+ })
21373
+ });
21374
+ object({
21375
+ crop: object({
21376
+ left: number$1(),
21377
+ top: number$1(),
21378
+ width: number$1().positive(),
21379
+ height: number$1().positive()
21380
+ }).optional(),
21381
+ content: object({
21382
+ width: number$1().int().positive(),
21383
+ height: number$1().int().positive()
21384
+ }),
21385
+ fit: _enum(["stretch", "contain"]),
21386
+ format: _enum([
21387
+ "rgb",
21388
+ "gray",
21389
+ "jpeg"
21390
+ ])
21391
+ });
21392
+ /**
21393
+ * Process-local frame identity. It is serializable so it can ride an in-process
21394
+ * capability call, but `registryId` deliberately prevents resolution in any
21395
+ * other process or execution group.
21396
+ */
21397
+ var FrameRefSchema = object({
21398
+ registryId: string().min(1),
21399
+ id: string().min(1),
21400
+ width: number$1().int().positive(),
21401
+ height: number$1().int().positive(),
21402
+ format: _enum(["rgb", "gray"]),
21403
+ timestamp: number$1(),
21404
+ capturedAt: number$1().optional()
21405
+ });
21406
+ var ModelFormatSchema$1 = _enum([
21407
+ "onnx",
21408
+ "coreml",
21409
+ "openvino",
21410
+ "tflite",
21411
+ "pt",
21412
+ "gguf"
21413
+ ]);
21414
+ var PipelineSlotSchema = _enum([
21415
+ "detector",
21416
+ "cropper",
21417
+ "classifier",
21418
+ "refiner",
21419
+ "audio-classifier"
21420
+ ]);
21421
+ var PipelineEngineChoiceSchema = object({
21422
+ runtime: literal("python"),
21423
+ backend: string(),
21424
+ format: ModelFormatSchema$1,
21425
+ device: string().optional()
21426
+ });
21427
+ /**
21428
+ * One pool's resolved provisioning (`getEffectiveTuning`). `numWorkers` is the
21429
+ * knob that lifts a saturated pool (`inference-devices.md`); `concurrency` is
21430
+ * per-worker in-flight requests. `deviceKey` echoes the pool asked about —
21431
+ * `null` for the node-default pool.
21432
+ */
21433
+ var EffectiveTuningSchema = object({
21434
+ deviceKey: string().nullable(),
21435
+ runtime: string(),
21436
+ batchMode: string(),
21437
+ windowMs: number$1(),
21438
+ maxBatchSize: number$1(),
21439
+ concurrency: number$1(),
21440
+ numWorkers: number$1()
21441
+ });
21442
+ var AvailableEngineSchema = object({
21443
+ engine: PipelineEngineChoiceSchema,
21444
+ devices: array(object({
21445
+ id: string(),
21446
+ label: string(),
21447
+ description: string().optional()
21448
+ })).readonly(),
21449
+ defaultDevice: string()
21450
+ });
21451
+ var PipelineDefaultStepSchema = lazy(() => object({
21452
+ addonId: string(),
21453
+ addonName: string(),
21454
+ slot: PipelineSlotSchema,
21455
+ inputClasses: array(string()).readonly(),
21456
+ outputClasses: array(string()).readonly(),
21457
+ enabled: boolean(),
21458
+ modelId: string(),
21459
+ children: array(PipelineDefaultStepSchema).readonly(),
21460
+ group: string().optional(),
21461
+ settings: record(string(), unknown()).optional()
21462
+ }));
21463
+ var PipelineTemplateStepSchema = lazy(() => object({
21464
+ addonId: string(),
21465
+ enabled: boolean(),
21466
+ modelId: string(),
21467
+ children: array(PipelineTemplateStepSchema).readonly(),
21468
+ settings: record(string(), unknown()).optional()
21469
+ }));
21470
+ var PipelineTemplateSchema$1 = object({
21471
+ id: string(),
21472
+ name: string(),
21473
+ createdAt: string(),
21474
+ updatedAt: string(),
21475
+ engine: PipelineEngineChoiceSchema,
21476
+ steps: array(PipelineTemplateStepSchema).readonly()
21477
+ });
21478
+ var PipelineModelOptionSchema = object({
21479
+ id: string(),
21480
+ name: string(),
21481
+ formats: record(string(), object({
21482
+ downloaded: boolean(),
21483
+ sizeMB: number$1()
21484
+ })),
21485
+ group: ModelVariantGroupSchema.optional(),
21486
+ legacy: boolean().optional(),
21487
+ provider: ModelProviderIdSchema.optional()
21488
+ });
21489
+ var ConfigFieldBridge = custom();
21490
+ var PipelineAddonSchemaSchema = object({
21491
+ id: string(),
21492
+ name: string(),
21493
+ slot: PipelineSlotSchema,
21494
+ inputClasses: array(string()).readonly(),
21495
+ outputClasses: array(string()).readonly(),
21496
+ childSlots: array(PipelineSlotSchema).readonly(),
21497
+ models: array(PipelineModelOptionSchema).readonly(),
21498
+ defaultModelId: string(),
21499
+ defaultModelIdByFormat: record(string(), string()).optional(),
21500
+ enabledByDefault: boolean().optional(),
21501
+ backfillIntoExistingOverrides: boolean().optional(),
21502
+ defaultConfidence: number$1(),
21503
+ group: string().optional(),
21504
+ configSchema: array(ConfigFieldBridge).readonly().optional()
21505
+ });
21506
+ var PipelineSlotSchemaSchema = object({
21507
+ id: PipelineSlotSchema,
21508
+ label: string(),
21509
+ priority: number$1(),
21510
+ parentSlot: PipelineSlotSchema.nullable(),
21511
+ addons: array(PipelineAddonSchemaSchema).readonly()
21512
+ });
21513
+ var PipelineSchemaSchema = object({
21514
+ availableEngines: array(AvailableEngineSchema).readonly(),
21515
+ selectedEngine: PipelineEngineChoiceSchema,
21516
+ slots: array(PipelineSlotSchemaSchema).readonly()
21517
+ });
21518
+ var EngineProvisioningSchema = object({
21519
+ runtimeId: _enum([
21520
+ "onnx",
21521
+ "openvino",
21522
+ "coreml",
21523
+ "edgetpu"
21524
+ ]).nullable(),
21525
+ device: string().nullable(),
21526
+ state: _enum([
21527
+ "idle",
21528
+ "installing",
21529
+ "verifying",
21530
+ "ready",
21531
+ "failed"
21532
+ ]),
21533
+ progress: number$1().optional(),
21534
+ error: string().optional(),
21535
+ nextRetryAt: number$1().optional(),
21536
+ /**
21537
+ * Gate A (config-correctness gate at engine change): human-readable
21538
+ * config issues surfaced EAGERLY when the node's engine changes — model
21539
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
21540
+ * has a <format> build"). Additive/optional: informational only, never
21541
+ * enforced here — `assertEngineReady` (readiness) still gates inference.
21542
+ * Absent/empty when the node-default tree resolves cleanly.
21543
+ */
21544
+ configIssues: array(string()).optional()
21545
+ });
21546
+ var PipelineStepInputSchema = lazy(() => object({
21547
+ addonId: string(),
21548
+ modelId: string().optional(),
21549
+ enabled: boolean().default(true),
21550
+ children: array(PipelineStepInputSchema).optional(),
21551
+ settings: record(string(), unknown()).optional(),
21552
+ jumpDeviceKey: string().optional()
21553
+ }));
21554
+ var ModelSubstitutionSchema = object({
21555
+ addonId: string(),
21556
+ chosen: string(),
21557
+ running: string(),
21558
+ format: string()
21559
+ });
21560
+ var PipelineValidationIssueSchema = object({
21561
+ addonId: string(),
21562
+ kind: _enum([
21563
+ "unknown-addon",
21564
+ "no-format-build",
21565
+ "unknown-device"
21566
+ ]),
21567
+ detail: string()
21568
+ });
21569
+ var PipelineValidationResultSchema = object({
21570
+ ok: boolean(),
21571
+ issues: array(PipelineValidationIssueSchema).readonly(),
21572
+ substitutions: array(ModelSubstitutionSchema).readonly(),
21573
+ /** The node's `currentEngine.format` this validation ran against. */
21574
+ format: string()
21575
+ });
21576
+ var ReferenceImageEntrySchema = object({
21577
+ filename: string(),
21578
+ stepIds: array(string()).readonly().optional()
21579
+ });
21580
+ var ReferenceImageBodySchema = object({
21581
+ base64: string(),
21582
+ filename: string()
21583
+ });
21584
+ var ReferenceAudioEntrySchema = object({
21585
+ filename: string(),
21586
+ sizeKb: number$1()
21587
+ });
21588
+ var ReferenceAudioBodySchema = object({ base64: string() });
21589
+ var AudioBackendSchema = object({
21590
+ id: string(),
21591
+ name: string(),
21592
+ description: string(),
21593
+ available: boolean(),
21594
+ /**
21595
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
21596
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
21597
+ * the benchmark UI to populate the `enabledMicroClasses` filter
21598
+ * specific to the selected backend without a separate fetch.
21599
+ */
21600
+ rawLabels: array(string()).readonly().optional()
21601
+ });
21602
+ var AudioCapabilitiesSchema = object({
21603
+ activeBackend: string(),
21604
+ availableBackends: array(AudioBackendSchema).readonly(),
21605
+ sampleRate: number$1(),
21606
+ chunkDurationMs: number$1()
21607
+ });
21608
+ var DownloadModelResultSchema = object({
21609
+ filePath: string(),
21610
+ sizeMB: number$1(),
21611
+ durationMs: number$1()
21612
+ });
21613
+ /**
21614
+ * Wrapper carrying a single test run's result. Replaces the legacy
21615
+ * ad-hoc `{labels: [{className, originalClass, score}]}` shape with the
21616
+ * canonical `AudioResult` from the Phase 6 output rework: one
21617
+ * `AudioDetection` per class above `minScore`, top-N candidates in
21618
+ * `debug.alternateLabels['audio-classifier']`, per-source timings in
21619
+ * `debug.stepTimings`. The outer `success`/`error` fields stay so the
21620
+ * benchmark UI can still report a clean failure when the classifier
21621
+ * cap isn't available.
21622
+ */
21623
+ var AudioTestResultSchema = object({
21624
+ success: boolean(),
21625
+ error: string().optional(),
21626
+ frame: custom().optional()
21627
+ });
21628
+ var PipelineConfigBridge = custom();
21629
+ var ConfigUISchemaBridge = custom();
21630
+ var ConfigUISchemaNullableBridge = custom();
21631
+ var InferenceCapabilitiesBridge = custom();
21632
+ var ModelAvailabilityListBridge = custom();
21633
+ var PipelineRunResultBridge = custom();
21634
+ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngineChoiceSchema), method(PipelineEngineChoiceSchema, array(PipelineDefaultStepSchema)), method(object({ nodeId: string() }), EngineProvisioningSchema), method(_void(), record(string(), object({
21635
+ modelId: string(),
21636
+ settings: record(string(), unknown()).readonly()
21637
+ }))), method(object({ steps: record(string(), object({
21638
+ modelId: string(),
21639
+ settings: record(string(), unknown()).readonly()
21640
+ })) }), object({ success: literal(true) }), {
21641
+ kind: "mutation",
21642
+ auth: "admin"
21643
+ }), method(object({ nodeId: string() }), object({
21644
+ success: literal(true),
21645
+ clearedDevices: number$1()
21673
21646
  }), {
21674
- kind: "query",
21675
- auth: "admin"
21676
- }), method(object({
21677
- deviceId: number$1(),
21678
- trackId: string(),
21679
- frameId: string(),
21680
- subject: RetrainAssistSubjectSchema,
21681
- /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
21682
- nodeId: string().optional()
21683
- }), RetrainAssistResultSchema, {
21684
- kind: "mutation",
21685
- auth: "admin"
21686
- }), method(object({
21687
- deviceId: number$1(),
21688
- source: DetectionSourceSchema,
21689
- zones: array(ZoneSchema).readonly().optional(),
21690
- detectionRules: array(ZoneRuleSchema).readonly().optional(),
21691
- zoneMembershipMinOverlap: number$1().min(0).max(1).optional(),
21692
- frames: array(ReplayFrameInputSchema).min(1)
21693
- }), RunReplayFrameProcessorResultSchema, {
21694
- kind: "mutation",
21695
- auth: "admin"
21696
- }), method(object({ trackId: string() }), array(RetrainAnnotationSchema).readonly(), {
21697
- kind: "query",
21698
- auth: "admin"
21699
- }), method(object({
21700
- deviceId: number$1(),
21701
- trackId: string(),
21702
- frameId: string(),
21703
- annotations: array(RetrainAnnotationDraftSchema)
21704
- }), array(RetrainAnnotationSchema).readonly(), {
21705
21647
  kind: "mutation",
21706
21648
  auth: "admin"
21707
- }), method(object({
21708
- deviceId: number$1(),
21709
- trackId: string()
21710
- }), RetrainTransitionResultSchema, {
21649
+ }), method(object({ nodeId: string() }), object({ unhealthy: array(object({
21650
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
21651
+ deviceKey: string(),
21652
+ /**
21653
+ * `failed` — the per-device restart budget is exhausted; no pool
21654
+ * will be spawned until an operator re-arms it or the runner
21655
+ * respawns. `backoff` — under budget, waiting out the backoff (or
21656
+ * a cached pool observed dead and not yet condemned).
21657
+ */
21658
+ state: _enum(["failed", "backoff"]),
21659
+ /** Epoch ms of the death that produced this state. */
21660
+ since: number$1(),
21661
+ /** Pool deaths inside the current window. */
21662
+ deaths: number$1(),
21663
+ /** The last death's message. */
21664
+ lastError: string()
21665
+ })).readonly() })), method(object({
21666
+ nodeId: string(),
21667
+ deviceKey: string()
21668
+ }), object({ rearmed: boolean() }), {
21711
21669
  kind: "mutation",
21712
21670
  auth: "admin"
21713
- }), method(object({
21714
- deviceId: number$1(),
21715
- trackId: string()
21716
- }), RetrainTransitionResultSchema, {
21671
+ }), method(_void(), PipelineSchemaSchema), method(_void(), array(PipelineDefaultStepSchema).readonly().nullable()), method(_void(), PipelineConfigBridge), method(_void(), ConfigUISchemaBridge), method(object({
21672
+ steps: array(PipelineStepInputSchema),
21673
+ deviceKey: string().optional()
21674
+ }), PipelineValidationResultSchema), method(_void(), array(PipelineTemplateSchema$1).readonly()), method(object({
21675
+ name: string(),
21676
+ steps: array(PipelineTemplateStepSchema).readonly(),
21677
+ engine: PipelineEngineChoiceSchema
21678
+ }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({
21679
+ id: string(),
21680
+ name: string().optional(),
21681
+ steps: array(PipelineTemplateStepSchema).readonly().optional()
21682
+ }), PipelineTemplateSchema$1, { kind: "mutation" }), method(object({ id: string() }), _void(), { kind: "mutation" }), method(_void(), InferenceCapabilitiesBridge), method(object({ addonId: string() }), ModelAvailabilityListBridge), method(object({
21683
+ addonId: string(),
21684
+ modelId: string(),
21685
+ format: ModelFormatSchema$1
21686
+ }), DownloadModelResultSchema, { kind: "mutation" }), method(object({
21687
+ addonId: string(),
21688
+ modelId: string(),
21689
+ format: ModelFormatSchema$1
21690
+ }), object({ success: literal(true) }), { kind: "mutation" }), method(object({
21691
+ steps: array(PipelineStepInputSchema).min(1),
21692
+ frame: FrameInputSchema.optional(),
21693
+ /**
21694
+ * Process-local lazy frame. Valid only when caller and provider resolve
21695
+ * in the same execution-group process; split/cross-node callers use
21696
+ * `frame`/`image` inline compatibility instead.
21697
+ */
21698
+ frameRef: FrameRefSchema.optional(),
21699
+ /**
21700
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
21701
+ * the decoded pixels live in. One more member of the one-of
21702
+ * frame/frameHandle/image/imageBase64/referenceImage group.
21703
+ */
21704
+ frameHandle: FrameHandleSchema.optional(),
21705
+ imageBase64: string().optional(),
21706
+ /**
21707
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
21708
+ * hops (hub → forked worker via Moleculer MsgPack) because it
21709
+ * skips the 33% base64 overhead + the per-call base64 decode on
21710
+ * the detection-pipeline worker. Callers can pass either; exactly
21711
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
21712
+ */
21713
+ image: _instanceof(Uint8Array).optional(),
21714
+ referenceImage: string().optional(),
21715
+ deviceId: number$1().optional(),
21716
+ sessionId: string().optional(),
21717
+ /**
21718
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
21719
+ * reference-image, and detail-subtree calls. 'frame' is the live
21720
+ * per-frame dispatch: ONLY root-plane steps run; crop children
21721
+ * (inputClasses ≠ null) are skipped and served per-track via
21722
+ * pipelineRunner.runDetailSubtree (two-plane design).
21723
+ */
21724
+ plane: _enum(["full", "frame"]).optional(),
21725
+ /**
21726
+ * Inference-device selector (Phase 2 multi-device). Format
21727
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
21728
+ * Omitted ⇒ the runner's default device (current single-engine
21729
+ * behaviour). Selects WHICH device pool of the node runs the call.
21730
+ */
21731
+ deviceKey: string().optional(),
21732
+ /**
21733
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
21734
+ * when the parent crop was resolved from the frame's retained NATIVE
21735
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
21736
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
21737
+ * resolution from that surface — the SAME quality path faces already
21738
+ * had — instead of the downscaled parent tile. `handle` keys the native
21739
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
21740
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
21741
+ * the executor's crop-normalized child ROI back into frame-normalized
21742
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
21743
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
21744
+ * (today's behaviour on the fallback path).
21745
+ */
21746
+ nativeCropRef: NativeCropRefSchema.optional(),
21747
+ /**
21748
+ * Offline REPLAY of stored media for `deviceId` (D56, D646): the
21749
+ * camera's zone gate and overrides apply as live, every executing
21750
+ * step runs EXACTLY the model it names on the `deviceKey` pool it
21751
+ * names or the call refuses (`model-not-servable` /
21752
+ * `device-not-servable`), and nothing reaches the camera's live
21753
+ * trace stream. Absent ⇒ unchanged behaviour.
21754
+ */
21755
+ replay: literal(true).optional()
21756
+ }), PipelineRunResultBridge, { kind: "mutation" }), method(object({
21757
+ steps: array(PipelineStepInputSchema).min(1),
21758
+ frames: array(FrameInputSchema).min(1).max(255),
21759
+ deviceId: number$1().optional(),
21760
+ sessionId: string().optional(),
21761
+ /**
21762
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
21763
+ * the batch to the Python pool's bench preprocess cache
21764
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
21765
+ * preprocessed ONCE and every later inference is a pure-inference cache
21766
+ * hit — the sustained-throughput run measures inference, not
21767
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
21768
+ * full preprocess every call, correct). Fresh per sustained run;
21769
+ * released via `uncacheFrame`.
21770
+ */
21771
+ frameId: number$1().int().nonnegative().optional(),
21772
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
21773
+ deviceKey: string().optional()
21774
+ }), object({ results: array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }), method(object({
21775
+ data: _instanceof(Uint8Array),
21776
+ width: number$1().int().positive(),
21777
+ height: number$1().int().positive(),
21778
+ format: _enum([
21779
+ "rgb",
21780
+ "bgr",
21781
+ "gray"
21782
+ ])
21783
+ }), object({
21784
+ frameId: number$1(),
21785
+ width: number$1(),
21786
+ height: number$1()
21787
+ }), { kind: "mutation" }), method(object({
21788
+ stepId: string(),
21789
+ frameId: number$1().int()
21790
+ }), record(string(), unknown()), { kind: "mutation" }), method(object({ frameId: number$1().int() }), _void(), { kind: "mutation" }), method(object({ deviceKey: string().optional() }), EffectiveTuningSchema), method(_void(), array(object({
21791
+ engineKey: string(),
21792
+ engine: PipelineEngineChoiceSchema,
21793
+ modelsLoaded: array(string()).readonly(),
21794
+ inUseByCameras: array(number$1()).readonly(),
21795
+ /**
21796
+ * Origin of this resident factory.
21797
+ * - `runtime` — main camera-serving engine (no idle TTL).
21798
+ * - `device-pool` — a concurrent per-device pool (Phase 2
21799
+ * multi-device, keyed by `deviceKey`) resolved
21800
+ * via `resolveDeviceFactory`. Runs alongside the
21801
+ * `runtime` engine on a DIFFERENT accelerator
21802
+ * (NPU / iGPU / Coral) — this is how the
21803
+ * Engines tab shows all pools running at once.
21804
+ */
21805
+ kind: _enum(["runtime", "device-pool"]),
21806
+ /** Native pid of the underlying Python pool (null when no pool). */
21807
+ poolPid: number$1().nullable(),
21808
+ /** ms since this factory was last used (null when not warm-tracked). */
21809
+ idleMs: number$1().nullable(),
21810
+ /** Idle TTL after which the factory self-evicts (null when not applicable). */
21811
+ idleTtlMs: number$1().nullable()
21812
+ })).readonly()), method(object({ engine: PipelineEngineChoiceSchema }), object({ success: literal(true) }), {
21717
21813
  kind: "mutation",
21718
21814
  auth: "admin"
21719
- }), method(object({ deviceIds: array(number$1()).optional() }), object({ url: string() }), {
21720
- kind: "query",
21721
- auth: "admin"
21722
21815
  }), method(object({
21723
- /** Which of the three event tables owns the row. */
21724
- ownerType: EventOwnerTypeSchema,
21725
- /** The event's rowid in that table (D474: an event id is a number). */
21726
- eventId: number$1().int(),
21727
- kind: MediaFileKindEnum.optional(),
21728
- deviceId: number$1()
21729
- }), array(MediaFileSchema).readonly()), method(object({
21730
- trackId: string(),
21731
- kinds: array(MediaFileKindEnum).optional(),
21732
- deviceId: number$1()
21733
- }), array(MediaFileSchema).readonly()), method(object({
21734
- trackId: string(),
21735
- deviceId: number$1()
21736
- }), array(MediaFileInfoSchema).readonly()), method(object({
21737
- ownerType: EventOwnerTypeSchema,
21738
- eventId: number$1().int(),
21739
- deviceId: number$1()
21740
- }), array(MediaFileInfoSchema).readonly()), method(object({
21741
- deviceId: number$1(),
21742
- ownerKind: MediaPresenceOwnerKindSchema,
21743
- ownerIds: array(string()).max(5e3)
21744
- }), array(string()).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
21745
- kind: "mutation",
21746
- auth: "admin"
21747
- }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
21816
+ engine: PipelineEngineChoiceSchema,
21817
+ force: boolean().optional()
21818
+ }), object({
21819
+ success: boolean(),
21820
+ reason: string().optional()
21821
+ }), {
21748
21822
  kind: "mutation",
21749
21823
  auth: "admin"
21750
- }), method(object({}), RebuildStatusSchema), object({
21751
- deviceId: number$1(),
21752
- timestamp: number$1(),
21753
- frameWidth: number$1(),
21754
- frameHeight: number$1(),
21755
- detections: array(OverlayDetectionSchema).readonly()
21756
- }), object({
21757
- deviceId: number$1(),
21758
- trackId: string(),
21759
- className: string()
21760
- }), object({
21761
- deviceId: number$1(),
21762
- trackId: string(),
21763
- className: string(),
21764
- durationMs: number$1()
21765
- }), object({
21766
- deviceId: number$1(),
21767
- kind: EventKindSchema,
21768
- eventId: string(),
21769
- timestamp: number$1()
21770
- });
21824
+ }), 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({
21825
+ addonId: string(),
21826
+ modelId: string(),
21827
+ filename: string().optional(),
21828
+ settings: record(string(), unknown()).optional()
21829
+ }), AudioTestResultSchema, { kind: "mutation" }), method(_void(), ConfigUISchemaNullableBridge);
21771
21830
  object({
21772
21831
  activeCameras: number$1(),
21773
21832
  throttledCameras: number$1(),
@@ -21793,6 +21852,66 @@ var CameraMetricsSchema = object({
21793
21852
  });
21794
21853
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: number$1() });
21795
21854
  /**
21855
+ * Zone — pure geometry + identity. NO filtering behaviour.
21856
+ *
21857
+ * Zones describe **where** in the frame the operator wants to flag
21858
+ * something; consumer-owned {@link ZoneRule} arrays describe **how**
21859
+ * each pipeline stage uses them. Splitting the two means a single
21860
+ * polygon "Driveway" can simultaneously back a motion-exclude rule,
21861
+ * a detection-include rule on `['car']`, and an occupancy aggregate
21862
+ * — without three duplicated polygons.
21863
+ *
21864
+ * Owned by the orchestrator addon (provider) and mirrored into the
21865
+ * `zones` device-state slice on every mutation. Consumers
21866
+ * (motion-wasm, pipeline-executor, analytics, admin UI) read either
21867
+ * via `api.zones.listZones` (one-shot) or via `dev.state.zones` (live
21868
+ * mirror with `onChanged`).
21869
+ *
21870
+ * Coordinates are normalised fractions of the frame (0–1) so zones
21871
+ * survive resolution changes and stream profile switches.
21872
+ *
21873
+ * `kind` discriminates between full polygons (closed regions used
21874
+ * for intrusion / occupancy filters) and tripwires (open 2-point
21875
+ * line segments used for cross events). Onboard / firmware-reported
21876
+ * zones (Reolink, ONVIF) are out of scope for now — see the deferred
21877
+ * task list.
21878
+ */
21879
+ var ZoneKindEnum = _enum(["polygon", "tripwire"]);
21880
+ /** Polygon vertex in fraction-of-frame coordinates (0–1). */
21881
+ var PolygonPointSchema = object({
21882
+ x: number$1(),
21883
+ y: number$1()
21884
+ });
21885
+ /** A camera detection zone — pure geometry/identity. */
21886
+ var ZoneSchema = object({
21887
+ id: string(),
21888
+ name: string(),
21889
+ kind: ZoneKindEnum.default("polygon"),
21890
+ /** Polygon vertices, fraction of frame (0–1). */
21891
+ polygon: array(PolygonPointSchema).readonly(),
21892
+ /** Visual color for UI rendering. */
21893
+ color: string().default("#3b82f6")
21894
+ });
21895
+ DeviceType.Camera, method(object({ deviceId: number$1() }), array(ZoneSchema).readonly()), method(object({
21896
+ deviceId: number$1(),
21897
+ zone: ZoneSchema
21898
+ }), _void(), {
21899
+ kind: "mutation",
21900
+ auth: "admin"
21901
+ }), method(object({
21902
+ deviceId: number$1(),
21903
+ zoneId: string()
21904
+ }), _void(), {
21905
+ kind: "mutation",
21906
+ auth: "admin"
21907
+ }), method(object({
21908
+ deviceId: number$1(),
21909
+ zone: ZoneSchema
21910
+ }), _void(), {
21911
+ kind: "mutation",
21912
+ auth: "admin"
21913
+ }), object({ zones: array(ZoneSchema).readonly() });
21914
+ /**
21796
21915
  * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
21797
21916
  * decode worker resolves it against the RETAINED native frame's real pixel dims,
21798
21917
  * so the caller supplies only the detection-res bbox divided by the detection
@@ -21979,7 +22098,17 @@ var DetailResultSchema = object({
21979
22098
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
21980
22099
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
21981
22100
  * consumers MUST prefer this when present (2026-07-22 native-gate fix). */
21982
- nativeFaceShortSidePx: number$1().optional()
22101
+ nativeFaceShortSidePx: number$1().optional(),
22102
+ /**
22103
+ * The five facial landmarks of a `face` detail, in the SAME frame space as
22104
+ * `bbox` (D644). Additive and optional: post-analysis stores them beside the
22105
+ * face's source frame so a future alignment can be re-derived exactly; a
22106
+ * runner that predates the field sends none and the face is stored without.
22107
+ */
22108
+ landmarks: array(object({
22109
+ x: number$1(),
22110
+ y: number$1()
22111
+ })).optional()
21983
22112
  });
21984
22113
  /**
21985
22114
  * Why an executing node REFUSED a stateless step run (`runStatelessStep`).
@@ -23365,7 +23494,11 @@ function buildDescriptor(capName, kind) {
23365
23494
  icon: legacyIcon(t.iconId)
23366
23495
  };
23367
23496
  }
23368
- Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
23497
+ /**
23498
+ * Sensor / control cap name → static event-kind descriptor. A linked device
23499
+ * contributes one entry per bound cap present in this map.
23500
+ */
23501
+ var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
23369
23502
  /**
23370
23503
  * server-management — per-NODE singleton capability for a node's ROOT
23371
23504
  * package lifecycle (runtime-updatable node packages).
@@ -29508,7 +29641,30 @@ var RenameNodeResultSchema = object({
29508
29641
  nodeId: string(),
29509
29642
  name: string()
29510
29643
  });
29511
- method(_void(), array(TopologyNodeSchema).readonly(), { auth: "admin" }), method(NodeAddonInputSchema, SuccessSchema, {
29644
+ /**
29645
+ * The cheap node list. A consumer's rule for a STORED node id (a rule's
29646
+ * `nodeIds`) that is missing from `nodes`, whatever `complete` says: render it
29647
+ * as a removable chip labelled unknown, never drop it (D49). Even a complete
29648
+ * list omits a node that joined and left between two history captures, so a
29649
+ * missing id means "not known to the hub", never "removed". Only the operator
29650
+ * removes it.
29651
+ *
29652
+ * `complete` changes only the WORDING: `false` = no history read has ever
29653
+ * answered, so the list may be missing offline nodes the hub does know (D315).
29654
+ *
29655
+ * `name` is the history row's name (≤ 30 s behind a rename), or the id when
29656
+ * the history has none.
29657
+ */
29658
+ var NodeDirectorySchema = object({
29659
+ nodes: array(object({
29660
+ id: string(),
29661
+ name: string(),
29662
+ isHub: boolean(),
29663
+ isOnline: boolean()
29664
+ })).readonly(),
29665
+ complete: boolean()
29666
+ });
29667
+ method(_void(), array(TopologyNodeSchema).readonly(), { auth: "admin" }), method(_void(), NodeDirectorySchema, { auth: "admin" }), method(NodeAddonInputSchema, SuccessSchema, {
29512
29668
  kind: "mutation",
29513
29669
  auth: "admin"
29514
29670
  }), method(NodeAddonInputSchema, SuccessSchema, {
@@ -30277,7 +30433,7 @@ var HardwareInfoSchema = object({
30277
30433
  coral: CoralInfoSchema.nullable().optional()
30278
30434
  });
30279
30435
  var PlatformScoreSchema = object({
30280
- runtime: _enum(["node", "python"]),
30436
+ runtime: literal("python"),
30281
30437
  backend: string(),
30282
30438
  format: _enum([
30283
30439
  "onnx",
@@ -30314,7 +30470,7 @@ var ModelRequirementSchema = object({
30314
30470
  });
30315
30471
  var ResolvedInferenceConfigSchema = object({
30316
30472
  modelId: string(),
30317
- runtime: _enum(["node", "python"]),
30473
+ runtime: literal("python"),
30318
30474
  backend: string(),
30319
30475
  format: ModelFormatSchema,
30320
30476
  reason: string()
@@ -32521,6 +32677,92 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), SceneMonitorStatusSc
32521
32677
  kind: "mutation",
32522
32678
  auth: "admin"
32523
32679
  });
32680
+ /**
32681
+ * Per-stage gating mode applied to the zones a rule references.
32682
+ *
32683
+ * - `include`: the rule contributes to a **whitelist** for its stage.
32684
+ * When at least one `include` rule fires for a stage, only entities
32685
+ * inside one of those zones pass that stage.
32686
+ * - `exclude`: the rule contributes to a **blacklist** for its stage.
32687
+ * Entities inside one of those zones are dropped at that stage.
32688
+ *
32689
+ * `monitor`-style observation (count without filtering) is not a rule
32690
+ * mode — zones without any matching rule are observed naturally by
32691
+ * `zone-analytics` (live snapshot + history), so an "I just want to
32692
+ * count, not filter" use case needs no rule at all.
32693
+ */
32694
+ var ZoneRuleModeEnum = _enum(["include", "exclude"]);
32695
+ /**
32696
+ * Per-consumer rule that references existing zones (geometry) and
32697
+ * defines how a specific pipeline stage should treat them. Each
32698
+ * consumer addon owns its own `ZoneRule[]` array in its per-device
32699
+ * settings:
32700
+ *
32701
+ * - `addon-motion-wasm` → `motionZoneRules: ZoneRule[]` (motion stage)
32702
+ * - `addon-detection-pipeline` → `detectionZoneRules: ZoneRule[]` (detection stage)
32703
+ * - future: notification rules, audio gating, etc.
32704
+ *
32705
+ * One rule applies to N zones (`zoneIds[]`) so the operator can
32706
+ * express "ignore motion in ALL of {garden, street}" with a single
32707
+ * rule. `classFilter` narrows the rule to specific object classes —
32708
+ * "drop person detections in the street, but keep cars" is one
32709
+ * `exclude` rule with `classFilter: ['person']`.
32710
+ *
32711
+ * `enabled` is a soft toggle — the operator can keep the rule
32712
+ * configured but inert without deleting it.
32713
+ */
32714
+ var ZoneRuleSchema = object({
32715
+ /** Stable rule id — survives edits, used by the UI for diffing. */
32716
+ id: string(),
32717
+ /** Optional human-readable label rendered in the rule editor. */
32718
+ name: string().optional(),
32719
+ /** Zones this rule targets. The rule's `mode` applies to ALL
32720
+ * listed zones (OR-set: a detection in any one of them counts).
32721
+ * At least one zone id required — a rule with no targets is a
32722
+ * configuration mistake and the form validator rejects it. */
32723
+ zoneIds: array(string()).min(1).readonly(),
32724
+ mode: ZoneRuleModeEnum,
32725
+ /**
32726
+ * Class names this rule applies to. Empty / undefined ⇒ rule
32727
+ * applies to every class. Class strings match the `macroClass`
32728
+ * field on detections (e.g. `person`, `car`, `dog`).
32729
+ */
32730
+ classFilter: array(string()).readonly().optional(),
32731
+ /**
32732
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
32733
+ * required to consider an entity "in the zone". Defaults to the
32734
+ * consumer's stage default when omitted. Kept for back-compat with
32735
+ * existing per-rule overrides; new operators pick the value via
32736
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
32737
+ * set, the lower-level engine reads it as a 0–1 fraction.
32738
+ */
32739
+ overlapThreshold: number$1().min(0).max(1).optional(),
32740
+ /**
32741
+ * Operator-friendly version of `overlapThreshold` — the percentage
32742
+ * of the detection's bbox that must lie inside the zone for the
32743
+ * rule to match. Documented default is 85%; the engine substitutes
32744
+ * that when the field is omitted (kept optional so existing rules
32745
+ * stored without it stay valid).
32746
+ *
32747
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
32748
+ * rule, the engine prefers `bboxInclusionPct` because it's the
32749
+ * field exposed in the UI. Internally both feed the same gate.
32750
+ */
32751
+ bboxInclusionPct: number$1().min(0).max(100).optional(),
32752
+ /**
32753
+ * When `true` and a detection has a segmentation mask, use the
32754
+ * mask for overlap instead of the bbox. Detection-stage only;
32755
+ * motion rules ignore this field.
32756
+ */
32757
+ preferMask: boolean().optional(),
32758
+ /**
32759
+ * Soft-toggle: `false` disables the rule without deleting it.
32760
+ * Defaults to `true` so operators creating a rule via the UI
32761
+ * see it active immediately.
32762
+ */
32763
+ enabled: boolean().default(true)
32764
+ });
32765
+ array(ZoneRuleSchema).readonly();
32524
32766
  object({
32525
32767
  /** Whether the script is currently executing. */
32526
32768
  isRunning: boolean(),
@@ -33902,6 +34144,42 @@ DeviceType.Camera, method(object({
33902
34144
  package: array(ZoneRuleSchema).readonly()
33903
34145
  });
33904
34146
  /**
34147
+ * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
34148
+ * valutatore puro del suo FRONTE.
34149
+ *
34150
+ * Viveva dentro il builtin virtual-doorbell
34151
+ * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
34152
+ * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
34153
+ * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
34154
+ * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
34155
+ * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
34156
+ * SPOSTA qui e il doorbell la ri-esporta.
34157
+ *
34158
+ * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
34159
+ * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
34160
+ * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
34161
+ * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
34162
+ */
34163
+ /**
34164
+ * Known binary / switch source caps → the boolean slice field whose
34165
+ * false→true rise counts as ACTIVE. Every entry is "fire on active".
34166
+ * Sensors whose "active" reading is not a plain boolean (presence's string
34167
+ * state, connectivity's connected flag) are deliberately excluded — a
34168
+ * reconnect is not a doorbell press, and it is not a recording either.
34169
+ */
34170
+ var SOURCE_CAP_ACTIVE_FIELD = {
34171
+ contact: "entryOpen",
34172
+ binary: "on",
34173
+ switch: "on",
34174
+ motion: "detected",
34175
+ flood: "flooded",
34176
+ gas: "detected",
34177
+ smoke: "detected",
34178
+ "carbon-monoxide": "detected",
34179
+ vibration: "detected",
34180
+ tamper: "tampered"
34181
+ };
34182
+ /**
33905
34183
  * Accessory device helpers — shared across drivers.
33906
34184
  *
33907
34185
  * Many vendor-specific drivers register accessory child devices on
@@ -38633,6 +38911,12 @@ Object.freeze({
38633
38911
  addonId: null,
38634
38912
  access: "create"
38635
38913
  },
38914
+ "nodes.directory": {
38915
+ capName: "nodes",
38916
+ capScope: "system",
38917
+ addonId: null,
38918
+ access: "view"
38919
+ },
38636
38920
  "nodes.executeQuery": {
38637
38921
  capName: "nodes",
38638
38922
  capScope: "system",
@@ -38801,6 +39085,12 @@ Object.freeze({
38801
39085
  addonId: null,
38802
39086
  access: "view"
38803
39087
  },
39088
+ "notificationRules.getRuleLastRuns": {
39089
+ capName: "notification-rules",
39090
+ capScope: "system",
39091
+ addonId: null,
39092
+ access: "view"
39093
+ },
38804
39094
  "notificationRules.getTemplateCatalog": {
38805
39095
  capName: "notification-rules",
38806
39096
  capScope: "system",
@@ -38825,6 +39115,12 @@ Object.freeze({
38825
39115
  addonId: null,
38826
39116
  access: "view"
38827
39117
  },
39118
+ "notificationRules.previewStateSummary": {
39119
+ capName: "notification-rules",
39120
+ capScope: "system",
39121
+ addonId: null,
39122
+ access: "view"
39123
+ },
38828
39124
  "notificationRules.previewTemplate": {
38829
39125
  capName: "notification-rules",
38830
39126
  capScope: "system",
@@ -39401,12 +39697,6 @@ Object.freeze({
39401
39697
  addonId: null,
39402
39698
  access: "create"
39403
39699
  },
39404
- "pipelineAnalytics.runReplayFrameProcessor": {
39405
- capName: "pipeline-analytics",
39406
- capScope: "device",
39407
- addonId: null,
39408
- access: "create"
39409
- },
39410
39700
  "pipelineAnalytics.saveRetrainAnnotations": {
39411
39701
  capName: "pipeline-analytics",
39412
39702
  capScope: "device",
@@ -43504,11 +43794,6 @@ Object.freeze({
43504
43794
  form: "single",
43505
43795
  optional: false
43506
43796
  }],
43507
- "pipelineAnalytics.runReplayFrameProcessor": [{
43508
- name: "deviceId",
43509
- form: "single",
43510
- optional: false
43511
- }],
43512
43797
  "pipelineAnalytics.saveRetrainAnnotations": [{
43513
43798
  name: "deviceId",
43514
43799
  form: "single",
@@ -44397,11 +44682,232 @@ var NC_AUDIO_DEFAULTS = {
44397
44682
  samplingSeconds: 10
44398
44683
  };
44399
44684
  new Set(["devices", "classes"]);
44685
+ /**
44686
+ * WHICH narrowing filter speaks about WHICH system-event kind — one predicate,
44687
+ * consumed by the rule ENGINE and by every rule EDITOR.
44688
+ *
44689
+ * ── The failure this file exists for ───────────────────────────────────────
44690
+ * `NcSystemEventConditionSchema` carries four optional narrowing lists
44691
+ * (`deviceIds`, `deviceTypes`, `nodeIds`, `packageNames`) beside a required
44692
+ * `kinds` gate. They are not four filters over one vocabulary: each belongs to
44693
+ * a FAMILY of kinds and is meaningless outside it. A node going away has no
44694
+ * device type; a completed export has no package; `stream-offline` deliberately
44695
+ * carries no device type at all (a stream belongs to a camera by construction).
44696
+ *
44697
+ * The engine applied all four to every kind, so a filter from one family failed
44698
+ * a kind from another family CLOSED. The operator's live rule "Eventi di
44699
+ * sistema" selected eleven kinds and set `deviceTypes: ['camera']` — the only
44700
+ * way to say "only my cameras" about the two liveness kinds — and NINE of the
44701
+ * eleven silently stopped being deliverable. Four days of history contained
44702
+ * `device-online` and `device-offline` and nothing else, across a window in
44703
+ * which agents were restarted and addons were updated.
44704
+ *
44705
+ * Both editors already computed this scoping, to decide which filter boxes to
44706
+ * render; the cap's own docblock already promised it ("Narrow a `device-*` kind
44707
+ * to these device TYPES"). Only the engine disagreed, and it disagreed in the
44708
+ * destructive direction. So the rule is stated ONCE, here, and the engine and
44709
+ * the editors are readers of it rather than three independent authors.
44710
+ *
44711
+ * ── The direction ─────────────────────────────────────────────────────────
44712
+ * Out of family ⇒ the filter is IGNORED, never fail-closed. An editor that
44713
+ * hides a box has already told the operator this filter does not speak about
44714
+ * these kinds, so honouring it as an exclusion contradicts the only
44715
+ * explanation the operator was ever given. Inside its family the filter still
44716
+ * narrows, and still fails CLOSED on a subject that cannot produce the identity
44717
+ * it tests (a device the mirror cannot name is not evidence of a camera).
44718
+ */
44719
+ /**
44720
+ * Every filter, for the callers that must cover all of them (guards, tests).
44721
+ *
44722
+ * `consumableLowPercent` (D642) is not a list but it is a filter in exactly
44723
+ * this sense: it belongs to a family of kinds (the consumable pair) and the
44724
+ * engine consults it only there. It differs in one way the readers must know —
44725
+ * its ABSENCE is not "no narrowing" but the default threshold, because a
44726
+ * consumable episode is always judged at SOME threshold.
44727
+ */
44728
+ var NC_SYSTEM_EVENT_FILTER_KEYS = [
44729
+ "deviceIds",
44730
+ "deviceTypes",
44731
+ "nodeIds",
44732
+ "packageNames",
44733
+ "consumableLowPercent"
44734
+ ];
44735
+ new Map(Object.entries({
44736
+ "device-online": "device-events",
44737
+ "device-offline": "device-events",
44738
+ "device-disabled": "device-events",
44739
+ "device-enabled": "device-events",
44740
+ "device-battery-low": "device-events",
44741
+ "device-battery-normal": "device-events",
44742
+ "device-consumable-low": "device-events",
44743
+ "device-consumable-normal": "device-events",
44744
+ "device-tank-low": "device-events",
44745
+ "device-tank-full": "device-events",
44746
+ "device-tank-normal": "device-events",
44747
+ "stream-online": "device-events",
44748
+ "stream-offline": "device-events",
44749
+ "detection-blind": "device-events",
44750
+ "node-online": "system",
44751
+ "node-offline": "system",
44752
+ "node-inference-unavailable": "system",
44753
+ "addon-crash-loop": "system",
44754
+ "addon-update-available": "system",
44755
+ "addon-updated": "system",
44756
+ "server-update-available": "system",
44757
+ "server-updated": "system",
44758
+ "wrapper-update-available": "system",
44759
+ "backup-completed": "system",
44760
+ "export-completed": "system",
44761
+ "alarm-triggered": "alarm",
44762
+ "alarm-armed": "alarm",
44763
+ "alarm-disarmed": "alarm",
44764
+ "alarm-arming": "alarm",
44765
+ "alarm-arm-refused": "alarm",
44766
+ "alarm-pending": "alarm",
44767
+ "alarm-rearmed": "alarm",
44768
+ "alarm-sensor-bypass": "alarm",
44769
+ "alarm-check-failed": "alarm",
44770
+ "camera-online": null,
44771
+ "camera-offline": null,
44772
+ "camera-disabled": null,
44773
+ "camera-enabled": null
44774
+ }));
44775
+ [...NC_SYSTEM_EVENT_FILTER_KEYS];
44776
+ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
44777
+ {
44778
+ id: "device-state",
44779
+ family: "device-events",
44780
+ label: "Device state",
44781
+ kinds: [
44782
+ "device-offline",
44783
+ "device-online",
44784
+ "device-disabled",
44785
+ "device-enabled"
44786
+ ]
44787
+ },
44788
+ {
44789
+ id: "streams",
44790
+ family: "device-events",
44791
+ label: "Streams",
44792
+ kinds: ["stream-offline", "stream-online"]
44793
+ },
44794
+ {
44795
+ id: "battery",
44796
+ family: "device-events",
44797
+ label: "Battery",
44798
+ kinds: ["device-battery-low", "device-battery-normal"]
44799
+ },
44800
+ {
44801
+ id: "consumables",
44802
+ family: "device-events",
44803
+ label: "Consumables",
44804
+ kinds: ["device-consumable-low", "device-consumable-normal"]
44805
+ },
44806
+ {
44807
+ id: "tanks",
44808
+ family: "device-events",
44809
+ label: "Tanks",
44810
+ kinds: [
44811
+ "device-tank-low",
44812
+ "device-tank-full",
44813
+ "device-tank-normal"
44814
+ ]
44815
+ },
44816
+ {
44817
+ id: "detection-blind",
44818
+ family: "device-events",
44819
+ label: "Detecting nothing",
44820
+ kinds: ["detection-blind"]
44821
+ },
44822
+ {
44823
+ id: "nodes",
44824
+ family: "system",
44825
+ label: "Nodes",
44826
+ kinds: [
44827
+ "node-offline",
44828
+ "node-online",
44829
+ "node-inference-unavailable"
44830
+ ]
44831
+ },
44832
+ {
44833
+ id: "addons",
44834
+ family: "system",
44835
+ label: "Addons",
44836
+ kinds: [
44837
+ "addon-crash-loop",
44838
+ "addon-update-available",
44839
+ "addon-updated"
44840
+ ]
44841
+ },
44842
+ {
44843
+ id: "updates",
44844
+ family: "system",
44845
+ label: "Server & container updates",
44846
+ kinds: [
44847
+ "server-update-available",
44848
+ "server-updated",
44849
+ "wrapper-update-available"
44850
+ ]
44851
+ },
44852
+ {
44853
+ id: "backups-exports",
44854
+ family: "system",
44855
+ label: "Backups & exports",
44856
+ kinds: ["backup-completed", "export-completed"]
44857
+ },
44858
+ {
44859
+ id: "alarm",
44860
+ family: "alarm",
44861
+ label: "Alarm",
44862
+ kinds: NC_ALARM_SYSTEM_EVENT_KINDS
44863
+ }
44864
+ ];
44865
+ function systemEventKindsOfFamily(family) {
44866
+ return NC_SYSTEM_EVENT_KIND_GROUP_SPECS.filter((g) => g.family === family).flatMap((g) => g.kinds);
44867
+ }
44400
44868
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
44869
+ /**
44870
+ * WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
44871
+ * persists (D640 "a sensor rule fires on the raise, the clear, or both").
44872
+ *
44873
+ * A device-event rule over a flood sensor notified on BOTH edges: water
44874
+ * detected, and back to dry. `NcConditions.sensorTransition` lets the rule
44875
+ * name one edge; this file answers, for one `SensorEvent`, which edge it is.
44876
+ *
44877
+ * NOT A SECOND TABLE. The "which boolean of this cap counts as HIGH" question
44878
+ * already has one answer, `SOURCE_CAP_ACTIVE_FIELD`
44879
+ * (`catalogs/sensor-active-state.ts`, the doorbell's and the recorder's edge
44880
+ * table), and a sensor event names its kind, not its cap. So the table here is
44881
+ * DERIVED: event kind → cap (`EVENT_KIND_BY_CAP`) → that cap's active field.
44882
+ * Only kinds whose taxonomy category is `sensor` take part — a switch has an
44883
+ * `on` boolean too, but "it triggers / it clears" is not a sentence about a
44884
+ * light, and offering it there would be a knob with a misleading label.
44885
+ * `presence` is a WORD (`home` / `not_home` / a zone), not a boolean, and is
44886
+ * absent from the source table, so it is absent here.
44887
+ *
44888
+ * `undefined` = never guessed: a kind with no raise (a doorbell press, a
44889
+ * button, a cover), or a slice whose field is missing or not a boolean. The
44890
+ * engine then does NOT filter — a flood alert must not go silent because one
44891
+ * read came back unreadable (the same stance as the alarm's `sensorEdgeOf`).
44892
+ *
44893
+ * Pure, RN-safe: the viewer imports the support rule from here.
44894
+ */
44895
+ /** Event kind → the boolean field of its cap slice that means "raised". */
44896
+ var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.entries(EVENT_KIND_BY_CAP).flatMap(([capName, descriptor]) => {
44897
+ const field = SOURCE_CAP_ACTIVE_FIELD[capName];
44898
+ if (field === void 0 || descriptor.category !== "sensor") return [];
44899
+ return [[descriptor.kind, field]];
44900
+ })));
44901
+ Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
44401
44902
  var RULE_BODY_ONLY = ["body"];
44402
44903
  var RULE_TEXT = ["title", "body"];
44403
44904
  var BATTERY = ["device-battery-low", "device-battery-normal"];
44404
44905
  var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
44906
+ var TANK = [
44907
+ "device-tank-low",
44908
+ "device-tank-full",
44909
+ "device-tank-normal"
44910
+ ];
44405
44911
  var UPDATES = [
44406
44912
  "addon-update-available",
44407
44913
  "server-update-available",
@@ -44427,12 +44933,189 @@ var DEVICE_EVENTS = [
44427
44933
  "device-enabled",
44428
44934
  ...BATTERY,
44429
44935
  ...CONSUMABLE,
44936
+ ...TANK,
44430
44937
  "stream-online",
44431
44938
  "stream-offline",
44432
44939
  "detection-blind",
44433
44940
  ...ALARM
44434
44941
  ];
44435
- [...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];
44942
+ /** Every kind a summary may speak about — the two summarisable families. */
44943
+ var SUMMARISABLE = [...systemEventKindsOfFamily("device-events"), ...systemEventKindsOfFamily("system")];
44944
+ /** One per summary SECTION (`NC_STATE_SUMMARY_SECTIONS` in the notification
44945
+ * centre): the section's lines, offered when one of its kinds is selected. */
44946
+ var STATE_SUMMARY_SECTION_VARS = [
44947
+ {
44948
+ name: "summaryDevices",
44949
+ label: "Summary: devices offline / switched off",
44950
+ example: "Garage — offline since 07:12",
44951
+ kinds: [
44952
+ "device-offline",
44953
+ "device-online",
44954
+ "device-disabled",
44955
+ "device-enabled"
44956
+ ]
44957
+ },
44958
+ {
44959
+ name: "summaryStreams",
44960
+ label: "Summary: streams offline",
44961
+ example: "Ingresso (high) — since 06:40",
44962
+ kinds: ["stream-offline", "stream-online"]
44963
+ },
44964
+ {
44965
+ name: "summaryBattery",
44966
+ label: "Summary: low batteries",
44967
+ example: "Sensore porta — 8 %",
44968
+ kinds: BATTERY
44969
+ },
44970
+ {
44971
+ name: "summaryConsumables",
44972
+ label: "Summary: consumables",
44973
+ example: "Robot — filter 10 %",
44974
+ kinds: CONSUMABLE
44975
+ },
44976
+ {
44977
+ name: "summaryTanks",
44978
+ label: "Summary: tanks",
44979
+ example: "Robot — clean water low",
44980
+ kinds: TANK
44981
+ },
44982
+ {
44983
+ name: "summaryDetection",
44984
+ label: "Summary: cameras detecting nothing",
44985
+ example: "Ingresso — since 05:10",
44986
+ kinds: ["detection-blind"]
44987
+ },
44988
+ {
44989
+ name: "summaryNodes",
44990
+ label: "Summary: nodes",
44991
+ example: "little-unraid — offline since 03:00",
44992
+ kinds: [
44993
+ "node-offline",
44994
+ "node-online",
44995
+ "node-inference-unavailable"
44996
+ ]
44997
+ },
44998
+ {
44999
+ name: "summaryAddons",
45000
+ label: "Summary: addons",
45001
+ example: "@camstack/addon-recorder 1.2.3 → 1.2.4",
45002
+ kinds: [
45003
+ "addon-crash-loop",
45004
+ "addon-update-available",
45005
+ "addon-updated"
45006
+ ]
45007
+ },
45008
+ {
45009
+ name: "summaryUpdates",
45010
+ label: "Summary: server & container updates",
45011
+ example: "server 1.2.377 → 1.2.378",
45012
+ kinds: [
45013
+ "server-update-available",
45014
+ "server-updated",
45015
+ "wrapper-update-available"
45016
+ ]
45017
+ }
45018
+ ];
45019
+ var STATE_SUMMARY_VARS = [
45020
+ {
45021
+ name: "summaryText",
45022
+ label: "Summary: every section",
45023
+ description: "One block per section with something to report, one line per item.",
45024
+ example: "Batterie scariche: Sensore porta — 8 %",
45025
+ group: "system",
45026
+ families: ["rule"],
45027
+ fields: [...RULE_TEXT],
45028
+ kinds: ["system"],
45029
+ systemEventKinds: [...SUMMARISABLE],
45030
+ systemEventDeliveries: ["summary"]
45031
+ },
45032
+ {
45033
+ name: "summaryCount",
45034
+ label: "Summary: items reported",
45035
+ example: "3",
45036
+ group: "system",
45037
+ families: ["rule"],
45038
+ fields: [...RULE_TEXT],
45039
+ kinds: ["system"],
45040
+ systemEventKinds: [...SUMMARISABLE],
45041
+ systemEventDeliveries: ["summary"]
45042
+ },
45043
+ ...STATE_SUMMARY_SECTION_VARS.map((v) => ({
45044
+ name: v.name,
45045
+ label: v.label,
45046
+ example: v.example,
45047
+ group: "system",
45048
+ families: ["rule"],
45049
+ fields: [...RULE_TEXT],
45050
+ kinds: ["system"],
45051
+ systemEventKinds: [...v.kinds],
45052
+ systemEventDeliveries: ["summary"]
45053
+ }))
45054
+ ];
45055
+ [
45056
+ [...DEVICE_EVENTS],
45057
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45058
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45059
+ [...RULE_TEXT],
45060
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45061
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45062
+ [...RULE_TEXT],
45063
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45064
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45065
+ ([...RULE_TEXT], [...COMBINED_ALARM]),
45066
+ [...RULE_TEXT],
45067
+ [...RULE_TEXT],
45068
+ [...RULE_TEXT],
45069
+ [...RULE_TEXT],
45070
+ [...RULE_TEXT],
45071
+ [...RULE_TEXT],
45072
+ [...RULE_TEXT],
45073
+ [...RULE_TEXT],
45074
+ [...RULE_BODY_ONLY],
45075
+ [...RULE_TEXT],
45076
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
45077
+ ([...RULE_TEXT], [...DEVICE_EVENTS]),
45078
+ ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
45079
+ ([...RULE_TEXT], [...CONSUMABLE]),
45080
+ ([...RULE_TEXT], [...TANK]),
45081
+ [...RULE_TEXT],
45082
+ ([...RULE_TEXT], [...UPDATES]),
45083
+ ([...RULE_TEXT], [...UPDATES]),
45084
+ [...RULE_TEXT],
45085
+ [...RULE_TEXT],
45086
+ [...RULE_TEXT],
45087
+ ([...RULE_TEXT], [...UPDATES]),
45088
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45089
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45090
+ ([...RULE_TEXT], [...UPDATES]),
45091
+ ([...RULE_TEXT], [...UPDATES]),
45092
+ ([...RULE_TEXT], [...PACKAGE_UPDATES]),
45093
+ [...RULE_TEXT],
45094
+ [...RULE_TEXT],
45095
+ [...RULE_TEXT],
45096
+ [...RULE_TEXT],
45097
+ [...RULE_TEXT],
45098
+ [...RULE_TEXT],
45099
+ [...RULE_TEXT],
45100
+ [...RULE_TEXT],
45101
+ [...RULE_TEXT],
45102
+ [...RULE_TEXT],
45103
+ [...RULE_TEXT],
45104
+ [...RULE_TEXT],
45105
+ [...RULE_TEXT],
45106
+ [...RULE_TEXT],
45107
+ [...RULE_TEXT],
45108
+ [...RULE_TEXT],
45109
+ [...RULE_TEXT],
45110
+ [...RULE_TEXT],
45111
+ [...RULE_TEXT],
45112
+ [...RULE_TEXT],
45113
+ [...RULE_TEXT],
45114
+ [...RULE_TEXT],
45115
+ [...RULE_TEXT],
45116
+ [...RULE_TEXT],
45117
+ ...STATE_SUMMARY_VARS
45118
+ ];
44436
45119
  /**
44437
45120
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
44438
45121
  *