camstack 1.2.204 → 1.2.206

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.
@@ -24024,9 +24024,9 @@ var require_zod = __commonJS({
24024
24024
  }
24025
24025
  });
24026
24026
 
24027
- // ../system/dist/dist-l6zKmHEb.js
24028
- var require_dist_l6zKmHEb = __commonJS({
24029
- "../system/dist/dist-l6zKmHEb.js"(exports) {
24027
+ // ../system/dist/dist-DRL3rEfN.js
24028
+ var require_dist_DRL3rEfN = __commonJS({
24029
+ "../system/dist/dist-DRL3rEfN.js"(exports) {
24030
24030
  "use strict";
24031
24031
  var zod = require_zod();
24032
24032
  __require("crypto");
@@ -27460,6 +27460,9 @@ var require_dist_l6zKmHEb = __commonJS({
27460
27460
  field: zod.z.string().min(1),
27461
27461
  on: zod.z.literal("advance")
27462
27462
  });
27463
+ function historyItemFieldPath(itemsPath, field) {
27464
+ return `${itemsPath}[].${field}`;
27465
+ }
27463
27466
  var HISTORY_DEFAULT_MIN_INTERVAL_MS = 1e4;
27464
27467
  function resolveCapMount(def) {
27465
27468
  if (def.mount) return def.mount;
@@ -27481,7 +27484,11 @@ var require_dist_l6zKmHEb = __commonJS({
27481
27484
  zod.z.literal(4)
27482
27485
  ]).optional(),
27483
27486
  tab: zod.z.string().optional(),
27484
- location: zod.z.enum(["settings", "top-tab"]).optional(),
27487
+ location: zod.z.enum([
27488
+ "settings",
27489
+ "top-tab",
27490
+ "hero"
27491
+ ]).optional(),
27485
27492
  order: zod.z.number().optional(),
27486
27493
  fields: zod.z.array(zod.z.any())
27487
27494
  });
@@ -32079,7 +32086,11 @@ var require_dist_l6zKmHEb = __commonJS({
32079
32086
  zod.z.literal(4)
32080
32087
  ]).optional(),
32081
32088
  tab: zod.z.string().optional(),
32082
- location: zod.z.enum(["settings", "top-tab"]).optional(),
32089
+ location: zod.z.enum([
32090
+ "settings",
32091
+ "top-tab",
32092
+ "hero"
32093
+ ]).optional(),
32083
32094
  order: zod.z.number().optional(),
32084
32095
  fields: zod.z.array(zod.z.any())
32085
32096
  });
@@ -37376,6 +37387,46 @@ var require_dist_l6zKmHEb = __commonJS({
37376
37387
  approxBytes: zod.z.number().nullable(),
37377
37388
  countedAt: zod.z.number().nullable()
37378
37389
  });
37390
+ var DeviceHistoryOrphanReasonSchema = zod.z.enum(["device-gone", "undeclared"]);
37391
+ var TierRowsSchema = zod.z.object({
37392
+ raw: zod.z.number().int(),
37393
+ "5m": zod.z.number().int(),
37394
+ "1h": zod.z.number().int(),
37395
+ "1d": zod.z.number().int()
37396
+ });
37397
+ var OrphanSeriesSchema = zod.z.object({
37398
+ seriesId: zod.z.number().int(),
37399
+ deviceId: zod.z.number().int(),
37400
+ key: zod.z.string(),
37401
+ scope: zod.z.string(),
37402
+ /** The metric's display name; `null` when its metric row is gone too. */
37403
+ displayName: zod.z.string().nullable(),
37404
+ reason: DeviceHistoryOrphanReasonSchema,
37405
+ /** `device-gone`: since when the device has been absent; `null` otherwise. */
37406
+ missingSince: zod.z.number().nullable(),
37407
+ firstTs: zod.z.number(),
37408
+ lastTs: zod.z.number(),
37409
+ rows: TierRowsSchema
37410
+ });
37411
+ var DeviceHistoryOrphanDeleteRefusalSchema = zod.z.enum([
37412
+ "not-found",
37413
+ "not-orphan",
37414
+ "failed"
37415
+ ]);
37416
+ var OrphanDeleteEntrySchema = zod.z.discriminatedUnion("ok", [zod.z.object({
37417
+ seriesId: zod.z.number().int(),
37418
+ ok: zod.z.literal(true),
37419
+ deleted: TierRowsSchema
37420
+ }), zod.z.object({
37421
+ seriesId: zod.z.number().int(),
37422
+ ok: zod.z.literal(false),
37423
+ reason: DeviceHistoryOrphanDeleteRefusalSchema
37424
+ })]);
37425
+ var OrphanListSchema = zod.z.object({
37426
+ series: zod.z.array(OrphanSeriesSchema).readonly(),
37427
+ /** More orphans exist than {@link DEVICE_HISTORY_ORPHAN_LIST_MAX}. */
37428
+ truncated: zod.z.boolean()
37429
+ });
37379
37430
  var DeviceHistoryStatusSchema = zod.z.object({
37380
37431
  startedAt: zod.z.number(),
37381
37432
  /** The store is declared, series loaded and the first reconcile has answered. */
@@ -37517,7 +37568,24 @@ var require_dist_l6zKmHEb = __commonJS({
37517
37568
  kind: "mutation",
37518
37569
  auth: "admin"
37519
37570
  }),
37520
- getStatus: method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" })
37571
+ getStatus: method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" }),
37572
+ /**
37573
+ * Device series no longer linked to a device (D917): the device is absent
37574
+ * from the listings, or the key is declared by no capability. REPORTED,
37575
+ * never purged by this judgement; each entry counts its rows per tier
37576
+ * (a primary-key range per series). Ordered by device, key, scope.
37577
+ */
37578
+ listOrphanSeries: method(zod.z.object({}), OrphanListSchema, { auth: "admin" }),
37579
+ /**
37580
+ * Delete orphan series by id. Each id is re-judged NOW: a series that is
37581
+ * not orphan (its device came back) is refused `not-orphan`, an unknown id
37582
+ * `not-found`. Deletes tier by tier in the retention slices, then the
37583
+ * series row; one entry per id, in order.
37584
+ */
37585
+ deleteOrphanSeries: method(zod.z.object({ seriesIds: zod.z.array(zod.z.number().int()).min(1).max(50) }), zod.z.object({ results: zod.z.array(OrphanDeleteEntrySchema).readonly() }), {
37586
+ kind: "mutation",
37587
+ auth: "admin"
37588
+ })
37521
37589
  }
37522
37590
  };
37523
37591
  var DeviceCaStatusSchema = zod.z.discriminatedUnion("state", [
@@ -70620,6 +70688,19 @@ var require_dist_l6zKmHEb = __commonJS({
70620
70688
  /** Whether the node's live probe reports the device as usable right now. */
70621
70689
  available: zod.z.boolean(),
70622
70690
  /**
70691
+ * Is the device on the node at all (D919)? `present` — the live probe
70692
+ * reported it; `absent` — the probe ANSWERED and did not report it, so only
70693
+ * its stored settings remain (unplugged, moved to another node): never
70694
+ * eligible, whatever its stored `enabled`, and forgettable with
70695
+ * `forgetInferenceDevice`; `unknown` — the probe did not answer, nothing is
70696
+ * claimed (D315). Optional only for an orchestrator older than the field.
70697
+ */
70698
+ presence: zod.z.enum([
70699
+ "present",
70700
+ "absent",
70701
+ "unknown"
70702
+ ]).optional(),
70703
+ /**
70623
70704
  * Whether this device participates in dispatch. AUTO default (spec C2):
70624
70705
  * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
70625
70706
  * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
@@ -71016,6 +71097,20 @@ var require_dist_l6zKmHEb = __commonJS({
71016
71097
  */
71017
71098
  getNodeInferenceDevices: method(zod.z.object({ nodeId: zod.z.string() }), NodeInferenceDevicesSchema),
71018
71099
  /**
71100
+ * Delete ONE stored device entry of a node — the "forget" of a device the
71101
+ * node no longer has (D919). Refused unless the node's probe ANSWERED and
71102
+ * did not report the key: a present device is switched off, not forgotten,
71103
+ * and an unreachable node proves nothing about its hardware. The rest of
71104
+ * the node's map is kept as stored.
71105
+ */
71106
+ forgetInferenceDevice: method(zod.z.object({
71107
+ agentNodeId: zod.z.string(),
71108
+ deviceKey: zod.z.string()
71109
+ }), zod.z.object({ success: zod.z.literal(true) }), {
71110
+ kind: "mutation",
71111
+ auth: "admin"
71112
+ }),
71113
+ /**
71019
71114
  * Reset one node's pipeline to its hardware-aware defaults — the HONEST
71020
71115
  * reset (replaces the executor's legacy `resetToDefault`, which cleared a
71021
71116
  * dead persisted step-tree seed nothing in the live path read):
@@ -76064,6 +76159,30 @@ var require_dist_l6zKmHEb = __commonJS({
76064
76159
  * whether persisting is worth a SQLite commit. */
76065
76160
  volatileStateFields: ["lastChangedAt"]
76066
76161
  };
76162
+ var ConsumableKindSchema = zod.z.enum([
76163
+ "wear",
76164
+ "supply",
76165
+ "waste"
76166
+ ]);
76167
+ var ConsumableFormSchema = zod.z.enum([
76168
+ "part",
76169
+ "tank",
76170
+ "product",
76171
+ "bin"
76172
+ ]);
76173
+ var ConsumableStatusSchema = zod.z.enum([
76174
+ "ok",
76175
+ "low",
76176
+ "empty",
76177
+ "full",
76178
+ "replace"
76179
+ ]);
76180
+ zod.z.enum([
76181
+ "ok",
76182
+ "low",
76183
+ "empty",
76184
+ "full"
76185
+ ]);
76067
76186
  var ConsumableRemainingUnitSchema = zod.z.enum([
76068
76187
  "days",
76069
76188
  "hours",
@@ -76078,10 +76197,14 @@ var require_dist_l6zKmHEb = __commonJS({
76078
76197
  key: zod.z.string().min(1),
76079
76198
  /** Display name. */
76080
76199
  label: zod.z.string().min(1),
76081
- /** Remaining life % when known (0..100). */
76200
+ /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
76082
76201
  level: zod.z.number().min(0).max(100).nullable(),
76083
- /** Discrete state when known (binary mode). */
76084
- status: zod.z.enum(["ok", "replace"]).nullable(),
76202
+ /** The provider's discrete verdict when known (binary mode). */
76203
+ status: ConsumableStatusSchema.nullable(),
76204
+ /** What the item is — decides whether low or full is bad. Absent = `wear`. */
76205
+ kind: ConsumableKindSchema.optional(),
76206
+ /** How the item is drawn (part, tank, product, bin). Absent = derived from `kind`. */
76207
+ form: ConsumableFormSchema.optional(),
76085
76208
  /** Ms epoch of the last replace, when known. */
76086
76209
  lastResetAt: zod.z.number().nullable(),
76087
76210
  /** Whether `reset()` is meaningful for this item. */
@@ -76104,7 +76227,7 @@ var require_dist_l6zKmHEb = __commonJS({
76104
76227
  kind: "widget",
76105
76228
  widgetId: "host/consumables-panel",
76106
76229
  tab: "consumables",
76107
- topTab: true,
76230
+ placement: "hero",
76108
76231
  label: "Consumables",
76109
76232
  order: 5
76110
76233
  } },
@@ -76166,9 +76289,37 @@ var require_dist_l6zKmHEb = __commonJS({
76166
76289
  */
76167
76290
  durability: "restored",
76168
76291
  /**
76169
- * History (D850): none — `items` is an array keyed by runtime ids; a history path never enters it.
76292
+ * History (D850, D917): one series per (item field, item `key`) — the item key
76293
+ * is the series scope, the item `label` names it. A vacuum tank's fill, a
76294
+ * brush's remaining life and a dishwasher's salt are each recorded the moment
76295
+ * their provider reports them; an item that appears later gets its series.
76170
76296
  */
76171
- history: "none",
76297
+ history: {
76298
+ fields: {},
76299
+ items: {
76300
+ path: "items",
76301
+ key: "key",
76302
+ label: "label",
76303
+ fields: {
76304
+ level: {
76305
+ kind: "gauge",
76306
+ unit: "%",
76307
+ deadband: 2,
76308
+ displayName: "Level"
76309
+ },
76310
+ status: {
76311
+ kind: "enum",
76312
+ displayName: "Status"
76313
+ },
76314
+ "remaining.value": {
76315
+ kind: "gauge",
76316
+ unitFrom: "remaining.unit",
76317
+ deadband: 1,
76318
+ displayName: "Remaining"
76319
+ }
76320
+ }
76321
+ }
76322
+ },
76172
76323
  /** Clocks, not content: a re-read that changed no item costs no commit. */
76173
76324
  volatileStateFields: ["lastChangedAt", "lastFetchedAt"]
76174
76325
  };
@@ -76529,11 +76680,6 @@ var require_dist_l6zKmHEb = __commonJS({
76529
76680
  "offline",
76530
76681
  "description-missing"
76531
76682
  ]);
76532
- var DishwasherConsumableLevelSchema = zod.z.enum([
76533
- "ok",
76534
- "low",
76535
- "empty"
76536
- ]);
76537
76683
  var DishwasherStatusSchema = zod.z.object({
76538
76684
  availability: DishwasherAvailabilitySchema,
76539
76685
  /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
@@ -76566,8 +76712,6 @@ var require_dist_l6zKmHEb = __commonJS({
76566
76712
  powerOn: zod.z.boolean().nullable(),
76567
76713
  /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
76568
76714
  powerState: zod.z.string().nullable(),
76569
- saltLevel: DishwasherConsumableLevelSchema.nullable(),
76570
- rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
76571
76715
  /**
76572
76716
  * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
76573
76717
  * combined care-and-filter reminder raised); false when every such reminder reads clear; null
@@ -79302,7 +79446,7 @@ var require_dist_l6zKmHEb = __commonJS({
79302
79446
  enabled: zod.z.boolean()
79303
79447
  }), zod.z.void(), {
79304
79448
  kind: "mutation",
79305
- auth: "admin"
79449
+ auth: "protected"
79306
79450
  }) },
79307
79451
  events: { onMotionTriggerChanged: { data: zod.z.object({
79308
79452
  deviceId: zod.z.number(),
@@ -79845,6 +79989,13 @@ var require_dist_l6zKmHEb = __commonJS({
79845
79989
  cpuModel: zod.z.string().nullable(),
79846
79990
  cpuCores: zod.z.number(),
79847
79991
  memoryMB: zod.z.number(),
79992
+ /**
79993
+ * The inference RUNTIMES installed on the node (`pythonRuntimes`: onnx,
79994
+ * openvino, edgetpu …) — not its devices. An installed runtime says nothing
79995
+ * about whether its hardware is there (a Coral delegate stays installed
79996
+ * after the stick leaves, D919); the live device set is
79997
+ * `pipelineOrchestrator.getNodeInferenceDevices`.
79998
+ */
79848
79999
  engines: zod.z.array(zod.z.string()).readonly(),
79849
80000
  isHub: zod.z.boolean(),
79850
80001
  isOnline: zod.z.boolean(),
@@ -81032,6 +81183,18 @@ var require_dist_l6zKmHEb = __commonJS({
81032
81183
  format: ModelFormatSchema,
81033
81184
  reason: zod.z.string()
81034
81185
  });
81186
+ var AcceleratorRescanSchema = zod.z.object({
81187
+ devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly(),
81188
+ appeared: zod.z.array(zod.z.string()).readonly(),
81189
+ disappeared: zod.z.array(zod.z.string()).readonly(),
81190
+ usbBus: zod.z.enum(["observed", "unobservable"]),
81191
+ decidedBy: zod.z.enum([
81192
+ "usb-ids",
81193
+ "pci",
81194
+ "delegate-fallback",
81195
+ "none"
81196
+ ])
81197
+ });
81035
81198
  var platformProbeCapability = {
81036
81199
  name: "platform-probe",
81037
81200
  scope: "system",
@@ -81040,7 +81203,18 @@ var require_dist_l6zKmHEb = __commonJS({
81040
81203
  getCapabilities: method(zod.z.void(), PlatformCapabilitiesSchema),
81041
81204
  getHardware: method(zod.z.void(), HardwareInfoSchema),
81042
81205
  resolveInferenceConfig: method(zod.z.object({ requirements: zod.z.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
81043
- resolveHwAccel: method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema)
81206
+ resolveHwAccel: method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
81207
+ /**
81208
+ * Re-probe this node's accelerators NOW (Coral USB ids + PCIe apex units;
81209
+ * `/sys` and `/dev` reads, no model load) and rebuild the device list that
81210
+ * `getCapabilities` serves. The same re-probe runs every 60 s on its own;
81211
+ * this is the operator's "rescan inference devices". Target a node with the
81212
+ * `{ nodeId }` routing input, like `getCapabilities`.
81213
+ */
81214
+ rescanAccelerators: method(zod.z.void(), AcceleratorRescanSchema, {
81215
+ kind: "mutation",
81216
+ auth: "admin"
81217
+ })
81044
81218
  }
81045
81219
  };
81046
81220
  var PowerMeterStatusSchema = zod.z.object({
@@ -84894,16 +85068,6 @@ var require_dist_l6zKmHEb = __commonJS({
84894
85068
  "drying",
84895
85069
  "error"
84896
85070
  ]);
84897
- var TankStatusSchema = zod.z.object({
84898
- /** Numeric fill 0..100 when the hardware reports a percentage; null otherwise. */
84899
- level: zod.z.number().min(0).max(100).nullable(),
84900
- /** Discrete state when the hardware is binary-mode; null otherwise. */
84901
- status: zod.z.enum([
84902
- "ok",
84903
- "low",
84904
- "full"
84905
- ]).nullable()
84906
- });
84907
85071
  var VacuumControlStatusSchema = zod.z.object({
84908
85072
  /** Lifecycle state of the vacuum. */
84909
85073
  state: VacuumStateSchema,
@@ -84915,14 +85079,6 @@ var require_dist_l6zKmHEb = __commonJS({
84915
85079
  fanSpeed: zod.z.string().nullable(),
84916
85080
  /** Speed tokens the hardware accepts — drives the UI selector. */
84917
85081
  availableFanSpeeds: zod.z.array(zod.z.string()),
84918
- /** Clean-water (mop) tank. Null when the hardware has no clean-water tank. */
84919
- cleanWater: TankStatusSchema.nullable(),
84920
- /** Dirty-water (recovery) tank. Null when the hardware has no dirty-water tank. */
84921
- dirtyWater: TankStatusSchema.nullable(),
84922
- /** Detergent tank. Null when the hardware has no detergent tank. */
84923
- detergent: TankStatusSchema.nullable(),
84924
- /** Dust bin. Null when the hardware has no dust bin. */
84925
- dustBin: TankStatusSchema.nullable(),
84926
85082
  /** 0..100 cleaning-completion percentage of the current task, or null. */
84927
85083
  progressPercent: zod.z.number().min(0).max(100).nullable(),
84928
85084
  /** Current error code (0 / null = no error). */
@@ -84984,35 +85140,14 @@ var require_dist_l6zKmHEb = __commonJS({
84984
85140
  */
84985
85141
  durability: "session",
84986
85142
  /**
84987
- * History (D850): recorded series. Tank levels are fixed-object paths (`cleanWater.level`, ...).
85143
+ * History (D850): recorded series. The tanks moved to `consumables` (D915), whose
85144
+ * items are keyed by runtime ids and record no history.
84988
85145
  */
84989
- history: { fields: {
84990
- batteryLevel: {
84991
- kind: "gauge",
84992
- unit: "%",
84993
- deadband: 2
84994
- },
84995
- "cleanWater.level": {
84996
- kind: "gauge",
84997
- unit: "%",
84998
- deadband: 2
84999
- },
85000
- "dirtyWater.level": {
85001
- kind: "gauge",
85002
- unit: "%",
85003
- deadband: 2
85004
- },
85005
- "detergent.level": {
85006
- kind: "gauge",
85007
- unit: "%",
85008
- deadband: 2
85009
- },
85010
- "dustBin.level": {
85011
- kind: "gauge",
85012
- unit: "%",
85013
- deadband: 2
85014
- }
85015
- } }
85146
+ history: { fields: { batteryLevel: {
85147
+ kind: "gauge",
85148
+ unit: "%",
85149
+ deadband: 2
85150
+ } } }
85016
85151
  };
85017
85152
  var ValveStateSchema = zod.z.enum([
85018
85153
  "open",
@@ -89307,6 +89442,12 @@ var require_dist_l6zKmHEb = __commonJS({
89307
89442
  addonId: null,
89308
89443
  access: "create"
89309
89444
  },
89445
+ "deviceHistory.deleteOrphanSeries": {
89446
+ capName: "device-history",
89447
+ capScope: "system",
89448
+ addonId: null,
89449
+ access: "delete"
89450
+ },
89310
89451
  "deviceHistory.getDeviceEnabled": {
89311
89452
  capName: "device-history",
89312
89453
  capScope: "system",
@@ -89325,6 +89466,12 @@ var require_dist_l6zKmHEb = __commonJS({
89325
89466
  addonId: null,
89326
89467
  access: "view"
89327
89468
  },
89469
+ "deviceHistory.listOrphanSeries": {
89470
+ capName: "device-history",
89471
+ capScope: "system",
89472
+ addonId: null,
89473
+ access: "view"
89474
+ },
89328
89475
  "deviceHistory.listSeries": {
89329
89476
  capName: "device-history",
89330
89477
  capScope: "system",
@@ -92949,6 +93096,12 @@ var require_dist_l6zKmHEb = __commonJS({
92949
93096
  addonId: null,
92950
93097
  access: "delete"
92951
93098
  },
93099
+ "pipelineOrchestrator.forgetInferenceDevice": {
93100
+ capName: "pipeline-orchestrator",
93101
+ capScope: "system",
93102
+ addonId: null,
93103
+ access: "delete"
93104
+ },
92952
93105
  "pipelineOrchestrator.getAgentLoad": {
92953
93106
  capName: "pipeline-orchestrator",
92954
93107
  capScope: "system",
@@ -93525,6 +93678,12 @@ var require_dist_l6zKmHEb = __commonJS({
93525
93678
  addonId: null,
93526
93679
  access: "view"
93527
93680
  },
93681
+ "platformProbe.rescanAccelerators": {
93682
+ capName: "platform-probe",
93683
+ capScope: "system",
93684
+ addonId: null,
93685
+ access: "create"
93686
+ },
93528
93687
  "platformProbe.resolveHwAccel": {
93529
93688
  capName: "platform-probe",
93530
93689
  capScope: "system",
@@ -99619,33 +99778,11 @@ var require_dist_l6zKmHEb = __commonJS({
99619
99778
  unit: "\xB0C",
99620
99779
  deadband: 0.1
99621
99780
  } },
99622
- "vacuum-control": {
99623
- "batteryLevel": {
99624
- kind: "gauge",
99625
- unit: "%",
99626
- deadband: 2
99627
- },
99628
- "cleanWater.level": {
99629
- kind: "gauge",
99630
- unit: "%",
99631
- deadband: 2
99632
- },
99633
- "dirtyWater.level": {
99634
- kind: "gauge",
99635
- unit: "%",
99636
- deadband: 2
99637
- },
99638
- "detergent.level": {
99639
- kind: "gauge",
99640
- unit: "%",
99641
- deadband: 2
99642
- },
99643
- "dustBin.level": {
99644
- kind: "gauge",
99645
- unit: "%",
99646
- deadband: 2
99647
- }
99648
- },
99781
+ "vacuum-control": { "batteryLevel": {
99782
+ kind: "gauge",
99783
+ unit: "%",
99784
+ deadband: 2
99785
+ } },
99649
99786
  "valve": { "position": {
99650
99787
  kind: "gauge",
99651
99788
  unit: "%",
@@ -99687,6 +99824,36 @@ var require_dist_l6zKmHEb = __commonJS({
99687
99824
  }
99688
99825
  }
99689
99826
  };
99827
+ var HISTORY_ITEM_CATALOG = { "consumables": {
99828
+ path: "items",
99829
+ key: "key",
99830
+ label: "label",
99831
+ fields: {
99832
+ "level": {
99833
+ kind: "gauge",
99834
+ unit: "%",
99835
+ deadband: 2,
99836
+ displayName: "Level"
99837
+ },
99838
+ "status": {
99839
+ kind: "enum",
99840
+ displayName: "Status",
99841
+ values: [
99842
+ "ok",
99843
+ "low",
99844
+ "empty",
99845
+ "full",
99846
+ "replace"
99847
+ ]
99848
+ },
99849
+ "remaining.value": {
99850
+ kind: "gauge",
99851
+ unitFrom: "remaining.unit",
99852
+ deadband: 1,
99853
+ displayName: "Remaining"
99854
+ }
99855
+ }
99856
+ } };
99690
99857
  var G711_SCALE_CORRECTION_DB = {
99691
99858
  PCMU: 20 * Math.log10(4),
99692
99859
  PCMA: 20 * Math.log10(8)
@@ -100173,7 +100340,7 @@ var require_dist_l6zKmHEb = __commonJS({
100173
100340
  ([...RULE_TEXT], [...COMBINED_ALARM]),
100174
100341
  ([...RULE_TEXT], [...DEVICE_EVENTS]),
100175
100342
  ([...RULE_TEXT], [...BATTERY, ...CONSUMABLE]),
100176
- ([...RULE_TEXT], [...CONSUMABLE]),
100343
+ ([...RULE_TEXT], [...CONSUMABLE, ...TANK]),
100177
100344
  ([...RULE_TEXT], [...TANK]),
100178
100345
  [...RULE_TEXT],
100179
100346
  ([...RULE_TEXT], [...UPDATES, ...ALERT_EPISODE]),
@@ -101258,6 +101425,12 @@ var require_dist_l6zKmHEb = __commonJS({
101258
101425
  return HISTORY_DEFAULT_MIN_INTERVAL_MS;
101259
101426
  }
101260
101427
  });
101428
+ Object.defineProperty(exports, "HISTORY_ITEM_CATALOG", {
101429
+ enumerable: true,
101430
+ get: function() {
101431
+ return HISTORY_ITEM_CATALOG;
101432
+ }
101433
+ });
101261
101434
  Object.defineProperty(exports, "HUB_MDNS_SERVICE_TYPE", {
101262
101435
  enumerable: true,
101263
101436
  get: function() {
@@ -102080,6 +102253,12 @@ var require_dist_l6zKmHEb = __commonJS({
102080
102253
  return gbToBytes;
102081
102254
  }
102082
102255
  });
102256
+ Object.defineProperty(exports, "historyItemFieldPath", {
102257
+ enumerable: true,
102258
+ get: function() {
102259
+ return historyItemFieldPath;
102260
+ }
102261
+ });
102083
102262
  Object.defineProperty(exports, "hydrateSchema", {
102084
102263
  enumerable: true,
102085
102264
  get: function() {
@@ -114178,11 +114357,11 @@ ${values.join("\n")}` : `${blockName} :`;
114178
114357
  }
114179
114358
  });
114180
114359
 
114181
- // ../system/dist/device-ca-CGcaSheI.js
114182
- var require_device_ca_CGcaSheI = __commonJS({
114183
- "../system/dist/device-ca-CGcaSheI.js"(exports) {
114360
+ // ../system/dist/device-ca-R4MZZtc0.js
114361
+ var require_device_ca_R4MZZtc0 = __commonJS({
114362
+ "../system/dist/device-ca-R4MZZtc0.js"(exports) {
114184
114363
  "use strict";
114185
- var require_dist10 = require_dist_l6zKmHEb();
114364
+ var require_dist10 = require_dist_DRL3rEfN();
114186
114365
  var require_x509_es = require_x509_es_C4cOo48M();
114187
114366
  var node_crypto = __require("crypto");
114188
114367
  var SYSTEM_SECRET_OWNER = "system";
@@ -114640,7 +114819,7 @@ var require_alerts_addon = __commonJS({
114640
114819
  [Symbol.toStringTag]: { value: "Module" }
114641
114820
  });
114642
114821
  require_chunk_Cek0wNdY();
114643
- var require_dist10 = require_dist_l6zKmHEb();
114822
+ var require_dist10 = require_dist_DRL3rEfN();
114644
114823
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
114645
114824
  function nextEpisodeId(input) {
114646
114825
  const { previous } = input;
@@ -116108,7 +116287,7 @@ var require_calendars_addon = __commonJS({
116108
116287
  [Symbol.toStringTag]: { value: "Module" }
116109
116288
  });
116110
116289
  require_chunk_Cek0wNdY();
116111
- var require_dist10 = require_dist_l6zKmHEb();
116290
+ var require_dist10 = require_dist_DRL3rEfN();
116112
116291
  var require_settle_sources = require_settle_sources_Bhsy57y();
116113
116292
  var zod = require_zod();
116114
116293
  var CALENDAR_COLOR_PALETTE = [
@@ -116324,11 +116503,11 @@ var require_calendars = __commonJS({
116324
116503
  }
116325
116504
  });
116326
116505
 
116327
- // ../system/dist/composition-sources-5rpWQhA1.js
116328
- var require_composition_sources_5rpWQhA1 = __commonJS({
116329
- "../system/dist/composition-sources-5rpWQhA1.js"(exports) {
116506
+ // ../system/dist/composition-sources-CRJffEiB.js
116507
+ var require_composition_sources_CRJffEiB = __commonJS({
116508
+ "../system/dist/composition-sources-CRJffEiB.js"(exports) {
116330
116509
  "use strict";
116331
- var require_dist10 = require_dist_l6zKmHEb();
116510
+ var require_dist10 = require_dist_DRL3rEfN();
116332
116511
  function normalizedNativeOwner(owner) {
116333
116512
  const trimmed = owner?.trim();
116334
116513
  return trimmed ? trimmed : null;
@@ -116443,8 +116622,8 @@ var require_composer_addon = __commonJS({
116443
116622
  [Symbol.toStringTag]: { value: "Module" }
116444
116623
  });
116445
116624
  require_chunk_Cek0wNdY();
116446
- var require_dist10 = require_dist_l6zKmHEb();
116447
- var require_composition_sources = require_composition_sources_5rpWQhA1();
116625
+ var require_dist10 = require_dist_DRL3rEfN();
116626
+ var require_composition_sources = require_composition_sources_CRJffEiB();
116448
116627
  var zod = require_zod();
116449
116628
  function isRecord$2(value) {
116450
116629
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -120254,7 +120433,7 @@ var require_console_logging = __commonJS({
120254
120433
  [Symbol.toStringTag]: { value: "Module" }
120255
120434
  });
120256
120435
  require_chunk_Cek0wNdY();
120257
- var require_dist10 = require_dist_l6zKmHEb();
120436
+ var require_dist10 = require_dist_DRL3rEfN();
120258
120437
  var require_formatter = require_formatter_DqAKDlvN();
120259
120438
  var LEVEL_RANK = {
120260
120439
  debug: 0,
@@ -124918,9 +125097,9 @@ var require_core_blocks_addon = __commonJS({
124918
125097
  "use strict";
124919
125098
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
124920
125099
  var require_chunk = require_chunk_Cek0wNdY();
124921
- var require_dist10 = require_dist_l6zKmHEb();
125100
+ var require_dist10 = require_dist_DRL3rEfN();
124922
125101
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
124923
- var require_composition_sources = require_composition_sources_5rpWQhA1();
125102
+ var require_composition_sources = require_composition_sources_CRJffEiB();
124924
125103
  var require_prefix_range = require_prefix_range_D_KueXbQ();
124925
125104
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
124926
125105
  var zod = require_zod();
@@ -127327,9 +127506,9 @@ var require_event_category_7AwNbPjm = __commonJS({
127327
127506
  }
127328
127507
  });
127329
127508
 
127330
- // ../types/dist/sleep-CD18m9HF.js
127331
- var require_sleep_CD18m9HF = __commonJS({
127332
- "../types/dist/sleep-CD18m9HF.js"(exports) {
127509
+ // ../types/dist/sleep-DVovpDRU.js
127510
+ var require_sleep_DVovpDRU = __commonJS({
127511
+ "../types/dist/sleep-DVovpDRU.js"(exports) {
127333
127512
  "use strict";
127334
127513
  var require_event_category = require_event_category_7AwNbPjm();
127335
127514
  var zod = require_zod();
@@ -132463,6 +132642,9 @@ var require_sleep_CD18m9HF = __commonJS({
132463
132642
  kbps: "kbps",
132464
132643
  fps: "fps"
132465
132644
  };
132645
+ function historyItemFieldPath(itemsPath, field) {
132646
+ return `${itemsPath}[].${field}`;
132647
+ }
132466
132648
  var HISTORY_DEFAULT_MIN_INTERVAL_MS = 1e4;
132467
132649
  function resolveCapMount(def) {
132468
132650
  if (def.mount) return def.mount;
@@ -132485,7 +132667,11 @@ var require_sleep_CD18m9HF = __commonJS({
132485
132667
  zod.z.literal(4)
132486
132668
  ]).optional(),
132487
132669
  tab: zod.z.string().optional(),
132488
- location: zod.z.enum(["settings", "top-tab"]).optional(),
132670
+ location: zod.z.enum([
132671
+ "settings",
132672
+ "top-tab",
132673
+ "hero"
132674
+ ]).optional(),
132489
132675
  order: zod.z.number().optional(),
132490
132676
  fields: zod.z.array(zod.z.any())
132491
132677
  });
@@ -135083,6 +135269,12 @@ var require_sleep_CD18m9HF = __commonJS({
135083
135269
  return hfModelUrl;
135084
135270
  }
135085
135271
  });
135272
+ Object.defineProperty(exports, "historyItemFieldPath", {
135273
+ enumerable: true,
135274
+ get: function() {
135275
+ return historyItemFieldPath;
135276
+ }
135277
+ });
135086
135278
  Object.defineProperty(exports, "hydrateSchema", {
135087
135279
  enumerable: true,
135088
135280
  get: function() {
@@ -135487,7 +135679,7 @@ var require_addon = __commonJS({
135487
135679
  "../types/dist/addon.js"(exports) {
135488
135680
  "use strict";
135489
135681
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
135490
- var require_sleep = require_sleep_CD18m9HF();
135682
+ var require_sleep = require_sleep_DVovpDRU();
135491
135683
  var require_event_category = require_event_category_7AwNbPjm();
135492
135684
  var require_err_msg = require_err_msg_BhVbr0NX();
135493
135685
  var CAP_INPUT_DEFAULTS = Object.freeze({
@@ -136486,24 +136678,6 @@ var require_addon = __commonJS({
136486
136678
  "path": "powerState",
136487
136679
  "valueType": "string"
136488
136680
  },
136489
- {
136490
- "path": "saltLevel",
136491
- "valueType": "enum",
136492
- "enumValues": [
136493
- "ok",
136494
- "low",
136495
- "empty"
136496
- ]
136497
- },
136498
- {
136499
- "path": "rinseAidLevel",
136500
- "valueType": "enum",
136501
- "enumValues": [
136502
- "ok",
136503
- "low",
136504
- "empty"
136505
- ]
136506
- },
136507
136681
  {
136508
136682
  "path": "filterCleaningDue",
136509
136683
  "valueType": "boolean"
@@ -137529,58 +137703,6 @@ var require_addon = __commonJS({
137529
137703
  "path": "fanSpeed",
137530
137704
  "valueType": "string"
137531
137705
  },
137532
- {
137533
- "path": "cleanWater.level",
137534
- "valueType": "number"
137535
- },
137536
- {
137537
- "path": "cleanWater.status",
137538
- "valueType": "enum",
137539
- "enumValues": [
137540
- "ok",
137541
- "low",
137542
- "full"
137543
- ]
137544
- },
137545
- {
137546
- "path": "dirtyWater.level",
137547
- "valueType": "number"
137548
- },
137549
- {
137550
- "path": "dirtyWater.status",
137551
- "valueType": "enum",
137552
- "enumValues": [
137553
- "ok",
137554
- "low",
137555
- "full"
137556
- ]
137557
- },
137558
- {
137559
- "path": "detergent.level",
137560
- "valueType": "number"
137561
- },
137562
- {
137563
- "path": "detergent.status",
137564
- "valueType": "enum",
137565
- "enumValues": [
137566
- "ok",
137567
- "low",
137568
- "full"
137569
- ]
137570
- },
137571
- {
137572
- "path": "dustBin.level",
137573
- "valueType": "number"
137574
- },
137575
- {
137576
- "path": "dustBin.status",
137577
- "valueType": "enum",
137578
- "enumValues": [
137579
- "ok",
137580
- "low",
137581
- "full"
137582
- ]
137583
- },
137584
137706
  {
137585
137707
  "path": "progressPercent",
137586
137708
  "valueType": "number"
@@ -138145,7 +138267,7 @@ var require_device_manager_addon = __commonJS({
138145
138267
  [Symbol.toStringTag]: { value: "Module" }
138146
138268
  });
138147
138269
  require_chunk_Cek0wNdY();
138148
- var require_dist10 = require_dist_l6zKmHEb();
138270
+ var require_dist10 = require_dist_DRL3rEfN();
138149
138271
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
138150
138272
  var require_prefix_range = require_prefix_range_D_KueXbQ();
138151
138273
  var require_prefix_walk = require_prefix_walk_Dv6MLZlM();
@@ -141083,7 +141205,7 @@ var require_device_manager_addon = __commonJS({
141083
141205
  title: ui.label,
141084
141206
  tab: ui.tab,
141085
141207
  order: ui.order ?? 0,
141086
- ...ui.topTab ? { location: "top-tab" } : {},
141208
+ ...ui.placement === "hero" ? { location: "hero" } : ui.topTab ? { location: "top-tab" } : {},
141087
141209
  fields: [{
141088
141210
  type: "widget",
141089
141211
  key: `${entry.capName}Widget`,
@@ -147217,7 +147339,7 @@ var require_hub_forwarder = __commonJS({
147217
147339
  [Symbol.toStringTag]: { value: "Module" }
147218
147340
  });
147219
147341
  require_chunk_Cek0wNdY();
147220
- var require_dist10 = require_dist_l6zKmHEb();
147342
+ var require_dist10 = require_dist_DRL3rEfN();
147221
147343
  var require_formatter = require_formatter_DqAKDlvN();
147222
147344
  var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
147223
147345
  var HubForwarderDestination = class {
@@ -147351,7 +147473,7 @@ var require_liveness_monitor_addon = __commonJS({
147351
147473
  "use strict";
147352
147474
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
147353
147475
  require_chunk_Cek0wNdY();
147354
- var require_dist10 = require_dist_l6zKmHEb();
147476
+ var require_dist10 = require_dist_DRL3rEfN();
147355
147477
  var NO_DEVICES = "liveness:no-devices";
147356
147478
  var ALL_OFFLINE = "liveness:all-devices-offline";
147357
147479
  var NO_FOOTAGE_PREFIX = "liveness:no-footage:";
@@ -147714,10 +147836,10 @@ var require_local_auth_addon = __commonJS({
147714
147836
  [Symbol.toStringTag]: { value: "Module" }
147715
147837
  });
147716
147838
  var require_chunk = require_chunk_Cek0wNdY();
147717
- var require_dist10 = require_dist_l6zKmHEb();
147839
+ var require_dist10 = require_dist_DRL3rEfN();
147718
147840
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
147719
147841
  var require_ms$1 = require_ms_LB3giGoU();
147720
- var require_device_ca = require_device_ca_CGcaSheI();
147842
+ var require_device_ca = require_device_ca_R4MZZtc0();
147721
147843
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
147722
147844
  var zod = require_zod();
147723
147845
  var node_crypto = __require("crypto");
@@ -158105,7 +158227,7 @@ var require_loki_logging = __commonJS({
158105
158227
  [Symbol.toStringTag]: { value: "Module" }
158106
158228
  });
158107
158229
  require_chunk_Cek0wNdY();
158108
- var require_dist10 = require_dist_l6zKmHEb();
158230
+ var require_dist10 = require_dist_DRL3rEfN();
158109
158231
  var require_log_read_filter = require_log_read_filter_Bi066jwU();
158110
158232
  var zod = require_zod();
158111
158233
  function sanitizeLabelName(raw) {
@@ -159066,7 +159188,7 @@ var require_native_metrics_addon = __commonJS({
159066
159188
  [Symbol.toStringTag]: { value: "Module" }
159067
159189
  });
159068
159190
  var require_chunk = require_chunk_Cek0wNdY();
159069
- var require_dist10 = require_dist_l6zKmHEb();
159191
+ var require_dist10 = require_dist_DRL3rEfN();
159070
159192
  var node_fs_promises = __require("fs/promises");
159071
159193
  var node_util = __require("util");
159072
159194
  var node_os = __require("os");
@@ -161633,7 +161755,7 @@ var require_device_history_addon = __commonJS({
161633
161755
  [Symbol.toStringTag]: { value: "Module" }
161634
161756
  });
161635
161757
  require_chunk_Cek0wNdY();
161636
- var require_dist10 = require_dist_l6zKmHEb();
161758
+ var require_dist10 = require_dist_DRL3rEfN();
161637
161759
  var FIVE_MINUTES_MS = 5 * 6e4;
161638
161760
  var HOUR_MS = 60 * 6e4;
161639
161761
  var DAY_MS = 24 * HOUR_MS;
@@ -161798,13 +161920,25 @@ var require_device_history_addon = __commonJS({
161798
161920
  }
161799
161921
  return deleted;
161800
161922
  }
161801
- /** Tier by tier in slices, then the series row (and whatever the slices missed). */
161923
+ /**
161924
+ * Tier by tier in slices, then the series row (and whatever the slices missed).
161925
+ * Answers the rows the slices deleted per tier. The caller removes the series
161926
+ * from the registry, which releases its per-series state.
161927
+ */
161802
161928
  async purgeSeries(series, now) {
161929
+ const deleted = {
161930
+ raw: 0,
161931
+ "5m": 0,
161932
+ "1h": 0,
161933
+ "1d": 0
161934
+ };
161803
161935
  for (const tier of TIERS) {
161804
161936
  const lo = tier === "raw" || tier === "5m" ? Math.max(series.firstTs - BUCKET_LEAD_MS, now - SHORT_TIER_LOOKBACK_MS) : series.firstTs - BUCKET_LEAD_MS;
161805
- await this.deleteSliced(tier, series.id, Math.max(0, lo), now + DAY_MS);
161937
+ deleted[tier] = await this.deleteSliced(tier, series.id, Math.max(0, lo), now + DAY_MS);
161938
+ this.deps.counters.addDeleted(tier, deleted[tier]);
161806
161939
  }
161807
161940
  await this.deps.store.deleteSeries(series.id);
161941
+ return deleted;
161808
161942
  }
161809
161943
  /**
161810
161944
  * Delete every series of a DELETED device now (D865). Driven by a tombstone
@@ -162129,13 +162263,13 @@ var require_device_history_addon = __commonJS({
162129
162263
  }
162130
162264
  }
162131
162265
  };
162132
- function isRecord$1(value) {
162266
+ function isRecord$2(value) {
162133
162267
  return typeof value === "object" && value !== null && !Array.isArray(value);
162134
162268
  }
162135
162269
  function readPath(slice, path) {
162136
162270
  let current = slice;
162137
162271
  for (const step of path.split(".")) {
162138
- if (!isRecord$1(current)) return void 0;
162272
+ if (!isRecord$2(current)) return void 0;
162139
162273
  current = current[step];
162140
162274
  }
162141
162275
  return current;
@@ -162157,13 +162291,16 @@ var require_device_history_addon = __commonJS({
162157
162291
  function capSeriesKey(capName, fieldPath) {
162158
162292
  return `${CAP_KEY_PREFIX}${capName}.${fieldPath}`;
162159
162293
  }
162294
+ function capItemSeriesKey(capName, items, field) {
162295
+ return capSeriesKey(capName, require_dist10.historyItemFieldPath(items.path, field));
162296
+ }
162160
162297
  function humanize(capName, fieldPath) {
162161
162298
  const field = fieldPath.replace(/([A-Z])/g, " $1").replace(/\./g, " ").toLowerCase();
162162
162299
  return `${capName.replace(/-/g, " ")} ${field}`;
162163
162300
  }
162164
- function catalogMetrics(catalog, now) {
162165
- return Object.entries(catalog).flatMap(([capName, fields]) => Object.entries(fields).map(([path, spec]) => ({
162166
- key: capSeriesKey(capName, path),
162301
+ function metricOf(key, capName, path, spec, now) {
162302
+ return {
162303
+ key,
162167
162304
  source: "cap",
162168
162305
  ownerAddon: "device-history",
162169
162306
  subjectKind: "device",
@@ -162173,7 +162310,15 @@ var require_device_history_addon = __commonJS({
162173
162310
  ingest: "points",
162174
162311
  declaredAt: now,
162175
162312
  values: spec.kind === "enum" ? spec.values ?? [] : null
162176
- })));
162313
+ };
162314
+ }
162315
+ function catalogMetrics(catalog, itemCatalog, now) {
162316
+ const fields = Object.entries(catalog).flatMap(([capName, specs]) => Object.entries(specs).map(([path, spec]) => metricOf(capSeriesKey(capName, path), capName, path, spec, now)));
162317
+ const items = Object.entries(itemCatalog).flatMap(([capName, block]) => Object.entries(block.fields).map(([field, spec]) => metricOf(capItemSeriesKey(capName, block, field), capName, field, spec, now)));
162318
+ return [...fields, ...items];
162319
+ }
162320
+ function isRecord$1(value) {
162321
+ return typeof value === "object" && value !== null && !Array.isArray(value);
162177
162322
  }
162178
162323
  var MAX_PARKED_DEVICES = 5e3;
162179
162324
  var SEED_CONCURRENCY = 8;
@@ -162198,7 +162343,8 @@ var require_device_history_addon = __commonJS({
162198
162343
  /** One `DeviceStateChanged`. Synchronous: the bus must never wait on us. */
162199
162344
  handleState(deviceId, capName, slice, at) {
162200
162345
  const fields = this.deps.catalog[capName];
162201
- if (fields === void 0) return;
162346
+ const items = this.deps.itemCatalog[capName];
162347
+ if (fields === void 0 && items === void 0) return;
162202
162348
  const { counters, directory } = this.deps;
162203
162349
  counters.eventsSeen += 1;
162204
162350
  if (this.deps.optedOut().has(deviceId)) {
@@ -162214,14 +162360,46 @@ var require_device_history_addon = __commonJS({
162214
162360
  counters.skip("not-a-real-device");
162215
162361
  return;
162216
162362
  }
162217
- for (const [path, spec] of Object.entries(fields)) {
162363
+ this.captureSlice(deviceId, type, capName, slice, at, false);
162364
+ }
162365
+ /** Every catalogued field and keyed item of one cap slice, for a real device. */
162366
+ captureSlice(deviceId, type, capName, slice, at, reconciling) {
162367
+ for (const [path, spec] of Object.entries(this.deps.catalog[capName] ?? {})) {
162218
162368
  if (spec.excludeDeviceTypes?.includes(type) === true) {
162219
- counters.skip("excluded-device-type");
162369
+ if (!reconciling) this.deps.counters.skip("excluded-device-type");
162220
162370
  continue;
162221
162371
  }
162222
- const value = this.readValue(slice, capName, path, spec);
162372
+ const key = capSeriesKey(capName, path);
162373
+ const value = this.readValue(slice, key, path, spec);
162223
162374
  if (value === null) continue;
162224
- this.consider(deviceId, capName, path, spec, value, at, false);
162375
+ this.consider(deviceId, key, "", spec, value, at, reconciling);
162376
+ }
162377
+ const items = this.deps.itemCatalog[capName];
162378
+ if (items !== void 0) this.captureItems(deviceId, type, capName, items, slice, at, reconciling);
162379
+ }
162380
+ /**
162381
+ * A keyed array (D917): each item with a usable key contributes its fields
162382
+ * under that key as the series scope. A value that is not an array is no
162383
+ * reading; an item without a string key cannot be followed and is counted.
162384
+ */
162385
+ captureItems(deviceId, type, capName, items, slice, at, reconciling) {
162386
+ const list = readPath(slice, items.path);
162387
+ if (!Array.isArray(list)) return;
162388
+ const { counters } = this.deps;
162389
+ if (list.length > 64) counters.skip("item-overflow", list.length - 64);
162390
+ for (const entry of list.slice(0, 64)) {
162391
+ const scope = isRecord$1(entry) ? entry[items.key] : void 0;
162392
+ if (!isRecord$1(entry) || typeof scope !== "string" || scope.length === 0 || scope.length > 128) {
162393
+ counters.skip("item-key-invalid");
162394
+ continue;
162395
+ }
162396
+ for (const [field, spec] of Object.entries(items.fields)) {
162397
+ if (spec.excludeDeviceTypes?.includes(type) === true) continue;
162398
+ const key = capItemSeriesKey(capName, items, field);
162399
+ const value = this.readValue(entry, key, field, spec);
162400
+ if (value === null) continue;
162401
+ this.consider(deviceId, key, scope, spec, value, at, reconciling);
162402
+ }
162225
162403
  }
162226
162404
  }
162227
162405
  /**
@@ -162229,7 +162407,7 @@ var require_device_history_addon = __commonJS({
162229
162407
  * `enum` state becomes the ordinal of its value in the metric's list; a value
162230
162408
  * the full list cannot take is refused and counted (`enum-overflow`).
162231
162409
  */
162232
- readValue(slice, capName, path, spec) {
162410
+ readValue(slice, key, path, spec) {
162233
162411
  if (spec.skipWhenSet !== void 0) {
162234
162412
  const gate = readPath(slice, spec.skipWhenSet);
162235
162413
  if (gate !== void 0 && gate !== null) return null;
@@ -162237,20 +162415,19 @@ var require_device_history_addon = __commonJS({
162237
162415
  if (spec.kind !== "enum") return readNumeric(slice, path, spec.kind);
162238
162416
  const raw = readPath(slice, path);
162239
162417
  if (typeof raw !== "string" || raw.length === 0) return null;
162240
- const ordinal = this.deps.registry.ordinalOf(capSeriesKey(capName, path), raw);
162418
+ const ordinal = this.deps.registry.ordinalOf(key, raw);
162241
162419
  if (ordinal === null) this.deps.counters.skip("enum-overflow");
162242
162420
  return ordinal;
162243
162421
  }
162244
- consider(deviceId, capName, path, spec, value, at, reconciling) {
162245
- const key = capSeriesKey(capName, path);
162422
+ consider(deviceId, key, scope, spec, value, at, reconciling) {
162246
162423
  const subject = {
162247
162424
  kind: "device",
162248
162425
  deviceId
162249
162426
  };
162250
- const identity = identityOf(key, `d:${deviceId}`, "");
162427
+ const identity = identityOf(key, `d:${deviceId}`, scope);
162251
162428
  const decision = decideCapture(spec, this.recorded.get(identity) ?? null, value, at, { ignoreFloor: reconciling });
162252
162429
  if (decision.action === "emit") {
162253
- this.emit(identity, key, subject, value, at);
162430
+ this.emit(identity, key, subject, scope, value, at);
162254
162431
  if (reconciling) this.deps.counters.reconciledPoints += 1;
162255
162432
  return;
162256
162433
  }
@@ -162258,6 +162435,7 @@ var require_device_history_addon = __commonJS({
162258
162435
  this.pending.set(identity, {
162259
162436
  key,
162260
162437
  subject,
162438
+ scope,
162261
162439
  spec,
162262
162440
  value
162263
162441
  });
@@ -162266,7 +162444,7 @@ var require_device_history_addon = __commonJS({
162266
162444
  this.pending.delete(identity);
162267
162445
  this.deps.counters.skip(decision.reason);
162268
162446
  }
162269
- emit(identity, key, subject, value, ts) {
162447
+ emit(identity, key, subject, scope, value, ts) {
162270
162448
  this.recorded.set(identity, {
162271
162449
  value,
162272
162450
  ts
@@ -162276,7 +162454,7 @@ var require_device_history_addon = __commonJS({
162276
162454
  this.deps.writer.enqueue({
162277
162455
  key,
162278
162456
  subject,
162279
- scope: "",
162457
+ scope,
162280
162458
  ts,
162281
162459
  value
162282
162460
  });
@@ -162293,7 +162471,7 @@ var require_device_history_addon = __commonJS({
162293
162471
  }
162294
162472
  const notBefore = (recorded?.ts ?? now) + minIntervalOf(p.spec);
162295
162473
  if (now < notBefore) continue;
162296
- this.emit(identity, p.key, p.subject, p.value, notBefore);
162474
+ this.emit(identity, p.key, p.subject, p.scope, p.value, notBefore);
162297
162475
  this.deps.counters.trailingEmitted += 1;
162298
162476
  }
162299
162477
  }
@@ -162341,16 +162519,7 @@ var require_device_history_addon = __commonJS({
162341
162519
  if (!Number.isInteger(deviceId) || optedOut.has(deviceId)) continue;
162342
162520
  const type = this.deps.directory.typeOf(deviceId);
162343
162521
  if (type === void 0 || !isRealDeviceType(type)) continue;
162344
- for (const [capName, fields] of Object.entries(this.deps.catalog)) {
162345
- const slice = caps[capName];
162346
- if (slice === void 0) continue;
162347
- for (const [path, spec] of Object.entries(fields)) {
162348
- if (spec.excludeDeviceTypes?.includes(type) === true) continue;
162349
- const value = this.readValue(slice, capName, path, spec);
162350
- if (value === null) continue;
162351
- this.consider(deviceId, capName, path, spec, value, now, true);
162352
- }
162353
- }
162522
+ for (const [capName, slice] of Object.entries(caps)) this.captureSlice(deviceId, type, capName, slice, now, true);
162354
162523
  }
162355
162524
  }
162356
162525
  /**
@@ -162376,8 +162545,8 @@ var require_device_history_addon = __commonJS({
162376
162545
  return this.pending.size;
162377
162546
  }
162378
162547
  /** The last recorded state of a series, for specs. */
162379
- lastRecorded(key, subjectId) {
162380
- return this.recorded.get(identityOf(key, subjectId, ""));
162548
+ lastRecorded(key, subjectId, scope = "") {
162549
+ return this.recorded.get(identityOf(key, subjectId, scope));
162381
162550
  }
162382
162551
  };
162383
162552
  var FLUSH_INTERVAL_MS = 1e4;
@@ -162796,8 +162965,10 @@ var require_device_history_addon = __commonJS({
162796
162965
  }
162797
162966
  var HistoryQuery = class {
162798
162967
  deps;
162968
+ itemFields;
162799
162969
  constructor(deps) {
162800
162970
  this.deps = deps;
162971
+ this.itemFields = itemFieldsByKey(deps.itemCatalog);
162801
162972
  }
162802
162973
  async query(input) {
162803
162974
  if (input.key === "derived:energy.kwh") return this.queryEnergy(input);
@@ -162985,12 +163156,14 @@ var require_device_history_addon = __commonJS({
162985
163156
  for (const s of registry.forSubject(subjectIdOf(subject))) {
162986
163157
  const metric = registry.metric(s.key);
162987
163158
  if (metric === void 0) continue;
163159
+ const item = this.itemFields.get(s.key);
163160
+ const named = item === void 0 || subject.kind !== "device" ? null : await this.itemNaming(subject.deviceId, item, s.scope, metric.unit, slices);
162988
163161
  infos.push({
162989
163162
  key: s.key,
162990
163163
  scope: s.scope,
162991
163164
  kind: metric.kind,
162992
- unit: await this.unitOf(subject, s, metric.unit, slices),
162993
- displayName: metric.displayName,
163165
+ unit: named !== null ? named.unit : await this.unitOf(subject, s, metric.unit, slices),
163166
+ displayName: named !== null ? `${named.label} \xB7 ${metric.displayName}` : metric.displayName,
162994
163167
  source: metric.source,
162995
163168
  values: metric.kind === "enum" ? metric.values ?? [] : null,
162996
163169
  firstTs: s.firstTs,
@@ -163011,15 +163184,38 @@ var require_device_history_addon = __commonJS({
163011
163184
  });
163012
163185
  return infos.toSorted((a, b) => a.key.localeCompare(b.key) || a.scope.localeCompare(b.scope));
163013
163186
  }
163187
+ /**
163188
+ * An item series (D917) is named after its item and takes its `unitFrom`
163189
+ * from it, both read from the device's CURRENT slice: the item's `label`,
163190
+ * else its key (the scope) when the item is gone or the slice unreadable.
163191
+ */
163192
+ async itemNaming(deviceId, item, scope, fixed, slices) {
163193
+ const slice = await this.sliceOf(deviceId, item.capName, slices);
163194
+ const list = slice === null ? void 0 : readPath(slice, item.items.path);
163195
+ const entry = Array.isArray(list) ? list.find((e) => isObject(e) && e[item.items.key] === scope) : void 0;
163196
+ const rawLabel = entry !== void 0 && item.items.label !== void 0 ? entry[item.items.label] : void 0;
163197
+ const label = typeof rawLabel === "string" && rawLabel.length > 0 ? rawLabel : scope;
163198
+ if (fixed !== null || item.spec.unitFrom === void 0) return {
163199
+ label,
163200
+ unit: fixed
163201
+ };
163202
+ return {
163203
+ label,
163204
+ unit: entry === void 0 ? null : readUnit(entry, item.spec.unitFrom)
163205
+ };
163206
+ }
163207
+ sliceOf(deviceId, capName, slices) {
163208
+ const pending = slices.get(capName) ?? this.deps.readCapSlice(deviceId, capName).catch(() => null);
163209
+ slices.set(capName, pending);
163210
+ return pending;
163211
+ }
163014
163212
  /** The metric's fixed unit, else the device's own (a `unitFrom` field), else unknown. */
163015
163213
  async unitOf(subject, series, fixed, slices) {
163016
163214
  if (fixed !== null || subject.kind !== "device" || !series.key.startsWith("cap:")) return fixed;
163017
163215
  const spec = this.specOf(series.key);
163018
163216
  if (spec?.unitFrom === void 0) return null;
163019
163217
  const capName = series.key.slice(CAP_KEY_PREFIX.length, series.key.indexOf("."));
163020
- const pending = slices.get(capName) ?? this.deps.readCapSlice(subject.deviceId, capName).catch(() => null);
163021
- slices.set(capName, pending);
163022
- const slice = await pending;
163218
+ const slice = await this.sliceOf(subject.deviceId, capName, slices);
163023
163219
  return slice === null ? null : readUnit(slice, spec.unitFrom);
163024
163220
  }
163025
163221
  specOf(key) {
@@ -163029,10 +163225,149 @@ var require_device_history_addon = __commonJS({
163029
163225
  return this.deps.catalog[body.slice(0, dot)]?.[body.slice(dot + 1)];
163030
163226
  }
163031
163227
  };
163228
+ function isObject(value) {
163229
+ return typeof value === "object" && value !== null && !Array.isArray(value);
163230
+ }
163231
+ function itemFieldsByKey(itemCatalog) {
163232
+ return new Map(Object.entries(itemCatalog).flatMap(([capName, items]) => Object.entries(items.fields).map(([field, spec]) => [capItemSeriesKey(capName, items, field), {
163233
+ capName,
163234
+ items,
163235
+ spec
163236
+ }])));
163237
+ }
163032
163238
  function withoutRaw(tiers) {
163033
163239
  const rollups = tiers.filter((t) => t !== "raw");
163034
163240
  return rollups.length > 0 ? rollups : ["5m"];
163035
163241
  }
163242
+ function judgeOrphan(series, declaredCapKeys, missingSince) {
163243
+ if (series.deviceId === null) return null;
163244
+ if (missingSince !== void 0) return {
163245
+ reason: "device-gone",
163246
+ missingSince
163247
+ };
163248
+ if (series.key.startsWith("cap:") && !declaredCapKeys.has(series.key)) return {
163249
+ reason: "undeclared",
163250
+ missingSince: null
163251
+ };
163252
+ return null;
163253
+ }
163254
+ var OrphanSeries = class {
163255
+ deps;
163256
+ constructor(deps) {
163257
+ this.deps = deps;
163258
+ }
163259
+ verdictOf(series) {
163260
+ const { departures, declaredCapKeys } = this.deps;
163261
+ return judgeOrphan(series, declaredCapKeys, series.deviceId === null ? void 0 : departures.missingSince(series.deviceId));
163262
+ }
163263
+ /** Every orphan, ordered by device, key and scope, the first {@link DEVICE_HISTORY_ORPHAN_LIST_MAX} counted. */
163264
+ async list() {
163265
+ const found = this.deps.registry.all().flatMap((series) => {
163266
+ const verdict = this.verdictOf(series);
163267
+ return verdict === null || series.deviceId === null ? [] : [{
163268
+ series,
163269
+ deviceId: series.deviceId,
163270
+ verdict
163271
+ }];
163272
+ }).toSorted((a, b) => a.deviceId - b.deviceId || a.series.key.localeCompare(b.series.key) || a.series.scope.localeCompare(b.series.scope));
163273
+ const out = [];
163274
+ for (const { series, deviceId, verdict } of found.slice(0, 200)) out.push({
163275
+ seriesId: series.id,
163276
+ deviceId,
163277
+ key: series.key,
163278
+ scope: series.scope,
163279
+ displayName: this.deps.registry.metric(series.key)?.displayName ?? null,
163280
+ reason: verdict.reason,
163281
+ missingSince: verdict.missingSince,
163282
+ firstTs: series.firstTs,
163283
+ lastTs: series.lastTs,
163284
+ rows: await this.rowsOf(series.id)
163285
+ });
163286
+ return {
163287
+ series: out,
163288
+ truncated: found.length > 200
163289
+ };
163290
+ }
163291
+ async rowsOf(seriesId) {
163292
+ const { store } = this.deps;
163293
+ return {
163294
+ raw: await store.countSeriesRows("raw", seriesId),
163295
+ "5m": await store.countSeriesRows("5m", seriesId),
163296
+ "1h": await store.countSeriesRows("1h", seriesId),
163297
+ "1d": await store.countSeriesRows("1d", seriesId)
163298
+ };
163299
+ }
163300
+ /** Delete orphans by id, one at a time, each re-judged now. One entry per id, in order. */
163301
+ async delete(seriesIds) {
163302
+ const out = [];
163303
+ for (const seriesId of seriesIds) out.push(await this.deleteOne(seriesId));
163304
+ return out;
163305
+ }
163306
+ async deleteOne(seriesId) {
163307
+ const { registry, retention, logger, clock } = this.deps;
163308
+ const series = registry.all().find((s) => s.id === seriesId);
163309
+ if (series === void 0) return {
163310
+ seriesId,
163311
+ ok: false,
163312
+ reason: "not-found"
163313
+ };
163314
+ const verdict = this.verdictOf(series);
163315
+ if (verdict === null || series.deviceId === null) {
163316
+ logger.info("device-history: refused to delete a series that is not orphan", {
163317
+ ...series.deviceId === null ? {} : { tags: { deviceId: series.deviceId } },
163318
+ meta: {
163319
+ seriesId,
163320
+ key: series.key,
163321
+ scope: series.scope
163322
+ }
163323
+ });
163324
+ return {
163325
+ seriesId,
163326
+ ok: false,
163327
+ reason: "not-orphan"
163328
+ };
163329
+ }
163330
+ try {
163331
+ const deleted = await retention.purgeSeries(series, clock());
163332
+ registry.remove(series.id);
163333
+ const rows = {
163334
+ raw: deleted.raw,
163335
+ "5m": deleted["5m"],
163336
+ "1h": deleted["1h"],
163337
+ "1d": deleted["1d"]
163338
+ };
163339
+ logger.info("device-history: deleted an orphan series", {
163340
+ tags: { deviceId: series.deviceId },
163341
+ meta: {
163342
+ seriesId,
163343
+ key: series.key,
163344
+ scope: series.scope,
163345
+ reason: verdict.reason,
163346
+ rows
163347
+ }
163348
+ });
163349
+ return {
163350
+ seriesId,
163351
+ ok: true,
163352
+ deleted: rows
163353
+ };
163354
+ } catch (err) {
163355
+ logger.warn("device-history: deleting an orphan series failed \u2014 it stays listed, retry the delete", {
163356
+ tags: { deviceId: series.deviceId },
163357
+ meta: {
163358
+ seriesId,
163359
+ key: series.key,
163360
+ error: err instanceof Error ? err.message : String(err)
163361
+ }
163362
+ });
163363
+ return {
163364
+ seriesId,
163365
+ ok: false,
163366
+ reason: "failed"
163367
+ };
163368
+ }
163369
+ }
163370
+ };
163036
163371
  var HISTORY_NAMESPACE = "device-history";
163037
163372
  var COLLECTIONS = {
163038
163373
  metrics: "metrics",
@@ -163632,6 +163967,17 @@ var require_device_history_addon = __commonJS({
163632
163967
  key: String(seriesId)
163633
163968
  });
163634
163969
  }
163970
+ /**
163971
+ * One series' rows of a tier: a range of the primary key (`series_id = ?`),
163972
+ * never a scan of the tier (D447, asserted in `history-query-plans.spec.ts`).
163973
+ */
163974
+ async countSeriesRows(tier, seriesId) {
163975
+ return this.door.count.query({
163976
+ namespace: HISTORY_NAMESPACE,
163977
+ collection: collectionOf(tier),
163978
+ filter: { where: { series_id: seriesId } }
163979
+ });
163980
+ }
163635
163981
  async countRows(tier) {
163636
163982
  return this.door.count.query({
163637
163983
  namespace: HISTORY_NAMESPACE,
@@ -164151,6 +164497,7 @@ var require_device_history_addon = __commonJS({
164151
164497
  query;
164152
164498
  retentionJob;
164153
164499
  departures;
164500
+ orphans;
164154
164501
  optedOutDevices = /* @__PURE__ */ new Set();
164155
164502
  tierCounts = /* @__PURE__ */ new Map();
164156
164503
  startedAt;
@@ -164159,7 +164506,7 @@ var require_device_history_addon = __commonJS({
164159
164506
  ready = false;
164160
164507
  constructor(deps) {
164161
164508
  this.deps = deps;
164162
- const { logger, clock, catalog } = deps;
164509
+ const { logger, clock, catalog, itemCatalog } = deps;
164163
164510
  this.startedAt = clock();
164164
164511
  this.store = new HistoryStore(deps.door);
164165
164512
  this.registry = new SeriesRegistry(this.store);
@@ -164183,6 +164530,7 @@ var require_device_history_addon = __commonJS({
164183
164530
  const optedOut = () => this.optedOutDevices;
164184
164531
  this.capture = new CaptureEngine({
164185
164532
  catalog,
164533
+ itemCatalog,
164186
164534
  registry: this.registry,
164187
164535
  store: this.store,
164188
164536
  directory: this.directory,
@@ -164216,6 +164564,7 @@ var require_device_history_addon = __commonJS({
164216
164564
  clock,
164217
164565
  retention: deps.retention,
164218
164566
  catalog,
164567
+ itemCatalog,
164219
164568
  readCapSlice: deps.readCapSlice
164220
164569
  });
164221
164570
  this.departures = new DepartureTracker(this.store, this.registry, logger);
@@ -164232,11 +164581,20 @@ var require_device_history_addon = __commonJS({
164232
164581
  counters: this.counters,
164233
164582
  logger
164234
164583
  });
164584
+ this.orphans = new OrphanSeries({
164585
+ store: this.store,
164586
+ registry: this.registry,
164587
+ departures: this.departures,
164588
+ retention: this.retentionJob,
164589
+ declaredCapKeys: new Set(catalogMetrics(catalog, itemCatalog, 0).map((m) => m.key)),
164590
+ logger,
164591
+ clock
164592
+ });
164235
164593
  }
164236
164594
  async prepare() {
164237
164595
  await this.store.declare();
164238
164596
  await this.registry.load();
164239
- for (const metric of catalogMetrics(this.deps.catalog, this.deps.clock())) await this.registry.declareMetric(metric);
164597
+ for (const metric of catalogMetrics(this.deps.catalog, this.deps.itemCatalog, this.deps.clock())) await this.registry.declareMetric(metric);
164240
164598
  this.optedOutDevices = await this.store.loadOptOuts();
164241
164599
  await this.departures.load();
164242
164600
  await this.directory.refresh();
@@ -164355,6 +164713,14 @@ var require_device_history_addon = __commonJS({
164355
164713
  this.optedOutDevices = new Set([...this.optedOutDevices].filter((id) => id !== deviceId));
164356
164714
  return { series };
164357
164715
  }
164716
+ /** Device series no longer linked to a device — reported, never purged here. */
164717
+ listOrphans() {
164718
+ return this.orphans.list();
164719
+ }
164720
+ /** Delete orphan series by id; a series that is not orphan NOW is refused. */
164721
+ deleteOrphans(seriesIds) {
164722
+ return this.orphans.delete(seriesIds);
164723
+ }
164358
164724
  /** Series held per device, for the admin clean-up listing. */
164359
164725
  seriesByDevice() {
164360
164726
  const out = /* @__PURE__ */ new Map();
@@ -164526,7 +164892,9 @@ var require_device_history_addon = __commonJS({
164526
164892
  recordBuckets: async (input) => this.requireRuntime().ingest.recordBuckets(callerOf(input), input.buckets),
164527
164893
  getDeviceEnabled: async (input) => ({ enabled: this.requireRuntime().isEnabled(input.deviceId) }),
164528
164894
  setDeviceEnabled: async (input) => ({ enabled: await this.requireRuntime().setEnabled(input.deviceId, input.enabled) }),
164529
- getStatus: async () => this.requireRuntime().status()
164895
+ getStatus: async () => this.requireRuntime().status(),
164896
+ listOrphanSeries: async () => this.requireRuntime().listOrphans(),
164897
+ deleteOrphanSeries: async (input) => ({ results: await this.requireRuntime().deleteOrphans(input.seriesIds) })
164530
164898
  };
164531
164899
  this.emitStoreReadiness("starting");
164532
164900
  this.ctx.logger.info("Initialized \u2014 device-history provider registered (store opens on hub link)");
@@ -164562,6 +164930,7 @@ var require_device_history_addon = __commonJS({
164562
164930
  const runtime = new HistoryRuntime({
164563
164931
  door: api.settingsStore,
164564
164932
  catalog: require_dist10.HISTORY_CATALOG,
164933
+ itemCatalog: require_dist10.HISTORY_ITEM_CATALOG,
164565
164934
  deviceSource: {
164566
164935
  listAll: async () => {
164567
164936
  return (await api.deviceManager.listAll.query({ projection: "slim" })).map((d) => ({
@@ -164679,7 +165048,7 @@ var require_filesystem_storage_addon = __commonJS({
164679
165048
  [Symbol.toStringTag]: { value: "Module" }
164680
165049
  });
164681
165050
  var require_chunk = require_chunk_Cek0wNdY();
164682
- var require_dist10 = require_dist_l6zKmHEb();
165051
+ var require_dist10 = require_dist_DRL3rEfN();
164683
165052
  var node_crypto = __require("crypto");
164684
165053
  var node_fs_promises = __require("fs/promises");
164685
165054
  var node_path = __require("path");
@@ -165789,12 +166158,12 @@ var require_lib = __commonJS({
165789
166158
  }
165790
166159
  });
165791
166160
 
165792
- // ../system/dist/wal-maintenance-B6LSQ_OZ.js
165793
- var require_wal_maintenance_B6LSQ_OZ = __commonJS({
165794
- "../system/dist/wal-maintenance-B6LSQ_OZ.js"(exports) {
166161
+ // ../system/dist/wal-maintenance-DYvzJiuK.js
166162
+ var require_wal_maintenance_DYvzJiuK = __commonJS({
166163
+ "../system/dist/wal-maintenance-DYvzJiuK.js"(exports) {
165795
166164
  "use strict";
165796
166165
  var require_chunk = require_chunk_Cek0wNdY();
165797
- var require_dist10 = require_dist_l6zKmHEb();
166166
+ var require_dist10 = require_dist_DRL3rEfN();
165798
166167
  var require_prefix_range = require_prefix_range_D_KueXbQ();
165799
166168
  var node_crypto = __require("crypto");
165800
166169
  var node_module = __require("module");
@@ -169978,9 +170347,9 @@ var require_sqlite_settings_addon = __commonJS({
169978
170347
  [Symbol.toStringTag]: { value: "Module" }
169979
170348
  });
169980
170349
  var require_chunk = require_chunk_Cek0wNdY();
169981
- var require_dist10 = require_dist_l6zKmHEb();
170350
+ var require_dist10 = require_dist_DRL3rEfN();
169982
170351
  var require_retired_settings_keys = require_retired_settings_keys_CGUqX_Oz();
169983
- var require_wal_maintenance = require_wal_maintenance_B6LSQ_OZ();
170352
+ var require_wal_maintenance = require_wal_maintenance_DYvzJiuK();
169984
170353
  var require_sqlite_snapshot_protocol = require_sqlite_snapshot_protocol_B9OndqZv();
169985
170354
  var require_wal_checkpoint_policy = require_wal_checkpoint_policy_d2z2qPqx();
169986
170355
  var node_fs_promises = __require("fs/promises");
@@ -171608,7 +171977,7 @@ var require_sqlite_storage = __commonJS({
171608
171977
  });
171609
171978
  require_chunk_Cek0wNdY();
171610
171979
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
171611
- var require_wal_maintenance = require_wal_maintenance_B6LSQ_OZ();
171980
+ var require_wal_maintenance = require_wal_maintenance_DYvzJiuK();
171612
171981
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
171613
171982
  var ConfigStore = class {
171614
171983
  db;
@@ -171692,7 +172061,7 @@ var require_storage_orchestrator_addon = __commonJS({
171692
172061
  [Symbol.toStringTag]: { value: "Module" }
171693
172062
  });
171694
172063
  var require_chunk = require_chunk_Cek0wNdY();
171695
- var require_dist10 = require_dist_l6zKmHEb();
172064
+ var require_dist10 = require_dist_DRL3rEfN();
171696
172065
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
171697
172066
  var zod = require_zod();
171698
172067
  var node_crypto = __require("crypto");
@@ -176279,12 +176648,12 @@ var require_storage_orchestrator = __commonJS({
176279
176648
  }
176280
176649
  });
176281
176650
 
176282
- // ../system/dist/agent-https-DJ8OzQOr.js
176283
- var require_agent_https_DJ8OzQOr = __commonJS({
176284
- "../system/dist/agent-https-DJ8OzQOr.js"(exports) {
176651
+ // ../system/dist/agent-https-D_oo3PNv.js
176652
+ var require_agent_https_D_oo3PNv = __commonJS({
176653
+ "../system/dist/agent-https-D_oo3PNv.js"(exports) {
176285
176654
  "use strict";
176286
176655
  var require_chunk = require_chunk_Cek0wNdY();
176287
- var require_dist10 = require_dist_l6zKmHEb();
176656
+ var require_dist10 = require_dist_DRL3rEfN();
176288
176657
  var require_x509_es = require_x509_es_C4cOo48M();
176289
176658
  var zod = require_zod();
176290
176659
  var node_crypto = __require("crypto");
@@ -177342,11 +177711,11 @@ var require_agent_https_DJ8OzQOr = __commonJS({
177342
177711
  }
177343
177712
  });
177344
177713
 
177345
- // ../system/dist/constants-CuO3rcw4.js
177346
- var require_constants_CuO3rcw4 = __commonJS({
177347
- "../system/dist/constants-CuO3rcw4.js"(exports) {
177714
+ // ../system/dist/constants-WF1AqoKF.js
177715
+ var require_constants_WF1AqoKF = __commonJS({
177716
+ "../system/dist/constants-WF1AqoKF.js"(exports) {
177348
177717
  "use strict";
177349
- var MESH_CA_KEY_REF = require_dist_l6zKmHEb().buildSecretRef({
177718
+ var MESH_CA_KEY_REF = require_dist_DRL3rEfN().buildSecretRef({
177350
177719
  owner: "system",
177351
177720
  scope: { kind: "global" },
177352
177721
  fieldPath: "meshCaKey"
@@ -194443,11 +194812,11 @@ var require_system_config_addon = __commonJS({
194443
194812
  [Symbol.toStringTag]: { value: "Module" }
194444
194813
  });
194445
194814
  var require_chunk = require_chunk_Cek0wNdY();
194446
- var require_dist10 = require_dist_l6zKmHEb();
194815
+ var require_dist10 = require_dist_DRL3rEfN();
194447
194816
  var require_safe_buffer$1 = require_safe_buffer_D3aoa_7X();
194448
194817
  var require_x509_es = require_x509_es_C4cOo48M();
194449
- var require_agent_https = require_agent_https_DJ8OzQOr();
194450
- var require_constants$8 = require_constants_CuO3rcw4();
194818
+ var require_agent_https = require_agent_https_D_oo3PNv();
194819
+ var require_constants$8 = require_constants_WF1AqoKF();
194451
194820
  var require_src$1 = require_src_CnAqG6EF();
194452
194821
  var zod = require_zod();
194453
194822
  var node_crypto = __require("crypto");
@@ -275499,7 +275868,7 @@ var require_winston_logging = __commonJS({
275499
275868
  [Symbol.toStringTag]: { value: "Module" }
275500
275869
  });
275501
275870
  var require_chunk = require_chunk_Cek0wNdY();
275502
- var require_dist10 = require_dist_l6zKmHEb();
275871
+ var require_dist10 = require_dist_DRL3rEfN();
275503
275872
  var require_formatter = require_formatter_DqAKDlvN();
275504
275873
  var require_log_file_read_protocol = require_log_file_read_protocol_D0WcLbIV();
275505
275874
  var node_path = __require("path");
@@ -283302,14 +283671,14 @@ var require_lib_8oZrELLm = __commonJS({
283302
283671
  }
283303
283672
  });
283304
283673
 
283305
- // ../system/dist/hub-mdns-ciao-CIno_SNn.js
283306
- var require_hub_mdns_ciao_CIno_SNn = __commonJS({
283307
- "../system/dist/hub-mdns-ciao-CIno_SNn.js"(exports) {
283674
+ // ../system/dist/hub-mdns-ciao-DUoTS6jN.js
283675
+ var require_hub_mdns_ciao_DUoTS6jN = __commonJS({
283676
+ "../system/dist/hub-mdns-ciao-DUoTS6jN.js"(exports) {
283308
283677
  "use strict";
283309
283678
  var require_chunk = require_chunk_Cek0wNdY();
283310
- var require_dist10 = require_dist_l6zKmHEb();
283679
+ var require_dist10 = require_dist_DRL3rEfN();
283311
283680
  var require_x509_es = require_x509_es_C4cOo48M();
283312
- var require_agent_https = require_agent_https_DJ8OzQOr();
283681
+ var require_agent_https = require_agent_https_D_oo3PNv();
283313
283682
  var node_crypto = __require("crypto");
283314
283683
  var _camstack_types_node = require_node();
283315
283684
  var node_fs_promises = __require("fs/promises");
@@ -284321,12 +284690,12 @@ var require_platform_key_source_Di7HrlGR = __commonJS({
284321
284690
  }
284322
284691
  });
284323
284692
 
284324
- // ../system/dist/restore-archive-B71JI-sP.js
284325
- var require_restore_archive_B71JI_sP = __commonJS({
284326
- "../system/dist/restore-archive-B71JI-sP.js"(exports) {
284693
+ // ../system/dist/restore-archive-Df6PsOHP.js
284694
+ var require_restore_archive_Df6PsOHP = __commonJS({
284695
+ "../system/dist/restore-archive-Df6PsOHP.js"(exports) {
284327
284696
  "use strict";
284328
284697
  var require_chunk = require_chunk_Cek0wNdY();
284329
- var require_dist10 = require_dist_l6zKmHEb();
284698
+ var require_dist10 = require_dist_DRL3rEfN();
284330
284699
  var zod = require_zod();
284331
284700
  var node_crypto = __require("crypto");
284332
284701
  var buffer = __require("buffer");
@@ -288821,9 +289190,9 @@ var require_restore_archive_B71JI_sP = __commonJS({
288821
289190
  }
288822
289191
  });
288823
289192
 
288824
- // ../system/dist/coral-accelerators-oUXmmbc2.js
288825
- var require_coral_accelerators_oUXmmbc2 = __commonJS({
288826
- "../system/dist/coral-accelerators-oUXmmbc2.js"(exports) {
289193
+ // ../system/dist/coral-accelerators-Cri32b7q.js
289194
+ var require_coral_accelerators_Cri32b7q = __commonJS({
289195
+ "../system/dist/coral-accelerators-Cri32b7q.js"(exports) {
288827
289196
  "use strict";
288828
289197
  var require_chunk = require_chunk_Cek0wNdY();
288829
289198
  var _camstack_types_node = require_node();
@@ -288846,9 +289215,9 @@ var require_coral_accelerators_oUXmmbc2 = __commonJS({
288846
289215
  override: process.env[_camstack_types_node.EDGETPU_LIB_ENV]
288847
289216
  };
288848
289217
  }
288849
- function countUsbCorals(fsx) {
289218
+ function countUsbCorals(devices, fsx) {
288850
289219
  let count = 0;
288851
- for (const device of fsx.listUsbDevices()) {
289220
+ for (const device of devices) {
288852
289221
  const vendor = fsx.readUsbAttr(device, "idVendor").toLowerCase();
288853
289222
  const product = fsx.readUsbAttr(device, "idProduct").toLowerCase();
288854
289223
  if (CORAL_USB_IDS.some((id) => id.vendor === vendor && id.product === product)) count++;
@@ -288882,7 +289251,12 @@ var require_coral_accelerators_oUXmmbc2 = __commonJS({
288882
289251
  };
288883
289252
  }
288884
289253
  function classifyCoralAccelerator(fsx, delegateHost = currentDelegateHost()) {
288885
- const usb = countUsbCorals(fsx);
289254
+ return detectCoralAccelerator(fsx, delegateHost).info;
289255
+ }
289256
+ function detectCoralAccelerator(fsx, delegateHost = currentDelegateHost()) {
289257
+ const usbDevices = fsx.listUsbDevices();
289258
+ const usbBus = usbDevices !== null && usbDevices.length > 0 ? "observed" : "unobservable";
289259
+ const usb = usbDevices === null ? 0 : countUsbCorals(usbDevices, fsx);
288886
289260
  const pci = classifyPci(fsx);
288887
289261
  const units = [];
288888
289262
  if (usb > 0) units.push({
@@ -288904,25 +289278,44 @@ var require_coral_accelerators_oUXmmbc2 = __commonJS({
288904
289278
  count: pci.notAccessible,
288905
289279
  reason: "device-not-accessible"
288906
289280
  });
288907
- if (units.length > 0 || unavailable.length > 0) return unavailable.length > 0 ? {
288908
- type: "coral-edgetpu",
288909
- units,
288910
- unavailable
288911
- } : {
288912
- type: "coral-edgetpu",
288913
- units
289281
+ if (units.length > 0 || unavailable.length > 0) return {
289282
+ info: unavailable.length > 0 ? {
289283
+ type: "coral-edgetpu",
289284
+ units,
289285
+ unavailable
289286
+ } : {
289287
+ type: "coral-edgetpu",
289288
+ units
289289
+ },
289290
+ usbBus,
289291
+ decidedBy: usb > 0 ? "usb-ids" : "pci"
288914
289292
  };
288915
- if ((0, _camstack_types_node.chooseEdgetpuDelegate)({
289293
+ if (usbBus === "unobservable" && (0, _camstack_types_node.chooseEdgetpuDelegate)({
288916
289294
  ...delegateHost,
288917
289295
  exists: (p) => fsx.exists(p)
288918
289296
  }) !== null) return {
288919
- type: "coral-edgetpu",
288920
- units: [{
288921
- bus: "usb",
288922
- count: 1
288923
- }]
289297
+ info: {
289298
+ type: "coral-edgetpu",
289299
+ units: [{
289300
+ bus: "usb",
289301
+ count: 1
289302
+ }]
289303
+ },
289304
+ usbBus,
289305
+ decidedBy: "delegate-fallback"
288924
289306
  };
288925
- return null;
289307
+ return {
289308
+ info: null,
289309
+ usbBus,
289310
+ decidedBy: "none"
289311
+ };
289312
+ }
289313
+ function safeListOrNull(dir) {
289314
+ try {
289315
+ return node_fs.default.readdirSync(dir);
289316
+ } catch {
289317
+ return null;
289318
+ }
288926
289319
  }
288927
289320
  function safeList(dir) {
288928
289321
  try {
@@ -288939,7 +289332,7 @@ var require_coral_accelerators_oUXmmbc2 = __commonJS({
288939
289332
  }
288940
289333
  }
288941
289334
  var nodeCoralFs = {
288942
- listUsbDevices: () => safeList("/sys/bus/usb/devices"),
289335
+ listUsbDevices: () => safeListOrNull("/sys/bus/usb/devices"),
288943
289336
  readUsbAttr: (device, attr) => safeRead(`/sys/bus/usb/devices/${device}/${attr}`),
288944
289337
  listPciDevices: () => safeList("/sys/bus/pci/devices"),
288945
289338
  readPciAttr: (device, attr) => safeRead(`/sys/bus/pci/devices/${device}/${attr}`),
@@ -288966,6 +289359,12 @@ var require_coral_accelerators_oUXmmbc2 = __commonJS({
288966
289359
  return classifyCoralAccelerator;
288967
289360
  }
288968
289361
  });
289362
+ Object.defineProperty(exports, "detectCoralAccelerator", {
289363
+ enumerable: true,
289364
+ get: function() {
289365
+ return detectCoralAccelerator;
289366
+ }
289367
+ });
288969
289368
  Object.defineProperty(exports, "nodeCoralFs", {
288970
289369
  enumerable: true,
288971
289370
  get: function() {
@@ -295567,8 +295966,8 @@ var require_dist3 = __commonJS({
295567
295966
  "use strict";
295568
295967
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
295569
295968
  var require_chunk = require_chunk_Cek0wNdY();
295570
- var require_dist10 = require_dist_l6zKmHEb();
295571
- var require_device_ca = require_device_ca_CGcaSheI();
295969
+ var require_dist10 = require_dist_DRL3rEfN();
295970
+ var require_device_ca = require_device_ca_R4MZZtc0();
295572
295971
  var require_read_all_rows$1 = require_read_all_rows_CVoO1ONM();
295573
295972
  var require_builtins_alerts_alerts_addon = require_alerts_addon();
295574
295973
  require_alerts();
@@ -295593,22 +295992,22 @@ var require_dist3 = __commonJS({
295593
295992
  var require_builtins_native_metrics_native_metrics_addon = require_native_metrics_addon();
295594
295993
  var require_builtins_device_history_device_history_addon = require_device_history_addon();
295595
295994
  var require_builtins_sqlite_storage_filesystem_storage_addon = require_filesystem_storage_addon();
295596
- var require_wal_maintenance = require_wal_maintenance_B6LSQ_OZ();
295995
+ var require_wal_maintenance = require_wal_maintenance_DYvzJiuK();
295597
295996
  var require_builtins_sqlite_storage_sqlite_settings_addon = require_sqlite_settings_addon();
295598
295997
  var require_builtins_sqlite_storage_index = require_sqlite_storage();
295599
295998
  var require_builtins_storage_orchestrator_storage_orchestrator_addon = require_storage_orchestrator_addon();
295600
295999
  require_storage_orchestrator();
295601
- var require_agent_https = require_agent_https_DJ8OzQOr();
295602
- var require_constants15 = require_constants_CuO3rcw4();
296000
+ var require_agent_https = require_agent_https_D_oo3PNv();
296001
+ var require_constants15 = require_constants_WF1AqoKF();
295603
296002
  var require_builtins_system_config_system_config_addon = require_system_config_addon();
295604
296003
  require_system_config();
295605
296004
  var require_builtins_winston_logging_index = require_winston_logging();
295606
296005
  var require_custom_action_registry = require_custom_action_registry_MRyes5j0();
295607
- var require_hub_mdns_ciao = require_hub_mdns_ciao_CIno_SNn();
296006
+ var require_hub_mdns_ciao = require_hub_mdns_ciao_DUoTS6jN();
295608
296007
  var require_platform_key_source = require_platform_key_source_Di7HrlGR();
295609
296008
  var require_resource_monitor = require_resource_monitor_mio9dSCT();
295610
- var require_restore_archive = require_restore_archive_B71JI_sP();
295611
- var require_coral_accelerators = require_coral_accelerators_oUXmmbc2();
296009
+ var require_restore_archive = require_restore_archive_Df6PsOHP();
296010
+ var require_coral_accelerators = require_coral_accelerators_Cri32b7q();
295612
296011
  var zod = require_zod();
295613
296012
  var node_crypto = __require("crypto");
295614
296013
  node_crypto = require_chunk.__toESM(node_crypto);
@@ -319051,7 +319450,7 @@ var require_dist4 = __commonJS({
319051
319450
  "../types/dist/index.js"(exports) {
319052
319451
  "use strict";
319053
319452
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
319054
- var require_sleep = require_sleep_CD18m9HF();
319453
+ var require_sleep = require_sleep_DVovpDRU();
319055
319454
  var require_event_category = require_event_category_7AwNbPjm();
319056
319455
  var require_composition = require_composition_eoYYVfOp();
319057
319456
  var require_node_color2 = require_node_color();
@@ -324553,7 +324952,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
324553
324952
  zod.z.literal(4)
324554
324953
  ]).optional(),
324555
324954
  tab: zod.z.string().optional(),
324556
- location: zod.z.enum(["settings", "top-tab"]).optional(),
324955
+ location: zod.z.enum([
324956
+ "settings",
324957
+ "top-tab",
324958
+ "hero"
324959
+ ]).optional(),
324557
324960
  order: zod.z.number().optional(),
324558
324961
  fields: zod.z.array(zod.z.any())
324559
324962
  });
@@ -329899,6 +330302,48 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
329899
330302
  approxBytes: zod.z.number().nullable(),
329900
330303
  countedAt: zod.z.number().nullable()
329901
330304
  });
330305
+ var DeviceHistoryOrphanReasonSchema = zod.z.enum(["device-gone", "undeclared"]);
330306
+ var TierRowsSchema = zod.z.object({
330307
+ raw: zod.z.number().int(),
330308
+ "5m": zod.z.number().int(),
330309
+ "1h": zod.z.number().int(),
330310
+ "1d": zod.z.number().int()
330311
+ });
330312
+ var OrphanSeriesSchema = zod.z.object({
330313
+ seriesId: zod.z.number().int(),
330314
+ deviceId: zod.z.number().int(),
330315
+ key: zod.z.string(),
330316
+ scope: zod.z.string(),
330317
+ /** The metric's display name; `null` when its metric row is gone too. */
330318
+ displayName: zod.z.string().nullable(),
330319
+ reason: DeviceHistoryOrphanReasonSchema,
330320
+ /** `device-gone`: since when the device has been absent; `null` otherwise. */
330321
+ missingSince: zod.z.number().nullable(),
330322
+ firstTs: zod.z.number(),
330323
+ lastTs: zod.z.number(),
330324
+ rows: TierRowsSchema
330325
+ });
330326
+ var DEVICE_HISTORY_ORPHAN_LIST_MAX = 200;
330327
+ var DEVICE_HISTORY_ORPHAN_DELETE_MAX = 50;
330328
+ var DeviceHistoryOrphanDeleteRefusalSchema = zod.z.enum([
330329
+ "not-found",
330330
+ "not-orphan",
330331
+ "failed"
330332
+ ]);
330333
+ var OrphanDeleteEntrySchema = zod.z.discriminatedUnion("ok", [zod.z.object({
330334
+ seriesId: zod.z.number().int(),
330335
+ ok: zod.z.literal(true),
330336
+ deleted: TierRowsSchema
330337
+ }), zod.z.object({
330338
+ seriesId: zod.z.number().int(),
330339
+ ok: zod.z.literal(false),
330340
+ reason: DeviceHistoryOrphanDeleteRefusalSchema
330341
+ })]);
330342
+ var OrphanListSchema = zod.z.object({
330343
+ series: zod.z.array(OrphanSeriesSchema).readonly(),
330344
+ /** More orphans exist than {@link DEVICE_HISTORY_ORPHAN_LIST_MAX}. */
330345
+ truncated: zod.z.boolean()
330346
+ });
329902
330347
  var DeviceHistoryStatusSchema = zod.z.object({
329903
330348
  startedAt: zod.z.number(),
329904
330349
  /** The store is declared, series loaded and the first reconcile has answered. */
@@ -330040,7 +330485,24 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
330040
330485
  kind: "mutation",
330041
330486
  auth: "admin"
330042
330487
  }),
330043
- getStatus: require_sleep.method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" })
330488
+ getStatus: require_sleep.method(zod.z.object({}), DeviceHistoryStatusSchema, { auth: "admin" }),
330489
+ /**
330490
+ * Device series no longer linked to a device (D917): the device is absent
330491
+ * from the listings, or the key is declared by no capability. REPORTED,
330492
+ * never purged by this judgement; each entry counts its rows per tier
330493
+ * (a primary-key range per series). Ordered by device, key, scope.
330494
+ */
330495
+ listOrphanSeries: require_sleep.method(zod.z.object({}), OrphanListSchema, { auth: "admin" }),
330496
+ /**
330497
+ * Delete orphan series by id. Each id is re-judged NOW: a series that is
330498
+ * not orphan (its device came back) is refused `not-orphan`, an unknown id
330499
+ * `not-found`. Deletes tier by tier in the retention slices, then the
330500
+ * series row; one entry per id, in order.
330501
+ */
330502
+ deleteOrphanSeries: require_sleep.method(zod.z.object({ seriesIds: zod.z.array(zod.z.number().int()).min(1).max(50) }), zod.z.object({ results: zod.z.array(OrphanDeleteEntrySchema).readonly() }), {
330503
+ kind: "mutation",
330504
+ auth: "admin"
330505
+ })
330044
330506
  }
330045
330507
  };
330046
330508
  var DeviceCaStatusSchema = zod.z.discriminatedUnion("state", [
@@ -364802,6 +365264,19 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
364802
365264
  /** Whether the node's live probe reports the device as usable right now. */
364803
365265
  available: zod.z.boolean(),
364804
365266
  /**
365267
+ * Is the device on the node at all (D919)? `present` — the live probe
365268
+ * reported it; `absent` — the probe ANSWERED and did not report it, so only
365269
+ * its stored settings remain (unplugged, moved to another node): never
365270
+ * eligible, whatever its stored `enabled`, and forgettable with
365271
+ * `forgetInferenceDevice`; `unknown` — the probe did not answer, nothing is
365272
+ * claimed (D315). Optional only for an orchestrator older than the field.
365273
+ */
365274
+ presence: zod.z.enum([
365275
+ "present",
365276
+ "absent",
365277
+ "unknown"
365278
+ ]).optional(),
365279
+ /**
364805
365280
  * Whether this device participates in dispatch. AUTO default (spec C2):
364806
365281
  * accelerators are opt-OUT (a discovered NPU/iGPU/Coral/ANE with no stored
364807
365282
  * entry is `true`); CPU is opt-IN (`false` by default — the fallback pool,
@@ -365200,6 +365675,20 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
365200
365675
  */
365201
365676
  getNodeInferenceDevices: require_sleep.method(zod.z.object({ nodeId: zod.z.string() }), NodeInferenceDevicesSchema),
365202
365677
  /**
365678
+ * Delete ONE stored device entry of a node — the "forget" of a device the
365679
+ * node no longer has (D919). Refused unless the node's probe ANSWERED and
365680
+ * did not report the key: a present device is switched off, not forgotten,
365681
+ * and an unreachable node proves nothing about its hardware. The rest of
365682
+ * the node's map is kept as stored.
365683
+ */
365684
+ forgetInferenceDevice: require_sleep.method(zod.z.object({
365685
+ agentNodeId: zod.z.string(),
365686
+ deviceKey: zod.z.string()
365687
+ }), zod.z.object({ success: zod.z.literal(true) }), {
365688
+ kind: "mutation",
365689
+ auth: "admin"
365690
+ }),
365691
+ /**
365203
365692
  * Reset one node's pipeline to its hardware-aware defaults — the HONEST
365204
365693
  * reset (replaces the executor's legacy `resetToDefault`, which cleared a
365205
365694
  * dead persisted step-tree seed nothing in the live path read):
@@ -370390,6 +370879,41 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370390
370879
  * whether persisting is worth a SQLite commit. */
370391
370880
  volatileStateFields: ["lastChangedAt"]
370392
370881
  };
370882
+ var ConsumableKindSchema = zod.z.enum([
370883
+ "wear",
370884
+ "supply",
370885
+ "waste"
370886
+ ]);
370887
+ var ConsumableFormSchema = zod.z.enum([
370888
+ "part",
370889
+ "tank",
370890
+ "product",
370891
+ "bin"
370892
+ ]);
370893
+ var ConsumableStatusSchema = zod.z.enum([
370894
+ "ok",
370895
+ "low",
370896
+ "empty",
370897
+ "full",
370898
+ "replace"
370899
+ ]);
370900
+ var ConsumableLevelSchema = zod.z.enum([
370901
+ "ok",
370902
+ "low",
370903
+ "empty",
370904
+ "full"
370905
+ ]);
370906
+ function consumableKindOf(item) {
370907
+ return item.kind ?? "wear";
370908
+ }
370909
+ var FORM_OF_KIND = {
370910
+ wear: "part",
370911
+ waste: "bin",
370912
+ supply: "product"
370913
+ };
370914
+ function consumableFormOf(item) {
370915
+ return item.form ?? FORM_OF_KIND[consumableKindOf(item)];
370916
+ }
370393
370917
  var ConsumableRemainingUnitSchema = zod.z.enum([
370394
370918
  "days",
370395
370919
  "hours",
@@ -370404,10 +370928,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370404
370928
  key: zod.z.string().min(1),
370405
370929
  /** Display name. */
370406
370930
  label: zod.z.string().min(1),
370407
- /** Remaining life % when known (0..100). */
370931
+ /** Remaining life % (`wear`) or fill % (`supply`, `waste`) when known (0..100). */
370408
370932
  level: zod.z.number().min(0).max(100).nullable(),
370409
- /** Discrete state when known (binary mode). */
370410
- status: zod.z.enum(["ok", "replace"]).nullable(),
370933
+ /** The provider's discrete verdict when known (binary mode). */
370934
+ status: ConsumableStatusSchema.nullable(),
370935
+ /** What the item is — decides whether low or full is bad. Absent = `wear`. */
370936
+ kind: ConsumableKindSchema.optional(),
370937
+ /** How the item is drawn (part, tank, product, bin). Absent = derived from `kind`. */
370938
+ form: ConsumableFormSchema.optional(),
370411
370939
  /** Ms epoch of the last replace, when known. */
370412
370940
  lastResetAt: zod.z.number().nullable(),
370413
370941
  /** Whether `reset()` is meaningful for this item. */
@@ -370430,7 +370958,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370430
370958
  kind: "widget",
370431
370959
  widgetId: "host/consumables-panel",
370432
370960
  tab: "consumables",
370433
- topTab: true,
370961
+ placement: "hero",
370434
370962
  label: "Consumables",
370435
370963
  order: 5
370436
370964
  } },
@@ -370492,9 +371020,37 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370492
371020
  */
370493
371021
  durability: "restored",
370494
371022
  /**
370495
- * History (D850): none — `items` is an array keyed by runtime ids; a history path never enters it.
371023
+ * History (D850, D917): one series per (item field, item `key`) — the item key
371024
+ * is the series scope, the item `label` names it. A vacuum tank's fill, a
371025
+ * brush's remaining life and a dishwasher's salt are each recorded the moment
371026
+ * their provider reports them; an item that appears later gets its series.
370496
371027
  */
370497
- history: "none",
371028
+ history: {
371029
+ fields: {},
371030
+ items: {
371031
+ path: "items",
371032
+ key: "key",
371033
+ label: "label",
371034
+ fields: {
371035
+ level: {
371036
+ kind: "gauge",
371037
+ unit: "%",
371038
+ deadband: 2,
371039
+ displayName: "Level"
371040
+ },
371041
+ status: {
371042
+ kind: "enum",
371043
+ displayName: "Status"
371044
+ },
371045
+ "remaining.value": {
371046
+ kind: "gauge",
371047
+ unitFrom: "remaining.unit",
371048
+ deadband: 1,
371049
+ displayName: "Remaining"
371050
+ }
371051
+ }
371052
+ }
371053
+ },
370498
371054
  /** Clocks, not content: a re-read that changed no item costs no commit. */
370499
371055
  volatileStateFields: ["lastChangedAt", "lastFetchedAt"]
370500
371056
  };
@@ -370855,11 +371411,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370855
371411
  "offline",
370856
371412
  "description-missing"
370857
371413
  ]);
370858
- var DishwasherConsumableLevelSchema = zod.z.enum([
370859
- "ok",
370860
- "low",
370861
- "empty"
370862
- ]);
370863
371414
  var DishwasherStatusSchema = zod.z.object({
370864
371415
  availability: DishwasherAvailabilitySchema,
370865
371416
  /** Coarse state for compact surfaces; `unavailable` whenever the appliance state is not known. */
@@ -370892,8 +371443,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
370892
371443
  powerOn: zod.z.boolean().nullable(),
370893
371444
  /** The raw power state (`On`, `Off`, `Standby`, …); null when not known. */
370894
371445
  powerState: zod.z.string().nullable(),
370895
- saltLevel: DishwasherConsumableLevelSchema.nullable(),
370896
- rinseAidLevel: DishwasherConsumableLevelSchema.nullable(),
370897
371446
  /**
370898
371447
  * True while the appliance asks for its filter to be cleaned (smart-filter, filter-system or
370899
371448
  * combined care-and-filter reminder raised); false when every such reminder reads clear; null
@@ -373628,7 +374177,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
373628
374177
  enabled: zod.z.boolean()
373629
374178
  }), zod.z.void(), {
373630
374179
  kind: "mutation",
373631
- auth: "admin"
374180
+ auth: "protected"
373632
374181
  }) },
373633
374182
  events: { onMotionTriggerChanged: { data: zod.z.object({
373634
374183
  deviceId: zod.z.number(),
@@ -374310,6 +374859,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
374310
374859
  cpuModel: zod.z.string().nullable(),
374311
374860
  cpuCores: zod.z.number(),
374312
374861
  memoryMB: zod.z.number(),
374862
+ /**
374863
+ * The inference RUNTIMES installed on the node (`pythonRuntimes`: onnx,
374864
+ * openvino, edgetpu …) — not its devices. An installed runtime says nothing
374865
+ * about whether its hardware is there (a Coral delegate stays installed
374866
+ * after the stick leaves, D919); the live device set is
374867
+ * `pipelineOrchestrator.getNodeInferenceDevices`.
374868
+ */
374313
374869
  engines: zod.z.array(zod.z.string()).readonly(),
374314
374870
  isHub: zod.z.boolean(),
374315
374871
  isOnline: zod.z.boolean(),
@@ -375499,6 +376055,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
375499
376055
  format: ModelFormatSchema,
375500
376056
  reason: zod.z.string()
375501
376057
  });
376058
+ var AcceleratorRescanSchema = zod.z.object({
376059
+ devices: zod.z.array(InferenceDeviceDescriptorSchema).readonly(),
376060
+ appeared: zod.z.array(zod.z.string()).readonly(),
376061
+ disappeared: zod.z.array(zod.z.string()).readonly(),
376062
+ usbBus: zod.z.enum(["observed", "unobservable"]),
376063
+ decidedBy: zod.z.enum([
376064
+ "usb-ids",
376065
+ "pci",
376066
+ "delegate-fallback",
376067
+ "none"
376068
+ ])
376069
+ });
375502
376070
  var platformProbeCapability = {
375503
376071
  name: "platform-probe",
375504
376072
  scope: "system",
@@ -375507,7 +376075,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
375507
376075
  getCapabilities: require_sleep.method(zod.z.void(), PlatformCapabilitiesSchema),
375508
376076
  getHardware: require_sleep.method(zod.z.void(), HardwareInfoSchema),
375509
376077
  resolveInferenceConfig: require_sleep.method(zod.z.object({ requirements: zod.z.array(ModelRequirementSchema).readonly() }), ResolvedInferenceConfigSchema),
375510
- resolveHwAccel: require_sleep.method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema)
376078
+ resolveHwAccel: require_sleep.method(zod.z.object({ prefer: HwAccelBackendInputSchema }), HwAccelResolutionSchema),
376079
+ /**
376080
+ * Re-probe this node's accelerators NOW (Coral USB ids + PCIe apex units;
376081
+ * `/sys` and `/dev` reads, no model load) and rebuild the device list that
376082
+ * `getCapabilities` serves. The same re-probe runs every 60 s on its own;
376083
+ * this is the operator's "rescan inference devices". Target a node with the
376084
+ * `{ nodeId }` routing input, like `getCapabilities`.
376085
+ */
376086
+ rescanAccelerators: require_sleep.method(zod.z.void(), AcceleratorRescanSchema, {
376087
+ kind: "mutation",
376088
+ auth: "admin"
376089
+ })
375511
376090
  }
375512
376091
  };
375513
376092
  var PowerMeterStatusSchema = zod.z.object({
@@ -379587,16 +380166,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379587
380166
  "drying",
379588
380167
  "error"
379589
380168
  ]);
379590
- var TankStatusSchema = zod.z.object({
379591
- /** Numeric fill 0..100 when the hardware reports a percentage; null otherwise. */
379592
- level: zod.z.number().min(0).max(100).nullable(),
379593
- /** Discrete state when the hardware is binary-mode; null otherwise. */
379594
- status: zod.z.enum([
379595
- "ok",
379596
- "low",
379597
- "full"
379598
- ]).nullable()
379599
- });
379600
380169
  var VacuumControlStatusSchema = zod.z.object({
379601
380170
  /** Lifecycle state of the vacuum. */
379602
380171
  state: VacuumStateSchema,
@@ -379608,14 +380177,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379608
380177
  fanSpeed: zod.z.string().nullable(),
379609
380178
  /** Speed tokens the hardware accepts — drives the UI selector. */
379610
380179
  availableFanSpeeds: zod.z.array(zod.z.string()),
379611
- /** Clean-water (mop) tank. Null when the hardware has no clean-water tank. */
379612
- cleanWater: TankStatusSchema.nullable(),
379613
- /** Dirty-water (recovery) tank. Null when the hardware has no dirty-water tank. */
379614
- dirtyWater: TankStatusSchema.nullable(),
379615
- /** Detergent tank. Null when the hardware has no detergent tank. */
379616
- detergent: TankStatusSchema.nullable(),
379617
- /** Dust bin. Null when the hardware has no dust bin. */
379618
- dustBin: TankStatusSchema.nullable(),
379619
380180
  /** 0..100 cleaning-completion percentage of the current task, or null. */
379620
380181
  progressPercent: zod.z.number().min(0).max(100).nullable(),
379621
380182
  /** Current error code (0 / null = no error). */
@@ -379677,35 +380238,14 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
379677
380238
  */
379678
380239
  durability: "session",
379679
380240
  /**
379680
- * History (D850): recorded series. Tank levels are fixed-object paths (`cleanWater.level`, ...).
380241
+ * History (D850): recorded series. The tanks moved to `consumables` (D915), whose
380242
+ * items are keyed by runtime ids and record no history.
379681
380243
  */
379682
- history: { fields: {
379683
- batteryLevel: {
379684
- kind: "gauge",
379685
- unit: "%",
379686
- deadband: 2
379687
- },
379688
- "cleanWater.level": {
379689
- kind: "gauge",
379690
- unit: "%",
379691
- deadband: 2
379692
- },
379693
- "dirtyWater.level": {
379694
- kind: "gauge",
379695
- unit: "%",
379696
- deadband: 2
379697
- },
379698
- "detergent.level": {
379699
- kind: "gauge",
379700
- unit: "%",
379701
- deadband: 2
379702
- },
379703
- "dustBin.level": {
379704
- kind: "gauge",
379705
- unit: "%",
379706
- deadband: 2
379707
- }
379708
- } }
380244
+ history: { fields: { batteryLevel: {
380245
+ kind: "gauge",
380246
+ unit: "%",
380247
+ deadband: 2
380248
+ } } }
379709
380249
  };
379710
380250
  var ValveStateSchema = zod.z.enum([
379711
380251
  "open",
@@ -385339,6 +385879,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
385339
385879
  addonId: null,
385340
385880
  access: "create"
385341
385881
  },
385882
+ "deviceHistory.deleteOrphanSeries": {
385883
+ capName: "device-history",
385884
+ capScope: "system",
385885
+ addonId: null,
385886
+ access: "delete"
385887
+ },
385342
385888
  "deviceHistory.getDeviceEnabled": {
385343
385889
  capName: "device-history",
385344
385890
  capScope: "system",
@@ -385357,6 +385903,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
385357
385903
  addonId: null,
385358
385904
  access: "view"
385359
385905
  },
385906
+ "deviceHistory.listOrphanSeries": {
385907
+ capName: "device-history",
385908
+ capScope: "system",
385909
+ addonId: null,
385910
+ access: "view"
385911
+ },
385360
385912
  "deviceHistory.listSeries": {
385361
385913
  capName: "device-history",
385362
385914
  capScope: "system",
@@ -388981,6 +389533,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
388981
389533
  addonId: null,
388982
389534
  access: "delete"
388983
389535
  },
389536
+ "pipelineOrchestrator.forgetInferenceDevice": {
389537
+ capName: "pipeline-orchestrator",
389538
+ capScope: "system",
389539
+ addonId: null,
389540
+ access: "delete"
389541
+ },
388984
389542
  "pipelineOrchestrator.getAgentLoad": {
388985
389543
  capName: "pipeline-orchestrator",
388986
389544
  capScope: "system",
@@ -389557,6 +390115,12 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
389557
390115
  addonId: null,
389558
390116
  access: "view"
389559
390117
  },
390118
+ "platformProbe.rescanAccelerators": {
390119
+ capName: "platform-probe",
390120
+ capScope: "system",
390121
+ addonId: null,
390122
+ access: "create"
390123
+ },
389560
390124
  "platformProbe.resolveHwAccel": {
389561
390125
  capName: "platform-probe",
389562
390126
  capScope: "system",
@@ -398884,33 +399448,11 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398884
399448
  unit: "\xB0C",
398885
399449
  deadband: 0.1
398886
399450
  } },
398887
- "vacuum-control": {
398888
- "batteryLevel": {
398889
- kind: "gauge",
398890
- unit: "%",
398891
- deadband: 2
398892
- },
398893
- "cleanWater.level": {
398894
- kind: "gauge",
398895
- unit: "%",
398896
- deadband: 2
398897
- },
398898
- "dirtyWater.level": {
398899
- kind: "gauge",
398900
- unit: "%",
398901
- deadband: 2
398902
- },
398903
- "detergent.level": {
398904
- kind: "gauge",
398905
- unit: "%",
398906
- deadband: 2
398907
- },
398908
- "dustBin.level": {
398909
- kind: "gauge",
398910
- unit: "%",
398911
- deadband: 2
398912
- }
398913
- },
399451
+ "vacuum-control": { "batteryLevel": {
399452
+ kind: "gauge",
399453
+ unit: "%",
399454
+ deadband: 2
399455
+ } },
398914
399456
  "valve": { "position": {
398915
399457
  kind: "gauge",
398916
399458
  unit: "%",
@@ -398952,12 +399494,41 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
398952
399494
  }
398953
399495
  }
398954
399496
  };
399497
+ var HISTORY_ITEM_CATALOG = { "consumables": {
399498
+ path: "items",
399499
+ key: "key",
399500
+ label: "label",
399501
+ fields: {
399502
+ "level": {
399503
+ kind: "gauge",
399504
+ unit: "%",
399505
+ deadband: 2,
399506
+ displayName: "Level"
399507
+ },
399508
+ "status": {
399509
+ kind: "enum",
399510
+ displayName: "Status",
399511
+ values: [
399512
+ "ok",
399513
+ "low",
399514
+ "empty",
399515
+ "full",
399516
+ "replace"
399517
+ ]
399518
+ },
399519
+ "remaining.value": {
399520
+ kind: "gauge",
399521
+ unitFrom: "remaining.unit",
399522
+ deadband: 1,
399523
+ displayName: "Remaining"
399524
+ }
399525
+ }
399526
+ } };
398955
399527
  var HISTORY_NONE_CAPS = [
398956
399528
  "audio-metrics",
398957
399529
  "automation-control",
398958
399530
  "camera-streams",
398959
399531
  "color",
398960
- "consumables",
398961
399532
  "control",
398962
399533
  "day-night",
398963
399534
  "device-discovery",
@@ -399242,7 +399813,9 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
399242
399813
  declareSeries: (input) => dispatch("deviceHistory", "declareSeries", "mutation", input),
399243
399814
  record: (input) => dispatch("deviceHistory", "record", "mutation", input),
399244
399815
  recordBuckets: (input) => dispatch("deviceHistory", "recordBuckets", "mutation", input),
399245
- getStatus: (input) => dispatch("deviceHistory", "getStatus", "query", input)
399816
+ getStatus: (input) => dispatch("deviceHistory", "getStatus", "query", input),
399817
+ listOrphanSeries: (input) => dispatch("deviceHistory", "listOrphanSeries", "query", input),
399818
+ deleteOrphanSeries: (input) => dispatch("deviceHistory", "deleteOrphanSeries", "mutation", input)
399246
399819
  },
399247
399820
  deviceManager: {
399248
399821
  allocateDeviceId: (input) => dispatch("deviceManager", "allocateDeviceId", "mutation", input),
@@ -399582,6 +400155,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
399582
400155
  setAgentReachableHost: (input) => dispatch("pipelineOrchestrator", "setAgentReachableHost", "mutation", input),
399583
400156
  setAgentInferenceDevices: (input) => dispatch("pipelineOrchestrator", "setAgentInferenceDevices", "mutation", input),
399584
400157
  getNodeInferenceDevices: (input) => dispatch("pipelineOrchestrator", "getNodeInferenceDevices", "query", input),
400158
+ forgetInferenceDevice: (input) => dispatch("pipelineOrchestrator", "forgetInferenceDevice", "mutation", input),
399585
400159
  resetNodePipelineDefaults: (input) => dispatch("pipelineOrchestrator", "resetNodePipelineDefaults", "mutation", input),
399586
400160
  planAutoTune: (input) => dispatch("pipelineOrchestrator", "planAutoTune", "query", input),
399587
400161
  applyAutoTune: (input) => dispatch("pipelineOrchestrator", "applyAutoTune", "mutation", input),
@@ -402332,17 +402906,18 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
402332
402906
  {
402333
402907
  name: "item",
402334
402908
  label: "Consumable",
402909
+ description: "The device's own name for the item: a filter, a brush, the salt, a tank.",
402335
402910
  example: "Filtro",
402336
402911
  group: "system",
402337
402912
  families: ["rule"],
402338
402913
  fields: [...RULE_TEXT],
402339
402914
  kinds: ["system"],
402340
- systemEventKinds: [...CONSUMABLE]
402915
+ systemEventKinds: [...CONSUMABLE, ...TANK]
402341
402916
  },
402342
402917
  {
402343
402918
  name: "tank",
402344
402919
  label: "Tank",
402345
- description: "Which tank: clean water, dirty water, detergent or dust bin.",
402920
+ description: "Which tank, as a stable token: cleanWater, dirtyWater, detergent, dustBin, or the device key of any other supply (salt, rinse aid).",
402346
402921
  example: "clean water",
402347
402922
  group: "system",
402348
402923
  families: ["rule"],
@@ -405190,15 +405765,22 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405190
405765
  function spareOf(c) {
405191
405766
  return c.spare ?? Number.POSITIVE_INFINITY;
405192
405767
  }
405193
- function electByShare(candidates) {
405194
- if (candidates.length === 0) return { kind: "none" };
405768
+ function loadOf(c) {
405769
+ return Math.max(c.running, 0) + Math.max(c.recent ?? 0, 0);
405770
+ }
405771
+ function scoreShares(candidates) {
405195
405772
  const shareSum = candidates.reduce((s, c) => s + Math.max(c.share, 0), 0);
405196
- const loadOf = (c) => Math.max(c.running, 0) + Math.max(c.recent ?? 0, 0);
405197
405773
  const running = candidates.reduce((s, c) => s + loadOf(c), 0);
405198
- const scored = candidates.map((candidate) => ({
405774
+ return candidates.map((candidate) => ({
405199
405775
  candidate,
405200
405776
  deficit: (shareSum > 0 ? Math.max(candidate.share, 0) / shareSum : 1 / candidates.length) * (running + 1) - loadOf(candidate)
405201
405777
  })).toSorted(byDeficit);
405778
+ }
405779
+ function electByShare(candidates) {
405780
+ if (candidates.length === 0) return { kind: "none" };
405781
+ return electFromScored(scoreShares(candidates));
405782
+ }
405783
+ function electFromScored(scored) {
405202
405784
  const turn = scored[0].candidate;
405203
405785
  if (!turn.capped && !turn.saturated) return {
405204
405786
  kind: "elected",
@@ -405222,6 +405804,42 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405222
405804
  reason: "share"
405223
405805
  };
405224
405806
  }
405807
+ var SHARE_AFFINITY_TOLERANCE_SESSIONS = 1;
405808
+ function toScoredCandidate(s) {
405809
+ return {
405810
+ key: s.candidate.key,
405811
+ share: s.candidate.share,
405812
+ running: s.candidate.running,
405813
+ recent: s.candidate.recent ?? 0,
405814
+ deficit: s.deficit
405815
+ };
405816
+ }
405817
+ function electByShareWithAffinity(candidates, previousKey, toleranceSessions = 1) {
405818
+ const scored = candidates.length === 0 ? [] : scoreShares(candidates);
405819
+ const plain = scored.length === 0 ? { kind: "none" } : electFromScored(scored);
405820
+ const turnKey = plain.kind === "elected" ? plain.key : null;
405821
+ const result = (election, sticky, affinityReason) => ({
405822
+ election,
405823
+ sticky,
405824
+ affinityReason,
405825
+ previousKey,
405826
+ turnKey,
405827
+ scored: scored.map(toScoredCandidate)
405828
+ });
405829
+ if (previousKey === null) return result(plain, false, "no-previous");
405830
+ if (turnKey === previousKey) return result(plain, true, "previous-is-turn");
405831
+ const previous = scored.find((s) => s.candidate.key === previousKey);
405832
+ if (previous === void 0) return result(plain, false, "previous-ineligible");
405833
+ if (previous.candidate.capped || previous.candidate.saturated) return result(plain, false, "previous-no-room");
405834
+ const elected = scored.find((s) => s.candidate.key === turnKey);
405835
+ if (elected === void 0) return result(plain, false, "share-off");
405836
+ if (previous.deficit >= elected.deficit - toleranceSessions - DEFICIT_EPSILON) return result({
405837
+ kind: "elected",
405838
+ key: previousKey,
405839
+ reason: "share"
405840
+ }, true, "within-one-session");
405841
+ return result(plain, false, "share-off");
405842
+ }
405225
405843
  var DEFAULT_WEIGHT = 1;
405226
405844
  function positive(value) {
405227
405845
  return value !== void 0 && value > 0 ? value : DEFAULT_WEIGHT;
@@ -405242,7 +405860,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
405242
405860
  const nodes = input.nodeIds.map((nodeId) => {
405243
405861
  const eligibleKeys = new Set(input.eligibleDevices[nodeId] ?? []);
405244
405862
  const named2 = weights.devices[nodeId] ?? {};
405245
- const keys = [.../* @__PURE__ */ new Set([...Object.keys(named2), ...eligibleKeys])].toSorted();
405863
+ const absent = new Set(input.absentDevices?.[nodeId] ?? []);
405864
+ const keys = [.../* @__PURE__ */ new Set([...Object.keys(named2), ...eligibleKeys])].filter((k) => !absent.has(k)).toSorted();
405246
405865
  const perDevice = {};
405247
405866
  for (const key of keys) perDevice[key] = positive(named2[key]);
405248
405867
  if (keys.length > 0) deviceWeights[nodeId] = perDevice;
@@ -407717,9 +408336,13 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407717
408336
  exports.ConnectionTestInputSchema = ConnectionTestInputSchema;
407718
408337
  exports.ConnectionTestOutcomeSchema = ConnectionTestOutcomeSchema;
407719
408338
  exports.ConnectivityStatusSchema = ConnectivityStatusSchema;
408339
+ exports.ConsumableFormSchema = ConsumableFormSchema;
407720
408340
  exports.ConsumableItemSchema = ConsumableItemSchema;
408341
+ exports.ConsumableKindSchema = ConsumableKindSchema;
408342
+ exports.ConsumableLevelSchema = ConsumableLevelSchema;
407721
408343
  exports.ConsumableRemainingSchema = ConsumableRemainingSchema;
407722
408344
  exports.ConsumableRemainingUnitSchema = ConsumableRemainingUnitSchema;
408345
+ exports.ConsumableStatusSchema = ConsumableStatusSchema;
407723
408346
  exports.ConsumablesStatusSchema = ConsumablesStatusSchema;
407724
408347
  exports.ContactStatusSchema = ContactStatusSchema;
407725
408348
  exports.ControlKindSchema = ControlKindSchema;
@@ -407800,6 +408423,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407800
408423
  exports.DEVICE_DO_ACTIONS = DEVICE_DO_ACTIONS;
407801
408424
  exports.DEVICE_ENROLLMENT_TTL_MS = DEVICE_ENROLLMENT_TTL_MS;
407802
408425
  exports.DEVICE_HISTORY_ENERGY_KEY = DEVICE_HISTORY_ENERGY_KEY;
408426
+ exports.DEVICE_HISTORY_ORPHAN_DELETE_MAX = DEVICE_HISTORY_ORPHAN_DELETE_MAX;
408427
+ exports.DEVICE_HISTORY_ORPHAN_LIST_MAX = DEVICE_HISTORY_ORPHAN_LIST_MAX;
407803
408428
  exports.DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS = DEVICE_HISTORY_QUERY_MANY_MAX_ITEMS;
407804
408429
  exports.DEVICE_NONCE_TTL_MS = DEVICE_NONCE_TTL_MS;
407805
408430
  exports.DEVICE_PROFILES = DEVICE_PROFILES;
@@ -407859,6 +408484,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407859
408484
  exports.DeviceExportUnexposeInputSchema = UnexposeInputSchema;
407860
408485
  exports.DeviceFeature = require_err_msg.DeviceFeature;
407861
408486
  exports.DeviceHistoryKindSchema = DeviceHistoryKindSchema;
408487
+ exports.DeviceHistoryOrphanDeleteRefusalSchema = DeviceHistoryOrphanDeleteRefusalSchema;
408488
+ exports.DeviceHistoryOrphanReasonSchema = DeviceHistoryOrphanReasonSchema;
407862
408489
  exports.DeviceHistoryProducerKindSchema = DeviceHistoryProducerKindSchema;
407863
408490
  exports.DeviceHistoryRefusalReasonSchema = DeviceHistoryRefusalReasonSchema;
407864
408491
  exports.DeviceHistoryResolutionSchema = DeviceHistoryResolutionSchema;
@@ -407891,7 +408518,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
407891
408518
  exports.DiscoveredTargetSchema = DiscoveredTargetSchema;
407892
408519
  exports.DishwasherAccessSchema = DishwasherAccessSchema;
407893
408520
  exports.DishwasherAvailabilitySchema = DishwasherAvailabilitySchema;
407894
- exports.DishwasherConsumableLevelSchema = DishwasherConsumableLevelSchema;
407895
408521
  exports.DishwasherOperationStateSchema = DishwasherOperationStateSchema;
407896
408522
  exports.DishwasherOptionDescriptorSchema = DishwasherOptionDescriptorSchema;
407897
408523
  exports.DishwasherOptionsSchema = DishwasherOptionsSchema;
@@ -408052,6 +408678,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
408052
408678
  exports.HISTORY_BUCKET_MS = HISTORY_BUCKET_MS;
408053
408679
  exports.HISTORY_CATALOG = HISTORY_CATALOG;
408054
408680
  exports.HISTORY_DEFAULT_MIN_INTERVAL_MS = require_sleep.HISTORY_DEFAULT_MIN_INTERVAL_MS;
408681
+ exports.HISTORY_ITEM_CATALOG = HISTORY_ITEM_CATALOG;
408055
408682
  exports.HISTORY_NONE_CAPS = HISTORY_NONE_CAPS;
408056
408683
  exports.HISTORY_UNIT_LABELS = require_sleep.HISTORY_UNIT_LABELS;
408057
408684
  exports.HOURS_PER_DAY = HOURS_PER_DAY;
@@ -408940,6 +409567,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
408940
409567
  exports.SERVER_NAME_MAX_LENGTH = SERVER_NAME_MAX_LENGTH;
408941
409568
  exports.SERVER_NAME_SETTING_KEY = SERVER_NAME_SETTING_KEY;
408942
409569
  exports.SESSION_PLACEMENT_SETTING_KEY = SESSION_PLACEMENT_SETTING_KEY;
409570
+ exports.SHARE_AFFINITY_TOLERANCE_SESSIONS = SHARE_AFFINITY_TOLERANCE_SESSIONS;
408943
409571
  exports.SHARE_SOURCE_SETTING_KEY = SHARE_SOURCE_SETTING_KEY;
408944
409572
  exports.SHARE_VIEW_KINDS = require_sleep.SHARE_VIEW_KINDS;
408945
409573
  exports.SHARE_VIEW_MAX_DEVICES = SHARE_VIEW_MAX_DEVICES;
@@ -409104,7 +409732,6 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409104
409732
  exports.TRANSCODE_DOWN_MAX_BITRATE_KBPS = TRANSCODE_DOWN_MAX_BITRATE_KBPS;
409105
409733
  exports.TRANSCODE_DOWN_MAX_HEIGHT = TRANSCODE_DOWN_MAX_HEIGHT;
409106
409734
  exports.TamperStatusSchema = TamperStatusSchema;
409107
- exports.TankStatusSchema = TankStatusSchema;
409108
409735
  exports.TargetKindCapsSchema = TargetKindCapsSchema;
409109
409736
  exports.TargetKindLevelSchema = TargetKindLevelSchema;
409110
409737
  exports.TargetKindSchema = TargetKindSchema;
@@ -409435,6 +410062,8 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409435
410062
  exports.conditionVisibleForKind = conditionVisibleForKind;
409436
410063
  exports.connectionTestCapability = connectionTestCapability;
409437
410064
  exports.connectivityCapability = connectivityCapability;
410065
+ exports.consumableFormOf = consumableFormOf;
410066
+ exports.consumableKindOf = consumableKindOf;
409438
410067
  exports.consumableLowPercentOf = consumableLowPercentOf;
409439
410068
  exports.consumablesCapability = consumablesCapability;
409440
410069
  exports.contactCapability = contactCapability;
@@ -409541,6 +410170,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409541
410170
  exports.egressTranscodeSharingKey = egressTranscodeSharingKey;
409542
410171
  exports.egressTransportFromRequest = egressTransportFromRequest;
409543
410172
  exports.electByShare = electByShare;
410173
+ exports.electByShareWithAffinity = electByShareWithAffinity;
409544
410174
  exports.eligibleUnderBenchmark = eligibleUnderBenchmark;
409545
410175
  exports.embeddingEncoderCapability = embeddingEncoderCapability;
409546
410176
  exports.embeddingModelFailedError = embeddingModelFailedError;
@@ -409634,6 +410264,7 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
409634
410264
  exports.heatingValveCapability = heatingValveCapability;
409635
410265
  exports.hfModelUrl = require_sleep.hfModelUrl;
409636
410266
  exports.historyFieldsFor = historyFieldsFor;
410267
+ exports.historyItemFieldPath = require_sleep.historyItemFieldPath;
409637
410268
  exports.htmlToText = htmlToText;
409638
410269
  exports.hubAddressHostname = hubAddressHostname;
409639
410270
  exports.hubMotionNativeProvider = hubMotionNativeProvider;