@camstack/addon-pipeline 1.2.292 → 1.2.294

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-DcTy-nJl.mjs → default-detection-model-0dPKRKUD.mjs} +62 -80
  4. package/dist/{default-detection-model-jjdmKKr3.js → default-detection-model-D24AJOTn.js} +67 -79
  5. package/dist/detection-pipeline/index.js +6 -5
  6. package/dist/detection-pipeline/index.mjs +6 -5
  7. package/dist/{dist-D7FonEUB.js → dist-CJR259Xf.js} +426 -22
  8. package/dist/{dist-Bn6BQW8q.mjs → dist-RXbmRAwP.mjs} +426 -22
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-D3C1krEi.js → node-DWg9zbY1.js} +1 -1
  12. package/dist/{node-BN0Na3HN.mjs → node-atmRSHPk.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +80 -5
  14. package/dist/pipeline-runner/index.mjs +80 -5
  15. package/dist/{process-memory-DI4jrCtX.js → process-memory-BJUXvTjd.js} +1 -1
  16. package/dist/{process-memory-CeZ8ms80.mjs → process-memory-BgFOHFnx.mjs} +1 -1
  17. package/dist/recorder/index.js +2 -2
  18. package/dist/recorder/index.mjs +2 -2
  19. package/dist/{segment-demux-js-COooJ-t0.js → segment-demux-js-C_fPJub3.js} +1 -1
  20. package/dist/{segment-demux-js-CNPzlcyO.mjs → segment-demux-js-G7wFpHzn.mjs} +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BfrONGaR.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-6IyM-BIn.mjs} +2 -2
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6IHzlLJ_.mjs +26 -0
  24. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DhImOdbA.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DoyA71_q.mjs} +1 -1
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-0k0JYZnk.mjs → hostInit-BBYHWS3M.mjs} +2 -2
  28. package/dist/stream-broker/index.js +3 -3
  29. package/dist/stream-broker/index.mjs +3 -3
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +1 -1
  32. package/python/inference_pool.py +33 -10
  33. package/python/postprocessors/scrfd.py +21 -3
  34. package/python/postprocessors/test_scrfd.py +151 -0
  35. package/python/test_inference_pool_ov_ppp_scrfd.py +264 -0
  36. package/python/test_inference_pool_scrfd_normalization.py +142 -0
  37. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-pSFdAaR0.mjs +0 -26
@@ -5360,7 +5360,7 @@ var ZodIssueCode = {
5360
5360
  var ZodFirstPartyTypeKind;
5361
5361
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5362
5362
  //#endregion
5363
- //#region ../types/dist/sleep-D_4W7xuQ.mjs
5363
+ //#region ../types/dist/sleep-DU7mu4wb.mjs
5364
5364
  /**
5365
5365
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5366
5366
  * window to float samples (D455).
@@ -11421,10 +11421,17 @@ var ModelCatalogEntrySchema = object({
11421
11421
  inputChannels: number().int().positive().optional(),
11422
11422
  labels: array(LabelDefinitionSchema).readonly(),
11423
11423
  inputLayout: _enum(["nchw", "nhwc"]).optional(),
11424
+ /**
11425
+ * `'scrfd'` applies InsightFace's SCRFD input contract, `(x - 127.5) / 128`
11426
+ * (upstream `insightface/model_zoo/scrfd.py`), instead of the historical
11427
+ * plain `/255`. Measured against COCO GT: 188 → 196 faces found on the
11428
+ * `scrfd-2.5g` catalog entry (2026-09-26 model-replacement spike, §2.4-2).
11429
+ */
11424
11430
  inputNormalization: _enum([
11425
11431
  "zero-one",
11426
11432
  "imagenet",
11427
- "none"
11433
+ "none",
11434
+ "scrfd"
11428
11435
  ]).optional(),
11429
11436
  /**
11430
11437
  * The model already applies softmax IN-GRAPH — its raw output is a
@@ -11526,7 +11533,8 @@ var ModelConvertMetadataSchema = object({
11526
11533
  inputNormalization: _enum([
11527
11534
  "zero-one",
11528
11535
  "imagenet",
11529
- "none"
11536
+ "none",
11537
+ "scrfd"
11530
11538
  ]).optional(),
11531
11539
  preprocessMode: _enum(["letterbox", "resize"]).optional(),
11532
11540
  outputFormat: _enum([
@@ -16965,18 +16973,30 @@ var LlmErrorCodeSchema = _enum([
16965
16973
  "budget-exceeded",
16966
16974
  "adapter-error"
16967
16975
  ]);
16968
- var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
16976
+ var LlmGenerateOkSchema = object({
16969
16977
  ok: literal(true),
16970
16978
  text: string(),
16971
16979
  model: string(),
16972
16980
  usage: LlmUsageSchema,
16973
16981
  truncated: boolean(),
16974
16982
  latencyMs: number()
16975
- }), object({
16983
+ });
16984
+ /**
16985
+ * WHO refused, on a `refusal`. `guardrail` = a safety filter in front of the
16986
+ * model rejected the request before it answered (Apple Foundation Models
16987
+ * refuses surveillance-framed prompts in 0.1 s); `model` = the model itself
16988
+ * declined. They are different faults: a guardrail refusal is a property of
16989
+ * the BACKEND, so the provider falls back to the next profile; a model refusal
16990
+ * is an answer. Absent on every other code, and on a refusal a wire cannot
16991
+ * attribute.
16992
+ */
16993
+ var LlmRefusalSourceSchema = _enum(["guardrail", "model"]);
16994
+ var LlmGenerateResultSchema = discriminatedUnion("ok", [LlmGenerateOkSchema, object({
16976
16995
  ok: literal(false),
16977
16996
  code: LlmErrorCodeSchema,
16978
16997
  message: string(),
16979
- retryAfterMs: number().optional()
16998
+ retryAfterMs: number().optional(),
16999
+ refusalSource: LlmRefusalSourceSchema.optional()
16980
17000
  })]);
16981
17001
  /**
16982
17002
  * `Uint8Array` is the sanctioned binary convention — superjson + the UDS
@@ -17206,12 +17226,78 @@ var LlmRuntimeDiskUsageSchema = object({
17206
17226
  modelsBytes: number(),
17207
17227
  freeBytes: number().optional()
17208
17228
  });
17209
- method(LlmGenerateBaseInputSchema.extend({
17229
+ var LlmRuntimeCompleteInputSchema = LlmGenerateBaseInputSchema.extend({
17210
17230
  images: array(LlmImageSchema).optional(),
17211
17231
  runtime: ManagedRuntimeConfigSchema,
17212
17232
  /** The managed profile's timeout, threaded by the hub provider. */
17213
17233
  timeoutMs: number().int().positive().optional()
17214
- }), LlmGenerateResultSchema, {
17234
+ });
17235
+ /**
17236
+ * Why Apple Foundation Models cannot run on a node that has it in principle.
17237
+ * The first three are Apple's own `UnavailableReason`s, verbatim; the rest are
17238
+ * ours: the OS predates the framework (macOS < 26), the helper binary is not
17239
+ * in the installed addon, or it crashed past the respawn bound.
17240
+ */
17241
+ var AppleFmUnavailableReasonSchema = _enum([
17242
+ "deviceNotEligible",
17243
+ "appleIntelligenceNotEnabled",
17244
+ "modelNotReady",
17245
+ "osTooOld",
17246
+ "helperMissing",
17247
+ "helperCrashed"
17248
+ ]);
17249
+ /**
17250
+ * Apple Foundation Models on ONE node — four states, never a boolean (D315).
17251
+ *
17252
+ * - `available` — the helper probed it; what it CAN do is reported
17253
+ * here (`imageInput`, `contextSize`, languages),
17254
+ * never typed by an operator.
17255
+ * - `unavailable` — a known absence, with the reason to act on.
17256
+ * - `unsupported-platform` — not a Mac; there is nothing to enable.
17257
+ * - `unknown` — the probe did not answer (not yet run, timed
17258
+ * out, helper crashed mid-probe). A failed
17259
+ * measurement is NEVER `unavailable` (D393).
17260
+ */
17261
+ var AppleFmAvailabilitySchema = discriminatedUnion("state", [
17262
+ object({
17263
+ state: literal("available"),
17264
+ osVersion: string(),
17265
+ /** Tokens, as the framework reports it. Read at runtime, never assumed. */
17266
+ contextSize: number().int().positive(),
17267
+ /** BCP-47-ish tags (`it-IT`) of the languages the model supports. */
17268
+ languages: array(string()),
17269
+ /** Whether prompts may carry images. `false` on a macOS 26 build. */
17270
+ imageInput: boolean()
17271
+ }),
17272
+ object({
17273
+ state: literal("unavailable"),
17274
+ reason: AppleFmUnavailableReasonSchema,
17275
+ osVersion: string().nullable(),
17276
+ detail: string()
17277
+ }),
17278
+ object({
17279
+ state: literal("unsupported-platform"),
17280
+ platform: string()
17281
+ }),
17282
+ object({
17283
+ state: literal("unknown"),
17284
+ error: string()
17285
+ })
17286
+ ]);
17287
+ /**
17288
+ * A generation on the node's Apple Foundation Models helper. A separate verb
17289
+ * from `complete`, whose `runtime` is a llama.cpp config: overloading it would
17290
+ * drag a GGUF model reference into a backend that has none. `images` is part
17291
+ * of the contract so vision needs no cap change later; a node whose model has
17292
+ * no image input REFUSES them (`unavailable`), never drops them.
17293
+ */
17294
+ var LlmRuntimeAppleCompleteInputSchema = LlmGenerateBaseInputSchema.extend({
17295
+ images: array(LlmImageSchema).optional(),
17296
+ /** The profile's timeout, threaded by the hub provider. */
17297
+ timeoutMs: number().int().positive().optional()
17298
+ });
17299
+ var LlmRuntimeCancelInputSchema = object({ requestId: string() });
17300
+ method(LlmRuntimeCompleteInputSchema, LlmGenerateResultSchema, {
17215
17301
  kind: "mutation",
17216
17302
  auth: "admin",
17217
17303
  timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
@@ -17229,7 +17315,14 @@ method(LlmGenerateBaseInputSchema.extend({
17229
17315
  }), method(object({ file: string() }), _void(), {
17230
17316
  kind: "mutation",
17231
17317
  auth: "admin"
17232
- }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" });
17318
+ }), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" }), method(LlmRuntimeCancelInputSchema, _void(), {
17319
+ kind: "mutation",
17320
+ auth: "admin"
17321
+ }), method(object({}), AppleFmAvailabilitySchema, { auth: "admin" }), method(LlmRuntimeAppleCompleteInputSchema, LlmGenerateResultSchema, {
17322
+ kind: "mutation",
17323
+ auth: "admin",
17324
+ timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
17325
+ });
17233
17326
  /**
17234
17327
  * `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
17235
17328
  * methods concat-fan across providers; single-row methods route to ONE
@@ -17246,7 +17339,8 @@ var LlmProfileKindSchema = _enum([
17246
17339
  "openai",
17247
17340
  "anthropic",
17248
17341
  "google",
17249
- "managed-local"
17342
+ "managed-local",
17343
+ "apple-foundation"
17250
17344
  ]);
17251
17345
  var LlmProfileSchema = object({
17252
17346
  id: string(),
@@ -17304,7 +17398,14 @@ var LlmProfileSchema = object({
17304
17398
  toolsEnabled: boolean().default(false),
17305
17399
  extraHeaders: record(string(), string()).optional(),
17306
17400
  /** kind === 'managed-local' only (spec §4). */
17307
- runtime: ManagedRuntimeConfigSchema.optional()
17401
+ runtime: ManagedRuntimeConfigSchema.optional(),
17402
+ /**
17403
+ * kind === 'apple-foundation' only: WHERE it runs — the only thing there is
17404
+ * to configure (no key, no URL, no download; `model` is `apple-system`).
17405
+ * `supportsVision` on such a row is stamped by the provider from the node's
17406
+ * reported `imageInput`, never taken from the operator (D62).
17407
+ */
17408
+ appleFoundation: object({ nodeId: string() }).optional()
17308
17409
  });
17309
17410
  /** ConfigUISchema tree passed through untyped on the wire (the
17310
17411
  * notification-output `ConfigSchemaPassthrough` precedent at
@@ -17386,7 +17487,9 @@ var LlmRuntimeNodeSchema = object({
17386
17487
  reachable: boolean(),
17387
17488
  status: LlmRuntimeStatusSchema.optional(),
17388
17489
  disk: LlmRuntimeDiskUsageSchema.optional(),
17389
- error: string().optional()
17490
+ error: string().optional(),
17491
+ /** Absent = the node did not answer the question (render as unknown). */
17492
+ apple: AppleFmAvailabilitySchema.optional()
17390
17493
  });
17391
17494
  var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
17392
17495
  var ProfileRefInputSchema = object({
@@ -19709,6 +19812,9 @@ var NcSystemEventKindSchema = _enum([
19709
19812
  "device-battery-normal",
19710
19813
  "device-consumable-low",
19711
19814
  "device-consumable-normal",
19815
+ "device-tank-low",
19816
+ "device-tank-full",
19817
+ "device-tank-normal",
19712
19818
  "stream-online",
19713
19819
  "stream-offline",
19714
19820
  "node-online",
@@ -19779,7 +19885,22 @@ var NcSystemEventConditionSchema = object({
19779
19885
  */
19780
19886
  excludeDeviceIds: array(number().int()).min(1).optional(),
19781
19887
  nodeIds: array(string().min(1)).min(1).optional(),
19782
- packageNames: array(string().min(1)).min(1).optional()
19888
+ packageNames: array(string().min(1)).min(1).optional(),
19889
+ /**
19890
+ * At or below this remaining-life %, a consumable is LOW for THIS rule
19891
+ * (D642). Absent = {@link NC_CONSUMABLE_LOW_PERCENT_DEFAULT}; at most
19892
+ * {@link NC_CONSUMABLE_LOW_PERCENT_MAX}, so every episode can recover. Scoped to the
19893
+ * consumable kinds (`systemEventFilterApplies`) and to the device-events
19894
+ * family; ignored on every other kind.
19895
+ *
19896
+ * It is a property of the RULE because the operator decided so: a rule at
19897
+ * 25 must be told about a filter at 20 %, a rule at 10 must not. The hub
19898
+ * keeps one episode per (threshold, device, item) — its own hysteresis, its
19899
+ * own reminder — and a rule hears only the episode judged at ITS threshold,
19900
+ * exactly as an occupancy rule hears only the edge of its own count. A
19901
+ * provider's own `replace` verdict is low at every threshold.
19902
+ */
19903
+ consumableLowPercent: number().int().min(1).max(95).optional()
19783
19904
  });
19784
19905
  /** Hard ceiling on a window (24h). A snooze that could not expire would be an
19785
19906
  * outage the operator asked for once and forgot. */
@@ -19922,6 +20043,16 @@ var NcCrossingSchema = _enum([
19922
20043
  "exit",
19923
20044
  "any"
19924
20045
  ]);
20046
+ /**
20047
+ * Which edge of a BOOLEAN sensor fires a device-event rule (D640).
20048
+ * - `raised` — the sensor went high: water detected, smoke, open, tampered.
20049
+ * - `cleared` — it went back: dry, clear, closed.
20050
+ * - absent — both edges, which is what every rule authored before this did.
20051
+ *
20052
+ * There is no third member for "both": absence already says it, and a value
20053
+ * that means the same as absence is a second way to write one rule.
20054
+ */
20055
+ var NcSensorTransitionSchema = _enum(["raised", "cleared"]);
19925
20056
  /** Admin-zone membership condition (zone IDs as stamped by the ZoneEngine). */
19926
20057
  var NcZoneConditionSchema = object({
19927
20058
  ids: array(string().min(1)).min(1),
@@ -20111,6 +20242,20 @@ var NcConditionsSchema = object({
20111
20242
  scene: NcSceneConditionSchema.optional(),
20112
20243
  /** Device scope — absent = all devices. */
20113
20244
  devices: array(number()).optional(),
20245
+ /**
20246
+ * Device EXCLUSION (D641) — "every device except these". Judged on the SAME
20247
+ * subject ids {@link devices} is judged on (the camera, the sensor that
20248
+ * fired, a linked set's sensor and row cameras), so a sensor fanned out per
20249
+ * linked camera is excluded exactly where an include would have tested it.
20250
+ * `min(1)`: an empty exclusion excludes nothing and is written as absent.
20251
+ *
20252
+ * Not for `system-event` rules — those carry their own
20253
+ * `systemEvent.excludeDeviceIds` (D636), and the hub refuses this key there.
20254
+ * Editors present the two lists as ONE field (All | Only these | All except
20255
+ * these); the engine evaluates each independently, so a legacy rule
20256
+ * carrying both still means "these, minus those".
20257
+ */
20258
+ devicesExclude: array(number()).min(1).optional(),
20114
20259
  /** Detector class names (any overlap with the record's class set). */
20115
20260
  classes: array(string().min(1)).optional(),
20116
20261
  /** Veto classes — any overlap fails the rule. */
@@ -20244,6 +20389,16 @@ var NcConditionsSchema = object({
20244
20389
  */
20245
20390
  sensorKinds: array(string().min(1)).optional(),
20246
20391
  /**
20392
+ * DEVICE-EVENT only (D640). Fire on one edge of a boolean sensor —
20393
+ * {@link NcSensorTransitionSchema}. Which field of the slice means "raised"
20394
+ * is `@camstack/types` → `notification/sensor-raised.ts`. A reading the
20395
+ * engine cannot place (a kind with no raise, an unreadable slice) PASSES:
20396
+ * the condition fails open towards notifying, never towards silence. Editors
20397
+ * offer it only when every chosen {@link sensorKinds} member has a raise
20398
+ * (`sensorTransitionOffered`), and the hub refuses it on any other delivery.
20399
+ */
20400
+ sensorTransition: NcSensorTransitionSchema.optional(),
20401
+ /**
20247
20402
  * PACKAGE-EVENT only. Which package phase fires the rule — `delivered`
20248
20403
  * (a parked parcel appeared), `picked-up` (it departed), or `both`. Fails
20249
20404
  * when the subject's phase does not match (a subject always carries a phase
@@ -20743,9 +20898,29 @@ var NcTestResultSchema = object({
20743
20898
  className: string().optional(),
20744
20899
  label: string().optional()
20745
20900
  });
20746
- /** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
20747
- * is the same union; the template-vars spec pins the two together. */
20901
+ /** The rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts` is the
20902
+ * same union; the template-vars spec pins the two together. `mixed` is DERIVED
20903
+ * (a stored system rule whose kinds span families, D637) and never authored. */
20748
20904
  var NcRuleKindSchema = _enum([
20905
+ "detection",
20906
+ "sensor",
20907
+ "occupancy",
20908
+ "sound",
20909
+ "device-events",
20910
+ "system",
20911
+ "alarm",
20912
+ "mixed"
20913
+ ]);
20914
+ /**
20915
+ * The kinds a SERVED template-variable descriptor may name: the five that
20916
+ * existed before D637, frozen. `getTemplateCatalog` ships `kinds` to every
20917
+ * installed viewer, and a viewer at `@camstack/types` 1.2.25x parses them with a
20918
+ * strict five-member enum — one `device-events` in one descriptor fails the
20919
+ * whole catalog parse. So the WIRE says `system`, and the MATCH widens it:
20920
+ * `templateVarKindApplies` reads a `system` descriptor as applying to every
20921
+ * system rule kind (`device-events`, `system`, `alarm`, `mixed`).
20922
+ */
20923
+ var NcTemplateVarKindSchema = _enum([
20749
20924
  "detection",
20750
20925
  "sensor",
20751
20926
  "occupancy",
@@ -20783,7 +20958,7 @@ var NcTemplateVarGroupSchema = _enum([
20783
20958
  *
20784
20959
  * `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
20785
20960
  * it has a value; absent = no restriction on that axis. `systemEventKinds` is
20786
- * read only for a `system` rule. `dynamic` declares a FAMILY of names
20961
+ * read only for a system-event rule (any family, or `mixed`). `dynamic` declares a FAMILY of names
20787
20962
  * (`count_<class>`): the descriptor's `name` is the example member.
20788
20963
  */
20789
20964
  var NcTemplateVarDescriptorSchema = object({
@@ -20794,7 +20969,7 @@ var NcTemplateVarDescriptorSchema = object({
20794
20969
  group: NcTemplateVarGroupSchema,
20795
20970
  families: array(NcTemplateFamilySchema).min(1),
20796
20971
  fields: array(NcTemplateFieldSchema).optional(),
20797
- kinds: array(NcRuleKindSchema).optional(),
20972
+ kinds: array(NcTemplateVarKindSchema).optional(),
20798
20973
  deliveries: array(NcDeliverySchema).optional(),
20799
20974
  systemEventKinds: array(NcSystemEventKindSchema).optional(),
20800
20975
  dynamic: object({
@@ -20842,6 +21017,7 @@ var NcConditionDescriptorSchema = object({
20842
21017
  /** Editor widget the UI renders — never hardcode per-condition forms. */
20843
21018
  valueType: _enum([
20844
21019
  "deviceIdList",
21020
+ "deviceIdExcludeList",
20845
21021
  "stringList",
20846
21022
  "number01",
20847
21023
  "number",
@@ -20852,6 +21028,7 @@ var NcConditionDescriptorSchema = object({
20852
21028
  "plateMatcher",
20853
21029
  "packagePhase",
20854
21030
  "crossingSelect",
21031
+ "sensorTransition",
20855
21032
  "polygonDraw",
20856
21033
  "occupancy",
20857
21034
  "audio",
@@ -21002,6 +21179,28 @@ var NcHistoryFilterSchema = object({
21002
21179
  limit: number().int().min(1).max(500).default(100)
21003
21180
  });
21004
21181
  /**
21182
+ * Every rule's last run, in one answer.
21183
+ *
21184
+ * A rule ABSENT from `runs` produced no delivery inside `horizonMs` — how far
21185
+ * back this answer looked, in ms before the hub's now. It is the outbox prune
21186
+ * window (7 days by default, operator-configurable), NARROWED when the read hit
21187
+ * its row bound: the answer never claims a window it did not read. `null` means
21188
+ * the operator keeps the outbox forever AND the read reached its end, so absent
21189
+ * means "never on record". A UI derives its "not in the last N days" from this
21190
+ * value, never from a constant — and only when the call ANSWERED (D315).
21191
+ */
21192
+ var NcRuleLastRunsSchema = object({
21193
+ runs: array(object({
21194
+ ruleId: string(),
21195
+ /** Fire time of the newest row (`createdAt`, epoch ms). */
21196
+ lastAt: number(),
21197
+ status: NcHistoryStatusSchema,
21198
+ /** The camera/device of that row (a digest row files under its first camera). */
21199
+ deviceId: number()
21200
+ })),
21201
+ horizonMs: number().nonnegative().nullable()
21202
+ });
21203
+ /**
21005
21204
  * What a snooze covers. Broader scopes win when several overlap, so one window
21006
21205
  * leaves ONE digest rather than a rule snooze and a whole-feed snooze both
21007
21206
  * summarising the same silence.
@@ -21182,6 +21381,14 @@ var NcAlarmModeCoverageSchema = object({
21182
21381
  /** Ids named by the covering rules. A SUBSET when `allDevices` is true. */
21183
21382
  deviceIds: array(number().int()),
21184
21383
  /**
21384
+ * D641: the ids NOT covered although `allDevices` is true — every unscoped
21385
+ * covering rule EXCLUDES them (`conditions.devicesExclude`) and no named rule
21386
+ * covers them. Always `[]` when `allDevices` is false. Excluded by the rules,
21387
+ * which is not the same as skipped by a switch: never in `skippedDevices`.
21388
+ * Defaulted so an answer computed before this field parses as "none".
21389
+ */
21390
+ allDevicesExcept: array(number().int()).default([]),
21391
+ /**
21185
21392
  * Devices this mode NAMES but cannot actually arm, each with the switch that
21186
21393
  * excludes it.
21187
21394
  *
@@ -21317,7 +21524,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
21317
21524
  })), method(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
21318
21525
  kind: "mutation",
21319
21526
  access: "view"
21320
- }), 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 }), {
21527
+ }), 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 }), {
21321
21528
  kind: "mutation",
21322
21529
  caller: "required"
21323
21530
  }), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
@@ -24561,7 +24768,17 @@ var DetailResultSchema = object({
24561
24768
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
24562
24769
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
24563
24770
  * consumers MUST prefer this when present (2026-07-22 native-gate fix). */
24564
- nativeFaceShortSidePx: number().optional()
24771
+ nativeFaceShortSidePx: number().optional(),
24772
+ /**
24773
+ * The five facial landmarks of a `face` detail, in the SAME frame space as
24774
+ * `bbox` (D644). Additive and optional: post-analysis stores them beside the
24775
+ * face's source frame so a future alignment can be re-derived exactly; a
24776
+ * runner that predates the field sends none and the face is stored without.
24777
+ */
24778
+ landmarks: array(object({
24779
+ x: number(),
24780
+ y: number()
24781
+ })).optional()
24565
24782
  });
24566
24783
  /**
24567
24784
  * Why an executing node REFUSED a stateless step run (`runStatelessStep`).
@@ -26139,7 +26356,11 @@ function buildDescriptor(capName, kind) {
26139
26356
  icon: legacyIcon(t.iconId)
26140
26357
  };
26141
26358
  }
26142
- Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
26359
+ /**
26360
+ * Sensor / control cap name → static event-kind descriptor. A linked device
26361
+ * contributes one entry per bound cap present in this map.
26362
+ */
26363
+ var EVENT_KIND_BY_CAP = Object.freeze(Object.fromEntries(Object.entries(CAP_TO_KIND).map(([capName, kind]) => [capName, buildDescriptor(capName, kind)])));
26143
26364
  /**
26144
26365
  * server-management — per-NODE singleton capability for a node's ROOT
26145
26366
  * package lifecycle (runtime-updatable node packages).
@@ -32767,7 +32988,30 @@ var RenameNodeResultSchema = object({
32767
32988
  nodeId: string(),
32768
32989
  name: string()
32769
32990
  });
32770
- method(_void(), array(TopologyNodeSchema).readonly(), { auth: "admin" }), method(NodeAddonInputSchema, SuccessSchema, {
32991
+ /**
32992
+ * The cheap node list. A consumer's rule for a STORED node id (a rule's
32993
+ * `nodeIds`) that is missing from `nodes`, whatever `complete` says: render it
32994
+ * as a removable chip labelled unknown, never drop it (D49). Even a complete
32995
+ * list omits a node that joined and left between two history captures, so a
32996
+ * missing id means "not known to the hub", never "removed". Only the operator
32997
+ * removes it.
32998
+ *
32999
+ * `complete` changes only the WORDING: `false` = no history read has ever
33000
+ * answered, so the list may be missing offline nodes the hub does know (D315).
33001
+ *
33002
+ * `name` is the history row's name (≤ 30 s behind a rename), or the id when
33003
+ * the history has none.
33004
+ */
33005
+ var NodeDirectorySchema = object({
33006
+ nodes: array(object({
33007
+ id: string(),
33008
+ name: string(),
33009
+ isHub: boolean(),
33010
+ isOnline: boolean()
33011
+ })).readonly(),
33012
+ complete: boolean()
33013
+ });
33014
+ method(_void(), array(TopologyNodeSchema).readonly(), { auth: "admin" }), method(_void(), NodeDirectorySchema, { auth: "admin" }), method(NodeAddonInputSchema, SuccessSchema, {
32771
33015
  kind: "mutation",
32772
33016
  auth: "admin"
32773
33017
  }), method(NodeAddonInputSchema, SuccessSchema, {
@@ -41875,12 +42119,30 @@ Object.freeze({
41875
42119
  addonId: null,
41876
42120
  access: "create"
41877
42121
  },
42122
+ "llmRuntime.appleAvailability": {
42123
+ capName: "llm-runtime",
42124
+ capScope: "system",
42125
+ addonId: null,
42126
+ access: "view"
42127
+ },
42128
+ "llmRuntime.cancel": {
42129
+ capName: "llm-runtime",
42130
+ capScope: "system",
42131
+ addonId: null,
42132
+ access: "create"
42133
+ },
41878
42134
  "llmRuntime.complete": {
41879
42135
  capName: "llm-runtime",
41880
42136
  capScope: "system",
41881
42137
  addonId: null,
41882
42138
  access: "create"
41883
42139
  },
42140
+ "llmRuntime.completeApple": {
42141
+ capName: "llm-runtime",
42142
+ capScope: "system",
42143
+ addonId: null,
42144
+ access: "create"
42145
+ },
41884
42146
  "llmRuntime.deleteModel": {
41885
42147
  capName: "llm-runtime",
41886
42148
  capScope: "system",
@@ -42553,6 +42815,12 @@ Object.freeze({
42553
42815
  addonId: null,
42554
42816
  access: "create"
42555
42817
  },
42818
+ "nodes.directory": {
42819
+ capName: "nodes",
42820
+ capScope: "system",
42821
+ addonId: null,
42822
+ access: "view"
42823
+ },
42556
42824
  "nodes.executeQuery": {
42557
42825
  capName: "nodes",
42558
42826
  capScope: "system",
@@ -42721,6 +42989,12 @@ Object.freeze({
42721
42989
  addonId: null,
42722
42990
  access: "view"
42723
42991
  },
42992
+ "notificationRules.getRuleLastRuns": {
42993
+ capName: "notification-rules",
42994
+ capScope: "system",
42995
+ addonId: null,
42996
+ access: "view"
42997
+ },
42724
42998
  "notificationRules.getTemplateCatalog": {
42725
42999
  capName: "notification-rules",
42726
43000
  capScope: "system",
@@ -48367,11 +48641,140 @@ var NC_AUDIO_DEFAULTS = {
48367
48641
  samplingSeconds: 10
48368
48642
  };
48369
48643
  new Set(["devices", "classes"]);
48644
+ /**
48645
+ * WHICH narrowing filter speaks about WHICH system-event kind — one predicate,
48646
+ * consumed by the rule ENGINE and by every rule EDITOR.
48647
+ *
48648
+ * ── The failure this file exists for ───────────────────────────────────────
48649
+ * `NcSystemEventConditionSchema` carries four optional narrowing lists
48650
+ * (`deviceIds`, `deviceTypes`, `nodeIds`, `packageNames`) beside a required
48651
+ * `kinds` gate. They are not four filters over one vocabulary: each belongs to
48652
+ * a FAMILY of kinds and is meaningless outside it. A node going away has no
48653
+ * device type; a completed export has no package; `stream-offline` deliberately
48654
+ * carries no device type at all (a stream belongs to a camera by construction).
48655
+ *
48656
+ * The engine applied all four to every kind, so a filter from one family failed
48657
+ * a kind from another family CLOSED. The operator's live rule "Eventi di
48658
+ * sistema" selected eleven kinds and set `deviceTypes: ['camera']` — the only
48659
+ * way to say "only my cameras" about the two liveness kinds — and NINE of the
48660
+ * eleven silently stopped being deliverable. Four days of history contained
48661
+ * `device-online` and `device-offline` and nothing else, across a window in
48662
+ * which agents were restarted and addons were updated.
48663
+ *
48664
+ * Both editors already computed this scoping, to decide which filter boxes to
48665
+ * render; the cap's own docblock already promised it ("Narrow a `device-*` kind
48666
+ * to these device TYPES"). Only the engine disagreed, and it disagreed in the
48667
+ * destructive direction. So the rule is stated ONCE, here, and the engine and
48668
+ * the editors are readers of it rather than three independent authors.
48669
+ *
48670
+ * ── The direction ─────────────────────────────────────────────────────────
48671
+ * Out of family ⇒ the filter is IGNORED, never fail-closed. An editor that
48672
+ * hides a box has already told the operator this filter does not speak about
48673
+ * these kinds, so honouring it as an exclusion contradicts the only
48674
+ * explanation the operator was ever given. Inside its family the filter still
48675
+ * narrows, and still fails CLOSED on a subject that cannot produce the identity
48676
+ * it tests (a device the mirror cannot name is not evidence of a camera).
48677
+ */
48678
+ /**
48679
+ * Every filter, for the callers that must cover all of them (guards, tests).
48680
+ *
48681
+ * `consumableLowPercent` (D642) is not a list but it is a filter in exactly
48682
+ * this sense: it belongs to a family of kinds (the consumable pair) and the
48683
+ * engine consults it only there. It differs in one way the readers must know —
48684
+ * its ABSENCE is not "no narrowing" but the default threshold, because a
48685
+ * consumable episode is always judged at SOME threshold.
48686
+ */
48687
+ var NC_SYSTEM_EVENT_FILTER_KEYS = [
48688
+ "deviceIds",
48689
+ "deviceTypes",
48690
+ "nodeIds",
48691
+ "packageNames",
48692
+ "consumableLowPercent"
48693
+ ];
48694
+ new Map(Object.entries({
48695
+ "device-online": "device-events",
48696
+ "device-offline": "device-events",
48697
+ "device-disabled": "device-events",
48698
+ "device-enabled": "device-events",
48699
+ "device-battery-low": "device-events",
48700
+ "device-battery-normal": "device-events",
48701
+ "device-consumable-low": "device-events",
48702
+ "device-consumable-normal": "device-events",
48703
+ "device-tank-low": "device-events",
48704
+ "device-tank-full": "device-events",
48705
+ "device-tank-normal": "device-events",
48706
+ "stream-online": "device-events",
48707
+ "stream-offline": "device-events",
48708
+ "detection-blind": "device-events",
48709
+ "node-online": "system",
48710
+ "node-offline": "system",
48711
+ "node-inference-unavailable": "system",
48712
+ "addon-crash-loop": "system",
48713
+ "addon-update-available": "system",
48714
+ "addon-updated": "system",
48715
+ "server-update-available": "system",
48716
+ "server-updated": "system",
48717
+ "wrapper-update-available": "system",
48718
+ "backup-completed": "system",
48719
+ "export-completed": "system",
48720
+ "alarm-triggered": "alarm",
48721
+ "alarm-armed": "alarm",
48722
+ "alarm-disarmed": "alarm",
48723
+ "alarm-arming": "alarm",
48724
+ "alarm-arm-refused": "alarm",
48725
+ "alarm-pending": "alarm",
48726
+ "alarm-rearmed": "alarm",
48727
+ "alarm-sensor-bypass": "alarm",
48728
+ "alarm-check-failed": "alarm",
48729
+ "camera-online": null,
48730
+ "camera-offline": null,
48731
+ "camera-disabled": null,
48732
+ "camera-enabled": null
48733
+ }));
48734
+ [...NC_SYSTEM_EVENT_FILTER_KEYS];
48370
48735
  NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
48736
+ /**
48737
+ * WHICH WAY A BOOLEAN SENSOR MOVED — read off the one slice a sensor event
48738
+ * persists (D640 "a sensor rule fires on the raise, the clear, or both").
48739
+ *
48740
+ * A device-event rule over a flood sensor notified on BOTH edges: water
48741
+ * detected, and back to dry. `NcConditions.sensorTransition` lets the rule
48742
+ * name one edge; this file answers, for one `SensorEvent`, which edge it is.
48743
+ *
48744
+ * NOT A SECOND TABLE. The "which boolean of this cap counts as HIGH" question
48745
+ * already has one answer, `SOURCE_CAP_ACTIVE_FIELD`
48746
+ * (`catalogs/sensor-active-state.ts`, the doorbell's and the recorder's edge
48747
+ * table), and a sensor event names its kind, not its cap. So the table here is
48748
+ * DERIVED: event kind → cap (`EVENT_KIND_BY_CAP`) → that cap's active field.
48749
+ * Only kinds whose taxonomy category is `sensor` take part — a switch has an
48750
+ * `on` boolean too, but "it triggers / it clears" is not a sentence about a
48751
+ * light, and offering it there would be a knob with a misleading label.
48752
+ * `presence` is a WORD (`home` / `not_home` / a zone), not a boolean, and is
48753
+ * absent from the source table, so it is absent here.
48754
+ *
48755
+ * `undefined` = never guessed: a kind with no raise (a doorbell press, a
48756
+ * button, a cover), or a slice whose field is missing or not a boolean. The
48757
+ * engine then does NOT filter — a flood alert must not go silent because one
48758
+ * read came back unreadable (the same stance as the alarm's `sensorEdgeOf`).
48759
+ *
48760
+ * Pure, RN-safe: the viewer imports the support rule from here.
48761
+ */
48762
+ /** Event kind → the boolean field of its cap slice that means "raised". */
48763
+ var NC_SENSOR_RAISED_FIELD_BY_KIND = Object.freeze(Object.fromEntries(Object.entries(EVENT_KIND_BY_CAP).flatMap(([capName, descriptor]) => {
48764
+ const field = SOURCE_CAP_ACTIVE_FIELD[capName];
48765
+ if (field === void 0 || descriptor.category !== "sensor") return [];
48766
+ return [[descriptor.kind, field]];
48767
+ })));
48768
+ Object.freeze(Object.keys(NC_SENSOR_RAISED_FIELD_BY_KIND));
48371
48769
  var RULE_BODY_ONLY = ["body"];
48372
48770
  var RULE_TEXT = ["title", "body"];
48373
48771
  var BATTERY = ["device-battery-low", "device-battery-normal"];
48374
48772
  var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
48773
+ var TANK = [
48774
+ "device-tank-low",
48775
+ "device-tank-full",
48776
+ "device-tank-normal"
48777
+ ];
48375
48778
  var UPDATES = [
48376
48779
  "addon-update-available",
48377
48780
  "server-update-available",
@@ -48397,12 +48800,13 @@ var DEVICE_EVENTS = [
48397
48800
  "device-enabled",
48398
48801
  ...BATTERY,
48399
48802
  ...CONSUMABLE,
48803
+ ...TANK,
48400
48804
  "stream-online",
48401
48805
  "stream-offline",
48402
48806
  "detection-blind",
48403
48807
  ...ALARM
48404
48808
  ];
48405
- [...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];
48809
+ [...DEVICE_EVENTS], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_BODY_ONLY], [...RULE_TEXT], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...BATTERY, ...CONSUMABLE], [...RULE_TEXT], [...CONSUMABLE], [...RULE_TEXT], [...TANK], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT];
48406
48810
  /**
48407
48811
  * TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
48408
48812
  *