@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
@@ -30,7 +30,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  }) : target, mod));
31
31
  //#endregion
32
32
  let node_crypto = require("node:crypto");
33
- //#region ../types/dist/event-category-CT-hz5Rb.mjs
33
+ //#region ../types/dist/event-category-hdMR7CTW.mjs
34
34
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
35
35
  EventCategory["SystemBoot"] = "system.boot";
36
36
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -795,6 +795,11 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
795
795
  */
796
796
  EventCategory["AccessoriesChanged"] = "accessories.onAccessoriesChanged";
797
797
  /**
798
+ * An action run started or ended. Emitted by the `actions` surface.
799
+ * Payload: `ActionRunChanged` (`{ actionId, runId, isRunning, outcome?, lastRunAt, lastError }`).
800
+ */
801
+ EventCategory["ActionsRunChanged"] = "actions.onRunChanged";
802
+ /**
798
803
  * Progress update from a running model conversion job.
799
804
  * Payload: `{ kind: 'model-convert', phase, sessionId?, pct?, detail? }`.
800
805
  * Emitted by `addon-model-studio` on the converting node.
@@ -5671,7 +5676,7 @@ var ZodIssueCode = {
5671
5676
  var ZodFirstPartyTypeKind;
5672
5677
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5673
5678
  //#endregion
5674
- //#region ../types/dist/sleep--jXS9hWo.mjs
5679
+ //#region ../types/dist/sleep-BHjNez61.mjs
5675
5680
  /**
5676
5681
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5677
5682
  * window to float samples (D455).
@@ -9019,6 +9024,8 @@ var CAP_ADVANCE_FIELDS = Object.freeze({ "doorbell": "lastPressedAt" });
9019
9024
  /** Every capability name, so addon code can check a name without the schema graph. */
9020
9025
  var CAP_NAMES = Object.freeze([
9021
9026
  "accessories",
9027
+ "action-source",
9028
+ "actions",
9022
9029
  "addon-pages",
9023
9030
  "addon-pages-source",
9024
9031
  "addon-routes",
@@ -10321,6 +10328,18 @@ Object.freeze({
10321
10328
  "path": "foodLevel",
10322
10329
  "valueType": "number"
10323
10330
  },
10331
+ {
10332
+ "path": "bowlFood",
10333
+ "valueType": "number"
10334
+ },
10335
+ {
10336
+ "path": "eating",
10337
+ "valueType": "boolean"
10338
+ },
10339
+ {
10340
+ "path": "supportsMarkRefilled",
10341
+ "valueType": "boolean"
10342
+ },
10324
10343
  {
10325
10344
  "path": "food1",
10326
10345
  "valueType": "number"
@@ -16942,7 +16961,23 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16942
16961
  kind: literal("schedule"),
16943
16962
  cron: string().min(1).max(120)
16944
16963
  }),
16945
- object({ kind: literal("manual") })
16964
+ object({ kind: literal("manual") }),
16965
+ object({
16966
+ kind: literal("detection"),
16967
+ /** Class names. A macro class (`animal`, `vehicle`, `person`, `package`)
16968
+ * matches every raw class that maps to it; any other name matches
16969
+ * exactly. */
16970
+ classes: array(string().min(1)).min(1),
16971
+ deviceIds: array(number().int().nonnegative()).min(1),
16972
+ /** The track must have visited at least one of these zones. */
16973
+ zones: array(string().min(1)).min(1).optional(),
16974
+ /** Best-so-far confidence the track must reach. */
16975
+ minScore: number().min(0).max(1).optional()
16976
+ }),
16977
+ object({
16978
+ kind: literal("interval"),
16979
+ minutes: number().int().min(1).max(1440)
16980
+ })
16946
16981
  ]);
16947
16982
  /**
16948
16983
  * One action step.
@@ -16962,28 +16997,47 @@ var AutomationTriggerSchema = discriminatedUnion("kind", [
16962
16997
  * is called. The runner still receives a number and is untouched; the
16963
16998
  * resolution is the recipe's job, not the runner's.
16964
16999
  */
17000
+ var AutomationWaitStepSchema = object({
17001
+ kind: literal("wait"),
17002
+ seconds: number().min(0).max(300)
17003
+ });
17004
+ var AutomationCapStepSchema = object({
17005
+ kind: literal("cap"),
17006
+ /**
17007
+ * One device, several same-family devices (locks / climate family / …),
17008
+ * or `{ $var }` for the device that triggered the run. An array is
17009
+ * fan-out at codegen — each id gets the same cap.method call.
17010
+ */
17011
+ deviceId: union([
17012
+ number().int(),
17013
+ array(number().int()).min(1),
17014
+ object({ $var: string().min(1) })
17015
+ ]),
17016
+ cap: string().min(1),
17017
+ method: string().min(1),
17018
+ /** Values may carry `{{vars.x}}` slots, or be exactly `{ $var: 'name' }`, which
17019
+ * SUBSTITUTE and do not evaluate (§3.2.3). Anything beyond substitution is
17020
+ * the expression leaf. */
17021
+ args: record(string(), unknown()).optional()
17022
+ });
17023
+ /**
17024
+ * The steps an internal Action may contain: cap and wait, and nothing that can
17025
+ * call back into an Action — so an Action can never reach itself (no cycles).
17026
+ */
17027
+ var ActionStepSchema = discriminatedUnion("kind", [AutomationWaitStepSchema, AutomationCapStepSchema]);
16965
17028
  var AutomationActionSchema = discriminatedUnion("kind", [
17029
+ AutomationWaitStepSchema,
17030
+ AutomationCapStepSchema,
16966
17031
  object({
16967
- kind: literal("wait"),
16968
- seconds: number().min(0).max(300)
16969
- }),
16970
- object({
16971
- kind: literal("cap"),
16972
- /**
16973
- * One device, several same-family devices (locks / climate family / …),
16974
- * or `{ $var }` for the device that triggered the run. An array is
16975
- * fan-out at codegen — each id gets the same cap.method call.
16976
- */
16977
- deviceId: union([
16978
- number().int(),
16979
- array(number().int()).min(1),
16980
- object({ $var: string().min(1) })
16981
- ]),
16982
- cap: string().min(1),
16983
- method: string().min(1),
16984
- /** Values may carry `{{vars.x}}` slots, which SUBSTITUTE and do not
16985
- * evaluate (§3.2.3). Anything beyond substitution is the expression leaf. */
16986
- args: record(string(), unknown()).optional()
17032
+ kind: literal("action"),
17033
+ /** An entry of the `actions` surface (`internal:<uuid>` or `<addonId>:<localId>`). */
17034
+ actionId: string().min(1),
17035
+ /** Values for the action's declared variables; the others take their default. */
17036
+ variables: record(string(), union([
17037
+ string(),
17038
+ number(),
17039
+ boolean()
17040
+ ])).optional()
16987
17041
  }),
16988
17042
  object({
16989
17043
  kind: literal("code"),
@@ -16992,10 +17046,378 @@ var AutomationActionSchema = discriminatedUnion("kind", [
16992
17046
  code: string().min(1).max(2e4)
16993
17047
  })
16994
17048
  ]);
17049
+ /**
17050
+ * How a trigger that arrives while the automation is busy (or cooling down) is
17051
+ * handled. One of the automation's execution modes, never a property of its
17052
+ * actions. Absent means `single`.
17053
+ *
17054
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
17055
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
17056
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
17057
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
17058
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
17059
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
17060
+ * wait, never the single/queued limits.
17061
+ */
17062
+ var AutomationRunModeSchema = discriminatedUnion("mode", [
17063
+ object({ mode: literal("single") }),
17064
+ object({ mode: literal("restart") }),
17065
+ object({
17066
+ mode: literal("queued"),
17067
+ max: number().int().min(1).max(10)
17068
+ }),
17069
+ object({
17070
+ mode: literal("parallel"),
17071
+ max: number().int().min(1).max(10)
17072
+ }),
17073
+ object({
17074
+ mode: literal("cooldown"),
17075
+ minutes: number().int().min(1).max(1440)
17076
+ })
17077
+ ]);
16995
17078
  var AutomationRecipeSchema = object({
16996
17079
  triggers: array(AutomationTriggerSchema),
16997
17080
  conditions: AutomationConditionSchema.optional(),
16998
- actions: array(AutomationActionSchema)
17081
+ actions: array(AutomationActionSchema),
17082
+ runMode: AutomationRunModeSchema.optional(),
17083
+ /** How many run traces the automation keeps (default {@link DEFAULT_TRACE_KEEP}). */
17084
+ keepTraces: number().int().min(1).max(100).optional()
17085
+ });
17086
+ /**
17087
+ * The class names a per-class COUNT can ever be keyed on — the macro ids, and
17088
+ * only those.
17089
+ *
17090
+ * `preserveOriginal: false` below is the whole reason this set exists: the
17091
+ * executor maps each raw COCO label onto its macro and DROPS everything else,
17092
+ * so a tracked detection's `className` is always one of {@link MACRO_LABELS}.
17093
+ * Any surface that indexes counts by class name — the ZoneAnalytics occupancy
17094
+ * snapshot's `byClass`, and therefore every occupancy rule — can match nothing
17095
+ * else. Derived from the catalog, never hand-listed, so a new macro joins it by
17096
+ * being declared once.
17097
+ *
17098
+ * Cost of not having it, 2026-08-12: the notification rule editor offered the
17099
+ * full taxonomy for an occupancy class (occupancy is the one condition that
17100
+ * does not go through the engine's macro-expanding `expandClassSelector`), two
17101
+ * live rules were authored on `car`, and both counters read 0 forever.
17102
+ */
17103
+ var DETECTION_MACRO_CLASSES = new Set([
17104
+ {
17105
+ id: "person",
17106
+ name: "Person"
17107
+ },
17108
+ {
17109
+ id: "vehicle",
17110
+ name: "Vehicle"
17111
+ },
17112
+ {
17113
+ id: "animal",
17114
+ name: "Animal"
17115
+ },
17116
+ {
17117
+ id: "package",
17118
+ name: "Package"
17119
+ }
17120
+ ].map((l) => l.id));
17121
+ /** True when `className` is a macro the detector can actually emit (and hence a
17122
+ * class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
17123
+ function isDetectionMacroClass(className) {
17124
+ return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
17125
+ }
17126
+ var COCO_TO_MACRO = {
17127
+ mapping: {
17128
+ person: "person",
17129
+ bicycle: "vehicle",
17130
+ car: "vehicle",
17131
+ motorcycle: "vehicle",
17132
+ airplane: "vehicle",
17133
+ bus: "vehicle",
17134
+ train: "vehicle",
17135
+ truck: "vehicle",
17136
+ boat: "vehicle",
17137
+ bird: "animal",
17138
+ cat: "animal",
17139
+ dog: "animal",
17140
+ horse: "animal",
17141
+ sheep: "animal",
17142
+ cow: "animal",
17143
+ elephant: "animal",
17144
+ bear: "animal",
17145
+ zebra: "animal",
17146
+ giraffe: "animal",
17147
+ suitcase: "package",
17148
+ backpack: "package",
17149
+ handbag: "package"
17150
+ },
17151
+ preserveOriginal: false
17152
+ };
17153
+ new Set(Object.values(COCO_TO_MACRO.mapping));
17154
+ /**
17155
+ * Run traces — what an automation (or, later, an Action run) DID, node by node,
17156
+ * so the editor can paint every block green or red with its reason.
17157
+ *
17158
+ * A trace is data, not a log: it covers the WHOLE tree — the trigger stage,
17159
+ * every condition node (containers and leaves, including the ones a
17160
+ * short-circuit never reached), the run-mode verdict and every action step —
17161
+ * each addressed by a stable path (`conditions.0.1`, `actions.2`, or `steps.N`
17162
+ * for an Action run). Skipped
17163
+ * evaluations are traced too, and identical consecutive skips collapse into a
17164
+ * repeat count so a trigger that fails every 5 minutes cannot flush real runs.
17165
+ *
17166
+ * {@link AutomationStepTraceSchema} is deliberately free of automation
17167
+ * vocabulary so an Action run records the same step shape.
17168
+ */
17169
+ /** A value a trace may show: JSON primitives and short lists, nothing deeper. */
17170
+ var TraceValueSchema = union([
17171
+ string(),
17172
+ number(),
17173
+ boolean(),
17174
+ _null(),
17175
+ array(union([string(), number()]))
17176
+ ]);
17177
+ var AutomationRunOutcomeSchema = _enum([
17178
+ "ran",
17179
+ "skipped-trigger",
17180
+ "skipped-conditions",
17181
+ "skipped-cooldown",
17182
+ "skipped-overlap",
17183
+ "failed",
17184
+ "aborted"
17185
+ ]);
17186
+ /** `not-evaluated` = a short-circuit never reached the node. */
17187
+ var TraceNodeOutcomeSchema = _enum([
17188
+ "pass",
17189
+ "fail",
17190
+ "not-evaluated"
17191
+ ]);
17192
+ var AutomationTriggerTraceSchema = object({
17193
+ kind: string().min(1),
17194
+ outcome: _enum(["pass", "fail"]),
17195
+ /** Why an event was rejected ("class person not in [dog]") or what fired. */
17196
+ reason: string(),
17197
+ /** deviceId, className, trackId, score … — only what the trigger exposes. */
17198
+ vars: record(string(), TraceValueSchema)
17199
+ });
17200
+ var AutomationConditionTraceNodeSchema = object({
17201
+ /** `conditions` for the root, `conditions.<i>` for its i-th child, … */
17202
+ path: string().min(1),
17203
+ kind: _enum([
17204
+ "all",
17205
+ "any",
17206
+ "not",
17207
+ "condition",
17208
+ "expression"
17209
+ ]),
17210
+ outcome: TraceNodeOutcomeSchema,
17211
+ reason: string(),
17212
+ deviceId: number().int().optional(),
17213
+ cap: string().optional(),
17214
+ fieldPath: string().optional(),
17215
+ operator: string().optional(),
17216
+ expected: TraceValueSchema.optional(),
17217
+ /** What the mirror held; `null` when it held nothing (unknown). */
17218
+ observed: TraceValueSchema.optional()
17219
+ });
17220
+ var AutomationRunVerdictSchema = object({
17221
+ mode: AutomationRunModeSchema,
17222
+ decision: _enum([
17223
+ "run",
17224
+ "queued",
17225
+ "restart",
17226
+ "drop",
17227
+ "cooldown",
17228
+ "bypass"
17229
+ ]),
17230
+ reason: string()
17231
+ });
17232
+ /** One executed (or not-run) step — shared by automations and Action runs. */
17233
+ var AutomationStepTraceSchema = object({
17234
+ /** `actions.<i>` */
17235
+ path: string().min(1),
17236
+ kind: string().min(1),
17237
+ cap: string().optional(),
17238
+ method: string().optional(),
17239
+ deviceId: number().int().optional(),
17240
+ startedAt: number().optional(),
17241
+ endedAt: number().optional(),
17242
+ outcome: _enum([
17243
+ "ok",
17244
+ "error",
17245
+ "not-run"
17246
+ ]),
17247
+ message: string().optional()
17248
+ });
17249
+ var AutomationRunTraceSchema = object({
17250
+ runId: string().min(1),
17251
+ startedAt: number(),
17252
+ endedAt: number(),
17253
+ trigger: AutomationTriggerTraceSchema,
17254
+ conditions: array(AutomationConditionTraceNodeSchema),
17255
+ verdict: AutomationRunVerdictSchema.optional(),
17256
+ steps: array(AutomationStepTraceSchema),
17257
+ outcome: AutomationRunOutcomeSchema,
17258
+ /** How many identical consecutive skips this trace stands for (>= 1). */
17259
+ repeat: number().int().min(1),
17260
+ /** When the latest of those repeats happened. */
17261
+ lastAt: number()
17262
+ });
17263
+ /**
17264
+ * What the hub's `runs` ledger holds for one owner (`automation:<blockId>` or
17265
+ * `action:<actionId>`): the cooldown stamp and the trace ring.
17266
+ */
17267
+ var AutomationStateDocSchema = object({
17268
+ version: literal(1),
17269
+ lastRunStartedAt: number().nullable(),
17270
+ traces: array(AutomationRunTraceSchema)
17271
+ });
17272
+ /**
17273
+ * Actions — named, runnable things an operator (or an automation) can start,
17274
+ * declared as LIST ENTRIES, never as devices.
17275
+ *
17276
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
17277
+ * hub's `actions` record kind) or declared by an addon through the
17278
+ * `action-source` collection (a Home Assistant script, a scene …). The one
17279
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
17280
+ * catalog of its own (D3) — every `list` asks the providers live.
17281
+ *
17282
+ * This file is the data half: the entry schema, the variables an entry may
17283
+ * declare, and the pure resolution of the values a run was given. It holds no
17284
+ * runtime.
17285
+ */
17286
+ /** What a variable's value is. `select` takes one of `options`. */
17287
+ var ActionVariableTypeSchema = _enum([
17288
+ "string",
17289
+ "number",
17290
+ "boolean",
17291
+ "select"
17292
+ ]);
17293
+ var ACTION_VARIABLE_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
17294
+ var ActionVariableValueSchema = union([
17295
+ string(),
17296
+ number(),
17297
+ boolean()
17298
+ ]);
17299
+ /**
17300
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
17301
+ * value) or `{{vars.name}}` (inside a string) in their args.
17302
+ */
17303
+ var ActionVariableSchema = object({
17304
+ name: string().regex(ACTION_VARIABLE_NAME_RE),
17305
+ label: string().min(1).max(120).optional(),
17306
+ type: ActionVariableTypeSchema,
17307
+ required: boolean().optional(),
17308
+ default: ActionVariableValueSchema.optional(),
17309
+ /** The allowed values of a `select`. */
17310
+ options: array(string().min(1)).min(1).optional()
17311
+ }).superRefine((v, ctx) => {
17312
+ if (v.type === "select" && v.options === void 0) ctx.addIssue({
17313
+ code: "custom",
17314
+ message: "a select variable needs options",
17315
+ path: ["options"]
17316
+ });
17317
+ if (v.default !== void 0 && variableValueError(v, v.default) !== null) ctx.addIssue({
17318
+ code: "custom",
17319
+ message: variableValueError(v, v.default) ?? "invalid default",
17320
+ path: ["default"]
17321
+ });
17322
+ });
17323
+ /** Why `value` is not a legal value for the variable, or null when it is. */
17324
+ function variableValueError(variable, value) {
17325
+ switch (variable.type) {
17326
+ case "string": return typeof value === "string" ? null : `variable "${variable.name}" must be a string`;
17327
+ case "number": return typeof value === "number" && Number.isFinite(value) ? null : `variable "${variable.name}" must be a number`;
17328
+ case "boolean": return typeof value === "boolean" ? null : `variable "${variable.name}" must be true or false`;
17329
+ case "select": return typeof value === "string" && (variable.options ?? []).includes(value) ? null : `variable "${variable.name}" must be one of ${(variable.options ?? []).join(", ")}`;
17330
+ }
17331
+ }
17332
+ var ActionSourceKindSchema = _enum(["internal", "addon"]);
17333
+ function isInternalActionId(actionId) {
17334
+ return actionId.startsWith("internal:") && actionId.length > 9;
17335
+ }
17336
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
17337
+ function addonIdOfActionId(actionId) {
17338
+ if (isInternalActionId(actionId)) return null;
17339
+ const at = actionId.indexOf(":");
17340
+ return at > 0 && at < actionId.length - 1 ? actionId.slice(0, at) : null;
17341
+ }
17342
+ var ActionStatusSchema = object({
17343
+ isRunning: boolean(),
17344
+ /** Ms epoch the latest run STARTED; null when it never ran. */
17345
+ lastRunAt: number().nullable(),
17346
+ /** The latest run's failure; null on success or when it never ran. */
17347
+ lastError: string().nullable()
17348
+ });
17349
+ var ActionEntrySchema = object({
17350
+ /** `internal:<uuid>` or `<addonId>:<localId>`. */
17351
+ id: string().min(3),
17352
+ name: string().min(1).max(120),
17353
+ /** The addon that declared it; `core-blocks` for an internal one. */
17354
+ addonId: string().min(1),
17355
+ source: ActionSourceKindSchema,
17356
+ /** An addon-declared entry is edited where it was declared. */
17357
+ readOnly: boolean(),
17358
+ /** Internal only: cap and wait steps. No action-in-action, so no cycles. */
17359
+ steps: array(ActionStepSchema).min(1).max(50).optional(),
17360
+ variables: array(ActionVariableSchema).max(20).optional(),
17361
+ status: ActionStatusSchema
17362
+ }).superRefine((entry, ctx) => {
17363
+ if (entry.source === "internal") {
17364
+ if (!isInternalActionId(entry.id)) ctx.addIssue({
17365
+ code: "custom",
17366
+ message: "an internal action id is internal:<uuid>",
17367
+ path: ["id"]
17368
+ });
17369
+ return;
17370
+ }
17371
+ if (entry.steps !== void 0) ctx.addIssue({
17372
+ code: "custom",
17373
+ message: "only an internal action carries steps",
17374
+ path: ["steps"]
17375
+ });
17376
+ if (!entry.readOnly) ctx.addIssue({
17377
+ code: "custom",
17378
+ message: "an addon action is read-only here",
17379
+ path: ["readOnly"]
17380
+ });
17381
+ if (addonIdOfActionId(entry.id) === null) ctx.addIssue({
17382
+ code: "custom",
17383
+ message: "an addon action id is <addonId>:<localId>",
17384
+ path: ["id"]
17385
+ });
17386
+ });
17387
+ /** What an operator writes for an internal action. */
17388
+ var ActionInputSchema = object({
17389
+ name: string().min(1).max(120),
17390
+ steps: array(ActionStepSchema).min(1).max(50),
17391
+ variables: array(ActionVariableSchema).max(20).optional()
17392
+ });
17393
+ /** Who started a run. Absent = the operator pressed it. */
17394
+ var ActionRunOriginSchema = object({
17395
+ /** The automation block whose step started it. */
17396
+ blockId: string().min(1).optional() });
17397
+ /** `started` = the caller did not ask to wait. */
17398
+ var ActionRunOutcomeSchema = union([AutomationRunOutcomeSchema, literal("started")]);
17399
+ var ActionRunResultSchema = object({
17400
+ runId: string().min(1),
17401
+ outcome: ActionRunOutcomeSchema,
17402
+ /** Why it did not run, or what failed. */
17403
+ message: string().optional()
17404
+ });
17405
+ _enum([
17406
+ "not-found",
17407
+ "read-only",
17408
+ "invalid-variables",
17409
+ "invalid-steps",
17410
+ "not-linked",
17411
+ "busy"
17412
+ ]);
17413
+ object({
17414
+ actionId: string(),
17415
+ runId: string(),
17416
+ isRunning: boolean(),
17417
+ /** Set when the run ended. */
17418
+ outcome: AutomationRunOutcomeSchema.optional(),
17419
+ lastRunAt: number().nullable(),
17420
+ lastError: string().nullable()
16999
17421
  });
17000
17422
  /**
17001
17423
  * `addon-pages` — system-scoped singleton aggregator cap. Public-facing
@@ -20675,7 +21097,18 @@ method(object({}), object({ blocks: array(CoreBlockSchema) }), { auth: "admin" }
20675
21097
  kind: "mutation",
20676
21098
  auth: "admin",
20677
21099
  access: "view"
20678
- });
21100
+ }), method(object({
21101
+ blockId: string().min(1),
21102
+ trace: AutomationRunTraceSchema.optional(),
21103
+ runStartedAt: number().optional(),
21104
+ keepTraces: number().int().min(1).max(100).optional()
21105
+ }), _void(), {
21106
+ kind: "mutation",
21107
+ auth: "admin"
21108
+ }), method(object({ blockId: string().min(1) }), AutomationStateDocSchema, { auth: "admin" }), method(object({ blockId: string().min(1) }), object({
21109
+ traces: array(AutomationRunTraceSchema),
21110
+ lastError: string().nullable()
21111
+ }), { auth: "admin" });
20679
21112
  /**
20680
21113
  * `custom-model-registry` — collection cap exposing operator-registered
20681
21114
  * custom detection models. Each provider (today: `addon-model-studio`)
@@ -46076,73 +46509,6 @@ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSche
46076
46509
  kind: "mutation",
46077
46510
  auth: "admin"
46078
46511
  });
46079
- /**
46080
- * The class names a per-class COUNT can ever be keyed on — the macro ids, and
46081
- * only those.
46082
- *
46083
- * `preserveOriginal: false` below is the whole reason this set exists: the
46084
- * executor maps each raw COCO label onto its macro and DROPS everything else,
46085
- * so a tracked detection's `className` is always one of {@link MACRO_LABELS}.
46086
- * Any surface that indexes counts by class name — the ZoneAnalytics occupancy
46087
- * snapshot's `byClass`, and therefore every occupancy rule — can match nothing
46088
- * else. Derived from the catalog, never hand-listed, so a new macro joins it by
46089
- * being declared once.
46090
- *
46091
- * Cost of not having it, 2026-08-12: the notification rule editor offered the
46092
- * full taxonomy for an occupancy class (occupancy is the one condition that
46093
- * does not go through the engine's macro-expanding `expandClassSelector`), two
46094
- * live rules were authored on `car`, and both counters read 0 forever.
46095
- */
46096
- var DETECTION_MACRO_CLASSES = new Set([
46097
- {
46098
- id: "person",
46099
- name: "Person"
46100
- },
46101
- {
46102
- id: "vehicle",
46103
- name: "Vehicle"
46104
- },
46105
- {
46106
- id: "animal",
46107
- name: "Animal"
46108
- },
46109
- {
46110
- id: "package",
46111
- name: "Package"
46112
- }
46113
- ].map((l) => l.id));
46114
- /** True when `className` is a macro the detector can actually emit (and hence a
46115
- * class a count can be keyed on) — see {@link DETECTION_MACRO_CLASSES}. */
46116
- function isDetectionMacroClass(className) {
46117
- return DETECTION_MACRO_CLASSES.has(className.trim().toLowerCase());
46118
- }
46119
- var COCO_TO_MACRO = {
46120
- mapping: {
46121
- person: "person",
46122
- bicycle: "vehicle",
46123
- car: "vehicle",
46124
- motorcycle: "vehicle",
46125
- airplane: "vehicle",
46126
- bus: "vehicle",
46127
- train: "vehicle",
46128
- truck: "vehicle",
46129
- boat: "vehicle",
46130
- bird: "animal",
46131
- cat: "animal",
46132
- dog: "animal",
46133
- horse: "animal",
46134
- sheep: "animal",
46135
- cow: "animal",
46136
- elephant: "animal",
46137
- bear: "animal",
46138
- zebra: "animal",
46139
- giraffe: "animal",
46140
- suitcase: "package",
46141
- backpack: "package",
46142
- handbag: "package"
46143
- },
46144
- preserveOriginal: false
46145
- };
46146
46512
  var AUDIO_MACRO_LABELS = [
46147
46513
  {
46148
46514
  id: "speech",
@@ -60810,6 +61176,84 @@ var audioMetricsCapability = {
60810
61176
  */
60811
61177
  history: "none"
60812
61178
  };
61179
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
61180
+ actionId: string().min(1),
61181
+ variables: record(string(), ActionVariableValueSchema).optional()
61182
+ }), _void(), {
61183
+ kind: "mutation",
61184
+ auth: "admin"
61185
+ }), method(object({ actionId: string().min(1) }), _void(), {
61186
+ kind: "mutation",
61187
+ auth: "admin"
61188
+ });
61189
+ /**
61190
+ * actions — the ONE surface for runnable actions, for the UI and for
61191
+ * automations. Provided by the core-blocks builtin on the hub.
61192
+ *
61193
+ * An action is a LIST ENTRY, not a device (D883): either internal (cap and wait
61194
+ * steps, kept in the hub's `actions` record kind) or declared by an addon over
61195
+ * `action-source`. `list` asks every `action-source` provider LIVE and overlays
61196
+ * the run status from the hub's `runs` ledger; there is no catalog here to
61197
+ * drift (D3).
61198
+ *
61199
+ * **Permissions follow the camera, as linked devices do.** An action is linked
61200
+ * to cameras by the operator (`setLinks`, D356: nothing links itself). A
61201
+ * non-admin may `get` / `run` an action only through a camera they may see that
61202
+ * links it: they name the camera (`deviceId`, a top-level field so the scope
61203
+ * gate sees it) and the provider refuses an action that camera does not link.
61204
+ * An admin, and an in-process caller (an automation block), may run any action.
61205
+ * Every refusal is typed (`[not-linked] …`) and logged.
61206
+ *
61207
+ * A second `run` of an action that is already running is REFUSED — traced and
61208
+ * logged — never queued.
61209
+ */
61210
+ var ActionIdInput = object({ actionId: string().min(1) });
61211
+ var ActionLinksSchema = object({
61212
+ deviceId: number().int().nonnegative(),
61213
+ actionIds: array(string().min(1)).max(100)
61214
+ });
61215
+ method(object({}), array(ActionEntrySchema), { auth: "admin" }), method(object({
61216
+ actionId: string().min(1),
61217
+ deviceId: number().int().nonnegative().optional()
61218
+ }), object({ action: ActionEntrySchema.nullable() }), {
61219
+ role: "viewer",
61220
+ auth: "protected",
61221
+ kind: "query",
61222
+ caller: "required"
61223
+ }), method(object({
61224
+ actionId: string().min(1),
61225
+ variables: record(string(), ActionVariableValueSchema).optional(),
61226
+ wait: boolean().optional(),
61227
+ origin: ActionRunOriginSchema.optional(),
61228
+ deviceId: number().int().nonnegative().optional()
61229
+ }), ActionRunResultSchema, {
61230
+ role: "viewer",
61231
+ auth: "protected",
61232
+ kind: "mutation",
61233
+ caller: "required"
61234
+ }), method(ActionIdInput, _void(), {
61235
+ kind: "mutation",
61236
+ auth: "admin"
61237
+ }), method(ActionIdInput, object({ traces: array(AutomationRunTraceSchema) }), { auth: "admin" }), method(object({ action: ActionInputSchema }), object({ action: ActionEntrySchema }), {
61238
+ kind: "mutation",
61239
+ auth: "admin"
61240
+ }), method(object({
61241
+ actionId: string().min(1),
61242
+ action: ActionInputSchema.partial()
61243
+ }), object({ action: ActionEntrySchema }), {
61244
+ kind: "mutation",
61245
+ auth: "admin"
61246
+ }), method(ActionIdInput, _void(), {
61247
+ kind: "mutation",
61248
+ auth: "admin"
61249
+ }), method(object({ deviceId: number().int().nonnegative() }), array(ActionEntrySchema), {
61250
+ role: "viewer",
61251
+ auth: "protected",
61252
+ kind: "query"
61253
+ }), method(object({ deviceId: number().int().nonnegative() }), ActionLinksSchema, { auth: "admin" }), method(ActionLinksSchema, ActionLinksSchema, {
61254
+ kind: "mutation",
61255
+ auth: "admin"
61256
+ });
60813
61257
  /**
60814
61258
  * Automation-control cap. Models HA `automation.*` entities on
60815
61259
  * `DeviceType.Automation`. An automation is a trigger+condition+
@@ -67404,10 +67848,20 @@ var PetFeederDeviceStatusSchema = _enum([
67404
67848
  ]);
67405
67849
  var gramsPortion = number().int().min(4).max(200);
67406
67850
  var PetFeederStatusSchema = object({
67407
- /** Food currently in the bowl (grams). Null when the device has not
67408
- * reported a reading yet. On dual-hopper models this is the combined
67409
- * bowl reading; per-hopper levels live in `food1`/`food2`. */
67851
+ /** Food in the TANK (hopper), as the feeder's own sensor reports it
67852
+ * (`state.food`). A coarse level code, not grams — `0` is empty. The
67853
+ * amount in the bowl is `bowlFood`. On dual-hopper models this is the
67854
+ * combined reading; per-hopper levels live in `food1`/`food2`. Null when
67855
+ * the device has not reported yet. */
67410
67856
  foodLevel: number().nullable().meta(readingMeta("none")),
67857
+ /** Food in the BOWL as the feeder reports it (`state.bowl`), in the
67858
+ * feeder's own units. Null when the model reports none. */
67859
+ bowlFood: number().nullable().meta(readingMeta("none")),
67860
+ /** True while the pet is eating (`state.eating`). */
67861
+ eating: boolean(),
67862
+ /** Whether `markFoodReplenished` does anything on this model. When false
67863
+ * the client must not offer it. */
67864
+ supportsMarkRefilled: boolean(),
67411
67865
  /** Hopper-1 food level (grams) on dual-hopper feeders; null on
67412
67866
  * single-hopper models. */
67413
67867
  food1: number().nullable().meta(readingMeta("none")),
@@ -67415,8 +67869,8 @@ var PetFeederStatusSchema = object({
67415
67869
  * single-hopper models. */
67416
67870
  food2: number().nullable().meta(readingMeta("none")),
67417
67871
  /** Derived low-food flag — mirrors the HA `food_level` binary_sensor
67418
- * (`device_class: problem`, on = low). True when the bowl is empty /
67419
- * below the feeder's low threshold. */
67872
+ * (`device_class: problem`, on = low). True when the tank reports empty,
67873
+ * or, on a dual-hopper model, when EITHER hopper is empty. */
67420
67874
  lowFood: boolean(),
67421
67875
  /** Battery charge 0..100 (%). Null on mains-powered models or when the
67422
67876
  * device has no battery reading. */
@@ -67484,7 +67938,9 @@ var petFeederCapability = {
67484
67938
  kind: "mutation",
67485
67939
  auth: "protected"
67486
67940
  }),
67487
- /** Mark a hopper as refilled (D4H/D4S/D4SH). */
67941
+ /** Tell the PetKit cloud a hopper was refilled so it stops its low-food
67942
+ * reminders (D4H/D4S/D4SH only — see `supportsMarkRefilled`). It does not
67943
+ * change any sensor reading. */
67488
67944
  markFoodReplenished: method(object({ deviceId: number().int().nonnegative() }), _void(), {
67489
67945
  kind: "mutation",
67490
67946
  auth: "protected"
@@ -67557,6 +68013,7 @@ var petFeederCapability = {
67557
68013
  */
67558
68014
  history: { fields: {
67559
68015
  foodLevel: { kind: "gauge" },
68016
+ bowlFood: { kind: "gauge" },
67560
68017
  food1: { kind: "gauge" },
67561
68018
  food2: { kind: "gauge" },
67562
68019
  batteryPower: {
@@ -74767,6 +75224,104 @@ Object.freeze({
74767
75224
  access: "create",
74768
75225
  role: "admin"
74769
75226
  },
75227
+ "actions.create": {
75228
+ capName: "actions",
75229
+ capScope: "system",
75230
+ addonId: null,
75231
+ access: "create",
75232
+ role: "admin"
75233
+ },
75234
+ "actions.delete": {
75235
+ capName: "actions",
75236
+ capScope: "system",
75237
+ addonId: null,
75238
+ access: "delete",
75239
+ role: "admin"
75240
+ },
75241
+ "actions.get": {
75242
+ capName: "actions",
75243
+ capScope: "system",
75244
+ addonId: null,
75245
+ access: "view",
75246
+ role: "viewer"
75247
+ },
75248
+ "actions.getLinks": {
75249
+ capName: "actions",
75250
+ capScope: "system",
75251
+ addonId: null,
75252
+ access: "view",
75253
+ role: "admin"
75254
+ },
75255
+ "actions.getTraces": {
75256
+ capName: "actions",
75257
+ capScope: "system",
75258
+ addonId: null,
75259
+ access: "view",
75260
+ role: "admin"
75261
+ },
75262
+ "actions.list": {
75263
+ capName: "actions",
75264
+ capScope: "system",
75265
+ addonId: null,
75266
+ access: "view",
75267
+ role: "admin"
75268
+ },
75269
+ "actions.listForCamera": {
75270
+ capName: "actions",
75271
+ capScope: "system",
75272
+ addonId: null,
75273
+ access: "view",
75274
+ role: "viewer"
75275
+ },
75276
+ "actions.run": {
75277
+ capName: "actions",
75278
+ capScope: "system",
75279
+ addonId: null,
75280
+ access: "create",
75281
+ role: "viewer"
75282
+ },
75283
+ "actions.setLinks": {
75284
+ capName: "actions",
75285
+ capScope: "system",
75286
+ addonId: null,
75287
+ access: "create",
75288
+ role: "admin"
75289
+ },
75290
+ "actions.stop": {
75291
+ capName: "actions",
75292
+ capScope: "system",
75293
+ addonId: null,
75294
+ access: "create",
75295
+ role: "admin"
75296
+ },
75297
+ "actions.update": {
75298
+ capName: "actions",
75299
+ capScope: "system",
75300
+ addonId: null,
75301
+ access: "create",
75302
+ role: "admin"
75303
+ },
75304
+ "actionSource.listActions": {
75305
+ capName: "action-source",
75306
+ capScope: "system",
75307
+ addonId: null,
75308
+ access: "view",
75309
+ role: "admin"
75310
+ },
75311
+ "actionSource.runAction": {
75312
+ capName: "action-source",
75313
+ capScope: "system",
75314
+ addonId: null,
75315
+ access: "create",
75316
+ role: "admin"
75317
+ },
75318
+ "actionSource.stopAction": {
75319
+ capName: "action-source",
75320
+ capScope: "system",
75321
+ addonId: null,
75322
+ access: "create",
75323
+ role: "admin"
75324
+ },
74770
75325
  "addonPages.listPages": {
74771
75326
  capName: "addon-pages",
74772
75327
  capScope: "system",
@@ -76048,6 +76603,13 @@ Object.freeze({
76048
76603
  access: "view",
76049
76604
  role: "admin"
76050
76605
  },
76606
+ "coreBlocks.getAutomationRunState": {
76607
+ capName: "core-blocks",
76608
+ capScope: "system",
76609
+ addonId: null,
76610
+ access: "view",
76611
+ role: "admin"
76612
+ },
76051
76613
  "coreBlocks.getBlockSettings": {
76052
76614
  capName: "core-blocks",
76053
76615
  capScope: "system",
@@ -76069,6 +76631,13 @@ Object.freeze({
76069
76631
  access: "view",
76070
76632
  role: "admin"
76071
76633
  },
76634
+ "coreBlocks.getTraces": {
76635
+ capName: "core-blocks",
76636
+ capScope: "system",
76637
+ addonId: null,
76638
+ access: "view",
76639
+ role: "admin"
76640
+ },
76072
76641
  "coreBlocks.getTypeDefs": {
76073
76642
  capName: "core-blocks",
76074
76643
  capScope: "system",
@@ -76104,6 +76673,13 @@ Object.freeze({
76104
76673
  access: "create",
76105
76674
  role: "admin"
76106
76675
  },
76676
+ "coreBlocks.reportAutomationRun": {
76677
+ capName: "core-blocks",
76678
+ capScope: "system",
76679
+ addonId: null,
76680
+ access: "create",
76681
+ role: "admin"
76682
+ },
76107
76683
  "coreBlocks.restart": {
76108
76684
  capName: "core-blocks",
76109
76685
  capScope: "system",
@@ -88258,6 +88834,8 @@ var ML = "model and training machinery — no device action";
88258
88834
  var INTERNAL = "internal plumbing between addons — never operator-facing";
88259
88835
  /** Every system-scoped cap, with why it is not a device's vocabulary. */
88260
88836
  var SYSTEM_CAP_VOCABULARY = {
88837
+ "action-source": system(INTERNAL),
88838
+ actions: system("declared actions — run from the Actions page and from the cameras that link them"),
88261
88839
  "addon-pages": system(UI_PLUMBING),
88262
88840
  "addon-pages-source": system(UI_PLUMBING),
88263
88841
  "addon-routes": system(UI_PLUMBING),
@@ -88454,6 +89032,31 @@ Object.freeze({
88454
89032
  form: "single",
88455
89033
  optional: false
88456
89034
  }],
89035
+ "actions.get": [{
89036
+ name: "deviceId",
89037
+ form: "single",
89038
+ optional: true
89039
+ }],
89040
+ "actions.getLinks": [{
89041
+ name: "deviceId",
89042
+ form: "single",
89043
+ optional: false
89044
+ }],
89045
+ "actions.listForCamera": [{
89046
+ name: "deviceId",
89047
+ form: "single",
89048
+ optional: false
89049
+ }],
89050
+ "actions.run": [{
89051
+ name: "deviceId",
89052
+ form: "single",
89053
+ optional: true
89054
+ }],
89055
+ "actions.setLinks": [{
89056
+ name: "deviceId",
89057
+ form: "single",
89058
+ optional: false
89059
+ }],
88457
89060
  "addonSettings.getDeviceSettings": [{
88458
89061
  name: "deviceId",
88459
89062
  form: "single",
@@ -94632,6 +95235,16 @@ function templateCarriesSummary(body) {
94632
95235
  if (body === void 0 || body.trim().length === 0) return true;
94633
95236
  return SUMMARY_CONTENT_VAR.test(body);
94634
95237
  }
95238
+ /**
95239
+ * Make a raw wire id readable when no dictionary knows it: `delivery_truck`
95240
+ * becomes "Delivery truck", `open-door` becomes "Open door". It invents no
95241
+ * translation — the words stay the id's own, only separators and the first
95242
+ * letter change. An empty id stays empty.
95243
+ */
95244
+ function humanizeId(id) {
95245
+ const spaced = id.replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
95246
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
95247
+ }
94635
95248
  new Set([
94636
95249
  "disarmed",
94637
95250
  "armed_home",
@@ -94664,6 +95277,18 @@ new Set([
94664
95277
  "live"
94665
95278
  ]);
94666
95279
  /**
95280
+ * ONE dictionary of human labels for event kinds (detection macros/subs,
95281
+ * sensors, audio macros, …) — the same vocabulary the viewer filters and the
95282
+ * notification centre speak (R7).
95283
+ *
95284
+ * Wire ids stay raw (`cat`, `animal`, `audio-dog`). What a human reads is a
95285
+ * label, and a surface that translates on its own is a surface that disagrees
95286
+ * with the next one. Admin UI, viewer and NC resolve through {@link eventKindLabel}.
95287
+ *
95288
+ * Keys match {@link EVENT_TAXONOMY}. An OPEN kind (a custom model class) falls
95289
+ * back to the raw word — never an exception, never an empty chip.
95290
+ */
95291
+ /**
94667
95292
  * Every kind the dictionary answers for. Order is stable; the set is held to
94668
95293
  * {@link EVENT_TAXONOMY} by the spec.
94669
95294
  */
@@ -95033,7 +95658,7 @@ function isEventKindLabelKey(key) {
95033
95658
  */
95034
95659
  function eventKindLabel(kind, locale) {
95035
95660
  const key = kind.trim().toLowerCase();
95036
- return isEventKindLabelKey(key) ? EVENT_KIND_LABELS[key][locale] : kind;
95661
+ return isEventKindLabelKey(key) ? EVENT_KIND_LABELS[key][locale] : humanizeId(kind);
95037
95662
  }
95038
95663
  AppPageLangSchema.options;
95039
95664
  record(string().min(1), string());
@@ -95654,6 +96279,7 @@ function cosineSimilarity(a, b) {
95654
96279
  const denom = Math.sqrt(normA) * Math.sqrt(normB);
95655
96280
  return denom === 0 ? 0 : dotProduct / denom;
95656
96281
  }
96282
+ String.raw`(?<=^|["'(\s>,\[{])`;
95657
96283
  var MB = 1024 * 1024;
95658
96284
  var DEFAULT_POOL_MEMORY_POLICY = {
95659
96285
  sampleIntervalMs: 6e4,