@camstack/addon-pipeline 1.2.622 → 1.2.624

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 (41) hide show
  1. package/dist/audio-analyzer/index.js +3 -3
  2. package/dist/audio-analyzer/index.mjs +3 -3
  3. package/dist/{custom-model-catalog-CKCK-QcT.js → custom-model-catalog-6zwaRe6H.js} +24 -11
  4. package/dist/{custom-model-catalog-wpMi2U7e.mjs → custom-model-catalog-Dx6W1kAo.mjs} +24 -5
  5. package/dist/detection-pipeline/index.js +16 -6
  6. package/dist/detection-pipeline/index.mjs +16 -6
  7. package/dist/{dist-DUFGDd_2.js → dist-63VVVhpw.js} +1371 -240
  8. package/dist/{dist-BF60SjmI.mjs → dist-BK3z-nti.mjs} +1311 -240
  9. package/dist/{model-catalogs-CqFDhb0B.mjs → model-catalogs-CllYj2Q2.mjs} +1 -1
  10. package/dist/{model-catalogs-CjTm_0O-.js → model-catalogs-DyOMg-xZ.js} +1 -1
  11. package/dist/motion-wasm/index.js +1 -1
  12. package/dist/motion-wasm/index.mjs +1 -1
  13. package/dist/{node-DkphOOM2.mjs → node-BAcWHimZ.mjs} +2 -2
  14. package/dist/{node-DzGVn7bD.js → node-DdaBnHEq.js} +2 -2
  15. package/dist/pipeline-runner/index.js +101 -460
  16. package/dist/pipeline-runner/index.mjs +94 -453
  17. package/dist/{process-memory-8kKexHYR.js → process-memory-CUlknrLl.js} +1 -1
  18. package/dist/{process-memory-CBzfdSvC.mjs → process-memory-OZwib1pK.mjs} +1 -1
  19. package/dist/recorder/index.js +21 -2
  20. package/dist/recorder/index.mjs +21 -2
  21. package/dist/{segment-demux-js-DQNoNSJt.mjs → segment-demux-js-C3rhCLCG.mjs} +1 -1
  22. package/dist/{segment-demux-js-DRLQb1pc.js → segment-demux-js-WNlg0SXP.js} +1 -1
  23. package/dist/stream-broker/_stub.js +2 -2
  24. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-BmANAxAe.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-Di7t7N3r.mjs} +2 -2
  25. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DDRGMoJ1.mjs +26 -0
  26. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CPQe09h1.mjs +26 -0
  27. package/dist/stream-broker/demux-worker-child.js +1 -1
  28. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  29. package/dist/stream-broker/{hostInit-B3hXAfr1.mjs → hostInit-CMDvBrqv.mjs} +2 -2
  30. package/dist/stream-broker/index.js +4 -4
  31. package/dist/stream-broker/index.mjs +4 -4
  32. package/dist/stream-broker/remoteEntry.js +1 -1
  33. package/package.json +1 -1
  34. package/python/accelerator_facts.py +275 -0
  35. package/python/inference_pool.py +19 -2
  36. package/python/postprocessors/ssd.py +12 -4
  37. package/python/postprocessors/test_ssd.py +64 -0
  38. package/python/test_accelerator_facts.py +105 -0
  39. package/python/webgpu_isolated.py +19 -0
  40. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-H6RKsaiZ.mjs +0 -26
  41. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CNoWeZHH.mjs +0 -26
@@ -1,5 +1,5 @@
1
1
  let node_crypto = require("node:crypto");
2
- //#region ../types/dist/event-category-fDPduoeC.mjs
2
+ //#region ../types/dist/event-category-Ceif8n-v.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -471,6 +471,10 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
471
471
  * on the device detail page.
472
472
  */
473
473
  EventCategory["DoorbellOnPressed"] = "doorbell.onPressed";
474
+ /** Mirrors `runningStateCapability.events.onStarted`. */
475
+ EventCategory["RunningStarted"] = "running.started";
476
+ /** Mirrors `runningStateCapability.events.onFinished`. */
477
+ EventCategory["RunningFinished"] = "running.finished";
474
478
  /**
475
479
  * Cap event fired by every device that registers the `event-emitter`
476
480
  * capability. Mirrors `eventEmitterCapability.events.onEvent`. Carries
@@ -4724,10 +4728,10 @@ var ZodType = /*@__PURE__*/ $constructor("ZodType", (inst, def) => {
4724
4728
  return array(this);
4725
4729
  },
4726
4730
  or(arg) {
4727
- return union([this, arg]);
4731
+ return union$1([this, arg]);
4728
4732
  },
4729
4733
  and(arg) {
4730
- return intersection(this, arg);
4734
+ return intersection$1(this, arg);
4731
4735
  },
4732
4736
  transform(tx) {
4733
4737
  return pipe(this, transform(tx));
@@ -5198,7 +5202,7 @@ var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
5198
5202
  inst._zod.processJSONSchema = (ctx, json, params) => unionProcessor(inst, ctx, json, params);
5199
5203
  inst.options = def.options;
5200
5204
  });
5201
- function union(options, params) {
5205
+ function union$1(options, params) {
5202
5206
  return new ZodUnion({
5203
5207
  type: "union",
5204
5208
  options,
@@ -5222,7 +5226,7 @@ var ZodIntersection = /*@__PURE__*/ $constructor("ZodIntersection", (inst, def)
5222
5226
  ZodType.init(inst, def);
5223
5227
  inst._zod.processJSONSchema = (ctx, json, params) => intersectionProcessor(inst, ctx, json, params);
5224
5228
  });
5225
- function intersection(left, right) {
5229
+ function intersection$1(left, right) {
5226
5230
  return new ZodIntersection({
5227
5231
  type: "intersection",
5228
5232
  left,
@@ -5563,7 +5567,7 @@ var ZodIssueCode = {
5563
5567
  var ZodFirstPartyTypeKind;
5564
5568
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5565
5569
  //#endregion
5566
- //#region ../types/dist/sleep-CHIEEJkY.mjs
5570
+ //#region ../types/dist/sleep-CYfIZzGv.mjs
5567
5571
  /**
5568
5572
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5569
5573
  * window to float samples (D455).
@@ -5934,7 +5938,7 @@ Object.fromEntries([
5934
5938
  * `hydrateSchema` to skip the `value` injection for structural-only fields.
5935
5939
  */
5936
5940
  function isValuelessField(field) {
5937
- return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button";
5941
+ return field.type === "separator" || field.type === "info" || field.type === "qr-code" || field.type === "button" || field.type === "object-array" || field.type === "widget" || field.type === "addon-action-button" || field.type === "device-action-button" || field.type === "account-sign-in" || field.type === "secret-presence";
5938
5942
  }
5939
5943
  function hydrateSchema(schema, values, options = {}) {
5940
5944
  return {
@@ -8180,7 +8184,7 @@ var RecordIndexSchema = object({
8180
8184
  fields: array(RecordFieldPathSchema).min(1).max(4),
8181
8185
  unique: boolean().optional()
8182
8186
  }).strict();
8183
- var RecordRetentionSchema = union([
8187
+ var RecordRetentionSchema = union$1([
8184
8188
  object({
8185
8189
  ttlMs: number().int().positive(),
8186
8190
  from: _enum(["at", "write"])
@@ -8333,7 +8337,7 @@ var CAM_PROFILE_ORDER = [
8333
8337
  * the operator picked. Today's one user is the audio analyser, whose rule is
8334
8338
  * "the lowest AVAILABLE native rung" (D537).
8335
8339
  */
8336
- var AudioStreamPickSchema = union([CamProfileSchema, literal("auto")]);
8340
+ var AudioStreamPickSchema = union$1([CamProfileSchema, literal("auto")]);
8337
8341
  var CamStreamKindSchema = _enum([
8338
8342
  "pull-rtsp",
8339
8343
  "pull-rtmp",
@@ -8765,7 +8769,7 @@ var ContributionSectionSchema = object({
8765
8769
  description: string().optional(),
8766
8770
  style: _enum(["card", "accordion"]).optional(),
8767
8771
  defaultCollapsed: boolean().optional(),
8768
- columns: union([
8772
+ columns: union$1([
8769
8773
  literal(1),
8770
8774
  literal(2),
8771
8775
  literal(3),
@@ -8929,7 +8933,7 @@ var HF_REPO = "camstack/camstack-models";
8929
8933
  `${HF_REPO}`;
8930
8934
  object({
8931
8935
  code: string().regex(/^[a-z][a-z0-9-]*(\.[a-z0-9-]+)+$/),
8932
- params: record(string(), union([string(), number()])).optional()
8936
+ params: record(string(), union$1([string(), number()])).optional()
8933
8937
  });
8934
8938
  var CamstackError = class extends Error {
8935
8939
  i18nCode;
@@ -9435,7 +9439,7 @@ function errMsg(err) {
9435
9439
  return String(err);
9436
9440
  }
9437
9441
  //#endregion
9438
- //#region ../types/dist/composition-CB6PMT9p.mjs
9442
+ //#region ../types/dist/composition-BJ8iAZb3.mjs
9439
9443
  var AUDIO_ENCODER_BY_CODEC = {
9440
9444
  opus: "libopus",
9441
9445
  aac: "aac",
@@ -10202,7 +10206,7 @@ portable("linux64"), portable("linuxarm64"), portable("macarm64"), portable("mac
10202
10206
  * ONE table for everyone who asks "is this device HIGH?": the
10203
10207
  * recorder's `RecordingTriggers.sensorDeviceIds` trigger, the post-analysis
10204
10208
  * sensor markers, the orchestrator's camera-sensors bridge and
10205
- * the sources of a composite doorbell (`doorbellFeatureSources`, D671).
10209
+ * the sources of a pulse (`compilePulseOverride`, D936).
10206
10210
  * A second copy would diverge at the first cap added, and the symptom — "the
10207
10211
  * sensor rings the doorbell but does not record" — is exactly D62.
10208
10212
  *
@@ -10572,6 +10576,20 @@ var table = {
10572
10576
  apply: () => {
10573
10577
  throw new ExpressionEvalError("count: stateful evaluation context is required");
10574
10578
  }
10579
+ },
10580
+ dwell: {
10581
+ minArgs: 3,
10582
+ maxArgs: 3,
10583
+ apply: () => {
10584
+ throw new ExpressionEvalError("dwell: stateful evaluation context is required");
10585
+ }
10586
+ },
10587
+ latchedAt: {
10588
+ minArgs: 1,
10589
+ maxArgs: 1,
10590
+ apply: () => {
10591
+ throw new ExpressionEvalError("latchedAt: stateful evaluation context is required");
10592
+ }
10575
10593
  }
10576
10594
  };
10577
10595
  Object.freeze(Object.assign(Object.create(null), table));
@@ -11032,7 +11050,7 @@ var CompositionFieldReadSchema = object({
11032
11050
  var CompositionFromSourceSchema = CompositionFieldReadSchema.extend({ kind: literal("from") });
11033
11051
  var CompositionBindingSchema = discriminatedUnion("kind", [CompositionFromSourceSchema, object({
11034
11052
  kind: literal("literal"),
11035
- value: union([
11053
+ value: union$1([
11036
11054
  string(),
11037
11055
  number(),
11038
11056
  boolean(),
@@ -11227,7 +11245,7 @@ var EncodeProfileSchema = object({
11227
11245
  */
11228
11246
  singleSlicePerFrame: boolean().optional()
11229
11247
  }),
11230
- audio: union([literal("passthrough"), object({
11248
+ audio: union$1([literal("passthrough"), object({
11231
11249
  codec: _enum([
11232
11250
  "opus",
11233
11251
  "aac",
@@ -11237,7 +11255,7 @@ var EncodeProfileSchema = object({
11237
11255
  ]),
11238
11256
  bitrateKbps: number().int().positive().optional(),
11239
11257
  sampleRateHz: number().int().positive().optional(),
11240
- channels: union([literal(1), literal(2)]).optional()
11258
+ channels: union$1([literal(1), literal(2)]).optional()
11241
11259
  })]),
11242
11260
  /**
11243
11261
  * ffmpeg input-side args, inserted between the fixed global flags
@@ -13982,7 +14000,7 @@ var StorageDrainProgressSchema = object({
13982
14000
  *
13983
14001
  * The orchestrator's `resolveRef(ref)` handles both cases.
13984
14002
  */
13985
- var StorageLocationRefSchema = union([StorageLocationTypeSchema, string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
14003
+ var StorageLocationRefSchema = union$1([StorageLocationTypeSchema, string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/)]);
13986
14004
  /**
13987
14005
  * `StorageLocationDeclaration` — a single storage-location entry declared by
13988
14006
  * an addon in its `package.json` under `camstack.storageLocations`.
@@ -15338,7 +15356,7 @@ var ViewerFeatureIdSchema = _enum([
15338
15356
  "scenes"
15339
15357
  ]);
15340
15358
  /** Which devices a viewer reaches: `allow` minus `block`. */
15341
- var ViewerDeviceAllowSchema = union([literal("*"), array(number().int().nonnegative())]);
15359
+ var ViewerDeviceAllowSchema = union$1([literal("*"), array(number().int().nonnegative())]);
15342
15360
  /**
15343
15361
  * The viewer grant, stored as ONE row of a user's `scopes`. A principal that
15344
15362
  * holds it is decided by the viewer rules ALONE — any other row it carries is
@@ -15402,8 +15420,8 @@ var MethodAccessSchema = _enum([
15402
15420
  * `access` is REQUIRED with min 1 element. `admin` bypass is the
15403
15421
  * "give me everything" escape; there is no wildcard scope.
15404
15422
  */
15405
- var AllowedProviderSchema = union([literal("*"), array(string())]);
15406
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
15423
+ var AllowedProviderSchema = union$1([literal("*"), array(string())]);
15424
+ var AllowedDevicesSchema = record(string(), union$1([literal("*"), array(string())]));
15407
15425
  var CapScopeSchema = _enum(["device", "system"]);
15408
15426
  /**
15409
15427
  * DeviceSelector (scope model v3 — 2026-08-12).
@@ -15509,7 +15527,7 @@ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("t
15509
15527
  * own rules alone. Kept apart from {@link TokenScope} so every surface that
15510
15528
  * edits capability rows (the scope picker, token minting) keeps its shape.
15511
15529
  */
15512
- var UserScopeSchema = union([ViewerTokenScopeSchema, TokenScopeSchema]);
15530
+ var UserScopeSchema = union$1([ViewerTokenScopeSchema, TokenScopeSchema]);
15513
15531
  object({
15514
15532
  id: string(),
15515
15533
  username: string(),
@@ -15558,7 +15576,7 @@ var AddonRoutesScopeSchema = object({
15558
15576
  access: array(MethodAccessSchema).min(1)
15559
15577
  });
15560
15578
  var PreferencesSelfScopeSchema = object({ type: literal("preferences-self") });
15561
- var CredentialScopeSchema = union([
15579
+ var CredentialScopeSchema = union$1([
15562
15580
  literal("*"),
15563
15581
  ShareViewScopeSchema,
15564
15582
  AddonRoutesScopeSchema,
@@ -15792,7 +15810,7 @@ object({
15792
15810
  /** The app executes it itself, silently (D858). */
15793
15811
  app: AppLoginPathSchema.nullable()
15794
15812
  });
15795
- var ExpressionBindingSourceSchema = union([
15813
+ var ExpressionBindingSourceSchema = union$1([
15796
15814
  object({
15797
15815
  kind: literal("field").optional(),
15798
15816
  sourceKey: string(),
@@ -15801,7 +15819,7 @@ var ExpressionBindingSourceSchema = union([
15801
15819
  }),
15802
15820
  object({
15803
15821
  kind: literal("literal"),
15804
- value: union([
15822
+ value: union$1([
15805
15823
  string(),
15806
15824
  number(),
15807
15825
  boolean(),
@@ -15844,11 +15862,11 @@ var AutomationConditionLeafSchema = object({
15844
15862
  cap: string().min(1),
15845
15863
  fieldPath: string().min(1),
15846
15864
  operator: AutomationConditionOperatorSchema,
15847
- value: union([
15865
+ value: union$1([
15848
15866
  string(),
15849
15867
  number(),
15850
15868
  boolean(),
15851
- array(union([string(), number()]))
15869
+ array(union$1([string(), number()]))
15852
15870
  ])
15853
15871
  });
15854
15872
  /**
@@ -15903,7 +15921,7 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
15903
15921
  cap: string().min(1),
15904
15922
  fieldPath: string().min(1),
15905
15923
  /** Fire when the field takes this value. Omit to fire on any change. */
15906
- becomes: union([
15924
+ becomes: union$1([
15907
15925
  string(),
15908
15926
  number(),
15909
15927
  boolean()
@@ -15957,7 +15975,7 @@ var AutomationActionSchema = discriminatedUnion("kind", [
15957
15975
  * or `{ $var }` for the device that triggered the run. An array is
15958
15976
  * fan-out at codegen — each id gets the same cap.method call.
15959
15977
  */
15960
- deviceId: union([
15978
+ deviceId: union$1([
15961
15979
  number().int(),
15962
15980
  array(number().int()).min(1),
15963
15981
  object({ $var: string().min(1) })
@@ -16125,7 +16143,7 @@ var ConfigSectionWithValuesSchema = object({
16125
16143
  descriptionKey: string().optional(),
16126
16144
  style: _enum(["card", "accordion"]).optional(),
16127
16145
  defaultCollapsed: boolean().optional(),
16128
- columns: union([
16146
+ columns: union$1([
16129
16147
  literal(1),
16130
16148
  literal(2),
16131
16149
  literal(3),
@@ -16943,7 +16961,7 @@ var CollectionColumnSchema = object({
16943
16961
  * collection surface simply cannot express their existing tables — which is
16944
16962
  * why they were still on the uncapped `table*` API.
16945
16963
  */
16946
- defaultValue: union([
16964
+ defaultValue: union$1([
16947
16965
  string(),
16948
16966
  number(),
16949
16967
  boolean()
@@ -17005,7 +17023,7 @@ method(object({
17005
17023
  }), object({
17006
17024
  /** The id the row HAS: assigned by SQLite on an insert, or the existing
17007
17025
  * row's own id on an update. */
17008
- id: union([string(), number()]) }), {
17026
+ id: union$1([string(), number()]) }), {
17009
17027
  kind: "mutation",
17010
17028
  auth: "admin"
17011
17029
  }), method(object({
@@ -17029,7 +17047,7 @@ id: union([string(), number()]) }), {
17029
17047
  * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
17030
17048
  * primary key — which is the only place an auto key is knowable.
17031
17049
  */
17032
- id: union([string(), number()]) }), {
17050
+ id: union$1([string(), number()]) }), {
17033
17051
  kind: "mutation",
17034
17052
  auth: "admin"
17035
17053
  }), method(object({
@@ -19144,7 +19162,7 @@ var CompositionFieldStateSchema = object({
19144
19162
  item: CompositionFieldItemSchema.optional(),
19145
19163
  state: CompositionFieldStateKindSchema,
19146
19164
  /** `null` whenever `state === 'unavailable'`; never a plausible stand-in (D393). */
19147
- value: union([
19165
+ value: union$1([
19148
19166
  string(),
19149
19167
  number(),
19150
19168
  boolean(),
@@ -19179,6 +19197,13 @@ var ComposerUnreadableBlockSchema = object({
19179
19197
  /** The parse error, in the operator's words. */
19180
19198
  error: string()
19181
19199
  });
19200
+ /** A confirmed release the hub did not accept. `deviceId` is the claim's device. */
19201
+ var UnreleasedClaimSchema = object({
19202
+ deviceId: number().int(),
19203
+ capName: string(),
19204
+ owner: string(),
19205
+ error: string()
19206
+ });
19182
19207
  method(object({
19183
19208
  /** The shared Blocks integration; null until `core-blocks` has reconciled it. */
19184
19209
  integrationId: string().nullable(),
@@ -19188,7 +19213,13 @@ method(object({
19188
19213
  /** Operator restarts: rebuild these runtimes and re-resolve their sources. Stores nothing (D62). */
19189
19214
  restartBlockIds: array(string()),
19190
19215
  /** Composition rows that exist but do not parse: held, never withdrawn, reported failed. */
19191
- unreadableBlocks: array(ComposerUnreadableBlockSchema)
19216
+ unreadableBlocks: array(ComposerUnreadableBlockSchema),
19217
+ /**
19218
+ * Operator-confirmed deletions. Their claims are released on this apply,
19219
+ * before any replacement block claims. A block that merely vanished from the
19220
+ * snapshot is not listed here and still waits for two applies (D49).
19221
+ */
19222
+ retiredBlockIds: array(string()).optional()
19192
19223
  }), object({
19193
19224
  blocks: array(object({
19194
19225
  blockId: string(),
@@ -19200,7 +19231,13 @@ method(object({
19200
19231
  * integration unknown, its own rows unread or read empty once). An empty
19201
19232
  * apply the caller owes is paid off only by one the composer acted on (D49).
19202
19233
  */
19203
- acted: boolean()
19234
+ acted: boolean(),
19235
+ /**
19236
+ * Confirmed releases this apply tried and the hub did not accept. Empty when
19237
+ * every one landed (or there was nothing to hand back). Absent on a result
19238
+ * that never got that far.
19239
+ */
19240
+ unreleasedClaims: array(UnreleasedClaimSchema).optional()
19204
19241
  }), {
19205
19242
  kind: "mutation",
19206
19243
  auth: "admin"
@@ -19243,7 +19280,15 @@ Object.freeze({
19243
19280
  kind: "fixed",
19244
19281
  result: ["number", "null"]
19245
19282
  },
19246
- count: NUMBER
19283
+ count: NUMBER,
19284
+ dwell: {
19285
+ kind: "fixed",
19286
+ result: ["boolean", "null"]
19287
+ },
19288
+ latchedAt: {
19289
+ kind: "fixed",
19290
+ result: ["number", "null"]
19291
+ }
19247
19292
  });
19248
19293
  `${COMPOSED_DEVICE_STABLE_ID_PREFIX}`;
19249
19294
  /**
@@ -19306,7 +19351,7 @@ var RawSecretMigrationReportSchema = object({
19306
19351
  });
19307
19352
  /** Where a block runs. The operator chooses — a block driving a device on an
19308
19353
  * agent is the reason placement is not fixed to the hub. */
19309
- var CoreBlockPlacementSchema = union([literal("hub"), string().min(1)]);
19354
+ var CoreBlockPlacementSchema = union$1([literal("hub"), string().min(1)]);
19310
19355
  /** What a block's process is doing. Mirrors the addon runner's own lifecycle so
19311
19356
  * a failing block reads the same way a failing addon does. `degraded` is a
19312
19357
  * composition on an existing device with a field handed back to the native
@@ -19703,7 +19748,7 @@ method(_void(), EngineInfoSchema, { auth: "admin" }), method(object({
19703
19748
  * for an absent one, or the ROWID SQLite assigned on an `INTEGER`
19704
19749
  * primary key — which is the only place an auto key is knowable.
19705
19750
  */
19706
- id: union([string(), number()]) }), {
19751
+ id: union$1([string(), number()]) }), {
19707
19752
  kind: "mutation",
19708
19753
  auth: "admin"
19709
19754
  }), method(object({
@@ -19711,7 +19756,7 @@ id: union([string(), number()]) }), {
19711
19756
  collection: string(),
19712
19757
  conflict: array(string()).readonly(),
19713
19758
  record: BulkRecordSchema
19714
- }), object({ id: union([string(), number()]) }), {
19759
+ }), object({ id: union$1([string(), number()]) }), {
19715
19760
  kind: "mutation",
19716
19761
  auth: "admin"
19717
19762
  }), method(object({
@@ -19838,20 +19883,20 @@ var RecordViewSchema = object({
19838
19883
  data: RecordBodySchema
19839
19884
  });
19840
19885
  /** Declared index fields only. Anything else does not compile, by design. */
19841
- var RecordWhereSchema = record(RecordFieldPathSchema, union([
19886
+ var RecordWhereSchema = record(RecordFieldPathSchema, union$1([
19842
19887
  string(),
19843
19888
  number(),
19844
19889
  boolean(),
19845
- object({ in: array(union([
19890
+ object({ in: array(union$1([
19846
19891
  string(),
19847
19892
  number(),
19848
19893
  boolean()
19849
19894
  ])).min(1).max(RECORD_BATCH_MAX) }),
19850
19895
  object({
19851
- gt: union([string(), number()]).optional(),
19852
- gte: union([string(), number()]).optional(),
19853
- lt: union([string(), number()]).optional(),
19854
- lte: union([string(), number()]).optional()
19896
+ gt: union$1([string(), number()]).optional(),
19897
+ gte: union$1([string(), number()]).optional(),
19898
+ lt: union$1([string(), number()]).optional(),
19899
+ lte: union$1([string(), number()]).optional()
19855
19900
  }).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
19856
19901
  ]));
19857
19902
  var KindShape = { kind: RecordKindNameSchema };
@@ -20002,7 +20047,7 @@ method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema)
20002
20047
  groupBy: RecordFieldPathSchema.optional(),
20003
20048
  where: RecordWhereSchema.optional()
20004
20049
  }), object({ rows: array(object({
20005
- group: union([
20050
+ group: union$1([
20006
20051
  string(),
20007
20052
  number(),
20008
20053
  _null()
@@ -20348,7 +20393,7 @@ var QueryWindowShape = {
20348
20393
  scope: string().optional(),
20349
20394
  from: number(),
20350
20395
  to: number(),
20351
- resolution: union([DeviceHistoryResolutionSchema, literal("auto")]).default("auto"),
20396
+ resolution: union$1([DeviceHistoryResolutionSchema, literal("auto")]).default("auto"),
20352
20397
  maxPoints: number().int().positive().max(5e3).default(1e3)
20353
20398
  };
20354
20399
  var QueryResultSchema = object({
@@ -20357,7 +20402,7 @@ var QueryResultSchema = object({
20357
20402
  truncated: boolean()
20358
20403
  });
20359
20404
  /** One read of a batch: a device's key or a node's key (exactly one subject). */
20360
- var QueryManyItemSchema = union([object({
20405
+ var QueryManyItemSchema = union$1([object({
20361
20406
  deviceId: number().int(),
20362
20407
  key: string(),
20363
20408
  scope: string().optional()
@@ -43309,7 +43354,7 @@ var LlmListModelsResultSchema = discriminatedUnion("ok", [object({
43309
43354
  code: LlmListModelsErrorCodeSchema,
43310
43355
  message: string()
43311
43356
  })]);
43312
- var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
43357
+ var LlmDefaultSelectorSchema = union$1([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
43313
43358
  var LlmDefaultSchema = object({
43314
43359
  selector: LlmDefaultSelectorSchema,
43315
43360
  profileId: string()
@@ -43647,7 +43692,7 @@ var LogReadQuerySchema = object({
43647
43692
  */
43648
43693
  fromNodeId: string().min(1).max(200).optional(),
43649
43694
  /** Any other tag, exact match (AND). */
43650
- tags: record(string(), union([string(), number()])).optional(),
43695
+ tags: record(string(), union$1([string(), number()])).optional(),
43651
43696
  /** Severity FLOOR: this level and above. */
43652
43697
  level: LogLevelSchema.optional(),
43653
43698
  /** Case-insensitive substring over message, scope, tag values and meta values. */
@@ -43674,7 +43719,7 @@ var LogReadResultSchema = object({
43674
43719
  level: LogLevelSchema,
43675
43720
  scope: string().optional(),
43676
43721
  message: string(),
43677
- tags: record(string(), union([string(), number()])).optional(),
43722
+ tags: record(string(), union$1([string(), number()])).optional(),
43678
43723
  meta: record(string(), unknown()).optional()
43679
43724
  })),
43680
43725
  /** Pass as `before` for the next older page; null when nothing older exists. */
@@ -45500,6 +45545,381 @@ function mapAudioLabelToMacro(label) {
45500
45545
  return _macroLookup.get(label.toLowerCase()) ?? null;
45501
45546
  }
45502
45547
  /**
45548
+ * Palette entry ids (D939). Audio is the one gradient; `audio-*` leaves are
45549
+ * not entries. `heatmapScrub` is a mark, not an event kind.
45550
+ */
45551
+ var PALETTE_ENTRY_IDS = [
45552
+ "motion",
45553
+ "audio",
45554
+ "recording",
45555
+ "live",
45556
+ "heatmapScrub",
45557
+ "person",
45558
+ "vehicle",
45559
+ "animal",
45560
+ "package",
45561
+ "sensor",
45562
+ "control",
45563
+ "face",
45564
+ "plate",
45565
+ "car",
45566
+ "truck",
45567
+ "bus",
45568
+ "motorcycle",
45569
+ "bicycle",
45570
+ "airplane",
45571
+ "boat",
45572
+ "train",
45573
+ "bird",
45574
+ "dog",
45575
+ "cat",
45576
+ "horse",
45577
+ "sheep",
45578
+ "cow",
45579
+ "elephant",
45580
+ "bear",
45581
+ "zebra",
45582
+ "giraffe",
45583
+ "package-delivered",
45584
+ "package-picked-up",
45585
+ "suitcase",
45586
+ "backpack",
45587
+ "handbag",
45588
+ "contact",
45589
+ "motion-sensor",
45590
+ "smoke",
45591
+ "flood",
45592
+ "gas",
45593
+ "carbon-monoxide",
45594
+ "vibration",
45595
+ "tamper",
45596
+ "presence",
45597
+ "enum-sensor",
45598
+ "device-event",
45599
+ "lock",
45600
+ "cover",
45601
+ "switch",
45602
+ "siren",
45603
+ "button",
45604
+ "doorbell"
45605
+ ];
45606
+ var AUDIO_ENTRY_ID = "audio";
45607
+ /** Six rungs, quiet → loud. The opaque end is the entry's hex. */
45608
+ var AUDIO_GRADIENT_ALPHAS = [
45609
+ .3,
45610
+ .42,
45611
+ .55,
45612
+ .7,
45613
+ .85,
45614
+ 1
45615
+ ];
45616
+ var PALETTE_PRESET_IDS = [
45617
+ "camstack",
45618
+ "high-contrast",
45619
+ "okabe-ito"
45620
+ ];
45621
+ /** Frozen palette tables (D939). The spec refuses a table that breaks the rule. */
45622
+ var CAMSTACK_HEX = {
45623
+ motion: "#d946ef",
45624
+ audio: "#059eb8",
45625
+ recording: "#ef4444",
45626
+ live: "#8d7a00",
45627
+ heatmapScrub: "#4b32ff",
45628
+ person: "#11a64c",
45629
+ vehicle: "#3b82f6",
45630
+ animal: "#ea6a09",
45631
+ package: "#924667",
45632
+ sensor: "#624bbc",
45633
+ control: "#944d24",
45634
+ face: "#ed4f8d",
45635
+ plate: "#4b7027",
45636
+ car: "#9684cf",
45637
+ truck: "#0f9b89",
45638
+ bus: "#a734a9",
45639
+ motorcycle: "#c1133a",
45640
+ bicycle: "#0e6a9b",
45641
+ airplane: "#7c9928",
45642
+ boat: "#b17933",
45643
+ train: "#ad6ca3",
45644
+ bird: "#0f7c55",
45645
+ dog: "#c82700",
45646
+ cat: "#7268f2",
45647
+ horse: "#cb795e",
45648
+ sheep: "#b8636b",
45649
+ cow: "#cb2969",
45650
+ elephant: "#1158d2",
45651
+ bear: "#8d5e00",
45652
+ zebra: "#0097d6",
45653
+ giraffe: "#8f61bb",
45654
+ "package-delivered": "#cd3f9b",
45655
+ "package-picked-up": "#388a08",
45656
+ suitcase: "#666800",
45657
+ backpack: "#ec5022",
45658
+ handbag: "#a76ef8",
45659
+ contact: "#ab5a49",
45660
+ "motion-sensor": "#b95d1a",
45661
+ smoke: "#dd3858",
45662
+ flood: "#b31e7d",
45663
+ gas: "#5b70b5",
45664
+ "carbon-monoxide": "#7b4e8e",
45665
+ vibration: "#e65bba",
45666
+ tamper: "#98464b",
45667
+ presence: "#3a9468",
45668
+ "enum-sensor": "#7885f8",
45669
+ "device-event": "#8c43c8",
45670
+ lock: "#649653",
45671
+ cover: "#c67597",
45672
+ switch: "#7a812a",
45673
+ siren: "#ac5d7f",
45674
+ button: "#b276cd",
45675
+ doorbell: "#0087ab"
45676
+ };
45677
+ var CAMSTACK_FALLBACK = [
45678
+ "#b34dc7",
45679
+ "#1d7d34",
45680
+ "#db6b6c",
45681
+ "#a43d29",
45682
+ "#0e6ee6",
45683
+ "#29a10b",
45684
+ "#a98731",
45685
+ "#0181c6"
45686
+ ];
45687
+ var HIGH_CONTRAST_HEX = {
45688
+ motion: "#964360",
45689
+ audio: "#23a132",
45690
+ recording: "#2494ca",
45691
+ live: "#bd7e2b",
45692
+ heatmapScrub: "#ad6eea",
45693
+ person: "#62611e",
45694
+ vehicle: "#f4534c",
45695
+ animal: "#1556cd",
45696
+ package: "#9c4415",
45697
+ sensor: "#066c48",
45698
+ control: "#f2479f",
45699
+ face: "#139d8a",
45700
+ plate: "#909024",
45701
+ car: "#8e35ab",
45702
+ truck: "#bd0b2b",
45703
+ bus: "#7a88ca",
45704
+ motorcycle: "#e26725",
45705
+ bicycle: "#a6679b",
45706
+ airplane: "#7c5720",
45707
+ boat: "#467a1b",
45708
+ train: "#6f65a4",
45709
+ bird: "#cb717f",
45710
+ dog: "#c62490",
45711
+ cat: "#b36850",
45712
+ horse: "#1872ac",
45713
+ sheep: "#927939",
45714
+ cow: "#428a5c",
45715
+ elephant: "#d41160",
45716
+ bear: "#799453",
45717
+ zebra: "#a85455",
45718
+ giraffe: "#746be8",
45719
+ "package-delivered": "#824a80",
45720
+ "package-picked-up": "#614ac1",
45721
+ suitcase: "#2f77f3",
45722
+ backpack: "#fa476d",
45723
+ handbag: "#c14cbe",
45724
+ contact: "#c83921",
45725
+ "motion-sensor": "#a07ebd",
45726
+ smoke: "#a25b13",
45727
+ flood: "#526bab",
45728
+ gas: "#924938",
45729
+ "carbon-monoxide": "#bb7e53",
45730
+ vibration: "#b66182",
45731
+ tamper: "#3c6931",
45732
+ presence: "#6c851d",
45733
+ "enum-sensor": "#ad2d48",
45734
+ "device-event": "#5e9b04",
45735
+ lock: "#00a062",
45736
+ cover: "#ad8624",
45737
+ switch: "#d05259",
45738
+ siren: "#cd67b3",
45739
+ button: "#c9746a",
45740
+ doorbell: "#338eeb"
45741
+ };
45742
+ var HIGH_CONTRAST_FALLBACK = [
45743
+ "#817f3b",
45744
+ "#a0733e",
45745
+ "#8658b2",
45746
+ "#956301",
45747
+ "#bc652d",
45748
+ "#647331",
45749
+ "#a14c9e",
45750
+ "#d14e71"
45751
+ ];
45752
+ var OKABE_ITO_HEX = {
45753
+ motion: "#b88128",
45754
+ audio: "#1096c8",
45755
+ recording: "#009e73",
45756
+ live: "#9c8c22",
45757
+ heatmapScrub: "#0072b2",
45758
+ person: "#d55e00",
45759
+ vehicle: "#c0739f",
45760
+ animal: "#226d18",
45761
+ package: "#91446c",
45762
+ sensor: "#217b56",
45763
+ control: "#648647",
45764
+ face: "#c22998",
45765
+ plate: "#fa4575",
45766
+ car: "#956301",
45767
+ truck: "#2f77f3",
45768
+ bus: "#7a4e86",
45769
+ motorcycle: "#f54597",
45770
+ bicycle: "#bb3e75",
45771
+ airplane: "#e054c0",
45772
+ boat: "#457942",
45773
+ train: "#867afa",
45774
+ bird: "#88914c",
45775
+ dog: "#1556cd",
45776
+ cat: "#71702c",
45777
+ horse: "#9b4fa4",
45778
+ sheep: "#c0559d",
45779
+ cow: "#d14f65",
45780
+ elephant: "#98470f",
45781
+ bear: "#595896",
45782
+ zebra: "#96790e",
45783
+ giraffe: "#cb717b",
45784
+ "package-delivered": "#139d8a",
45785
+ "package-picked-up": "#906ead",
45786
+ suitcase: "#d02939",
45787
+ backpack: "#d41160",
45788
+ handbag: "#f64f54",
45789
+ contact: "#7258d2",
45790
+ "motion-sensor": "#b66182",
45791
+ smoke: "#627abb",
45792
+ flood: "#b86ae4",
45793
+ gas: "#a95261",
45794
+ "carbon-monoxide": "#c65d3c",
45795
+ vibration: "#7a40b7",
45796
+ tamper: "#914b34",
45797
+ presence: "#9481c3",
45798
+ "enum-sensor": "#ac6d47",
45799
+ "device-event": "#7b6fce",
45800
+ lock: "#c67662",
45801
+ cover: "#a6564d",
45802
+ switch: "#ad2c4e",
45803
+ siren: "#348e08",
45804
+ button: "#d27303",
45805
+ doorbell: "#55770f"
45806
+ };
45807
+ var OKABE_ITO_FALLBACK = [
45808
+ "#9a3784",
45809
+ "#a7392c",
45810
+ "#409d50",
45811
+ "#6987e7",
45812
+ "#ee5b3d",
45813
+ "#c0430d",
45814
+ "#af79b3",
45815
+ "#4b6628"
45816
+ ];
45817
+ /**
45818
+ * The display palette (D939). One table of colours, three presets, one
45819
+ * validator. Taxonomy colours and every consumer read this — there is no
45820
+ * second hex table.
45821
+ */
45822
+ var paletteHexSchema = string().regex(/^#[0-9a-f]{6}$/);
45823
+ var paletteSwatchSchema = discriminatedUnion("form", [object({
45824
+ form: literal("solid"),
45825
+ hex: paletteHexSchema
45826
+ }), object({
45827
+ form: literal("gradient"),
45828
+ hex: paletteHexSchema,
45829
+ alphas: array(number().gt(0).lte(1)).length(AUDIO_GRADIENT_ALPHAS.length)
45830
+ })]);
45831
+ var paletteReadySchema = object({
45832
+ status: literal("ready"),
45833
+ presetId: _enum(PALETTE_PRESET_IDS),
45834
+ customized: boolean(),
45835
+ source: _enum(["stored", "unconfigured"]),
45836
+ swatches: record(string(), paletteSwatchSchema),
45837
+ fallbacks: array(paletteHexSchema).length(8)
45838
+ });
45839
+ var paletteResultSchema = discriminatedUnion("status", [paletteReadySchema, object({
45840
+ status: literal("unknown"),
45841
+ reason: string()
45842
+ })]);
45843
+ var setPaletteInputSchema = union$1([object({ presetId: string() }), object({
45844
+ presetId: string(),
45845
+ overrides: record(string(), paletteSwatchSchema)
45846
+ })]);
45847
+ object({
45848
+ presetId: string(),
45849
+ overrides: record(string(), paletteSwatchSchema)
45850
+ });
45851
+ function solid(hex) {
45852
+ return {
45853
+ form: "solid",
45854
+ hex
45855
+ };
45856
+ }
45857
+ function audioGradient(hex) {
45858
+ return {
45859
+ form: "gradient",
45860
+ hex,
45861
+ alphas: [...AUDIO_GRADIENT_ALPHAS]
45862
+ };
45863
+ }
45864
+ function swatchesFrom(hexes) {
45865
+ const out = {};
45866
+ for (const id of PALETTE_ENTRY_IDS) {
45867
+ const hex = hexes[id];
45868
+ if (hex === void 0) throw new Error(`palette table missing ${id}`);
45869
+ out[id] = id === "audio" ? audioGradient(hex) : solid(hex);
45870
+ }
45871
+ return out;
45872
+ }
45873
+ function preset(id, name, hexes, fallbacks) {
45874
+ return {
45875
+ id,
45876
+ name,
45877
+ swatches: swatchesFrom(hexes),
45878
+ fallbacks
45879
+ };
45880
+ }
45881
+ var DISPLAY_PRESETS = [
45882
+ preset("camstack", "CamStack", CAMSTACK_HEX, CAMSTACK_FALLBACK),
45883
+ preset("high-contrast", "High contrast", HIGH_CONTRAST_HEX, HIGH_CONTRAST_FALLBACK),
45884
+ preset("okabe-ito", "Colour-blind safe", OKABE_ITO_HEX, OKABE_ITO_FALLBACK)
45885
+ ];
45886
+ function presetById(id) {
45887
+ return DISPLAY_PRESETS.find((item) => item.id === id);
45888
+ }
45889
+ function bundledCamstack() {
45890
+ const presetRow = presetById("camstack");
45891
+ if (presetRow === void 0) throw new Error("CamStack preset missing");
45892
+ return readyOf(presetRow, "unconfigured", false);
45893
+ }
45894
+ function readyOf(row, source, customized) {
45895
+ return {
45896
+ status: "ready",
45897
+ presetId: row.id,
45898
+ customized,
45899
+ source,
45900
+ swatches: row.swatches,
45901
+ fallbacks: [...row.fallbacks]
45902
+ };
45903
+ }
45904
+ function hashFallback(kind, fallbacks) {
45905
+ let hash = 0;
45906
+ for (let i = 0; i < kind.length; i++) hash = hash * 31 + (kind.codePointAt(i) ?? 0) >>> 0;
45907
+ const hex = fallbacks[hash % fallbacks.length];
45908
+ if (hex === void 0) throw new Error("display palette has no fallback colour");
45909
+ return hex;
45910
+ }
45911
+ /** Colour a kind paints with. Audio leaves share the audio gradient's hex. */
45912
+ function paintHex(swatches, fallbacks, kind) {
45913
+ const key = kind.trim().toLowerCase();
45914
+ if (key.startsWith("audio-")) {
45915
+ const audio = swatches[AUDIO_ENTRY_ID];
45916
+ return audio !== void 0 ? audio.hex : hashFallback(key, fallbacks);
45917
+ }
45918
+ const direct = swatches[key];
45919
+ if (direct !== void 0) return direct.hex;
45920
+ return hashFallback(key, fallbacks);
45921
+ }
45922
+ /**
45503
45923
  * Unified event-kind taxonomy — THE single source of truth for
45504
45924
  * `kind → { parentKind, category, level, color, iconId, labelKey, label,
45505
45925
  * icon }`.
@@ -45513,109 +45933,88 @@ function mapAudioLabelToMacro(label) {
45513
45933
  * - `addon-post-analysis/shared/frame/box-drawer.ts` (DEFAULT_COLOR)
45514
45934
  * - the COCO / audio class maps (macro ↔ sub relationships)
45515
45935
  *
45516
- * The DATA (serializable — color/iconId/labelKey/parentKind) lives here in
45517
- * `@camstack/types`. The UI-side mapping `iconId → lucide component` and
45518
- * `labelKey → t()` lives in `@camstack/ui-library`. UIs never hardcode a
45519
- * color or an icon: they read this dictionary (server descriptors carry the
45520
- * fields inline; the client resolves color/icon/label from `iconId`/`kind`).
45936
+ * The DATA (serializable — parentKind / iconId / labelKey) lives here in
45937
+ * `@camstack/types`. Colour is NOT a second table: each entry's `color` is
45938
+ * the bundled CamStack display palette (D939). The effective cluster palette
45939
+ * is the `display-palette` capability. The UI-side mapping `iconId → lucide
45940
+ * component` and `labelKey → t()` lives in `@camstack/ui-library`.
45521
45941
  *
45522
45942
  * Two levels only (v1 YAGNI): macro → sub. `person` is a leaf macro.
45523
45943
  */
45524
- var TAXONOMY_COLORS = {
45525
- motion: "#f59e0b",
45526
- audio: "#06b6d4",
45527
- person: "#22c55e",
45528
- vehicle: "#3b82f6",
45529
- animal: "#f97316",
45530
- package: "#a855f7",
45531
- sensor: "#8b5cf6",
45532
- control: "#10b981",
45533
- genericDetection: "#64748b"
45534
- };
45535
- var DETECTION_SUB_COLORS = {
45536
- car: "#f59e0b",
45537
- truck: "#d97706",
45538
- bus: "#b45309",
45539
- motorcycle: "#eab308",
45540
- bicycle: "#ca8a04",
45541
- airplane: "#60a5fa",
45542
- boat: "#2563eb",
45543
- train: "#1d4ed8",
45544
- bird: "#14b8a6",
45545
- dog: "#84cc16",
45546
- cat: "#f97316",
45547
- horse: "#a16207",
45548
- sheep: "#a3a3a3",
45549
- cow: "#78716c",
45550
- elephant: "#6b7280",
45551
- bear: "#7c2d12",
45552
- zebra: "#404040",
45553
- giraffe: "#d4a373"
45554
- };
45944
+ var BUNDLED_PALETTE = bundledCamstack();
45945
+ function kindColor(kind) {
45946
+ return paintHex(BUNDLED_PALETTE.swatches, BUNDLED_PALETTE.fallbacks, kind);
45947
+ }
45948
+ function firstFallback(fallbacks) {
45949
+ const hex = fallbacks[0];
45950
+ if (hex === void 0) throw new Error("display palette has no fallback colour");
45951
+ return hex;
45952
+ }
45953
+ firstFallback(BUNDLED_PALETTE.fallbacks);
45555
45954
  function titleCase(id) {
45556
45955
  return id.split(/[-_ ]/).filter((p) => p.length > 0).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
45557
45956
  }
45558
45957
  var entries = /* @__PURE__ */ new Map();
45559
- function macro(kind, category, color, iconId, label) {
45958
+ function macro(kind, category, iconId, label) {
45560
45959
  entries.set(kind, {
45561
45960
  kind,
45562
45961
  parentKind: null,
45563
45962
  level: "macro",
45564
45963
  category,
45565
- color,
45964
+ color: kindColor(kind),
45566
45965
  iconId,
45567
45966
  labelKey: `eventKind.${kind}`,
45568
45967
  label
45569
45968
  });
45570
45969
  }
45571
- function sub(kind, parentKind, category, color, iconId, label) {
45970
+ function sub(kind, parentKind, category, iconId, label) {
45572
45971
  entries.set(kind, {
45573
45972
  kind,
45574
45973
  parentKind,
45575
45974
  level: "sub",
45576
45975
  category,
45577
- color,
45976
+ color: kindColor(kind),
45578
45977
  iconId,
45579
45978
  labelKey: `eventKind.${kind}`,
45580
45979
  label
45581
45980
  });
45582
45981
  }
45583
- macro("motion", "motion", TAXONOMY_COLORS.motion, "motion", "Motion");
45584
- macro("audio", "audio", TAXONOMY_COLORS.audio, "audio", "Audio");
45585
- macro("person", "detection", TAXONOMY_COLORS.person, "person", "Person");
45586
- macro("vehicle", "detection", TAXONOMY_COLORS.vehicle, "vehicle", "Vehicle");
45587
- macro("animal", "detection", TAXONOMY_COLORS.animal, "animal", "Animal");
45588
- macro("package", "package", TAXONOMY_COLORS.package, "package", "Package");
45589
- macro("sensor", "sensor", TAXONOMY_COLORS.sensor, "sensor", "Sensor");
45590
- macro("control", "control", TAXONOMY_COLORS.control, "control", "Control");
45982
+ macro("motion", "motion", "motion", "Motion");
45983
+ macro("audio", "audio", "audio", "Audio");
45984
+ macro("person", "detection", "person", "Person");
45985
+ macro("vehicle", "detection", "vehicle", "Vehicle");
45986
+ macro("animal", "detection", "animal", "Animal");
45987
+ macro("package", "package", "package", "Package");
45988
+ macro("sensor", "sensor", "sensor", "Sensor");
45989
+ macro("control", "control", "control", "Control");
45591
45990
  for (const [cocoClass, macroClass] of Object.entries(COCO_TO_MACRO.mapping)) {
45592
45991
  if (macroClass !== "vehicle" && macroClass !== "animal") continue;
45593
45992
  if (entries.has(cocoClass)) continue;
45594
- sub(cocoClass, macroClass, "detection", DETECTION_SUB_COLORS[cocoClass] ?? TAXONOMY_COLORS.genericDetection, cocoClass, titleCase(cocoClass));
45993
+ sub(cocoClass, macroClass, "detection", cocoClass, titleCase(cocoClass));
45595
45994
  }
45596
- sub("package-delivered", "package", "package", TAXONOMY_COLORS.package, "package", "Package delivered");
45597
- sub("package-picked-up", "package", "package", TAXONOMY_COLORS.package, "package", "Package picked up");
45598
- sub("contact", "sensor", "sensor", "#f59e0b", "door", "Contact");
45599
- sub("motion-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "pir", "Motion sensor");
45600
- sub("smoke", "sensor", "sensor", "#ef4444", "smoke", "Smoke");
45601
- sub("flood", "sensor", "sensor", "#3b82f6", "water", "Water leak");
45602
- sub("gas", "sensor", "sensor", "#ef4444", "gas", "Gas");
45603
- sub("carbon-monoxide", "sensor", "sensor", "#dc2626", "smoke", "Carbon monoxide");
45604
- sub("vibration", "sensor", "sensor", "#eab308", "vibration", "Vibration");
45605
- sub("tamper", "sensor", "sensor", "#f97316", "tamper", "Tamper");
45606
- sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
45607
- sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
45608
- sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
45609
- sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
45610
- sub("cover", "control", "control", "#0ea5e9", "door", "Cover");
45611
- sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
45612
- sub("siren", "control", "control", "#dc2626", "siren", "Siren");
45613
- sub("button", "control", "control", "#10b981", "button", "Button");
45614
- sub("doorbell", "control", "control", "#a855f7", "doorbell", "Doorbell");
45995
+ sub("package-delivered", "package", "package", "package", "Package delivered");
45996
+ sub("package-picked-up", "package", "package", "package", "Package picked up");
45997
+ sub("contact", "sensor", "sensor", "door", "Contact");
45998
+ sub("motion-sensor", "sensor", "sensor", "pir", "Motion sensor");
45999
+ sub("smoke", "sensor", "sensor", "smoke", "Smoke");
46000
+ sub("flood", "sensor", "sensor", "water", "Water leak");
46001
+ sub("gas", "sensor", "sensor", "gas", "Gas");
46002
+ sub("carbon-monoxide", "sensor", "sensor", "smoke", "Carbon monoxide");
46003
+ sub("vibration", "sensor", "sensor", "vibration", "Vibration");
46004
+ sub("tamper", "sensor", "sensor", "tamper", "Tamper");
46005
+ sub("presence", "sensor", "sensor", "presence", "Presence");
46006
+ sub("enum-sensor", "sensor", "sensor", "generic", "Sensor state");
46007
+ sub("device-event", "sensor", "sensor", "button", "Device event");
46008
+ sub("lock", "control", "control", "lock", "Lock");
46009
+ sub("cover", "control", "control", "door", "Cover");
46010
+ sub("switch", "control", "control", "switch", "Switch");
46011
+ sub("siren", "control", "control", "siren", "Siren");
46012
+ sub("button", "control", "control", "button", "Button");
46013
+ sub("doorbell", "control", "control", "doorbell", "Doorbell");
45615
46014
  for (const l of AUDIO_MACRO_LABELS) {
45616
46015
  const kind = `audio-${l.id}`;
45617
46016
  if (entries.has(kind)) continue;
45618
- sub(kind, "audio", "audio", TAXONOMY_COLORS.audio, kind, l.name);
46017
+ sub(kind, "audio", "audio", kind, l.name);
45619
46018
  }
45620
46019
  /** The complete taxonomy dictionary, keyed by kind. */
45621
46020
  var EVENT_TAXONOMY = Object.freeze(Object.fromEntries(entries));
@@ -45882,9 +46281,6 @@ var NcSystemEventKindSchema = _enum([
45882
46281
  "device-battery-normal",
45883
46282
  "device-consumable-low",
45884
46283
  "device-consumable-normal",
45885
- "device-tank-low",
45886
- "device-tank-full",
45887
- "device-tank-normal",
45888
46284
  "stream-online",
45889
46285
  "stream-offline",
45890
46286
  "node-online",
@@ -45931,13 +46327,14 @@ var NcSystemEventKindSchema = _enum([
45931
46327
  "camera-enabled"
45932
46328
  ]);
45933
46329
  /** The legacy tail of {@link NcSystemEventKindSchema} — see its docblock. */
45934
- var NC_LEGACY_SYSTEM_EVENT_KINDS = new Set([
46330
+ var NC_LEGACY_SYSTEM_EVENT_KINDS = [
45935
46331
  "camera-online",
45936
46332
  "camera-offline",
45937
46333
  "camera-disabled",
45938
46334
  "camera-enabled"
45939
- ]);
45940
- NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KINDS.has(kind));
46335
+ ];
46336
+ var NC_LEGACY_SYSTEM_EVENT_KIND_SET = new Set(NC_LEGACY_SYSTEM_EVENT_KINDS);
46337
+ NcSystemEventKindSchema.options.filter((kind) => !NC_LEGACY_SYSTEM_EVENT_KIND_SET.has(kind));
45941
46338
  /**
45942
46339
  * The panel's transitions, as ONE list.
45943
46340
  *
@@ -47056,18 +47453,11 @@ var NcRuleSchema = NcRuleInputSchema.extend({
47056
47453
  */
47057
47454
  disabledTargetIds: array(string()).default([])
47058
47455
  });
47059
- /**
47060
- * The state-summary SECTIONS (D647 / D698 / D708): the D637 kind groups that
47061
- * report a still-true state. `addons` is the crash-looped addons only and
47062
- * `addon-updates` the addons that are behind (D708 split them). `backups-exports` and `alarm` are not sections — a completed
47063
- * job is not an open state, and the alarm stays instant.
47064
- */
47065
47456
  var NcStateSummarySectionIdSchema = _enum([
47066
47457
  "device-state",
47067
47458
  "streams",
47068
47459
  "battery",
47069
47460
  "consumables",
47070
- "tanks",
47071
47461
  "detection-blind",
47072
47462
  "nodes",
47073
47463
  "addons",
@@ -48165,7 +48555,7 @@ var OauthIntegrationDescriptorSchema = object({
48165
48555
  * The value is baked into the authorization code at `/authorize` and travels
48166
48556
  * on the tokens, so editing this field changes FUTURE links only.
48167
48557
  */
48168
- refreshTokenTtlSec: union([number().int().positive(), literal("never")]).optional()
48558
+ refreshTokenTtlSec: union$1([number().int().positive(), literal("never")]).optional()
48169
48559
  });
48170
48560
  method(_void(), OauthIntegrationDescriptorSchema, { auth: "admin" });
48171
48561
  /**
@@ -48201,7 +48591,7 @@ var OidcConnectionUpdateInputSchema = object({
48201
48591
  icon: string().optional(),
48202
48592
  issuerUrl: string().optional(),
48203
48593
  clientId: string().optional(),
48204
- clientSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
48594
+ clientSecret: union$1([string(), object({ $secretClear: literal(true) })]).optional(),
48205
48595
  redirectUri: string().optional(),
48206
48596
  scopes: string().optional(),
48207
48597
  usernameClaim: _enum([
@@ -48368,6 +48758,18 @@ Object.freeze({
48368
48758
  reason: "camera sensor mappings not loaded"
48369
48759
  });
48370
48760
  /**
48761
+ * What the vision model must answer before a package event is emitted
48762
+ * when a packages rule has "Confirm with the LLM" on.
48763
+ *
48764
+ * `package` is the verdict. `confidence` is 0..1. `reason` is the model's
48765
+ * one-line explanation, stored on the event so a drop is not silent.
48766
+ */
48767
+ var PackageLlmVerdictSchema = object({
48768
+ package: boolean(),
48769
+ confidence: number().min(0).max(1),
48770
+ reason: string().min(1)
48771
+ });
48772
+ /**
48371
48773
  * A model choice has a SCOPE, and a scope change has a workflow.
48372
48774
  *
48373
48775
  * ## The two scopes
@@ -48844,7 +49246,7 @@ var TrackFlagsSchema = object({
48844
49246
  * `trained` without a re-fetch. */
48845
49247
  retrainStatus: RetrainStatusSchema
48846
49248
  });
48847
- union([literal(1), literal(2)]);
49249
+ union$1([literal(1), literal(2)]);
48848
49250
  /**
48849
49251
  * WHO decided a label, and when. Carried per tier so a value can be traced to
48850
49252
  * the step and model that produced it — which is what makes the write rule
@@ -49351,6 +49753,21 @@ var ObjectEventSchema = object({
49351
49753
  * pick-up written before D684 — UNKNOWN, never 0 and never `timestamp`.
49352
49754
  */
49353
49755
  observedAt: number().optional(),
49756
+ /**
49757
+ * The delivery was emitted without a vision-model yes (D931).
49758
+ *
49759
+ * Set only when the packages rule asked for an LLM confirm and the model
49760
+ * was unreachable, timed out, or answered something that did not parse.
49761
+ * A notification reads it so it can say "possible" instead of a definite
49762
+ * delivery. Absent on every other row, and on a confirm that said yes.
49763
+ */
49764
+ packagePossible: boolean().optional(),
49765
+ /**
49766
+ * The vision model's verdict, when a packages rule asked for one and the
49767
+ * model answered the schema (D931). A "no" never becomes a row — the
49768
+ * detector drops it and logs the reason. Present on a yes.
49769
+ */
49770
+ packageLlmVerdict: PackageLlmVerdictSchema.optional(),
49354
49771
  /** The parent track's key-event importance [0,1], propagated to every object
49355
49772
  * event of the track (so an event row can be sorted by importance without a
49356
49773
  * track join). Absent on legacy rows / before the track was scored. */
@@ -51731,11 +52148,18 @@ var KeptReasonSchema = _enum(["custom-pin"]);
51731
52148
  * Where a choice's latency came from (D791): this node's own benchmark
51732
52149
  * result, or the hardware-class table. Measured on the node beats the table.
51733
52150
  */
51734
- var LatencySourceSchema = discriminatedUnion("kind", [object({ kind: literal("table") }), object({
51735
- kind: literal("node-benchmark"),
51736
- resultId: string(),
51737
- measuredAtMs: number()
51738
- })]);
52151
+ var LatencySourceSchema = discriminatedUnion("kind", [
52152
+ object({ kind: literal("table") }),
52153
+ object({
52154
+ kind: literal("node-benchmark"),
52155
+ resultId: string(),
52156
+ measuredAtMs: number()
52157
+ }),
52158
+ object({
52159
+ kind: literal("estimated"),
52160
+ estimatedFrom: HardwareClassIdSchema
52161
+ })
52162
+ ]);
51739
52163
  var DeviceModelChoiceSchema = object({
51740
52164
  modelId: string(),
51741
52165
  p50Ms: number(),
@@ -51743,6 +52167,11 @@ var DeviceModelChoiceSchema = object({
51743
52167
  measured: boolean(),
51744
52168
  /** Absent on a plan from before D791 — read as the table. */
51745
52169
  source: LatencySourceSchema.optional(),
52170
+ /**
52171
+ * The measured class this latency was scaled from (D930). Absent when the
52172
+ * cell is the table or a node benchmark.
52173
+ */
52174
+ estimatedFrom: HardwareClassIdSchema.optional(),
51746
52175
  /** Parallel sessions at the camera rate with the headroom kept free: ⌊fps × (1 − headroom) / camera fps⌋. */
51747
52176
  capacitySessions: number(),
51748
52177
  personRecall: number()
@@ -52644,7 +53073,7 @@ var DetailResultSchema = object({
52644
53073
  * stops enriching rather than guessing, which is why addon-pipeline is
52645
53074
  * deployed BEFORE addon-post-analysis.
52646
53075
  */
52647
- labelTier: union([literal(1), literal(2)]).optional(),
53076
+ labelTier: union$1([literal(1), literal(2)]).optional(),
52648
53077
  /** Model that produced `label` — carried into the tier's attribution. */
52649
53078
  labelModelId: string().optional(),
52650
53079
  alignedCropJpeg: string().optional(),
@@ -54844,7 +55273,7 @@ method(_void(), ServerPackageStatusSchema, { auth: "admin" }), method(_void(), S
54844
55273
  */
54845
55274
  var EmailAddressSchema = email();
54846
55275
  var SendEmailInputSchema = object({
54847
- to: union([EmailAddressSchema, array(EmailAddressSchema).min(1)]),
55276
+ to: union$1([EmailAddressSchema, array(EmailAddressSchema).min(1)]),
54848
55277
  cc: array(EmailAddressSchema).optional(),
54849
55278
  bcc: array(EmailAddressSchema).optional(),
54850
55279
  /** RFC 5322 `From` field. Most relays will reject if the domain
@@ -55065,7 +55494,7 @@ var SsoBridgeClaimsSchema = object({
55065
55494
  * editing a descriptor must not retroactively extend or shorten a link the
55066
55495
  * operator already consented to.
55067
55496
  */
55068
- refreshTtl: union([number().int().positive(), literal("never")]).optional(),
55497
+ refreshTtl: union$1([number().int().positive(), literal("never")]).optional(),
55069
55498
  /**
55070
55499
  * Opaque handle of a hub-side LINK intent (D782). Set by an OIDC callback
55071
55500
  * that was started from a signed-in user's "Link" button: the finish then
@@ -55112,7 +55541,7 @@ method(object({
55112
55541
  * went through the numeric path would fail at mint time and break
55113
55542
  * linking rather than produce an eternal token.
55114
55543
  */
55115
- ttlSec: union([number().int().positive(), literal("never")]).optional()
55544
+ ttlSec: union$1([number().int().positive(), literal("never")]).optional()
55116
55545
  }), object({ token: string() }), { auth: "admin" }), method(object({ token: string() }), SsoBridgeClaimsSchema.nullable(), { auth: "admin" });
55117
55546
  var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object({
55118
55547
  providerId: string().min(1),
@@ -55680,7 +56109,7 @@ method(_void(), array(TerminalProfileInfoSchema).readonly(), { auth: "admin" }),
55680
56109
  */
55681
56110
  var TurnServerSchema = object({
55682
56111
  /** Single URL or list of URLs (e.g. "turn:turn.example.com:3478?transport=udp"). */
55683
- urls: union([string(), array(string())]),
56112
+ urls: union$1([string(), array(string())]),
55684
56113
  username: string().optional(),
55685
56114
  credential: string().optional()
55686
56115
  });
@@ -55878,7 +56307,7 @@ var VectorMetricSchema = _enum([
55878
56307
  * Callers keep their rich records in their own store and put only the filterable
55879
56308
  * columns here (deviceId, timestamp, className, modelId, …).
55880
56309
  */
55881
- var VectorMetadataSchema = record(string(), union([
56310
+ var VectorMetadataSchema = record(string(), union$1([
55882
56311
  string(),
55883
56312
  number(),
55884
56313
  boolean()
@@ -55891,7 +56320,7 @@ var VectorMetadataSchema = record(string(), union([
55891
56320
  * caller will treat as matches.
55892
56321
  */
55893
56322
  var VectorFilterSchema = object({
55894
- equals: record(string(), union([
56323
+ equals: record(string(), union$1([
55895
56324
  string(),
55896
56325
  number(),
55897
56326
  boolean()
@@ -58232,20 +58661,68 @@ var BatteryStatusSchema = object({
58232
58661
  */
58233
58662
  binary: boolean().optional()
58234
58663
  });
58664
+ var BATTERY_DEVICE_TYPES = [
58665
+ DeviceType.Camera,
58666
+ DeviceType.Sensor,
58667
+ DeviceType.Button,
58668
+ DeviceType.Switch,
58669
+ DeviceType.PetFeeder,
58670
+ DeviceType.Container,
58671
+ DeviceType.Thermostat
58672
+ ];
58673
+ /** Generic Customize target for the percentage reading (D936). */
58674
+ var batteryCustomize = {
58675
+ version: 1,
58676
+ targets: [{
58677
+ id: "percentage",
58678
+ cardinality: "scalar",
58679
+ vocabulary: {
58680
+ cap: "battery",
58681
+ stateField: "percentage"
58682
+ },
58683
+ deviceTypes: BATTERY_DEVICE_TYPES,
58684
+ apply: "add-or-replace",
58685
+ path: "percentage",
58686
+ sources: [{
58687
+ kind: "field",
58688
+ valueKinds: ["number", "string"],
58689
+ transforms: [{ kind: "identity" }, {
58690
+ kind: "linear",
58691
+ defaultScale: 1,
58692
+ defaultOffset: 0,
58693
+ clampMin: 0,
58694
+ clampMax: 100
58695
+ }]
58696
+ }],
58697
+ companions: [
58698
+ {
58699
+ kind: "constant",
58700
+ path: "charging",
58701
+ value: "unknown",
58702
+ when: "add"
58703
+ },
58704
+ {
58705
+ kind: "constant",
58706
+ path: "sleeping",
58707
+ value: false,
58708
+ when: "add"
58709
+ },
58710
+ {
58711
+ kind: "clock",
58712
+ path: "lastUpdated",
58713
+ when: "add"
58714
+ }
58715
+ ],
58716
+ sourceReport: { status: "assignment-status" },
58717
+ advancedExpression: true
58718
+ }]
58719
+ };
58235
58720
  var batteryCapability = {
58236
58721
  name: "battery",
58237
58722
  scope: "device",
58238
58723
  deviceNative: true,
58239
58724
  mode: "singleton",
58240
- deviceTypes: [
58241
- DeviceType.Camera,
58242
- DeviceType.Sensor,
58243
- DeviceType.Button,
58244
- DeviceType.Switch,
58245
- DeviceType.PetFeeder,
58246
- DeviceType.Container,
58247
- DeviceType.Thermostat
58248
- ],
58725
+ deviceTypes: BATTERY_DEVICE_TYPES,
58249
58726
  methods: {
58250
58727
  /**
58251
58728
  * Explicitly wake the camera from low-power sleep ahead of a
@@ -58329,6 +58806,7 @@ onStatusChanged: { data: object({
58329
58806
  kind: "gauge",
58330
58807
  unit: "%"
58331
58808
  } } },
58809
+ customize: batteryCustomize,
58332
58810
  /** Clock fields: written, but excluded from the compare that decides
58333
58811
  * whether persisting is worth a SQLite commit. */
58334
58812
  volatileStateFields: ["lastUpdated"]
@@ -59031,6 +59509,7 @@ var ConsumablesStatusSchema = object({
59031
59509
  })),
59032
59510
  lastChangedAt: number()
59033
59511
  });
59512
+ Object.values(DeviceType), [...ConsumableRemainingUnitSchema.options], [...ConsumableStatusSchema.options], [...ConsumableKindSchema.options], [...ConsumableFormSchema.options];
59034
59513
  Object.values(DeviceType), method(object({
59035
59514
  deviceId: number().int().nonnegative(),
59036
59515
  key: string().min(1)
@@ -59055,7 +59534,7 @@ object({
59055
59534
  /** Current value. Numeric kind → number; select/text/datetime → string.
59056
59535
  * Datetime values are ISO 8601 strings (full timestamp, date-only,
59057
59536
  * or time-only — HA accepts all three forms). */
59058
- value: union([number(), string()]),
59537
+ value: union$1([number(), string()]),
59059
59538
  /** Acceptable values when `kind === 'select'`. Empty for other kinds. */
59060
59539
  options: array(string()),
59061
59540
  /** Ms epoch when the slice was last updated. */
@@ -59223,6 +59702,30 @@ DeviceType.Camera, method(object({ deviceId: number() }), DayNightOptionsSchema)
59223
59702
  auth: "admin"
59224
59703
  });
59225
59704
  /**
59705
+ * display-palette — the cluster's one colour table (D939).
59706
+ *
59707
+ * Hub singleton. `getPalette` reports `unknown` when the settings store
59708
+ * cannot be read; it does not pretend the bundled preset was stored.
59709
+ * `setPalette` refuses a palette that fails the ΔE / contrast rule and names
59710
+ * the pairs.
59711
+ */
59712
+ var presetSummarySchema = object({
59713
+ id: string(),
59714
+ name: string(),
59715
+ swatches: record(string(), paletteSwatchSchema),
59716
+ fallbacks: array(string())
59717
+ });
59718
+ method(_void(), paletteResultSchema, {
59719
+ auth: "protected",
59720
+ role: "viewer"
59721
+ }), method(_void(), array(presetSummarySchema).readonly(), {
59722
+ auth: "protected",
59723
+ role: "viewer"
59724
+ }), method(setPaletteInputSchema, paletteReadySchema, {
59725
+ kind: "mutation",
59726
+ auth: "admin"
59727
+ });
59728
+ /**
59226
59729
  * Generic device-level status snapshot. Auto-registered by `BaseDevice`
59227
59730
  * for every device, regardless of provider — the kernel needs a uniform
59228
59731
  * cap-keyed slice for the basic device flags every consumer expects to
@@ -59369,7 +59872,7 @@ object({
59369
59872
  });
59370
59873
  var DeviceIdSchema = number().int().nonnegative();
59371
59874
  /** A value a program option or setting can take. */
59372
- var DishwasherValueSchema = union([
59875
+ var DishwasherValueSchema = union$1([
59373
59876
  boolean(),
59374
59877
  string(),
59375
59878
  number()
@@ -59502,6 +60005,7 @@ DeviceType.Dishwasher, method(object({
59502
60005
  kind: "mutation",
59503
60006
  auth: "protected"
59504
60007
  }), method(object({ deviceId: DeviceIdSchema }), DishwasherOptionsSchema, { auth: "protected" });
60008
+ DeviceType.Camera;
59505
60009
  object({
59506
60010
  /** Ms epoch of the last press. null = never observed since this provider started. */
59507
60011
  lastPressedAt: number().nullable(),
@@ -61294,6 +61798,7 @@ authKey: string().optional() }), object({
61294
61798
  /** Human-readable error when `ok: false`. */
61295
61799
  error: string().optional()
61296
61800
  }), { kind: "mutation" });
61801
+ DeviceType.Camera;
61297
61802
  object({
61298
61803
  detected: boolean(),
61299
61804
  /** Ms epoch of the last detected-true observation. Null if never detected. */
@@ -62682,6 +63187,30 @@ includeCrops: boolean().optional() }).optional(), array(VehicleSchema).readonly(
62682
63187
  kind: "mutation",
62683
63188
  auth: "admin"
62684
63189
  });
63190
+ var CORE_ULTRA_265K_IGPU_CLOCK_MHZ = 2e3;
63191
+ var positiveOrNull = number().nullable().transform((value) => value !== null && Number.isFinite(value) && value > 0 ? value : null);
63192
+ var textOrNull = string().nullable().transform((value) => value !== null && value.trim() !== "" ? value : null);
63193
+ var PrecisionGopsSchema = object({
63194
+ int8: positiveOrNull,
63195
+ fp16: positiveOrNull
63196
+ });
63197
+ /** What one accelerator device reported. Every failed query is null. */
63198
+ var AcceleratorComputeSchema = object({
63199
+ deviceKey: string(),
63200
+ fullDeviceName: textOrNull,
63201
+ executionUnits: positiveOrNull,
63202
+ clockMHz: positiveOrNull,
63203
+ gops: PrecisionGopsSchema.nullable(),
63204
+ architecture: textOrNull,
63205
+ npuName: textOrNull,
63206
+ tops: positiveOrNull,
63207
+ vendor: textOrNull,
63208
+ webgpuArchitecture: textOrNull,
63209
+ webgpuDevice: textOrNull,
63210
+ computeUnits: positiveOrNull
63211
+ });
63212
+ object({ accelerators: array(AcceleratorComputeSchema).nullable() });
63213
+ 64 * CORE_ULTRA_265K_IGPU_CLOCK_MHZ;
62685
63214
  var ModelFormatSchema = _enum(MODEL_FORMATS);
62686
63215
  var HwAccelBackendInputSchema = _enum([
62687
63216
  "videotoolbox",
@@ -62795,7 +63324,12 @@ var HardwareInfoSchema = object({
62795
63324
  availableRAM_MB: number(),
62796
63325
  gpu: GpuInfoSchema.nullable(),
62797
63326
  npu: NpuInfoSchema.nullable(),
62798
- coral: CoralInfoSchema.nullable().optional()
63327
+ coral: CoralInfoSchema.nullable().optional(),
63328
+ /**
63329
+ * Per-accelerator compute (D930). Omitted by a node that has not probed it.
63330
+ * `null` fields inside an entry are a failed query, never a zero.
63331
+ */
63332
+ accelerators: array(AcceleratorComputeSchema).readonly().optional()
62799
63333
  });
62800
63334
  var PlatformScoreSchema = object({
62801
63335
  runtime: literal("python"),
@@ -63328,6 +63862,24 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
63328
63862
  deviceId: number(),
63329
63863
  status: PtzAutotrackStatusSchema
63330
63864
  });
63865
+ var RunningStateSourceSchema = _enum(["native", "assigned"]);
63866
+ object({
63867
+ /** null = unknown — never coerced to false (D393). */
63868
+ running: boolean().nullable(),
63869
+ /** Epoch ms the current boolean began; null while `running` is null. */
63870
+ since: number().nullable(),
63871
+ source: RunningStateSourceSchema,
63872
+ phase: string().nullable(),
63873
+ /** null until a reading has been published (D393). */
63874
+ lastFetchedAt: number().nullable()
63875
+ });
63876
+ object({
63877
+ deviceId: number(),
63878
+ since: number().nullable(),
63879
+ phase: string().nullable()
63880
+ });
63881
+ Object.values(DeviceType);
63882
+ DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
63331
63883
  DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, method(object({ deviceId: number().int().nonnegative() }), object({ success: literal(true) }), {
63332
63884
  kind: "mutation",
63333
63885
  auth: "admin"
@@ -65691,7 +66243,20 @@ var ZoneRuleSchema = object({
65691
66243
  * Defaults to `true` so operators creating a rule via the UI
65692
66244
  * see it active immediately.
65693
66245
  */
65694
- enabled: boolean().default(true)
66246
+ enabled: boolean().default(true),
66247
+ /**
66248
+ * Package stage only. When true, a package candidate may open from a
66249
+ * scene difference in this rule's zone after a person leaves. The
66250
+ * package model, when it boxes something overlapping, is extra evidence
66251
+ * and is not the gate. Absent or false keeps today's model path.
66252
+ */
66253
+ detectBySceneDifference: boolean().optional(),
66254
+ /**
66255
+ * Package stage only. When true, a promoted package is shown to the
66256
+ * `llm` cap before the delivery event is emitted. Absent or false emits
66257
+ * as today. Usable with or without `detectBySceneDifference`.
66258
+ */
66259
+ confirmWithLlm: boolean().optional()
65695
66260
  });
65696
66261
  array(ZoneRuleSchema).readonly();
65697
66262
  object({
@@ -66503,8 +67068,8 @@ var UserSummarySchema = object({
66503
67068
  id: string(),
66504
67069
  username: string(),
66505
67070
  isAdmin: boolean().default(false),
66506
- allowedProviders: union([array(string()), literal("*")]),
66507
- allowedDevices: record(string(), union([array(string()), literal("*")])),
67071
+ allowedProviders: union$1([array(string()), literal("*")]),
67072
+ allowedDevices: record(string(), union$1([array(string()), literal("*")])),
66508
67073
  /** Capability rows, or ONE viewer row (D921) — see `UserScopeSchema`. */
66509
67074
  scopes: array(UserScopeSchema).default([]),
66510
67075
  /**
@@ -66521,15 +67086,15 @@ var CreateUserInputSchema = object({
66521
67086
  username: string(),
66522
67087
  password: string().min(6),
66523
67088
  isAdmin: boolean().default(false),
66524
- allowedProviders: union([array(string()), literal("*")]).optional(),
66525
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
67089
+ allowedProviders: union$1([array(string()), literal("*")]).optional(),
67090
+ allowedDevices: record(string(), union$1([array(string()), literal("*")])).optional(),
66526
67091
  scopes: array(UserScopeSchema).optional()
66527
67092
  });
66528
67093
  var UpdateUserInputSchema = object({
66529
67094
  id: string(),
66530
67095
  isAdmin: boolean().optional(),
66531
- allowedProviders: union([array(string()), literal("*")]).optional(),
66532
- allowedDevices: record(string(), union([array(string()), literal("*")])).optional(),
67096
+ allowedProviders: union$1([array(string()), literal("*")]).optional(),
67097
+ allowedDevices: record(string(), union$1([array(string()), literal("*")])).optional(),
66533
67098
  scopes: array(UserScopeSchema).optional()
66534
67099
  });
66535
67100
  var OauthSessionSummarySchema = object({
@@ -66648,7 +67213,7 @@ method(_void(), array(UserSummarySchema), { auth: "admin" }), method(CreateUserI
66648
67213
  * From `OauthIntegrationDescriptor.refreshTokenTtlSec`. Baked into the
66649
67214
  * code so the link carries its own lifetime; omit for the 30-day
66650
67215
  * default. */
66651
- refreshTtlSec: union([number().int().positive(), literal("never")]).optional()
67216
+ refreshTtlSec: union$1([number().int().positive(), literal("never")]).optional()
66652
67217
  }), object({ code: string() }), {
66653
67218
  kind: "mutation",
66654
67219
  access: "create"
@@ -67138,7 +67703,7 @@ var TrainingProviderUpdateEntryInputSchema = object({
67138
67703
  entryId: string().min(1),
67139
67704
  name: string().min(1),
67140
67705
  tokenId: string().optional(),
67141
- tokenSecret: union([string(), object({ $secretClear: literal(true) })]).optional(),
67706
+ tokenSecret: union$1([string(), object({ $secretClear: literal(true) })]).optional(),
67142
67707
  environment: string().optional(),
67143
67708
  gpuType: string().optional(),
67144
67709
  region: string().optional(),
@@ -70641,6 +71206,27 @@ Object.freeze({
70641
71206
  access: "create",
70642
71207
  role: "viewer"
70643
71208
  },
71209
+ "displayPalette.getPalette": {
71210
+ capName: "display-palette",
71211
+ capScope: "system",
71212
+ addonId: null,
71213
+ access: "view",
71214
+ role: "viewer"
71215
+ },
71216
+ "displayPalette.listPresets": {
71217
+ capName: "display-palette",
71218
+ capScope: "system",
71219
+ addonId: null,
71220
+ access: "view",
71221
+ role: "viewer"
71222
+ },
71223
+ "displayPalette.setPalette": {
71224
+ capName: "display-palette",
71225
+ capScope: "system",
71226
+ addonId: null,
71227
+ access: "create",
71228
+ role: "admin"
71229
+ },
70644
71230
  "doorbell.getStatus": {
70645
71231
  capName: "doorbell",
70646
71232
  capScope: "device",
@@ -75191,6 +75777,13 @@ Object.freeze({
75191
75777
  access: "create",
75192
75778
  role: "admin"
75193
75779
  },
75780
+ "runningState.getStatus": {
75781
+ capName: "running-state",
75782
+ capScope: "device",
75783
+ addonId: null,
75784
+ access: "view",
75785
+ role: "viewer"
75786
+ },
75194
75787
  "sceneMonitor.captureReference": {
75195
75788
  capName: "scene-monitor",
75196
75789
  capScope: "device",
@@ -79905,6 +80498,32 @@ var APPLIANCE_CAP_VOCABULARY = {
79905
80498
  }],
79906
80499
  events: { onSent: { notOperatorFacing: "a delivery receipt of a send this system made — not a fact about the world" } }
79907
80500
  },
80501
+ "running-state": {
80502
+ scope: "device",
80503
+ noun: t("Running", "In funzione"),
80504
+ state: [{
80505
+ kind: "boolean",
80506
+ field: "running",
80507
+ label: t("Running", "In funzione"),
80508
+ whenTrue: t("When {device} starts", "Quando {device} inizia"),
80509
+ whenFalse: t("When {device} finishes", "Quando {device} finisce"),
80510
+ icon: "play"
80511
+ }],
80512
+ events: {
80513
+ onStarted: {
80514
+ category: EventCategory.RunningStarted,
80515
+ label: t("Started running", "Ha iniziato a funzionare"),
80516
+ phrase: t("When {device} starts running", "Quando {device} inizia a funzionare"),
80517
+ icon: "play"
80518
+ },
80519
+ onFinished: {
80520
+ category: EventCategory.RunningFinished,
80521
+ label: t("Finished", "Ha finito"),
80522
+ phrase: t("When {device} finishes", "Quando {device} finisce"),
80523
+ icon: "square"
80524
+ }
80525
+ }
80526
+ },
79908
80527
  update: {
79909
80528
  scope: "device",
79910
80529
  noun: t("Firmware update", "Aggiornamento firmware"),
@@ -81064,6 +81683,7 @@ var SYSTEM_CAP_VOCABULARY = {
81064
81683
  "device-provider": system(INTERNAL),
81065
81684
  "device-removal-hook": system(INTERNAL),
81066
81685
  "device-state": system("the runtime-state mirror every trigger already reads through"),
81686
+ "display-palette": system("the cluster display palette — one colour table for every client, not a device action"),
81067
81687
  "embedding-encoder": system(ML),
81068
81688
  "face-gallery": system("the face gallery — curated on its own page"),
81069
81689
  "failure-contribution": system(INTERNAL),
@@ -83445,6 +84065,11 @@ Object.freeze({
83445
84065
  form: "single",
83446
84066
  optional: true
83447
84067
  }],
84068
+ "runningState.getStatus": [{
84069
+ name: "deviceId",
84070
+ form: "single",
84071
+ optional: false
84072
+ }],
83448
84073
  "sceneMonitor.captureReference": [{
83449
84074
  name: "deviceId",
83450
84075
  form: "single",
@@ -83943,6 +84568,96 @@ Object.freeze({
83943
84568
  "smtp-provider": "email"
83944
84569
  });
83945
84570
  DeviceType.Camera;
84571
+ DeviceType.Camera, DeviceType.Sensor, DeviceType.Button, DeviceType.Switch, DeviceType.PetFeeder, DeviceType.Container, DeviceType.Thermostat, DeviceType.Camera, DeviceType.Hub, DeviceType.Light, DeviceType.Siren, DeviceType.Switch, DeviceType.Sensor, DeviceType.Thermostat, DeviceType.Climate, DeviceType.Button, DeviceType.EventEmitter, DeviceType.Update, DeviceType.Generic, DeviceType.Notifier, DeviceType.Script, DeviceType.Automation, DeviceType.Lock, DeviceType.Cover, DeviceType.Valve, DeviceType.Humidifier, DeviceType.WaterHeater, DeviceType.Fan, DeviceType.MediaPlayer, DeviceType.AlarmPanel, DeviceType.Control, DeviceType.Presence, DeviceType.Weather, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.Container, DeviceType.Image, DeviceType.PetFeeder, DeviceType.Dishwasher, DeviceType.Camera, DeviceType.Camera, DeviceType.Dishwasher, DeviceType.Vacuum, DeviceType.LawnMower, DeviceType.MediaPlayer, DeviceType.WaterHeater, DeviceType.Humidifier, DeviceType.Fan, DeviceType.Climate, DeviceType.Thermostat, DeviceType.PetFeeder;
84572
+ /**
84573
+ * A saved Customize assignment (D936). The schema is the type: callers use
84574
+ * `z.infer`, they do not restate the shape.
84575
+ */
84576
+ var OverrideSourceSchema = object({
84577
+ addonId: string().min(1),
84578
+ stableId: string().min(1),
84579
+ cap: string().min(1),
84580
+ fieldPath: string().min(1)
84581
+ });
84582
+ var CustomizeTransformDraftSchema = discriminatedUnion("kind", [
84583
+ object({ kind: literal("identity") }),
84584
+ object({ kind: literal("invert") }),
84585
+ object({
84586
+ kind: literal("threshold"),
84587
+ comparison: _enum(["gte", "lte"]),
84588
+ threshold: number(),
84589
+ startDwellMs: number().nonnegative(),
84590
+ stopDwellMs: number().nonnegative()
84591
+ }),
84592
+ object({
84593
+ kind: literal("linear"),
84594
+ scale: number(),
84595
+ offset: number(),
84596
+ clampMin: number().optional(),
84597
+ clampMax: number().optional()
84598
+ }),
84599
+ object({
84600
+ kind: literal("enum-map"),
84601
+ mapping: record(string(), string().nullable())
84602
+ }),
84603
+ object({ kind: literal("number") })
84604
+ ]);
84605
+ var ScalarOverrideDraftSchema = object({
84606
+ kind: literal("scalar"),
84607
+ cap: string().min(1),
84608
+ targetId: string().min(1),
84609
+ source: OverrideSourceSchema,
84610
+ transform: CustomizeTransformDraftSchema
84611
+ });
84612
+ var KeyedArrayOverrideDraftSchema = object({
84613
+ kind: literal("keyedArray"),
84614
+ cap: string().min(1),
84615
+ targetId: string().min(1),
84616
+ key: string().min(1),
84617
+ label: string().min(1),
84618
+ fieldPath: string().min(1),
84619
+ source: OverrideSourceSchema,
84620
+ transform: CustomizeTransformDraftSchema
84621
+ });
84622
+ var PulseOverrideDraftSchema = object({
84623
+ kind: literal("pulse"),
84624
+ cap: string().min(1),
84625
+ targetId: string().min(1),
84626
+ enabled: boolean(),
84627
+ debounceMs: number().nonnegative(),
84628
+ sources: array(OverrideSourceSchema).min(1)
84629
+ });
84630
+ var SpanSourceDraftSchema = OverrideSourceSchema.extend({ mode: _enum([
84631
+ "in-addition",
84632
+ "confirm",
84633
+ "replace"
84634
+ ]).optional() });
84635
+ discriminatedUnion("kind", [
84636
+ ScalarOverrideDraftSchema,
84637
+ KeyedArrayOverrideDraftSchema,
84638
+ PulseOverrideDraftSchema,
84639
+ object({
84640
+ kind: literal("span"),
84641
+ cap: string().min(1),
84642
+ targetId: string().min(1),
84643
+ timeoutMs: number().nonnegative(),
84644
+ cooldownMs: number().nonnegative().optional(),
84645
+ sources: array(SpanSourceDraftSchema)
84646
+ })
84647
+ ]);
84648
+ object({
84649
+ targetId: string().min(1),
84650
+ configured: boolean(),
84651
+ source: _enum(["native", "assigned"]),
84652
+ state: _enum([
84653
+ "active",
84654
+ "stale",
84655
+ "released",
84656
+ "unavailable"
84657
+ ]),
84658
+ reason: string().nullable()
84659
+ });
84660
+ String.raw`rose\(active(\d+), changed(\d+), (\d+(?:\.\d+)?)\)`;
83946
84661
  var G711_SCALE_CORRECTION_DB = {
83947
84662
  PCMU: 20 * Math.log10(4),
83948
84663
  PCMA: 20 * Math.log10(8)
@@ -84045,40 +84760,7 @@ var NC_AUDIO_DEFAULTS = {
84045
84760
  samplingSeconds: 10
84046
84761
  };
84047
84762
  new Set(["devices", "classes"]);
84048
- /**
84049
- * WHICH narrowing filter speaks about WHICH system-event kind — one predicate,
84050
- * consumed by the rule ENGINE and by every rule EDITOR.
84051
- *
84052
- * ── The failure this file exists for ───────────────────────────────────────
84053
- * `NcSystemEventConditionSchema` carries four optional narrowing lists
84054
- * (`deviceIds`, `deviceTypes`, `nodeIds`, `packageNames`) beside a required
84055
- * `kinds` gate. They are not four filters over one vocabulary: each belongs to
84056
- * a FAMILY of kinds and is meaningless outside it. A node going away has no
84057
- * device type; a completed export has no package; `stream-offline` deliberately
84058
- * carries no device type at all (a stream belongs to a camera by construction).
84059
- *
84060
- * The engine applied all four to every kind, so a filter from one family failed
84061
- * a kind from another family CLOSED. The operator's live rule "Eventi di
84062
- * sistema" selected eleven kinds and set `deviceTypes: ['camera']` — the only
84063
- * way to say "only my cameras" about the two liveness kinds — and NINE of the
84064
- * eleven silently stopped being deliverable. Four days of history contained
84065
- * `device-online` and `device-offline` and nothing else, across a window in
84066
- * which agents were restarted and addons were updated.
84067
- *
84068
- * Both editors already computed this scoping, to decide which filter boxes to
84069
- * render; the cap's own docblock already promised it ("Narrow a `device-*` kind
84070
- * to these device TYPES"). Only the engine disagreed, and it disagreed in the
84071
- * destructive direction. So the rule is stated ONCE, here, and the engine and
84072
- * the editors are readers of it rather than three independent authors.
84073
- *
84074
- * ── The direction ─────────────────────────────────────────────────────────
84075
- * Out of family ⇒ the filter is IGNORED, never fail-closed. An editor that
84076
- * hides a box has already told the operator this filter does not speak about
84077
- * these kinds, so honouring it as an exclusion contradicts the only
84078
- * explanation the operator was ever given. Inside its family the filter still
84079
- * narrows, and still fails CLOSED on a subject that cannot produce the identity
84080
- * it tests (a device the mirror cannot name is not evidence of a camera).
84081
- */
84763
+ new Set(NC_LEGACY_SYSTEM_EVENT_KINDS);
84082
84764
  /**
84083
84765
  * Every filter, for the callers that must cover all of them (guards, tests).
84084
84766
  *
@@ -84104,9 +84786,6 @@ new Map(Object.entries({
84104
84786
  "device-battery-normal": "device-events",
84105
84787
  "device-consumable-low": "device-events",
84106
84788
  "device-consumable-normal": "device-events",
84107
- "device-tank-low": "device-events",
84108
- "device-tank-full": "device-events",
84109
- "device-tank-normal": "device-events",
84110
84789
  "stream-online": "device-events",
84111
84790
  "stream-offline": "device-events",
84112
84791
  "detection-blind": "device-events",
@@ -84183,16 +84862,6 @@ var NC_SYSTEM_EVENT_KIND_GROUP_SPECS = [
84183
84862
  label: "Consumables",
84184
84863
  kinds: ["device-consumable-low", "device-consumable-normal"]
84185
84864
  },
84186
- {
84187
- id: "tanks",
84188
- family: "device-events",
84189
- label: "Tanks",
84190
- kinds: [
84191
- "device-tank-low",
84192
- "device-tank-full",
84193
- "device-tank-normal"
84194
- ]
84195
- },
84196
84865
  {
84197
84866
  id: "detection-blind",
84198
84867
  family: "device-events",
@@ -84350,11 +85019,6 @@ var RULE_BODY_ONLY = ["body"];
84350
85019
  var RULE_TEXT = ["title", "body"];
84351
85020
  var BATTERY = ["device-battery-low", "device-battery-normal"];
84352
85021
  var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
84353
- var TANK = [
84354
- "device-tank-low",
84355
- "device-tank-full",
84356
- "device-tank-normal"
84357
- ];
84358
85022
  var ALERT_EPISODE = [
84359
85023
  "alert-critical",
84360
85024
  "alert-error",
@@ -84386,7 +85050,6 @@ var DEVICE_EVENTS = [
84386
85050
  "device-enabled",
84387
85051
  ...BATTERY,
84388
85052
  ...CONSUMABLE,
84389
- ...TANK,
84390
85053
  "stream-online",
84391
85054
  "stream-offline",
84392
85055
  "detection-blind",
@@ -84432,12 +85095,6 @@ var STATE_SUMMARY_SECTION_VARS = [
84432
85095
  example: "Robot — filter 10 %",
84433
85096
  kinds: CONSUMABLE
84434
85097
  },
84435
- {
84436
- name: "summaryTanks",
84437
- label: "Summary: tanks",
84438
- example: "Robot — clean water low",
84439
- kinds: TANK
84440
- },
84441
85098
  {
84442
85099
  name: "summaryDetection",
84443
85100
  label: "Summary: cameras detecting nothing",
@@ -84553,8 +85210,7 @@ var STATE_SUMMARY_VARS = [
84553
85210
  ([...RULE_TEXT], [...COMBINED_ALARM]),
84554
85211
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
84555
85212
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
84556
- ([...RULE_TEXT], [...CONSUMABLE, ...TANK]),
84557
- ([...RULE_TEXT], [...TANK]),
85213
+ ([...RULE_TEXT], [...CONSUMABLE]),
84558
85214
  [...RULE_TEXT],
84559
85215
  ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
84560
85216
  ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
@@ -84658,15 +85314,6 @@ new Map([
84658
85314
  snapshotLabel: "Consumables low now",
84659
85315
  snapshotSection: "consumables"
84660
85316
  },
84661
- {
84662
- id: "tanks",
84663
- family: "device-events",
84664
- label: "Tanks (low or full / back to normal)",
84665
- bad: ["device-tank-low", "device-tank-full"],
84666
- recovery: ["device-tank-normal"],
84667
- snapshotLabel: "Tanks low or full now",
84668
- snapshotSection: "tanks"
84669
- },
84670
85317
  {
84671
85318
  id: "motion-noise",
84672
85319
  family: "device-events",
@@ -85223,6 +85870,430 @@ function buildBenchStepTree(schema, rootModelId, enabledStepIds) {
85223
85870
  ]
85224
85871
  }];
85225
85872
  }
85873
+ /** The object-detection step id — the first-level detector both recipes run. */
85874
+ var OBJECT_DETECTION_STEP_ID = "object-detection";
85875
+ /** Motion regions older than this describe a different frame. */
85876
+ var RECOVERY_MOTION_MAX_AGE_MS = 2e3;
85877
+ /** Area of a normalised rectangle, never negative. Shared with the D396 recipe. */
85878
+ function normalizedArea(r) {
85879
+ return Math.max(0, r.w) * Math.max(0, r.h);
85880
+ }
85881
+ var area = normalizedArea;
85882
+ function intersection(a, b) {
85883
+ return Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
85884
+ }
85885
+ /**
85886
+ * Share of `region` not covered by any detection box. The sum of pairwise
85887
+ * intersections over-counts where detections overlap each other, which only
85888
+ * makes the pass MORE conservative; clipped at 1.
85889
+ */
85890
+ function uncoveredFraction(region, detections) {
85891
+ const regionArea = area(region);
85892
+ if (regionArea <= 0) return 0;
85893
+ let covered = 0;
85894
+ for (const detection of detections) covered += intersection(region, detection);
85895
+ return 1 - Math.min(1, covered / regionArea);
85896
+ }
85897
+ /**
85898
+ * THE square-placement rule of this pass — one owner, shared by both recipes.
85899
+ *
85900
+ * A crop is a square of `side` native pixels centred where it can be and slid
85901
+ * inside the frame where it cannot. Both region rules place their squares
85902
+ * through this function so a crop means the same rectangle whichever rule
85903
+ * chose the region.
85904
+ */
85905
+ function squareAround(centreX, centreY, side, native) {
85906
+ return {
85907
+ left: Math.round(Math.max(0, Math.min(native.width - side, centreX - side / 2))),
85908
+ top: Math.round(Math.max(0, Math.min(native.height - side, centreY - side / 2))),
85909
+ side
85910
+ };
85911
+ }
85912
+ function contains(crop, x, y) {
85913
+ return x >= crop.left && x <= crop.left + crop.side && y >= crop.top && y <= crop.top + crop.side;
85914
+ }
85915
+ /**
85916
+ * Squares for one region: one when it fits the largest side, otherwise up to
85917
+ * two tiles along its longer axis (a person-sized blob with something small
85918
+ * at either end). The padded region decides the side, never the frame.
85919
+ */
85920
+ function cropsForRegion(region, native) {
85921
+ const widthPx = region.w * native.width;
85922
+ const heightPx = region.h * native.height;
85923
+ const longest = Math.max(widthPx, heightPx);
85924
+ const padded = longest * 1.5;
85925
+ const side = Math.round(Math.min(Math.max(padded, 640), 800));
85926
+ const centreX = (region.x + region.w / 2) * native.width;
85927
+ const centreY = (region.y + region.h / 2) * native.height;
85928
+ if (padded <= 800) return [squareAround(centreX, centreY, side, native)];
85929
+ const alongX = widthPx >= heightPx;
85930
+ const quarter = longest / 4;
85931
+ return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
85932
+ }
85933
+ /**
85934
+ * The ONE place the two region gates are decided. `planRecoveryCrops` filters
85935
+ * on it and {@link countRecoverySkips} counts it, so the report can never
85936
+ * describe a gate the planner does not actually apply.
85937
+ */
85938
+ function classifyRegion(region, uncovered) {
85939
+ if (area(region) > .25) return "too-large";
85940
+ if (uncovered < .35) return "covered";
85941
+ return "eligible";
85942
+ }
85943
+ /**
85944
+ * WHY A FRAME PLANNED NO CROP, AS A NUMBER (D378).
85945
+ *
85946
+ * The pass report counts the passes that RAN, so "the second pass stops running
85947
+ * when a person is beside the dog" — a plausible reading of
85948
+ * {@link RECOVERY_MAX_REGION_FRAME_FRACTION} and
85949
+ * {@link RECOVERY_MIN_UNCOVERED_FRACTION}, and a live hypothesis on camera 615
85950
+ * — could not be confirmed or killed from the logs at all. This classifies the
85951
+ * regions a frame offered, against the same predicate the planner filters on.
85952
+ */
85953
+ function countRecoverySkips(input) {
85954
+ let tooLarge = 0;
85955
+ let covered = 0;
85956
+ let eligible = 0;
85957
+ for (const region of input.regions) switch (classifyRegion(region, uncoveredFraction(region, input.detections))) {
85958
+ case "too-large":
85959
+ tooLarge += 1;
85960
+ break;
85961
+ case "covered":
85962
+ covered += 1;
85963
+ break;
85964
+ case "eligible":
85965
+ eligible += 1;
85966
+ break;
85967
+ }
85968
+ return {
85969
+ tooLarge,
85970
+ covered,
85971
+ eligible
85972
+ };
85973
+ }
85974
+ function planRecoveryCrops(input) {
85975
+ const { native } = input;
85976
+ if (Math.min(native.width, native.height) < 640) return [];
85977
+ const ranked = input.regions.map((region) => ({
85978
+ region,
85979
+ uncovered: uncoveredFraction(region, input.detections)
85980
+ })).filter(({ region, uncovered }) => classifyRegion(region, uncovered) === "eligible").toSorted((a, b) => area(b.region) * b.uncovered - area(a.region) * a.uncovered);
85981
+ const planned = [];
85982
+ for (const { region } of ranked) for (const crop of cropsForRegion(region, native)) {
85983
+ if (planned.length >= 2) return planned;
85984
+ const centreX = crop.left + crop.side / 2;
85985
+ const centreY = crop.top + crop.side / 2;
85986
+ if (planned.some((existing) => contains(existing, centreX, centreY))) continue;
85987
+ planned.push(crop);
85988
+ }
85989
+ return planned;
85990
+ }
85991
+ /**
85992
+ * Boxes the executor produced on the crop VIEW → the result's own space.
85993
+ *
85994
+ * Three spaces meet here: the view the model saw (`view`, ≤640 px), the native
85995
+ * square it was cut from (`crop`, native px) and the space the full-frame
85996
+ * result reports in (`result`, native px on the `frameRef` path, the inline
85997
+ * ≤640 view otherwise). A box touching the view edge was clipped by the crop
85998
+ * and is dropped: half a person is not a person, and the tracker would take
85999
+ * the half as the whole.
86000
+ */
86001
+ function remapCropDetections(detections, crop, view, result, native) {
86002
+ const viewToNative = crop.side / Math.max(1, view.width);
86003
+ const nativeToResultX = result.width / Math.max(1, native.width);
86004
+ const nativeToResultY = result.height / Math.max(1, native.height);
86005
+ const out = [];
86006
+ for (const detection of detections) {
86007
+ const { x, y, width, height } = detection.bbox;
86008
+ if (x <= 2 || y <= 2 || x + width >= view.width - 2 || y + height >= view.height - 2) continue;
86009
+ out.push({
86010
+ ...detection,
86011
+ nativeRecovery: {
86012
+ score: detection.score,
86013
+ cropSidePx: crop.side,
86014
+ viewPx: view.width
86015
+ },
86016
+ bbox: {
86017
+ x: (crop.left + x * viewToNative) * nativeToResultX,
86018
+ y: (crop.top + y * viewToNative) * nativeToResultY,
86019
+ width: width * viewToNative * nativeToResultX,
86020
+ height: height * viewToNative * nativeToResultY
86021
+ }
86022
+ });
86023
+ }
86024
+ return out;
86025
+ }
86026
+ function boxIntersection(a, b) {
86027
+ return Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y));
86028
+ }
86029
+ function sameSubject(recovered, existing) {
86030
+ const inter = boxIntersection(recovered, existing);
86031
+ if (inter <= 0) return false;
86032
+ const areaR = recovered.width * recovered.height;
86033
+ const union = areaR + existing.width * existing.height - inter;
86034
+ if (union > 0 && inter / union >= .5) return true;
86035
+ return areaR > 0 && inter / areaR >= .7;
86036
+ }
86037
+ /**
86038
+ * Class-aware NMS of the recovered boxes against the full-frame ones. Every
86039
+ * full-frame box is kept as it is — the second pass only ADDS. A recovered box
86040
+ * is added when no full-frame box of the same macro class is the same subject
86041
+ * (`RECOVERY_FUSE_IOU`) or contains it (`RECOVERY_FUSE_CONTAINMENT`). Ids are
86042
+ * re-minted so a crop's `d0` cannot collide with the frame's.
86043
+ */
86044
+ function fuseRecoveredDetections(full, recovered) {
86045
+ const accepted = [];
86046
+ for (const [index, candidate] of recovered.entries()) {
86047
+ if ([...full, ...accepted].some((existing) => existing.macroClass === candidate.macroClass && sameSubject(candidate.bbox, existing.bbox))) continue;
86048
+ accepted.push({
86049
+ ...candidate,
86050
+ id: `recovered-${index}-${candidate.id}`
86051
+ });
86052
+ }
86053
+ return [...full, ...accepted];
86054
+ }
86055
+ /**
86056
+ * The step the second pass runs: the enabled first-level object detector of
86057
+ * the camera's own tree, with its own model and no children. Same model as
86058
+ * the full pass so a recovered box is comparable to a full-frame one.
86059
+ */
86060
+ function selectRecoveryStep(steps) {
86061
+ const detector = steps.find((step) => step.addonId === "object-detection" && step.enabled !== false);
86062
+ if (!detector) return null;
86063
+ return {
86064
+ addonId: detector.addonId,
86065
+ ...detector.modelId !== void 0 ? { modelId: detector.modelId } : {},
86066
+ enabled: true
86067
+ };
86068
+ }
86069
+ /**
86070
+ * Read the opt-in. Anything that is not the boolean `true` is the shipped
86071
+ * rule: a stored string or a number is a mis-write (this repo has shipped a
86072
+ * setting written into the wrong sqlite table before), and a mis-write must
86073
+ * not silently change what a camera detects on.
86074
+ */
86075
+ function readRecoveryRecipe(settings) {
86076
+ return settings?.["motionCropDispatch"] === true ? "motion-crop" : "uncovered-region";
86077
+ }
86078
+ /**
86079
+ * The recipe this camera's resolved step tree selects.
86080
+ *
86081
+ * Reads the ENABLED first-level detector's own settings, which is where
86082
+ * `pipelineOrchestrator.setCameraStepSettings` writes a per-camera value. A
86083
+ * tree with no enabled detector runs no second pass at all, so the answer
86084
+ * there is the shipped rule and it is never used.
86085
+ */
86086
+ function readRecoveryRecipeForSteps(steps) {
86087
+ return readRecoveryRecipe(steps.find((step) => step.addonId === "object-detection" && step.enabled !== false)?.settings);
86088
+ }
86089
+ /**
86090
+ * A motion box is grown ×1.5 about its centre before anything else.
86091
+ *
86092
+ * The motion step's box is the CHANGED pixels, which on a moving animal is the
86093
+ * leading edge plus its shadow, not the animal. The detector needs the subject
86094
+ * and the context it was trained on.
86095
+ */
86096
+ var MOTION_CROP_MOTION_PAD = 1.5;
86097
+ /**
86098
+ * An intersecting tracked box is grown ×1.25 — less, because it is already a
86099
+ * subject rectangle rather than a change mask.
86100
+ */
86101
+ var MOTION_CROP_TRACKED_PAD = 1.25;
86102
+ /**
86103
+ * The floor a `person` or `vehicle` box must clear when it came out of a CROP
86104
+ * rather than out of the full frame.
86105
+ *
86106
+ * Collected, not assumed: 3 085 quiet frames produced 27 motion-positive
86107
+ * frames and 28 crop invocations. The full-frame arm produced 0 boxes above
86108
+ * any gate; the crop arm produced 3, of which two are genuine false positives,
86109
+ * and both are night-IR corners upscaled into a 320 model — `vehicle 0.749`
86110
+ * and `person 0.701`. Zero false `animal` at any gate, which is why `animal`
86111
+ * is deliberately absent from this table: the class the pass exists to recover
86112
+ * is the class that never produced a false positive, and gating it would cost
86113
+ * the whole measured gain.
86114
+ *
86115
+ * This is a CROP-ONLY floor, applied on top of the per-macro floors the
86116
+ * executor already enforced. A full-frame `person` at 0.70 is untouched — the
86117
+ * full-frame arm is the one arm with ground truth behind its floor
86118
+ * (`minConfidencePerson` 0.65, raised on 2026-07-30 from a measured
86119
+ * distribution).
86120
+ *
86121
+ * The cost of this floor is stated rather than assumed away: it can also
86122
+ * remove a real distant person or vehicle recovered from a crop at 0.70–0.80.
86123
+ * Nothing in the quiet-footage sample measures that, so the floor is a
86124
+ * constant here rather than a slider, and the runner reports every drop it
86125
+ * makes per camera.
86126
+ */
86127
+ var CROP_CLASS_FLOORS = Object.freeze({
86128
+ person: .8,
86129
+ vehicle: .8
86130
+ });
86131
+ /**
86132
+ * Apply the crop-only class floors, REPORTING every drop.
86133
+ *
86134
+ * A class with no entry in {@link CROP_CLASS_FLOORS} is kept: a floor this
86135
+ * table cannot express is not a floor of zero, and silently dropping a class
86136
+ * nobody measured is the exact shape of silent drop this repo has paid for
86137
+ * three separate times.
86138
+ */
86139
+ function applyCropClassFloors(detections) {
86140
+ const kept = [];
86141
+ const dropped = [];
86142
+ for (const detection of detections) {
86143
+ const floor = CROP_CLASS_FLOORS[detection.macroClass];
86144
+ if (floor !== void 0 && detection.score < floor) {
86145
+ dropped.push({
86146
+ macroClass: detection.macroClass,
86147
+ score: detection.score,
86148
+ floor
86149
+ });
86150
+ continue;
86151
+ }
86152
+ kept.push(detection);
86153
+ }
86154
+ return {
86155
+ kept,
86156
+ dropped
86157
+ };
86158
+ }
86159
+ function scaleAbout(rect, factor) {
86160
+ const w = rect.w * factor;
86161
+ const h = rect.h * factor;
86162
+ return {
86163
+ x: rect.x + rect.w / 2 - w / 2,
86164
+ y: rect.y + rect.h / 2 - h / 2,
86165
+ w,
86166
+ h
86167
+ };
86168
+ }
86169
+ function overlaps(a, b) {
86170
+ return Math.min(a.x + a.w, b.x + b.w) > Math.max(a.x, b.x) && Math.min(a.y + a.h, b.y + b.h) > Math.max(a.y, b.y);
86171
+ }
86172
+ function union(a, b) {
86173
+ const x = Math.min(a.x, b.x);
86174
+ const y = Math.min(a.y, b.y);
86175
+ return {
86176
+ x,
86177
+ y,
86178
+ w: Math.max(a.x + a.w, b.x + b.w) - x,
86179
+ h: Math.max(a.y + a.h, b.y + b.h) - y
86180
+ };
86181
+ }
86182
+ function clampToFrame(rect) {
86183
+ const x = Math.max(0, rect.x);
86184
+ const y = Math.max(0, rect.y);
86185
+ return {
86186
+ x,
86187
+ y,
86188
+ w: Math.min(1, rect.x + rect.w) - x,
86189
+ h: Math.min(1, rect.y + rect.h) - y
86190
+ };
86191
+ }
86192
+ /**
86193
+ * Merge transitively every seed whose padded box overlaps another, and answer
86194
+ * the union rectangles.
86195
+ *
86196
+ * Transitive on purpose: a person, the dog beside them and the dog's shadow
86197
+ * are three components of one subject, and cutting three crops for them spends
86198
+ * two inferences on pixels the first one already showed the detector.
86199
+ */
86200
+ function mergeSeeds(seeds) {
86201
+ const merged = [];
86202
+ for (const seed of seeds) {
86203
+ let current = seed;
86204
+ let changed = true;
86205
+ while (changed) {
86206
+ changed = false;
86207
+ for (let i = merged.length - 1; i >= 0; i -= 1) {
86208
+ const other = merged[i];
86209
+ if (other === void 0 || !overlaps(current, other)) continue;
86210
+ current = union(current, other);
86211
+ merged.splice(i, 1);
86212
+ changed = true;
86213
+ }
86214
+ }
86215
+ merged.push(current);
86216
+ }
86217
+ return merged;
86218
+ }
86219
+ /**
86220
+ * The regions the measured recipe judges this frame on.
86221
+ *
86222
+ * Motion boxes padded {@link MOTION_CROP_MOTION_PAD}; every full-frame box
86223
+ * that intersects a RAW motion box padded {@link MOTION_CROP_TRACKED_PAD} and
86224
+ * added as a seed of its own; everything that overlaps merged; the result
86225
+ * clamped to the frame, ordered largest first and capped at
86226
+ * {@link MOTION_CROP_MAX_REGIONS}.
86227
+ *
86228
+ * There is deliberately no coverage gate and no size gate here. Those belong
86229
+ * to the shipped rule, and they are what makes it a rule about a subject
86230
+ * BESIDE a detected one; the measurement says the pass is worth running on the
86231
+ * detected subject's own region too.
86232
+ */
86233
+ function planMotionCropRegions(input) {
86234
+ if (input.regions.length === 0) return [];
86235
+ const seeds = input.regions.map((r) => scaleAbout(r, MOTION_CROP_MOTION_PAD));
86236
+ for (const detection of input.detections) {
86237
+ if (!input.regions.some((region) => overlaps(detection, region))) continue;
86238
+ seeds.push(scaleAbout(detection, MOTION_CROP_TRACKED_PAD));
86239
+ }
86240
+ return mergeSeeds(seeds).map(clampToFrame).filter((r) => r.w > 0 && r.h > 0).toSorted((a, b) => normalizedArea(b) - normalizedArea(a)).slice(0, 3);
86241
+ }
86242
+ /**
86243
+ * The square the detector is fed for one measured region.
86244
+ *
86245
+ * "Grow to the detector's input aspect ratio without ever shrinking" is a
86246
+ * SQUARE here, and that is a fact about the production detector rather than a
86247
+ * simplification: `yolov9m-320-int8` takes 320×320, as does every hardware
86248
+ * default in the catalog.
86249
+ *
86250
+ * The side is the region's longer side, floored at
86251
+ * {@link RECOVERY_CROP_MIN_SIDE_PX} — the growth is what magnifies the subject
86252
+ * — and ceilinged at {@link RECOVERY_CROP_MAX_SIDE_PX}. The ceiling is NOT a
86253
+ * simplification either: at 1024 px the same 320 model scored the reference
86254
+ * dog 0.52–0.76, and at 1280 px lost it entirely
86255
+ * (`docs/benchmarks/pipeline-frame-model-eval.md`, case `miss-dog-615`). A
86256
+ * region longer than the ceiling is TILED along its longer axis, never zoomed
86257
+ * out.
86258
+ */
86259
+ function motionCropsForRegion(region, native) {
86260
+ const widthPx = region.w * native.width;
86261
+ const heightPx = region.h * native.height;
86262
+ const longest = Math.max(widthPx, heightPx);
86263
+ const side = Math.round(Math.min(Math.max(longest, 640), 800));
86264
+ const centreX = (region.x + region.w / 2) * native.width;
86265
+ const centreY = (region.y + region.h / 2) * native.height;
86266
+ if (longest <= 800) return [squareAround(centreX, centreY, side, native)];
86267
+ const alongX = widthPx >= heightPx;
86268
+ const quarter = longest / 4;
86269
+ return [squareAround(alongX ? centreX - quarter : centreX, alongX ? centreY : centreY - quarter, side, native), squareAround(alongX ? centreX + quarter : centreX, alongX ? centreY : centreY + quarter, side, native)];
86270
+ }
86271
+ /** `true` when `inner` lies entirely inside `outer`. */
86272
+ function cropContains(outer, inner) {
86273
+ return inner.left >= outer.left && inner.top >= outer.top && inner.left + inner.side <= outer.left + outer.side && inner.top + inner.side <= outer.top + outer.side;
86274
+ }
86275
+ /**
86276
+ * The crops the measured recipe runs on this frame.
86277
+ *
86278
+ * Dedupe is by CONTAINMENT only. Two squares that merely overlap both run: the
86279
+ * pixels the second adds are pixels no other crop shows the detector, and the
86280
+ * fusion step downstream already refuses a duplicate BOX. Dropping a crop
86281
+ * because it overlapped would drop the subject sitting at its edge, which is
86282
+ * the whole population this pass exists for.
86283
+ *
86284
+ * A frame that cannot hold the smallest crop yields nothing: it is already
86285
+ * seen at scale.
86286
+ */
86287
+ function planMotionCrops(input) {
86288
+ const { native } = input;
86289
+ if (Math.min(native.width, native.height) < 640) return [];
86290
+ const planned = [];
86291
+ for (const region of planMotionCropRegions(input)) for (const crop of motionCropsForRegion(region, native)) {
86292
+ if (planned.some((existing) => cropContains(existing, crop))) continue;
86293
+ planned.push(crop);
86294
+ }
86295
+ return planned;
86296
+ }
85226
86297
  /** Side of the square, as a multiple of the face box's longer side. */
85227
86298
  var ROOT_FACE_REFINE_SCALE = 1.6;
85228
86299
  /**
@@ -86778,6 +87849,12 @@ Object.defineProperty(exports, "NodeBenchRunnerInputSchema", {
86778
87849
  return NodeBenchRunnerInputSchema;
86779
87850
  }
86780
87851
  });
87852
+ Object.defineProperty(exports, "OBJECT_DETECTION_STEP_ID", {
87853
+ enumerable: true,
87854
+ get: function() {
87855
+ return OBJECT_DETECTION_STEP_ID;
87856
+ }
87857
+ });
86781
87858
  Object.defineProperty(exports, "OpsLogEntrySchema", {
86782
87859
  enumerable: true,
86783
87860
  get: function() {
@@ -86826,6 +87903,12 @@ Object.defineProperty(exports, "RECORDING_SOURCE_CAMSTACK_ADDON", {
86826
87903
  return RECORDING_SOURCE_CAMSTACK_ADDON;
86827
87904
  }
86828
87905
  });
87906
+ Object.defineProperty(exports, "RECOVERY_MOTION_MAX_AGE_MS", {
87907
+ enumerable: true,
87908
+ get: function() {
87909
+ return RECOVERY_MOTION_MAX_AGE_MS;
87910
+ }
87911
+ });
86829
87912
  Object.defineProperty(exports, "ROOT_SUPPLIABLE_STEP_IDS", {
86830
87913
  enumerable: true,
86831
87914
  get: function() {
@@ -86886,6 +87969,12 @@ Object.defineProperty(exports, "allowedSuppliedEffect", {
86886
87969
  return allowedSuppliedEffect;
86887
87970
  }
86888
87971
  });
87972
+ Object.defineProperty(exports, "applyCropClassFloors", {
87973
+ enumerable: true,
87974
+ get: function() {
87975
+ return applyCropClassFloors;
87976
+ }
87977
+ });
86889
87978
  Object.defineProperty(exports, "applyModelBoundSettings", {
86890
87979
  enumerable: true,
86891
87980
  get: function() {
@@ -86988,6 +88077,12 @@ Object.defineProperty(exports, "containingParentIndex", {
86988
88077
  return containingParentIndex;
86989
88078
  }
86990
88079
  });
88080
+ Object.defineProperty(exports, "countRecoverySkips", {
88081
+ enumerable: true,
88082
+ get: function() {
88083
+ return countRecoverySkips;
88084
+ }
88085
+ });
86991
88086
  Object.defineProperty(exports, "createEvent", {
86992
88087
  enumerable: true,
86993
88088
  get: function() {
@@ -87168,6 +88263,12 @@ Object.defineProperty(exports, "ffmpegRate", {
87168
88263
  return ffmpegRate;
87169
88264
  }
87170
88265
  });
88266
+ Object.defineProperty(exports, "fuseRecoveredDetections", {
88267
+ enumerable: true,
88268
+ get: function() {
88269
+ return fuseRecoveredDetections;
88270
+ }
88271
+ });
87171
88272
  Object.defineProperty(exports, "gbToBytes", {
87172
88273
  enumerable: true,
87173
88274
  get: function() {
@@ -87396,6 +88497,18 @@ Object.defineProperty(exports, "pipelineRunnerCapability", {
87396
88497
  return pipelineRunnerCapability;
87397
88498
  }
87398
88499
  });
88500
+ Object.defineProperty(exports, "planMotionCrops", {
88501
+ enumerable: true,
88502
+ get: function() {
88503
+ return planMotionCrops;
88504
+ }
88505
+ });
88506
+ Object.defineProperty(exports, "planRecoveryCrops", {
88507
+ enumerable: true,
88508
+ get: function() {
88509
+ return planRecoveryCrops;
88510
+ }
88511
+ });
87399
88512
  Object.defineProperty(exports, "preprocess", {
87400
88513
  enumerable: true,
87401
88514
  get: function() {
@@ -87408,6 +88521,12 @@ Object.defineProperty(exports, "rankStreams", {
87408
88521
  return rankStreams;
87409
88522
  }
87410
88523
  });
88524
+ Object.defineProperty(exports, "readRecoveryRecipeForSteps", {
88525
+ enumerable: true,
88526
+ get: function() {
88527
+ return readRecoveryRecipeForSteps;
88528
+ }
88529
+ });
87411
88530
  Object.defineProperty(exports, "record", {
87412
88531
  enumerable: true,
87413
88532
  get: function() {
@@ -87438,6 +88557,12 @@ Object.defineProperty(exports, "recordingSignalCapability", {
87438
88557
  return recordingSignalCapability;
87439
88558
  }
87440
88559
  });
88560
+ Object.defineProperty(exports, "remapCropDetections", {
88561
+ enumerable: true,
88562
+ get: function() {
88563
+ return remapCropDetections;
88564
+ }
88565
+ });
87441
88566
  Object.defineProperty(exports, "resolveClusterStepModelId", {
87442
88567
  enumerable: true,
87443
88568
  get: function() {
@@ -87504,6 +88629,12 @@ Object.defineProperty(exports, "selectAssignedProfileSlots", {
87504
88629
  return selectAssignedProfileSlots;
87505
88630
  }
87506
88631
  });
88632
+ Object.defineProperty(exports, "selectRecoveryStep", {
88633
+ enumerable: true,
88634
+ get: function() {
88635
+ return selectRecoveryStep;
88636
+ }
88637
+ });
87507
88638
  Object.defineProperty(exports, "sleep", {
87508
88639
  enumerable: true,
87509
88640
  get: function() {
@@ -87561,7 +88692,7 @@ Object.defineProperty(exports, "supportedRuntimes", {
87561
88692
  Object.defineProperty(exports, "union", {
87562
88693
  enumerable: true,
87563
88694
  get: function() {
87564
- return union;
88695
+ return union$1;
87565
88696
  }
87566
88697
  });
87567
88698
  Object.defineProperty(exports, "userError", {