@camstack/addon-post-analysis 1.2.644 → 1.2.646

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (16) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{clip-model-registry-Bu2ZxGpm.js → clip-model-registry-52GHtVGU.js} +725 -99
  3. package/dist/{clip-model-registry-CnFDRU_E.mjs → clip-model-registry-f9VyNPxT.mjs} +725 -99
  4. package/dist/embedding-encoder/index.js +1 -1
  5. package/dist/embedding-encoder/index.mjs +1 -1
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-CN1G5ocH.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-B6ySUn_6.mjs} +2 -2
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DDmbZknW.mjs +26 -0
  9. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DgC84J8L.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-OkB37BMo.mjs → hostInit-DsERTEkD.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +3 -1
  12. package/dist/pipeline-analytics/index.mjs +3 -1
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +1 -1
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B8Q_nFhl.mjs +0 -26
  16. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CVItuOhm.mjs +0 -26
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
2
+ //#region ../types/dist/event-category-hdMR7CTW.mjs
3
3
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4
4
  EventCategory["SystemBoot"] = "system.boot";
5
5
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -764,6 +764,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
764
764
  */
765
765
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
766
766
  /**
767
+ * An action run started or ended. Emitted by the `actions` surface.
768
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
769
+ */
770
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
771
+ /**
767
772
  * Progress update from a running model conversion job.
768
773
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
769
774
  * Emitted by `addon-model-studio` on the converting node.
@@ -5640,7 +5645,7 @@ var ZodIssueCode = {
5640
5645
  var ZodFirstPartyTypeKind;
5641
5646
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5642
5647
  //#endregion
5643
- //#region ../types/dist/sleep--jXS9hWo.mjs
5648
+ //#region ../types/dist/sleep-BHjNez61.mjs
5644
5649
  /**
5645
5650
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5646
5651
  * window to float samples (D455).
@@ -8988,6 +8993,8 @@ var CAP_ADVANCE_FIELDS = Object.freeze({ "doorbell": "lastPressedAt" });
8988
8993
  /** Every capability name, so addon code can check a name without the schema graph. */
8989
8994
  var CAP_NAMES = Object.freeze([
8990
8995
  "accessories",
8996
+ "action-source",
8997
+ "actions",
8991
8998
  "addon-pages",
8992
8999
  "addon-pages-source",
8993
9000
  "addon-routes",
@@ -10290,6 +10297,18 @@ Object.freeze({
10290
10297
  "path": "foodLevel",
10291
10298
  "valueType": "number"
10292
10299
  },
10300
+ {
10301
+ "path": "bowlFood",
10302
+ "valueType": "number"
10303
+ },
10304
+ {
10305
+ "path": "eating",
10306
+ "valueType": "boolean"
10307
+ },
10308
+ {
10309
+ "path": "supportsMarkRefilled",
10310
+ "valueType": "boolean"
10311
+ },
10293
10312
  {
10294
10313
  "path": "food1",
10295
10314
  "valueType": "number"
@@ -16911,7 +16930,23 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16911
16930
  kind: literal("schedule"),
16912
16931
  cron: string().min(1).max(120)
16913
16932
  }),
16914
- object({ kind: literal("manual") })
16933
+ object({ kind: literal("manual") }),
16934
+ object({
16935
+ kind: literal("detection"),
16936
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16937
+ * matches every raw class that maps to it; any other name matches
16938
+ * exactly. */
16939
+ classes: array(string().min(1)).min(1),
16940
+ deviceIds: array(number().int().nonnegative()).min(1),
16941
+ /** The track must have visited at least one of these zones. */
16942
+ zones: array(string().min(1)).min(1).optional(),
16943
+ /** Best-so-far confidence the track must reach. */
16944
+ minScore: number().min(0).max(1).optional()
16945
+ }),
16946
+ object({
16947
+ kind: literal("interval"),
16948
+ minutes: number().int().min(1).max(1440)
16949
+ })
16915
16950
  ]);
16916
16951
  /**
16917
16952
  * One action step.
@@ -16931,28 +16966,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16931
16966
  * is called. The runner still receives a number and is untouched; the
16932
16967
  * resolution is the recipe's job, not the runner's.
16933
16968
  */
16969
+ var AutomationWaitStepSchema = object({
16970
+ kind: literal("wait"),
16971
+ seconds: number().min(0).max(300)
16972
+ });
16973
+ var AutomationCapStepSchema = object({
16974
+ kind: literal("cap"),
16975
+ /**
16976
+ * One device, several same-family devices (locks / climate family / …),
16977
+ * or `{ $var }` for the device that triggered the run. An array is
16978
+ * fan-out at codegen — each id gets the same cap.method call.
16979
+ */
16980
+ deviceId: union([
16981
+ number().int(),
16982
+ array(number().int()).min(1),
16983
+ object({ $var: string().min(1) })
16984
+ ]),
16985
+ cap: string().min(1),
16986
+ method: string().min(1),
16987
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
16988
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
16989
+ * the expression leaf. */
16990
+ args: record(string(), unknown()).optional()
16991
+ });
16992
+ /**
16993
+ * The steps an internal Action may contain: cap and wait, and nothing that can
16994
+ * call back into an Action — so an Action can never reach itself (no cycles).
16995
+ */
16996
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
16934
16997
  var AutomationActionSchema = discriminatedUnion("kind", [
16998
+ AutomationWaitStepSchema,
16999
+ AutomationCapStepSchema,
16935
17000
  object({
16936
- kind: literal("wait"),
16937
- seconds: number().min(0).max(300)
16938
- }),
16939
- object({
16940
- kind: literal("cap"),
16941
- /**
16942
- * One device, several same-family devices (locks / climate family / …),
16943
- * or `{ $var }` for the device that triggered the run. An array is
16944
- * fan-out at codegen — each id gets the same cap.method call.
16945
- */
16946
- deviceId: union([
16947
- number().int(),
16948
- array(number().int()).min(1),
16949
- object({ $var: string().min(1) })
16950
- ]),
16951
- cap: string().min(1),
16952
- method: string().min(1),
16953
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
16954
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
16955
- args: record(string(), unknown()).optional()
17001
+ kind: literal("action"),
17002
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
17003
+ actionId: string().min(1),
17004
+ /** Values for the action's declared variables; the others take their default. */
17005
+ variables: record(string(), union([
17006
+ string(),
17007
+ number(),
17008
+ boolean()
17009
+ ])).optional()
16956
17010
  }),
16957
17011
  object({
16958
17012
  kind: literal("code"),
@@ -16961,10 +17015,378 @@ var AutomationActionSchema = discriminatedUnion("kind", [
16961
17015
  code: string().min(1).max(2e4)
16962
17016
  })
16963
17017
  ]);
17018
+ /**
17019
+ * How a trigger that arrives while the automation is busy (or cooling down) is
17020
+ * handled. One of the automation's execution modes, never a property of its
17021
+ * actions. Absent means `single`.
17022
+ *
17023
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
17024
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
17025
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
17026
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
17027
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
17028
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
17029
+ * wait, never the single/queued limits.
17030
+ */
17031
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
17032
+ object({ mode: literal("single") }),
17033
+ object({ mode: literal("restart") }),
17034
+ object({
17035
+ mode: literal("queued"),
17036
+ max: number().int().min(1).max(10)
17037
+ }),
17038
+ object({
17039
+ mode: literal("parallel"),
17040
+ max: number().int().min(1).max(10)
17041
+ }),
17042
+ object({
17043
+ mode: literal("cooldown"),
17044
+ minutes: number().int().min(1).max(1440)
17045
+ })
17046
+ ]);
16964
17047
  var AutomationRecipeSchema = object({
16965
17048
  triggers: array(AutomationTriggerSchema),
16966
17049
  conditions: AutomationConditionSchema.optional(),
16967
- actions: array(AutomationActionSchema)
17050
+ actions: array(AutomationActionSchema),
17051
+ runMode: AutomationRunModeSchema.optional(),
17052
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
17053
+ keepTraces: number().int().min(1).max(100).optional()
17054
+ });
17055
+ /**
17056
+ * The class names a per-class COUNT can ever be keyed on — the macro ids, and
17057
+ * only those.
17058
+ *
17059
+ * `preserveOriginal: false` below is the whole reason this set exists: the
17060
+ * executor maps each raw COCO label onto its macro and DROPS everything else,
17061
+ * so a tracked detection's `className` is always one of {@link MACRO_LABELS}.
17062
+ * Any surface that indexes counts by class name — the ZoneAnalytics occupancy
17063
+ * snapshot's `byClass`, and therefore every occupancy rule — can match nothing
17064
+ * else. Derived from the catalog, never hand-listed, so a new macro joins it by
17065
+ * being declared once.
17066
+ *
17067
+ * Cost of not having it, 2026-08-12: the notification rule editor offered the
17068
+ * full taxonomy for an occupancy class (occupancy is the one condition that
17069
+ * does not go through the engine's macro-expanding `expandClassSelector`), two
17070
+ * live rules were authored on `car`, and both counters read 0 forever.
17071
+ */
17072
+ var DETECTION_MACRO_CLASSES = new Set([
17073
+ {
17074
+ id: "person",
17075
+ name: "Person"
17076
+ },
17077
+ {
17078
+ id: "vehicle",
17079
+ name: "Vehicle"
17080
+ },
17081
+ {
17082
+ id: "animal",
17083
+ name: "Animal"
17084
+ },
17085
+ {
17086
+ id: "package",
17087
+ name: "Package"
17088
+ }
17089
+ ].map((l) => l.id));
17090
+ /** True when `className` is a macro the detector can actually emit (and hence a
17091
+ * class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
17092
+ function isDetectionMacroClass(className) {
17093
+ return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
17094
+ }
17095
+ var COCO_TO_MACRO = {
17096
+ mapping: {
17097
+ person: "person",
17098
+ bicycle: "vehicle",
17099
+ car: "vehicle",
17100
+ motorcycle: "vehicle",
17101
+ airplane: "vehicle",
17102
+ bus: "vehicle",
17103
+ train: "vehicle",
17104
+ truck: "vehicle",
17105
+ boat: "vehicle",
17106
+ bird: "animal",
17107
+ cat: "animal",
17108
+ dog: "animal",
17109
+ horse: "animal",
17110
+ sheep: "animal",
17111
+ cow: "animal",
17112
+ elephant: "animal",
17113
+ bear: "animal",
17114
+ zebra: "animal",
17115
+ giraffe: "animal",
17116
+ suitcase: "package",
17117
+ backpack: "package",
17118
+ handbag: "package"
17119
+ },
17120
+ preserveOriginal: false
17121
+ };
17122
+ new Set(Object.values(COCO_TO_MACRO.mapping));
17123
+ /**
17124
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
17125
+ * so the editor can paint every block green or red with its reason.
17126
+ *
17127
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
17128
+ * every condition node (containers and leaves, including the ones a
17129
+ * short-circuit never reached), the run-mode verdict and every action step —
17130
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
17131
+ * for an Action run). Skipped
17132
+ * evaluations are traced too, and identical consecutive skips collapse into a
17133
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
17134
+ *
17135
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
17136
+ * vocabulary so an Action run records the same step shape.
17137
+ */
17138
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
17139
+ var TraceValueSchema = union([
17140
+ string(),
17141
+ number(),
17142
+ boolean(),
17143
+ _null(),
17144
+ array(union([string(), number()]))
17145
+ ]);
17146
+ var AutomationRunOutcomeSchema = _enum([
17147
+ "ran",
17148
+ "skipped-trigger",
17149
+ "skipped-conditions",
17150
+ "skipped-cooldown",
17151
+ "skipped-overlap",
17152
+ "failed",
17153
+ "aborted"
17154
+ ]);
17155
+ /** `not-evaluated` = a short-circuit never reached the node. */
17156
+ var TraceNodeOutcomeSchema = _enum([
17157
+ "pass",
17158
+ "fail",
17159
+ "not-evaluated"
17160
+ ]);
17161
+ var AutomationTriggerTraceSchema = object({
17162
+ kind: string().min(1),
17163
+ outcome: _enum(["pass", "fail"]),
17164
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
17165
+ reason: string(),
17166
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
17167
+ vars: record(string(), TraceValueSchema)
17168
+ });
17169
+ var AutomationConditionTraceNodeSchema = object({
17170
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
17171
+ path: string().min(1),
17172
+ kind: _enum([
17173
+ "all",
17174
+ "any",
17175
+ "not",
17176
+ "condition",
17177
+ "expression"
17178
+ ]),
17179
+ outcome: TraceNodeOutcomeSchema,
17180
+ reason: string(),
17181
+ deviceId: number().int().optional(),
17182
+ cap: string().optional(),
17183
+ fieldPath: string().optional(),
17184
+ operator: string().optional(),
17185
+ expected: TraceValueSchema.optional(),
17186
+ /** What the mirror held; `null` when it held nothing (unknown). */
17187
+ observed: TraceValueSchema.optional()
17188
+ });
17189
+ var AutomationRunVerdictSchema = object({
17190
+ mode: AutomationRunModeSchema,
17191
+ decision: _enum([
17192
+ "run",
17193
+ "queued",
17194
+ "restart",
17195
+ "drop",
17196
+ "cooldown",
17197
+ "bypass"
17198
+ ]),
17199
+ reason: string()
17200
+ });
17201
+ /** One executed (or not-run) step — shared by automations and Action runs. */
17202
+ var AutomationStepTraceSchema = object({
17203
+ /** `actions.<i>` */
17204
+ path: string().min(1),
17205
+ kind: string().min(1),
17206
+ cap: string().optional(),
17207
+ method: string().optional(),
17208
+ deviceId: number().int().optional(),
17209
+ startedAt: number().optional(),
17210
+ endedAt: number().optional(),
17211
+ outcome: _enum([
17212
+ "ok",
17213
+ "error",
17214
+ "not-run"
17215
+ ]),
17216
+ message: string().optional()
17217
+ });
17218
+ var AutomationRunTraceSchema = object({
17219
+ runId: string().min(1),
17220
+ startedAt: number(),
17221
+ endedAt: number(),
17222
+ trigger: AutomationTriggerTraceSchema,
17223
+ conditions: array(AutomationConditionTraceNodeSchema),
17224
+ verdict: AutomationRunVerdictSchema.optional(),
17225
+ steps: array(AutomationStepTraceSchema),
17226
+ outcome: AutomationRunOutcomeSchema,
17227
+ /** How many identical consecutive skips this trace stands for (>= 1). */
17228
+ repeat: number().int().min(1),
17229
+ /** When the latest of those repeats happened. */
17230
+ lastAt: number()
17231
+ });
17232
+ /**
17233
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
17234
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
17235
+ */
17236
+ var AutomationStateDocSchema = object({
17237
+ version: literal(1),
17238
+ lastRunStartedAt: number().nullable(),
17239
+ traces: array(AutomationRunTraceSchema)
17240
+ });
17241
+ /**
17242
+ * Actions — named, runnable things an operator (or an automation) can start,
17243
+ * declared as LIST ENTRIES, never as devices.
17244
+ *
17245
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
17246
+ * hub's `actions` record kind) or declared by an addon through the
17247
+ * `action-source` collection (a Home Assistant script, a scene …). The one
17248
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
17249
+ * catalog of its own (D3) — every `list` asks the providers live.
17250
+ *
17251
+ * This file is the data half: the entry schema, the variables an entry may
17252
+ * declare, and the pure resolution of the values a run was given. It holds no
17253
+ * runtime.
17254
+ */
17255
+ /** What a variable's value is. `select` takes one of `options`. */
17256
+ var ActionVariableTypeSchema = _enum([
17257
+ "string",
17258
+ "number",
17259
+ "boolean",
17260
+ "select"
17261
+ ]);
17262
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
17263
+ var ActionVariableValueSchema = union([
17264
+ string(),
17265
+ number(),
17266
+ boolean()
17267
+ ]);
17268
+ /**
17269
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
17270
+ * value) or `{{vars.name}}` (inside a string) in their args.
17271
+ */
17272
+ var ActionVariableSchema = object({
17273
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
17274
+ label: string().min(1).max(120).optional(),
17275
+ type: ActionVariableTypeSchema,
17276
+ required: boolean().optional(),
17277
+ default: ActionVariableValueSchema.optional(),
17278
+ /** The allowed values of a `select`. */
17279
+ options: array(string().min(1)).min(1).optional()
17280
+ }).superRefine((v, ctx) => {
17281
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
17282
+ code: "custom",
17283
+ message: "a select variable needs options",
17284
+ path: ["options"]
17285
+ });
17286
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
17287
+ code: "custom",
17288
+ message: variableValueError(v, v.default) ?? "invalid default",
17289
+ path: ["default"]
17290
+ });
17291
+ });
17292
+ /** Why `value` is not a legal value for the variable, or null when it is. */
17293
+ function variableValueError(variable, value) {
17294
+ switch (variable.type) {
17295
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
17296
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
17297
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
17298
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
17299
+ }
17300
+ }
17301
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
17302
+ function isInternalActionId(actionId) {
17303
+ return actionId.startsWith("internal:") && actionId.length > 9;
17304
+ }
17305
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
17306
+ function addonIdOfActionId(actionId) {
17307
+ if (isInternalActionId(actionId)) return null;
17308
+ const at = actionId.indexOf(":");
17309
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
17310
+ }
17311
+ var ActionStatusSchema = object({
17312
+ isRunning: boolean(),
17313
+ /** Ms epoch the latest run STARTED; null when it never ran. */
17314
+ lastRunAt: number().nullable(),
17315
+ /** The latest run's failure; null on success or when it never ran. */
17316
+ lastError: string().nullable()
17317
+ });
17318
+ var ActionEntrySchema = object({
17319
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
17320
+ id: string().min(3),
17321
+ name: string().min(1).max(120),
17322
+ /** The addon that declared it; `core-blocks` for an internal one. */
17323
+ addonId: string().min(1),
17324
+ source: ActionSourceKindSchema,
17325
+ /** An addon-declared entry is edited where it was declared. */
17326
+ readOnly: boolean(),
17327
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
17328
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
17329
+ variables: array(ActionVariableSchema).max(20).optional(),
17330
+ status: ActionStatusSchema
17331
+ }).superRefine((entry, ctx) => {
17332
+ if (entry.source === "internal") {
17333
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
17334
+ code: "custom",
17335
+ message: "an internal action id is internal:<uuid>",
17336
+ path: ["id"]
17337
+ });
17338
+ return;
17339
+ }
17340
+ if (entry.steps !== void 0) ctx.addIssue({
17341
+ code: "custom",
17342
+ message: "only an internal action carries steps",
17343
+ path: ["steps"]
17344
+ });
17345
+ if (!entry.readOnly) ctx.addIssue({
17346
+ code: "custom",
17347
+ message: "an addon action is read-only here",
17348
+ path: ["readOnly"]
17349
+ });
17350
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
17351
+ code: "custom",
17352
+ message: "an addon action id is <addonId>:<localId>",
17353
+ path: ["id"]
17354
+ });
17355
+ });
17356
+ /** What an operator writes for an internal action. */
17357
+ var ActionInputSchema = object({
17358
+ name: string().min(1).max(120),
17359
+ steps: array(ActionStepSchema).min(1).max(50),
17360
+ variables: array(ActionVariableSchema).max(20).optional()
17361
+ });
17362
+ /** Who started a run. Absent = the operator pressed it. */
17363
+ var ActionRunOriginSchema = object({
17364
+ /** The automation block whose step started it. */
17365
+ blockId: string().min(1).optional() });
17366
+ /** `started` = the caller did not ask to wait. */
17367
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
17368
+ var ActionRunResultSchema = object({
17369
+ runId: string().min(1),
17370
+ outcome: ActionRunOutcomeSchema,
17371
+ /** Why it did not run, or what failed. */
17372
+ message: string().optional()
17373
+ });
17374
+ _enum([
17375
+ "not-found",
17376
+ "read-only",
17377
+ "invalid-variables",
17378
+ "invalid-steps",
17379
+ "not-linked",
17380
+ "busy"
17381
+ ]);
17382
+ object({
17383
+ actionId: string(),
17384
+ runId: string(),
17385
+ isRunning: boolean(),
17386
+ /** Set when the run ended. */
17387
+ outcome: AutomationRunOutcomeSchema.optional(),
17388
+ lastRunAt: number().nullable(),
17389
+ lastError: string().nullable()
16968
17390
  });
16969
17391
  /**
16970
17392
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -20644,7 +21066,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
20644
21066
  kind: "mutation",
20645
21067
  auth: "admin",
20646
21068
  access: "view"
20647
- });
21069
+ }), method(object({
21070
+ blockId: string().min(1),
21071
+ trace: AutomationRunTraceSchema.optional(),
21072
+ runStartedAt: number().optional(),
21073
+ keepTraces: number().int().min(1).max(100).optional()
21074
+ }), _void(), {
21075
+ kind: "mutation",
21076
+ auth: "admin"
21077
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
21078
+ traces: array(AutomationRunTraceSchema),
21079
+ lastError: string().nullable()
21080
+ }), { auth: "admin" });
20648
21081
  /**
20649
21082
  * `custom-model-registry` — collection cap exposing operator-registered
20650
21083
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -46045,73 +46478,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
46045
46478
  kind: "mutation",
46046
46479
  auth: "admin"
46047
46480
  });
46048
- /**
46049
- * The class names a per-class COUNT can ever be keyed on — the macro ids, and
46050
- * only those.
46051
- *
46052
- * `preserveOriginal: false` below is the whole reason this set exists: the
46053
- * executor maps each raw COCO label onto its macro and DROPS everything else,
46054
- * so a tracked detection's `className` is always one of {@link MACRO_LABELS}.
46055
- * Any surface that indexes counts by class name — the ZoneAnalytics occupancy
46056
- * snapshot's `byClass`, and therefore every occupancy rule — can match nothing
46057
- * else. Derived from the catalog, never hand-listed, so a new macro joins it by
46058
- * being declared once.
46059
- *
46060
- * Cost of not having it, 2026-08-12: the notification rule editor offered the
46061
- * full taxonomy for an occupancy class (occupancy is the one condition that
46062
- * does not go through the engine's macro-expanding `expandClassSelector`), two
46063
- * live rules were authored on `car`, and both counters read 0 forever.
46064
- */
46065
- var DETECTION_MACRO_CLASSES = new Set([
46066
- {
46067
- id: "person",
46068
- name: "Person"
46069
- },
46070
- {
46071
- id: "vehicle",
46072
- name: "Vehicle"
46073
- },
46074
- {
46075
- id: "animal",
46076
- name: "Animal"
46077
- },
46078
- {
46079
- id: "package",
46080
- name: "Package"
46081
- }
46082
- ].map((l) => l.id));
46083
- /** True when `className` is a macro the detector can actually emit (and hence a
46084
- * class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
46085
- function isDetectionMacroClass(className) {
46086
- return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
46087
- }
46088
- var COCO_TO_MACRO = {
46089
- mapping: {
46090
- person: "person",
46091
- bicycle: "vehicle",
46092
- car: "vehicle",
46093
- motorcycle: "vehicle",
46094
- airplane: "vehicle",
46095
- bus: "vehicle",
46096
- train: "vehicle",
46097
- truck: "vehicle",
46098
- boat: "vehicle",
46099
- bird: "animal",
46100
- cat: "animal",
46101
- dog: "animal",
46102
- horse: "animal",
46103
- sheep: "animal",
46104
- cow: "animal",
46105
- elephant: "animal",
46106
- bear: "animal",
46107
- zebra: "animal",
46108
- giraffe: "animal",
46109
- suitcase: "package",
46110
- backpack: "package",
46111
- handbag: "package"
46112
- },
46113
- preserveOriginal: false
46114
- };
46115
46481
  var AUDIO_MACRO_LABELS = [
46116
46482
  {
46117
46483
  id: "speech",
@@ -60779,6 +61145,84 @@ var audioMetricsCapability = {
60779
61145
  */
60780
61146
  history: "none"
60781
61147
  };
61148
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
61149
+ actionId: string().min(1),
61150
+ variables: record(string(), ActionVariableValueSchema).optional()
61151
+ }), _void(), {
61152
+ kind: "mutation",
61153
+ auth: "admin"
61154
+ }), method(object({ actionId: string().min(1) }), _void(), {
61155
+ kind: "mutation",
61156
+ auth: "admin"
61157
+ });
61158
+ /**
61159
+ * actions — the ONE surface for runnable actions, for the UI and for
61160
+ * automations. Provided by the core-blocks builtin on the hub.
61161
+ *
61162
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
61163
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
61164
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
61165
+ * the run status from the hub's `runs` ledger; there is no catalog here to
61166
+ * drift (D3).
61167
+ *
61168
+ * **Permissions follow the camera, as linked devices do.** An action is linked
61169
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
61170
+ * non-admin may `get` / `run` an action only through a camera they may see that
61171
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
61172
+ * gate sees it) and the provider refuses an action that camera does not link.
61173
+ * An admin, and an in-process caller (an automation block), may run any action.
61174
+ * Every refusal is typed (`[not-linked] …`) and logged.
61175
+ *
61176
+ * A second `run` of an action that is already running is REFUSED — traced and
61177
+ * logged — never queued.
61178
+ */
61179
+ var ActionIdInput = object({ actionId: string().min(1) });
61180
+ var ActionLinksSchema = object({
61181
+ deviceId: number().int().nonnegative(),
61182
+ actionIds: array(string().min(1)).max(100)
61183
+ });
61184
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
61185
+ actionId: string().min(1),
61186
+ deviceId: number().int().nonnegative().optional()
61187
+ }), object({ action: ActionEntrySchema.nullable() }), {
61188
+ role: "viewer",
61189
+ auth: "protected",
61190
+ kind: "query",
61191
+ caller: "required"
61192
+ }), method(object({
61193
+ actionId: string().min(1),
61194
+ variables: record(string(), ActionVariableValueSchema).optional(),
61195
+ wait: boolean().optional(),
61196
+ origin: ActionRunOriginSchema.optional(),
61197
+ deviceId: number().int().nonnegative().optional()
61198
+ }), ActionRunResultSchema, {
61199
+ role: "viewer",
61200
+ auth: "protected",
61201
+ kind: "mutation",
61202
+ caller: "required"
61203
+ }), method(ActionIdInput, _void(), {
61204
+ kind: "mutation",
61205
+ auth: "admin"
61206
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
61207
+ kind: "mutation",
61208
+ auth: "admin"
61209
+ }), method(object({
61210
+ actionId: string().min(1),
61211
+ action: ActionInputSchema.partial()
61212
+ }), object({ action: ActionEntrySchema }), {
61213
+ kind: "mutation",
61214
+ auth: "admin"
61215
+ }), method(ActionIdInput, _void(), {
61216
+ kind: "mutation",
61217
+ auth: "admin"
61218
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
61219
+ role: "viewer",
61220
+ auth: "protected",
61221
+ kind: "query"
61222
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
61223
+ kind: "mutation",
61224
+ auth: "admin"
61225
+ });
60782
61226
  /**
60783
61227
  * Automation-control cap. Models HA `automation.*` entities on
60784
61228
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -67373,10 +67817,20 @@ var PetFeederDeviceStatusSchema = _enum([
67373
67817
  ]);
67374
67818
  var gramsPortion = number().int().min(4).max(200);
67375
67819
  var PetFeederStatusSchema = object({
67376
- /** Food currently in the bowl (grams). Null when the device has not
67377
- * reported a reading yet. On dual-hopper models this is the combined
67378
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
67820
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
67821
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
67822
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
67823
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
67824
+ * the device has not reported yet. */
67379
67825
  foodLevel: number().nullable().meta(readingMeta("none")),
67826
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
67827
+ * feeder's own units. Null when the model reports none. */
67828
+ bowlFood: number().nullable().meta(readingMeta("none")),
67829
+ /** True while the pet is eating (`state.eating`). */
67830
+ eating: boolean(),
67831
+ /** Whether `markFoodReplenished` does anything on this model. When false
67832
+ * the client must not offer it. */
67833
+ supportsMarkRefilled: boolean(),
67380
67834
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
67381
67835
  * single-hopper models. */
67382
67836
  food1: number().nullable().meta(readingMeta("none")),
@@ -67384,8 +67838,8 @@ var PetFeederStatusSchema = object({
67384
67838
  * single-hopper models. */
67385
67839
  food2: number().nullable().meta(readingMeta("none")),
67386
67840
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
67387
- * (`device_class: problem`, on = low). True when the bowl is empty /
67388
- * below the feeder's low threshold. */
67841
+ * (`device_class: problem`, on = low). True when the tank reports empty,
67842
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
67389
67843
  lowFood: boolean(),
67390
67844
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
67391
67845
  * device has no battery reading. */
@@ -67453,7 +67907,9 @@ var petFeederCapability = {
67453
67907
  kind: "mutation",
67454
67908
  auth: "protected"
67455
67909
  }),
67456
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
67910
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
67911
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
67912
+ * change any sensor reading. */
67457
67913
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
67458
67914
  kind: "mutation",
67459
67915
  auth: "protected"
@@ -67526,6 +67982,7 @@ var petFeederCapability = {
67526
67982
  */
67527
67983
  history: { fields: {
67528
67984
  foodLevel: { kind: "gauge" },
67985
+ bowlFood: { kind: "gauge" },
67529
67986
  food1: { kind: "gauge" },
67530
67987
  food2: { kind: "gauge" },
67531
67988
  batteryPower: {
@@ -74736,6 +75193,104 @@ Object.freeze({
74736
75193
  access: "create",
74737
75194
  role: "admin"
74738
75195
  },
75196
+ "actions.create": {
75197
+ capName: "actions",
75198
+ capScope: "system",
75199
+ addonId: null,
75200
+ access: "create",
75201
+ role: "admin"
75202
+ },
75203
+ "actions.delete": {
75204
+ capName: "actions",
75205
+ capScope: "system",
75206
+ addonId: null,
75207
+ access: "delete",
75208
+ role: "admin"
75209
+ },
75210
+ "actions.get": {
75211
+ capName: "actions",
75212
+ capScope: "system",
75213
+ addonId: null,
75214
+ access: "view",
75215
+ role: "viewer"
75216
+ },
75217
+ "actions.getLinks": {
75218
+ capName: "actions",
75219
+ capScope: "system",
75220
+ addonId: null,
75221
+ access: "view",
75222
+ role: "admin"
75223
+ },
75224
+ "actions.getTraces": {
75225
+ capName: "actions",
75226
+ capScope: "system",
75227
+ addonId: null,
75228
+ access: "view",
75229
+ role: "admin"
75230
+ },
75231
+ "actions.list": {
75232
+ capName: "actions",
75233
+ capScope: "system",
75234
+ addonId: null,
75235
+ access: "view",
75236
+ role: "admin"
75237
+ },
75238
+ "actions.listForCamera": {
75239
+ capName: "actions",
75240
+ capScope: "system",
75241
+ addonId: null,
75242
+ access: "view",
75243
+ role: "viewer"
75244
+ },
75245
+ "actions.run": {
75246
+ capName: "actions",
75247
+ capScope: "system",
75248
+ addonId: null,
75249
+ access: "create",
75250
+ role: "viewer"
75251
+ },
75252
+ "actions.setLinks": {
75253
+ capName: "actions",
75254
+ capScope: "system",
75255
+ addonId: null,
75256
+ access: "create",
75257
+ role: "admin"
75258
+ },
75259
+ "actions.stop": {
75260
+ capName: "actions",
75261
+ capScope: "system",
75262
+ addonId: null,
75263
+ access: "create",
75264
+ role: "admin"
75265
+ },
75266
+ "actions.update": {
75267
+ capName: "actions",
75268
+ capScope: "system",
75269
+ addonId: null,
75270
+ access: "create",
75271
+ role: "admin"
75272
+ },
75273
+ "actionSource.listActions": {
75274
+ capName: "action-source",
75275
+ capScope: "system",
75276
+ addonId: null,
75277
+ access: "view",
75278
+ role: "admin"
75279
+ },
75280
+ "actionSource.runAction": {
75281
+ capName: "action-source",
75282
+ capScope: "system",
75283
+ addonId: null,
75284
+ access: "create",
75285
+ role: "admin"
75286
+ },
75287
+ "actionSource.stopAction": {
75288
+ capName: "action-source",
75289
+ capScope: "system",
75290
+ addonId: null,
75291
+ access: "create",
75292
+ role: "admin"
75293
+ },
74739
75294
  "addonPages.listPages": {
74740
75295
  capName: "addon-pages",
74741
75296
  capScope: "system",
@@ -76017,6 +76572,13 @@ Object.freeze({
76017
76572
  access: "view",
76018
76573
  role: "admin"
76019
76574
  },
76575
+ "coreBlocks.getAutomationRunState": {
76576
+ capName: "core-blocks",
76577
+ capScope: "system",
76578
+ addonId: null,
76579
+ access: "view",
76580
+ role: "admin"
76581
+ },
76020
76582
  "coreBlocks.getBlockSettings": {
76021
76583
  capName: "core-blocks",
76022
76584
  capScope: "system",
@@ -76038,6 +76600,13 @@ Object.freeze({
76038
76600
  access: "view",
76039
76601
  role: "admin"
76040
76602
  },
76603
+ "coreBlocks.getTraces": {
76604
+ capName: "core-blocks",
76605
+ capScope: "system",
76606
+ addonId: null,
76607
+ access: "view",
76608
+ role: "admin"
76609
+ },
76041
76610
  "coreBlocks.getTypeDefs": {
76042
76611
  capName: "core-blocks",
76043
76612
  capScope: "system",
@@ -76073,6 +76642,13 @@ Object.freeze({
76073
76642
  access: "create",
76074
76643
  role: "admin"
76075
76644
  },
76645
+ "coreBlocks.reportAutomationRun": {
76646
+ capName: "core-blocks",
76647
+ capScope: "system",
76648
+ addonId: null,
76649
+ access: "create",
76650
+ role: "admin"
76651
+ },
76076
76652
  "coreBlocks.restart": {
76077
76653
  capName: "core-blocks",
76078
76654
  capScope: "system",
@@ -88227,6 +88803,8 @@ var ML = "model and training machinery — no device action";
88227
88803
  var INTERNAL = "internal plumbing between addons — never operator-facing";
88228
88804
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
88229
88805
  var SYSTEM_CAP_VOCABULARY = {
88806
+ "action-source": system(INTERNAL),
88807
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
88230
88808
  "addon-pages": system(UI_PLUMBING),
88231
88809
  "addon-pages-source": system(UI_PLUMBING),
88232
88810
  "addon-routes": system(UI_PLUMBING),
@@ -88423,6 +89001,31 @@ Object.freeze({
88423
89001
  form: "single",
88424
89002
  optional: false
88425
89003
  }],
89004
+ "actions.get": [{
89005
+ name: "deviceId",
89006
+ form: "single",
89007
+ optional: true
89008
+ }],
89009
+ "actions.getLinks": [{
89010
+ name: "deviceId",
89011
+ form: "single",
89012
+ optional: false
89013
+ }],
89014
+ "actions.listForCamera": [{
89015
+ name: "deviceId",
89016
+ form: "single",
89017
+ optional: false
89018
+ }],
89019
+ "actions.run": [{
89020
+ name: "deviceId",
89021
+ form: "single",
89022
+ optional: true
89023
+ }],
89024
+ "actions.setLinks": [{
89025
+ name: "deviceId",
89026
+ form: "single",
89027
+ optional: false
89028
+ }],
88426
89029
  "addonSettings.getDeviceSettings": [{
88427
89030
  name: "deviceId",
88428
89031
  form: "single",
@@ -94601,6 +95204,16 @@ function templateCarriesSummary(body) {
94601
95204
  if (body === void 0 || body.trim().length === 0) return true;
94602
95205
  return SUMMARY_CONTENT_VAR.test(body);
94603
95206
  }
95207
+ /**
95208
+ * Make a raw wire id readable when no dictionary knows it: `delivery_truck`
95209
+ * becomes "Delivery truck", `open-door` becomes "Open door". It invents no
95210
+ * translation — the words stay the id's own, only separators and the first
95211
+ * letter change. An empty id stays empty.
95212
+ */
95213
+ function humanizeId(id) {
95214
+ const spaced = id.replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
95215
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
95216
+ }
94604
95217
  new Set([
94605
95218
  "disarmed",
94606
95219
  "armed_home",
@@ -94633,6 +95246,18 @@ new Set([
94633
95246
  "live"
94634
95247
  ]);
94635
95248
  /**
95249
+ * ONE dictionary of human labels for event kinds (detection macros/subs,
95250
+ * sensors, audio macros, …) — the same vocabulary the viewer filters and the
95251
+ * notification centre speak (R7).
95252
+ *
95253
+ * Wire ids stay raw (`cat`, `animal`, `audio-dog`). What a human reads is a
95254
+ * label, and a surface that translates on its own is a surface that disagrees
95255
+ * with the next one. Admin UI, viewer and NC resolve through {@link eventKindLabel}.
95256
+ *
95257
+ * Keys match {@link EVENT_TAXONOMY}. An OPEN kind (a custom model class) falls
95258
+ * back to the raw word — never an exception, never an empty chip.
95259
+ */
95260
+ /**
94636
95261
  * Every kind the dictionary answers for. Order is stable; the set is held to
94637
95262
  * {@link EVENT_TAXONOMY} by the spec.
94638
95263
  */
@@ -95002,7 +95627,7 @@ function isEventKindLabelKey(key) {
95002
95627
  */
95003
95628
  function eventKindLabel(kind, locale) {
95004
95629
  const key = kind.trim().toLowerCase();
95005
- return isEventKindLabelKey(key) ? EVENT_KIND_LABELS[key][locale] : kind;
95630
+ return isEventKindLabelKey(key) ? EVENT_KIND_LABELS[key][locale] : humanizeId(kind);
95006
95631
  }
95007
95632
  AppPageLangSchema.options;
95008
95633
  record(string().min(1), string());
@@ -95623,6 +96248,7 @@ function cosineSimilarity(a, b) {
95623
96248
  const denom = Math.sqrt(normA) * Math.sqrt(normB);
95624
96249
  return denom === 0 ? 0 : dotProduct / denom;
95625
96250
  }
96251
+ String.raw`(?<=^|["'(\s>,\[{])`;
95626
96252
  var MB = 1024 * 1024;
95627
96253
  var DEFAULT_POOL_MEMORY_POLICY = {
95628
96254
  sampleIntervalMs: 6e4,