@camstack/types 1.2.429 → 1.2.430

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 (35) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/addon.js +4 -3
  3. package/dist/addon.mjs +4 -3
  4. package/dist/automation/action.d.ts +210 -0
  5. package/dist/automation/condition-eval.d.ts +64 -0
  6. package/dist/automation/detection-trigger.d.ts +66 -0
  7. package/dist/automation/index.d.ts +12 -2
  8. package/dist/automation/recipe.d.ts +103 -1
  9. package/dist/automation/run-gate.d.ts +43 -0
  10. package/dist/automation/step-runner.d.ts +82 -0
  11. package/dist/automation/trace.d.ts +325 -0
  12. package/dist/capabilities/action-source.cap.d.ts +74 -0
  13. package/dist/capabilities/actions.cap.d.ts +459 -0
  14. package/dist/capabilities/core-blocks.cap.d.ts +761 -0
  15. package/dist/capabilities/index.d.ts +5 -1
  16. package/dist/client.js +73 -49
  17. package/dist/client.mjs +73 -49
  18. package/dist/enums/event-category.d.ts +5 -0
  19. package/dist/enums.js +1 -1
  20. package/dist/enums.mjs +1 -1
  21. package/dist/{event-category-Ave2VSxY.js → event-category-BhzwWQCH.js} +5 -0
  22. package/dist/{event-category-CT-hz5Rb.mjs → event-category-hdMR7CTW.mjs} +5 -0
  23. package/dist/generated/addon-api.d.ts +135 -0
  24. package/dist/generated/capability-router-map.d.ts +10 -4
  25. package/dist/generated/collection-array-methods.d.ts +1 -1
  26. package/dist/generated/device-proxy.d.ts +2 -0
  27. package/dist/generated/method-access-map.d.ts +1 -1
  28. package/dist/generated/method-device-selectors.d.ts +2 -2
  29. package/dist/generated/system-proxy.d.ts +5 -1
  30. package/dist/index.d.ts +4 -2
  31. package/dist/index.js +3032 -1182
  32. package/dist/index.mjs +2980 -1183
  33. package/dist/{sleep-hRhTSDlN.js → sleep-5zRztaHa.js} +12 -2
  34. package/dist/{sleep-BE7IAR2p.mjs → sleep-BHjNez61.mjs} +12 -2
  35. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.430] - 2026-10-27
11
+
12
+ ### Added
13
+
14
+ - Actions: declared list entries (action-source, actions caps), an action step for automations, and run traces kept in the hub ledger
15
+ - Automations gain detection and interval triggers, per-automation run modes (single, restart, queued, parallel, cooldown), condition reads from an in-memory mirror, and a ring of run traces
16
+
10
17
  ## [1.2.429] - 2026-10-27
11
18
 
12
19
  ### Added
package/dist/addon.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-hRhTSDlN.js");
3
- const require_event_category = require("./event-category-Ave2VSxY.js");
2
+ const require_sleep = require("./sleep-5zRztaHa.js");
3
+ const require_event_category = require("./event-category-BhzwWQCH.js");
4
4
  const require_err_msg = require("./err-msg-U7lLUMNz.js");
5
5
  //#region src/generated/cap-input-defaults.ts
6
6
  /**
@@ -362,9 +362,10 @@ function materialise(defaults) {
362
362
  * barrel (`ALL_CAPABILITY_DEFINITIONS`), which would cost ~144MB RSS per
363
363
  * forked runner. See docs/decisions/adr-0028.md.
364
364
  *
365
- * Coverage: 26 collection caps, 44 array methods.
365
+ * Coverage: 27 collection caps, 45 array methods.
366
366
  */
367
367
  var COLLECTION_ARRAY_METHODS = Object.freeze({
368
+ "action-source": ["listActions"],
368
369
  "addon-pages-source": ["listPages"],
369
370
  "addon-routes": ["getRoutes"],
370
371
  "addon-widgets-source": ["listWidgets"],
package/dist/addon.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { $ as expandCapMethods, $n as REVALIDATE_AFTER_MS, $r as isSecretMarker, $t as RecordFieldPathSchema, A as HF_REPO, An as RecordUniqueViolationError, Ar as buildSecretRef, B as withoutCallerStamp, Bn as RECORD_CLASSES, C as addonSliceHandleApi, Cn as InvalidRecordError, Dn as RecordNotFoundError, En as RECORDS_ERROR_CODES, Fn as UnknownRecordKindError, Fr as parseSecretRef, G as adminUiCapability, Gn as RECORD_MAX_DATA_BYTES, Hn as RECORD_DEFAULT_MAX_DATA_BYTES, I as deviceOpsCapability, In as asRecordsError, Jn as RECORD_QUERY_MAX, Jt as defineEntryKind, Kn as RECORD_MAX_INDEXES, L as viewerUiCapability, Ln as recordsErrorFromMessage, M as CAP_ADVANCE_FIELDS, Mn as RevConflictError, Mr as globalSecretScope, Nn as TransactRefusedError, Nr as isSecretRef, On as RecordSecretUnavailableError, Or as SECRET_REF_SCHEME, P as CAP_STATE_LEAVES, Pn as UnindexedFilterError, Pr as normalizeSecretOwner, Qn as resolveSecretViewsIn, Qr as isSecretFieldView, Qt as EntryDescriptorSchema, R as DEVICE_CHILDREN_BATCH_MAX, Rn as RECORD_BATCH_MAX, Rr as createEvent, Sn as ForeignSecretMarkerError, Tn as PlaintextSecretError, U as describeCustomActions, Un as RECORD_DELETE_BATCH, Vn as RECORD_DATABASES, Wn as RECORD_ID_MAX_LENGTH, Xn as RECORD_SETTINGS_MAX_ROWS, Xr as classifySecretWrite, Xt as createRecordsAccess, Yn as RECORD_SECRET_COLLECTION_PREFIX, Yr as SECRET_CLEAR, Yt as entryBody, Zn as RECORD_SWEEP_INTERVAL_MS, Zr as isSecretClear, Zt as assertTransactScope, _ as readErrorI18n, _n as collectZodSecretPaths, an as assertValidDeclaration, ar as registerKnownSecret, b as isDeviceScopedCap, bn as DeclarationRefusedError, c as asString, cn as REMOVE_LEAF, cr as PEER_BYTES_CHUNK_BYTES, ct as readNodePin, d as parseJsonUnknown, dn as parseSecretPath, dr as PEER_BYTES_TICKET_TTL_MS, dt as ReadinessRegistry, ei as isSecretMarkerLike, en as RecordIndexSchema, er as createSecretsAccess, f as hfModelUrl, fn as recordArrayItemPath, ft as ReadinessTimeoutError, g as ErrorI18nPayloadSchema, gr as BaseAddon, hn as secretArrayPrefixes, hr as DisposerChain, ht as scopeKey, in as StoredRecordKindDeclarationSchema, ir as knownSecretCount, jn as RecordsError, jr as formatSecretScope, kn as RecordTooLargeError, kr as arrayItemFieldPath, ln as collectSecretLeaves, lr as PEER_BYTES_MAX_OUTSTANDING, mn as recordSecretRef, ni as toSecretFieldView, nn as RecordKindNameSchema, nr as clearKnownSecrets, o as asJsonObject, on as declarationsOf, or as scrubKnownSecrets, p as BOOT_RECOVERY_BACKOFF_MS, pn as recordSecretCollection, pr as DATAPLANE_SECRET_HEADER, qn as RECORD_MAX_INDEX_FIELDS, qt as makeInMemoryRecords, r as maskUrlCredentials, rn as RecordRetentionSchema, rr as forgetKnownSecret, sn as defineRecordKinds, sr as shortSecretRefHash, st as nodePin, t as sleep, ti as makeSecretMarker, tn as RecordKindDeclarationSchema, tr as MIN_SCRUBBED_LENGTH, un as mapSecretLeaves, ur as PEER_BYTES_STALL_MS, ut as MODEL_FORMATS, vn as isZodSecret, vr as normalizeAddonInitResult, w as createEventBusSliceSource, wn as NoCallerError, x as createDeviceProxy, xn as FilterRequiredError, y as DEVICE_SCOPED_CAPS, yn as BatchTooLargeError, z as CALLER_STAMPED_CAPABILITIES, zn as RECORD_BATCH_MAX_BYTES, zr as emitReadiness } from "./sleep-BE7IAR2p.mjs";
2
- import { t as EventCategory } from "./event-category-CT-hz5Rb.mjs";
1
+ import { $ as expandCapMethods, $n as REVALIDATE_AFTER_MS, $r as isSecretMarker, $t as RecordFieldPathSchema, A as HF_REPO, An as RecordUniqueViolationError, Ar as buildSecretRef, B as withoutCallerStamp, Bn as RECORD_CLASSES, C as addonSliceHandleApi, Cn as InvalidRecordError, Dn as RecordNotFoundError, En as RECORDS_ERROR_CODES, Fn as UnknownRecordKindError, Fr as parseSecretRef, G as adminUiCapability, Gn as RECORD_MAX_DATA_BYTES, Hn as RECORD_DEFAULT_MAX_DATA_BYTES, I as deviceOpsCapability, In as asRecordsError, Jn as RECORD_QUERY_MAX, Jt as defineEntryKind, Kn as RECORD_MAX_INDEXES, L as viewerUiCapability, Ln as recordsErrorFromMessage, M as CAP_ADVANCE_FIELDS, Mn as RevConflictError, Mr as globalSecretScope, Nn as TransactRefusedError, Nr as isSecretRef, On as RecordSecretUnavailableError, Or as SECRET_REF_SCHEME, P as CAP_STATE_LEAVES, Pn as UnindexedFilterError, Pr as normalizeSecretOwner, Qn as resolveSecretViewsIn, Qr as isSecretFieldView, Qt as EntryDescriptorSchema, R as DEVICE_CHILDREN_BATCH_MAX, Rn as RECORD_BATCH_MAX, Rr as createEvent, Sn as ForeignSecretMarkerError, Tn as PlaintextSecretError, U as describeCustomActions, Un as RECORD_DELETE_BATCH, Vn as RECORD_DATABASES, Wn as RECORD_ID_MAX_LENGTH, Xn as RECORD_SETTINGS_MAX_ROWS, Xr as classifySecretWrite, Xt as createRecordsAccess, Yn as RECORD_SECRET_COLLECTION_PREFIX, Yr as SECRET_CLEAR, Yt as entryBody, Zn as RECORD_SWEEP_INTERVAL_MS, Zr as isSecretClear, Zt as assertTransactScope, _ as readErrorI18n, _n as collectZodSecretPaths, an as assertValidDeclaration, ar as registerKnownSecret, b as isDeviceScopedCap, bn as DeclarationRefusedError, c as asString, cn as REMOVE_LEAF, cr as PEER_BYTES_CHUNK_BYTES, ct as readNodePin, d as parseJsonUnknown, dn as parseSecretPath, dr as PEER_BYTES_TICKET_TTL_MS, dt as ReadinessRegistry, ei as isSecretMarkerLike, en as RecordIndexSchema, er as createSecretsAccess, f as hfModelUrl, fn as recordArrayItemPath, ft as ReadinessTimeoutError, g as ErrorI18nPayloadSchema, gr as BaseAddon, hn as secretArrayPrefixes, hr as DisposerChain, ht as scopeKey, in as StoredRecordKindDeclarationSchema, ir as knownSecretCount, jn as RecordsError, jr as formatSecretScope, kn as RecordTooLargeError, kr as arrayItemFieldPath, ln as collectSecretLeaves, lr as PEER_BYTES_MAX_OUTSTANDING, mn as recordSecretRef, ni as toSecretFieldView, nn as RecordKindNameSchema, nr as clearKnownSecrets, o as asJsonObject, on as declarationsOf, or as scrubKnownSecrets, p as BOOT_RECOVERY_BACKOFF_MS, pn as recordSecretCollection, pr as DATAPLANE_SECRET_HEADER, qn as RECORD_MAX_INDEX_FIELDS, qt as makeInMemoryRecords, r as maskUrlCredentials, rn as RecordRetentionSchema, rr as forgetKnownSecret, sn as defineRecordKinds, sr as shortSecretRefHash, st as nodePin, t as sleep, ti as makeSecretMarker, tn as RecordKindDeclarationSchema, tr as MIN_SCRUBBED_LENGTH, un as mapSecretLeaves, ur as PEER_BYTES_STALL_MS, ut as MODEL_FORMATS, vn as isZodSecret, vr as normalizeAddonInitResult, w as createEventBusSliceSource, wn as NoCallerError, x as createDeviceProxy, xn as FilterRequiredError, y as DEVICE_SCOPED_CAPS, yn as BatchTooLargeError, z as CALLER_STAMPED_CAPABILITIES, zn as RECORD_BATCH_MAX_BYTES, zr as emitReadiness } from "./sleep-BHjNez61.mjs";
2
+ import { t as EventCategory } from "./event-category-hdMR7CTW.mjs";
3
3
  import { a as DeviceType, t as errMsg } from "./err-msg-DIorGYeC.mjs";
4
4
  //#region src/generated/cap-input-defaults.ts
5
5
  /**
@@ -361,9 +361,10 @@ function materialise(defaults) {
361
361
  * barrel (`ALL_CAPABILITY_DEFINITIONS`), which would cost ~144MB RSS per
362
362
  * forked runner. See docs/decisions/adr-0028.md.
363
363
  *
364
- * Coverage: 26 collection caps, 44 array methods.
364
+ * Coverage: 27 collection caps, 45 array methods.
365
365
  */
366
366
  var COLLECTION_ARRAY_METHODS = Object.freeze({
367
+ "action-source": ["listActions"],
367
368
  "addon-pages-source": ["listPages"],
368
369
  "addon-routes": ["getRoutes"],
369
370
  "addon-widgets-source": ["listWidgets"],
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Actions — named, runnable things an operator (or an automation) can start,
3
+ * declared as LIST ENTRIES, never as devices.
4
+ *
5
+ * An entry is either **internal** (a short list of cap/wait steps kept in the
6
+ * hub's `actions` record kind) or declared by an addon through the
7
+ * `action-source` collection (a Home Assistant script, a scene …). The one
8
+ * surface the UI and automations talk to is the `actions` cap; it keeps no
9
+ * catalog of its own (D3) — every `list` asks the providers live.
10
+ *
11
+ * This file is the data half: the entry schema, the variables an entry may
12
+ * declare, and the pure resolution of the values a run was given. It holds no
13
+ * runtime.
14
+ */
15
+ import { z } from 'zod';
16
+ /** What a variable's value is. `select` takes one of `options`. */
17
+ export declare const ActionVariableTypeSchema: z.ZodEnum<{
18
+ boolean: "boolean";
19
+ number: "number";
20
+ select: "select";
21
+ string: "string";
22
+ }>;
23
+ export type ActionVariableType = z.infer<typeof ActionVariableTypeSchema>;
24
+ export declare const ActionVariableValueSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
25
+ export type ActionVariableValue = z.infer<typeof ActionVariableValueSchema>;
26
+ /**
27
+ * One declared variable. Steps reference it as `{ $var: 'name' }` (the whole
28
+ * value) or `{{vars.name}}` (inside a string) in their args.
29
+ */
30
+ export declare const ActionVariableSchema: z.ZodObject<{
31
+ name: z.ZodString;
32
+ label: z.ZodOptional<z.ZodString>;
33
+ type: z.ZodEnum<{
34
+ boolean: "boolean";
35
+ number: "number";
36
+ select: "select";
37
+ string: "string";
38
+ }>;
39
+ required: z.ZodOptional<z.ZodBoolean>;
40
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
41
+ options: z.ZodOptional<z.ZodArray<z.ZodString>>;
42
+ }, z.core.$strip>;
43
+ export type ActionVariable = z.infer<typeof ActionVariableSchema>;
44
+ export type ResolvedActionVariables = {
45
+ readonly ok: true;
46
+ readonly values: Readonly<Record<string, ActionVariableValue>>;
47
+ } | {
48
+ readonly ok: false;
49
+ readonly error: string;
50
+ };
51
+ /**
52
+ * The values a run starts with: what the caller gave, checked against the
53
+ * declaration, with defaults filled. A value the entry did not declare is
54
+ * REFUSED — a typo in a variable name must not silently run with the default.
55
+ */
56
+ export declare function resolveActionVariables(declared: readonly ActionVariable[] | undefined, given: Readonly<Record<string, ActionVariableValue>> | undefined): ResolvedActionVariables;
57
+ export declare const ActionSourceKindSchema: z.ZodEnum<{
58
+ addon: "addon";
59
+ internal: "internal";
60
+ }>;
61
+ export type ActionSourceKind = z.infer<typeof ActionSourceKindSchema>;
62
+ /** Prefix of an internal action's id. */
63
+ export declare const INTERNAL_ACTION_PREFIX = "internal:";
64
+ export declare function internalActionId(uuid: string): string;
65
+ export declare function isInternalActionId(actionId: string): boolean;
66
+ /** The addon behind a `<addonId>:<localId>` id, or null for anything else (internal included). */
67
+ export declare function addonIdOfActionId(actionId: string): string | null;
68
+ export declare const ActionStatusSchema: z.ZodObject<{
69
+ isRunning: z.ZodBoolean;
70
+ lastRunAt: z.ZodNullable<z.ZodNumber>;
71
+ lastError: z.ZodNullable<z.ZodString>;
72
+ }, z.core.$strip>;
73
+ export type ActionStatus = z.infer<typeof ActionStatusSchema>;
74
+ export declare const IDLE_ACTION_STATUS: ActionStatus;
75
+ export declare const ActionEntrySchema: z.ZodObject<{
76
+ id: z.ZodString;
77
+ name: z.ZodString;
78
+ addonId: z.ZodString;
79
+ source: z.ZodEnum<{
80
+ addon: "addon";
81
+ internal: "internal";
82
+ }>;
83
+ readOnly: z.ZodBoolean;
84
+ steps: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
85
+ kind: z.ZodLiteral<"wait">;
86
+ seconds: z.ZodNumber;
87
+ }, z.core.$strip>, z.ZodObject<{
88
+ kind: z.ZodLiteral<"cap">;
89
+ deviceId: z.ZodUnion<readonly [z.ZodNumber, z.ZodArray<z.ZodNumber>, z.ZodObject<{
90
+ $var: z.ZodString;
91
+ }, z.core.$strip>]>;
92
+ cap: z.ZodString;
93
+ method: z.ZodString;
94
+ args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
95
+ }, z.core.$strip>], "kind">>>;
96
+ variables: z.ZodOptional<z.ZodArray<z.ZodObject<{
97
+ name: z.ZodString;
98
+ label: z.ZodOptional<z.ZodString>;
99
+ type: z.ZodEnum<{
100
+ boolean: "boolean";
101
+ number: "number";
102
+ select: "select";
103
+ string: "string";
104
+ }>;
105
+ required: z.ZodOptional<z.ZodBoolean>;
106
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
107
+ options: z.ZodOptional<z.ZodArray<z.ZodString>>;
108
+ }, z.core.$strip>>>;
109
+ status: z.ZodObject<{
110
+ isRunning: z.ZodBoolean;
111
+ lastRunAt: z.ZodNullable<z.ZodNumber>;
112
+ lastError: z.ZodNullable<z.ZodString>;
113
+ }, z.core.$strip>;
114
+ }, z.core.$strip>;
115
+ export type ActionEntry = z.infer<typeof ActionEntrySchema>;
116
+ /** What an operator writes for an internal action. */
117
+ export declare const ActionInputSchema: z.ZodObject<{
118
+ name: z.ZodString;
119
+ steps: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
120
+ kind: z.ZodLiteral<"wait">;
121
+ seconds: z.ZodNumber;
122
+ }, z.core.$strip>, z.ZodObject<{
123
+ kind: z.ZodLiteral<"cap">;
124
+ deviceId: z.ZodUnion<readonly [z.ZodNumber, z.ZodArray<z.ZodNumber>, z.ZodObject<{
125
+ $var: z.ZodString;
126
+ }, z.core.$strip>]>;
127
+ cap: z.ZodString;
128
+ method: z.ZodString;
129
+ args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
130
+ }, z.core.$strip>], "kind">>;
131
+ variables: z.ZodOptional<z.ZodArray<z.ZodObject<{
132
+ name: z.ZodString;
133
+ label: z.ZodOptional<z.ZodString>;
134
+ type: z.ZodEnum<{
135
+ boolean: "boolean";
136
+ number: "number";
137
+ select: "select";
138
+ string: "string";
139
+ }>;
140
+ required: z.ZodOptional<z.ZodBoolean>;
141
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
142
+ options: z.ZodOptional<z.ZodArray<z.ZodString>>;
143
+ }, z.core.$strip>>>;
144
+ }, z.core.$strip>;
145
+ export type ActionInput = z.infer<typeof ActionInputSchema>;
146
+ /** Who started a run. Absent = the operator pressed it. */
147
+ export declare const ActionRunOriginSchema: z.ZodObject<{
148
+ blockId: z.ZodOptional<z.ZodString>;
149
+ }, z.core.$strip>;
150
+ export type ActionRunOrigin = z.infer<typeof ActionRunOriginSchema>;
151
+ /** `started` = the caller did not ask to wait. */
152
+ export declare const ActionRunOutcomeSchema: z.ZodUnion<readonly [z.ZodEnum<{
153
+ aborted: "aborted";
154
+ failed: "failed";
155
+ ran: "ran";
156
+ "skipped-conditions": "skipped-conditions";
157
+ "skipped-cooldown": "skipped-cooldown";
158
+ "skipped-overlap": "skipped-overlap";
159
+ "skipped-trigger": "skipped-trigger";
160
+ }>, z.ZodLiteral<"started">]>;
161
+ export type ActionRunOutcome = z.infer<typeof ActionRunOutcomeSchema>;
162
+ export declare const ActionRunResultSchema: z.ZodObject<{
163
+ runId: z.ZodString;
164
+ outcome: z.ZodUnion<readonly [z.ZodEnum<{
165
+ aborted: "aborted";
166
+ failed: "failed";
167
+ ran: "ran";
168
+ "skipped-conditions": "skipped-conditions";
169
+ "skipped-cooldown": "skipped-cooldown";
170
+ "skipped-overlap": "skipped-overlap";
171
+ "skipped-trigger": "skipped-trigger";
172
+ }>, z.ZodLiteral<"started">]>;
173
+ message: z.ZodOptional<z.ZodString>;
174
+ }, z.core.$strip>;
175
+ export type ActionRunResult = z.infer<typeof ActionRunResultSchema>;
176
+ export declare const ActionErrorCodeSchema: z.ZodEnum<{
177
+ busy: "busy";
178
+ "invalid-steps": "invalid-steps";
179
+ "invalid-variables": "invalid-variables";
180
+ "not-found": "not-found";
181
+ "not-linked": "not-linked";
182
+ "read-only": "read-only";
183
+ }>;
184
+ export type ActionErrorCode = z.infer<typeof ActionErrorCodeSchema>;
185
+ /**
186
+ * A refusal with a reason a caller can branch on. The code leads the message
187
+ * (`[read-only] …`) because a cap call flattens an error to its text.
188
+ */
189
+ export declare class ActionError extends Error {
190
+ readonly code: ActionErrorCode;
191
+ constructor(code: ActionErrorCode, message: string);
192
+ }
193
+ /** The run-state change `actions.onRunChanged` carries. */
194
+ export declare const ActionRunChangedSchema: z.ZodObject<{
195
+ actionId: z.ZodString;
196
+ runId: z.ZodString;
197
+ isRunning: z.ZodBoolean;
198
+ outcome: z.ZodOptional<z.ZodEnum<{
199
+ aborted: "aborted";
200
+ failed: "failed";
201
+ ran: "ran";
202
+ "skipped-conditions": "skipped-conditions";
203
+ "skipped-cooldown": "skipped-cooldown";
204
+ "skipped-overlap": "skipped-overlap";
205
+ "skipped-trigger": "skipped-trigger";
206
+ }>>;
207
+ lastRunAt: z.ZodNullable<z.ZodNumber>;
208
+ lastError: z.ZodNullable<z.ZodString>;
209
+ }, z.core.$strip>;
210
+ export type ActionRunChanged = z.infer<typeof ActionRunChangedSchema>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Evaluating an automation's condition tree against the ConditionMirror.
3
+ *
4
+ * D49: a trigger is a one-shot event, so its gate must never hang on a read
5
+ * that can fail. Every field a condition leaf names is therefore kept in
6
+ * memory — filled at start, updated off device state-changed events, reconciled
7
+ * on a timer — and evaluation reads that memory ONLY. A value the mirror does
8
+ * not hold is UNKNOWN, which is `fail` with a reason, never a throw and never a
9
+ * `pass`. A failed refresh changes nothing: the previous value stays.
10
+ *
11
+ * The evaluator returns a node per tree position (containers, leaves, and the
12
+ * ones a short-circuit never reached) with a stable path and a human reason, so
13
+ * the editor can paint each block.
14
+ */
15
+ import type { AutomationCondition } from './recipe.js';
16
+ import type { AutomationConditionTraceNode } from './trace.js';
17
+ /** One device cap whose slice the mirror must hold. */
18
+ export type MirrorSource = {
19
+ readonly kind: 'device';
20
+ readonly deviceId: number;
21
+ readonly cap: string;
22
+ } | {
23
+ readonly kind: 'stable';
24
+ readonly stableId: string;
25
+ readonly cap: string;
26
+ };
27
+ /** Every (device, cap) a tree reads, deduplicated. */
28
+ export declare function collectMirrorSources(cond: AutomationCondition | undefined): MirrorSource[];
29
+ export interface ConditionReader {
30
+ /** `undefined` = unknown. */
31
+ read(deviceId: number, cap: string, fieldPath: string): unknown;
32
+ readStable(stableId: string, cap: string, fieldPath: string): unknown;
33
+ }
34
+ export interface MirrorRefreshReport {
35
+ readonly updated: number;
36
+ /** Sources whose read failed or answered nothing — their old value stays. */
37
+ readonly kept: number;
38
+ }
39
+ export interface MirrorRefreshFetchers {
40
+ readonly fetchSlice: (deviceId: number, cap: string) => Promise<unknown>;
41
+ /** stableId → device id. A rejection leaves the previous map. */
42
+ readonly fetchStableIds: () => Promise<ReadonlyMap<string, number>>;
43
+ }
44
+ export declare class ConditionMirror implements ConditionReader {
45
+ private readonly sources;
46
+ private readonly slices;
47
+ private stableIds;
48
+ private readonly watched;
49
+ constructor(sources: readonly MirrorSource[]);
50
+ /** A `device.state-changed` event. Slices nobody reads are ignored. */
51
+ apply(deviceId: number, cap: string, slice: unknown): void;
52
+ private isStableTarget;
53
+ read(deviceId: number, cap: string, fieldPath: string): unknown;
54
+ readStable(stableId: string, cap: string, fieldPath: string): unknown;
55
+ /** Fill / reconcile. A read that fails or answers nothing keeps the old value. */
56
+ refresh(fetchers: MirrorRefreshFetchers): Promise<MirrorRefreshReport>;
57
+ }
58
+ export interface ConditionEvaluation {
59
+ readonly pass: boolean;
60
+ /** Pre-order: a container, then its children. */
61
+ readonly nodes: readonly AutomationConditionTraceNode[];
62
+ }
63
+ /** No condition tree is a pass with no nodes. */
64
+ export declare function evaluateConditionTree(cond: AutomationCondition | undefined, reader: ConditionReader, now: number): ConditionEvaluation;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The `detection` trigger's per-event decision: does this track-lifecycle
3
+ * event start a run, and if not, WHY not.
4
+ *
5
+ * Source: `EventCategory.PipelineAnalyticsTrackLifecycle` — payload
6
+ * {@link PipelineAnalyticsTrackLifecyclePayload} (`deviceId`, `trackId`,
7
+ * `phase` start|update|end, `bestClassName`, `bestConfidence`,
8
+ * `zonesVisited`). The event carries the device in its DATA, not on its
9
+ * `source`, so the filter reads `data.deviceId`.
10
+ *
11
+ * ONE fire per `(deviceId, trackId)`: a track fires the first time it satisfies
12
+ * the class, score and zone conditions (which may be on an `update`, once its
13
+ * best confidence has risen), and never again. The fired set is bounded and
14
+ * forgets a track on its `end` or after {@link FIRED_TTL_MS}, so a track whose
15
+ * `end` was dropped (events are telemetry, D8) cannot pin memory.
16
+ */
17
+ import type { AutomationTrigger } from './recipe.js';
18
+ export type DetectionTrigger = Extract<AutomationTrigger, {
19
+ kind: 'detection';
20
+ }>;
21
+ /** A track that never ended is forgotten after this long. */
22
+ export declare const FIRED_TTL_MS: number;
23
+ /** Hard bound on the fired set. */
24
+ export declare const FIRED_MAX = 512;
25
+ /** The fields of a track-lifecycle event the trigger reads. */
26
+ export interface TrackLifecycleInput {
27
+ readonly deviceId?: unknown;
28
+ readonly trackId?: unknown;
29
+ readonly phase?: unknown;
30
+ readonly bestClassName?: unknown;
31
+ readonly bestConfidence?: unknown;
32
+ readonly zonesVisited?: unknown;
33
+ }
34
+ /** What a detection run exposes to conditions and actions. */
35
+ export interface DetectionVars {
36
+ readonly deviceId: number;
37
+ readonly className: string;
38
+ readonly trackId: string;
39
+ readonly score: number;
40
+ }
41
+ export type DetectionDecision =
42
+ /** Not this trigger's business (other device, malformed, an `end`): neither fired nor traced. */
43
+ {
44
+ readonly kind: 'ignore';
45
+ readonly why: string;
46
+ } | {
47
+ readonly kind: 'reject';
48
+ readonly reason: string;
49
+ readonly vars: DetectionVars;
50
+ } | {
51
+ readonly kind: 'fire';
52
+ readonly reason: string;
53
+ readonly vars: DetectionVars;
54
+ };
55
+ /** `animal` matches a dog; `dog` matches only a dog (a cat is also an animal). */
56
+ export declare function detectionClassMatches(wanted: string, actual: string): boolean;
57
+ export declare class DetectionTriggerFilter {
58
+ private readonly trigger;
59
+ /** `${deviceId}|${trackId}` → fired at. Insertion-ordered, so the oldest is first. */
60
+ private readonly fired;
61
+ constructor(trigger: DetectionTrigger);
62
+ get firedCount(): number;
63
+ evaluate(event: TrackLifecycleInput, now: number): DetectionDecision;
64
+ private remember;
65
+ private expire;
66
+ }
@@ -10,5 +10,15 @@
10
10
  */
11
11
  export type { GenerateAutomationBlockInput } from './codegen.js';
12
12
  export { generateAutomationBlock } from './codegen.js';
13
- export type { AutomationAction, AutomationCondition, AutomationConditionAll, AutomationConditionAny, AutomationConditionExpression, AutomationConditionLeaf, AutomationConditionNot, AutomationConditionOperator, AutomationRecipe, AutomationTrigger, } from './recipe.js';
14
- export { AutomationActionSchema, AutomationConditionOperatorSchema, AutomationConditionSchema, AutomationRecipeSchema, AutomationTriggerSchema, conditionDepth, countConditionLeaves, MAX_CONDITION_DEPTH, MAX_CONDITION_LEAVES, validateRecipeBounds, } from './recipe.js';
13
+ export type { ConditionEvaluation, ConditionReader, MirrorRefreshFetchers, MirrorRefreshReport, MirrorSource, } from './condition-eval.js';
14
+ export { collectMirrorSources, ConditionMirror, evaluateConditionTree, } from './condition-eval.js';
15
+ export type { DetectionDecision, DetectionTrigger, DetectionVars, TrackLifecycleInput, } from './detection-trigger.js';
16
+ export { DetectionTriggerFilter, detectionClassMatches, FIRED_MAX, FIRED_TTL_MS, } from './detection-trigger.js';
17
+ export type { CooldownInput, CooldownVerdict, RunGateAdmission } from './run-gate.js';
18
+ export { RunGate } from './run-gate.js';
19
+ export type { AutomationConditionTraceNode, AutomationRunOutcome, AutomationRunTrace, AutomationRunVerdict, AutomationStateDoc, AutomationStepTrace, AutomationTriggerTrace, TraceNodeOutcome, TraceValue, } from './trace.js';
20
+ export { AutomationConditionTraceNodeSchema, EMPTY_AUTOMATION_STATE, mergeSkipTraces, AutomationRunOutcomeSchema, AutomationRunTraceSchema, AutomationRunVerdictSchema, AutomationStateDocSchema, AutomationStepTraceSchema, AutomationTriggerTraceSchema, appendTrace, buildTriggerTrace, isSkippedTrace, skipSignature, TraceNodeOutcomeSchema, TraceValueSchema, } from './trace.js';
21
+ export type { ActionStep, AutomationAction, AutomationCondition, AutomationConditionAll, AutomationConditionAny, AutomationConditionExpression, AutomationConditionLeaf, AutomationConditionNot, AutomationConditionOperator, AutomationRecipe, AutomationRunMode, AutomationTrigger, } from './recipe.js';
22
+ export { ActionStepSchema, AutomationActionSchema, AutomationCapStepSchema, AutomationWaitStepSchema, AutomationConditionOperatorSchema, AutomationConditionSchema, AutomationRecipeSchema, AutomationRunModeSchema, AutomationTriggerSchema, conditionDepth, countConditionLeaves, DEFAULT_TRACE_KEEP, MAX_TRACE_KEEP, MAX_CONDITION_DEPTH, MAX_CONDITION_LEAVES, validateRecipeBounds, } from './recipe.js';
23
+ export * from './action.js';
24
+ export * from './step-runner.js';
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * Three vocabularies, each salvaged rather than invented:
13
13
  *
14
- * - **Triggers** — the only genuinely new surface. Four members, none of which
14
+ * - **Triggers** — the only genuinely new surface. Six members, none of which
15
15
  * touches notification delivery. The NC keeps its four *pipeline* triggers;
16
16
  * these are device/time triggers and the two sets do not overlap.
17
17
  * - **Conditions** — `ALL`/`ANY`/`NOT` over two leaf kinds, bounded. The
@@ -108,6 +108,15 @@ export declare const AutomationTriggerSchema: z.ZodDiscriminatedUnion<[z.ZodObje
108
108
  cron: z.ZodString;
109
109
  }, z.core.$strip>, z.ZodObject<{
110
110
  kind: z.ZodLiteral<"manual">;
111
+ }, z.core.$strip>, z.ZodObject<{
112
+ kind: z.ZodLiteral<"detection">;
113
+ classes: z.ZodArray<z.ZodString>;
114
+ deviceIds: z.ZodArray<z.ZodNumber>;
115
+ zones: z.ZodOptional<z.ZodArray<z.ZodString>>;
116
+ minScore: z.ZodOptional<z.ZodNumber>;
117
+ }, z.core.$strip>, z.ZodObject<{
118
+ kind: z.ZodLiteral<"interval">;
119
+ minutes: z.ZodNumber;
111
120
  }, z.core.$strip>], "kind">;
112
121
  export type AutomationTrigger = z.infer<typeof AutomationTriggerSchema>;
113
122
  /**
@@ -128,6 +137,36 @@ export type AutomationTrigger = z.infer<typeof AutomationTriggerSchema>;
128
137
  * is called. The runner still receives a number and is untouched; the
129
138
  * resolution is the recipe's job, not the runner's.
130
139
  */
140
+ export declare const AutomationWaitStepSchema: z.ZodObject<{
141
+ kind: z.ZodLiteral<"wait">;
142
+ seconds: z.ZodNumber;
143
+ }, z.core.$strip>;
144
+ export declare const AutomationCapStepSchema: z.ZodObject<{
145
+ kind: z.ZodLiteral<"cap">;
146
+ deviceId: z.ZodUnion<readonly [z.ZodNumber, z.ZodArray<z.ZodNumber>, z.ZodObject<{
147
+ $var: z.ZodString;
148
+ }, z.core.$strip>]>;
149
+ cap: z.ZodString;
150
+ method: z.ZodString;
151
+ args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
152
+ }, z.core.$strip>;
153
+ /**
154
+ * The steps an internal Action may contain: cap and wait, and nothing that can
155
+ * call back into an Action — so an Action can never reach itself (no cycles).
156
+ */
157
+ export declare const ActionStepSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
158
+ kind: z.ZodLiteral<"wait">;
159
+ seconds: z.ZodNumber;
160
+ }, z.core.$strip>, z.ZodObject<{
161
+ kind: z.ZodLiteral<"cap">;
162
+ deviceId: z.ZodUnion<readonly [z.ZodNumber, z.ZodArray<z.ZodNumber>, z.ZodObject<{
163
+ $var: z.ZodString;
164
+ }, z.core.$strip>]>;
165
+ cap: z.ZodString;
166
+ method: z.ZodString;
167
+ args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
168
+ }, z.core.$strip>], "kind">;
169
+ export type ActionStep = z.infer<typeof ActionStepSchema>;
131
170
  export declare const AutomationActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
132
171
  kind: z.ZodLiteral<"wait">;
133
172
  seconds: z.ZodNumber;
@@ -139,11 +178,46 @@ export declare const AutomationActionSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
139
178
  cap: z.ZodString;
140
179
  method: z.ZodString;
141
180
  args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
181
+ }, z.core.$strip>, z.ZodObject<{
182
+ kind: z.ZodLiteral<"action">;
183
+ actionId: z.ZodString;
184
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
142
185
  }, z.core.$strip>, z.ZodObject<{
143
186
  kind: z.ZodLiteral<"code">;
144
187
  code: z.ZodString;
145
188
  }, z.core.$strip>], "kind">;
146
189
  export type AutomationAction = z.infer<typeof AutomationActionSchema>;
190
+ /**
191
+ * How a trigger that arrives while the automation is busy (or cooling down) is
192
+ * handled. One of the automation's execution modes, never a property of its
193
+ * actions. Absent means `single`.
194
+ *
195
+ * - `single` — drop the trigger while a run is in progress (traced + logged).
196
+ * - `restart` — abort the run in progress (at its next step boundary), start fresh.
197
+ * - `queued` — queue up to `max` triggers, drop the rest (traced).
198
+ * - `parallel` — up to `max` runs at once, drop the rest (traced).
199
+ * - `cooldown` — `single`, and no new run until `minutes` after the last run
200
+ * STARTED; persisted across a restart. A manual trigger bypasses only this
201
+ * wait, never the single/queued limits.
202
+ */
203
+ export declare const AutomationRunModeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
204
+ mode: z.ZodLiteral<"single">;
205
+ }, z.core.$strip>, z.ZodObject<{
206
+ mode: z.ZodLiteral<"restart">;
207
+ }, z.core.$strip>, z.ZodObject<{
208
+ mode: z.ZodLiteral<"queued">;
209
+ max: z.ZodNumber;
210
+ }, z.core.$strip>, z.ZodObject<{
211
+ mode: z.ZodLiteral<"parallel">;
212
+ max: z.ZodNumber;
213
+ }, z.core.$strip>, z.ZodObject<{
214
+ mode: z.ZodLiteral<"cooldown">;
215
+ minutes: z.ZodNumber;
216
+ }, z.core.$strip>], "mode">;
217
+ export type AutomationRunMode = z.infer<typeof AutomationRunModeSchema>;
218
+ /** Run traces kept per automation when the recipe does not say. */
219
+ export declare const DEFAULT_TRACE_KEEP = 20;
220
+ export declare const MAX_TRACE_KEEP = 100;
147
221
  export declare const AutomationRecipeSchema: z.ZodObject<{
148
222
  triggers: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
149
223
  kind: z.ZodLiteral<"device-state">;
@@ -163,6 +237,15 @@ export declare const AutomationRecipeSchema: z.ZodObject<{
163
237
  cron: z.ZodString;
164
238
  }, z.core.$strip>, z.ZodObject<{
165
239
  kind: z.ZodLiteral<"manual">;
240
+ }, z.core.$strip>, z.ZodObject<{
241
+ kind: z.ZodLiteral<"detection">;
242
+ classes: z.ZodArray<z.ZodString>;
243
+ deviceIds: z.ZodArray<z.ZodNumber>;
244
+ zones: z.ZodOptional<z.ZodArray<z.ZodString>>;
245
+ minScore: z.ZodOptional<z.ZodNumber>;
246
+ }, z.core.$strip>, z.ZodObject<{
247
+ kind: z.ZodLiteral<"interval">;
248
+ minutes: z.ZodNumber;
166
249
  }, z.core.$strip>], "kind">>;
167
250
  conditions: z.ZodOptional<z.ZodType<AutomationCondition, unknown, z.core.$ZodTypeInternals<AutomationCondition, unknown>>>;
168
251
  actions: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -176,10 +259,29 @@ export declare const AutomationRecipeSchema: z.ZodObject<{
176
259
  cap: z.ZodString;
177
260
  method: z.ZodString;
178
261
  args: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
262
+ }, z.core.$strip>, z.ZodObject<{
263
+ kind: z.ZodLiteral<"action">;
264
+ actionId: z.ZodString;
265
+ variables: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
179
266
  }, z.core.$strip>, z.ZodObject<{
180
267
  kind: z.ZodLiteral<"code">;
181
268
  code: z.ZodString;
182
269
  }, z.core.$strip>], "kind">>;
270
+ runMode: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
271
+ mode: z.ZodLiteral<"single">;
272
+ }, z.core.$strip>, z.ZodObject<{
273
+ mode: z.ZodLiteral<"restart">;
274
+ }, z.core.$strip>, z.ZodObject<{
275
+ mode: z.ZodLiteral<"queued">;
276
+ max: z.ZodNumber;
277
+ }, z.core.$strip>, z.ZodObject<{
278
+ mode: z.ZodLiteral<"parallel">;
279
+ max: z.ZodNumber;
280
+ }, z.core.$strip>, z.ZodObject<{
281
+ mode: z.ZodLiteral<"cooldown">;
282
+ minutes: z.ZodNumber;
283
+ }, z.core.$strip>], "mode">>;
284
+ keepTraces: z.ZodOptional<z.ZodNumber>;
183
285
  }, z.core.$strip>;
184
286
  export type AutomationRecipe = z.infer<typeof AutomationRecipeSchema>;
185
287
  /** Depth of a tree, root container = 1. A bare leaf is depth 1. */
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The admission gate of an automation — what its execution mode decides when a
3
+ * trigger arrives while a run is in progress, and (for `cooldown`) when the
4
+ * last run is too recent.
5
+ *
6
+ * In memory by design (D49): the decision is taken off in-process counters and
7
+ * never off a read that can fail. Only the cooldown's `lastRunStartedAt` is
8
+ * persisted (by the block, through `ctx.storage`) so a restart cannot re-fire
9
+ * inside the window; it is handed in, not read here.
10
+ *
11
+ * Usage: `admit()` once per trigger; every admitted run (`run` / `restart`, or
12
+ * a queued one when it is dequeued) ends with exactly one `release()`, which
13
+ * says whether a queued trigger now owns the freed slot.
14
+ */
15
+ import type { AutomationRunMode } from './recipe.js';
16
+ export interface RunGateAdmission {
17
+ readonly decision: 'run' | 'queued' | 'restart' | 'drop';
18
+ readonly reason: string;
19
+ }
20
+ export interface CooldownInput {
21
+ /** A manual trigger is operator intent: it skips the cooldown wait only. */
22
+ readonly manual: boolean;
23
+ readonly now: number;
24
+ readonly lastRunStartedAt: number | null;
25
+ }
26
+ export interface CooldownVerdict {
27
+ readonly ok: boolean;
28
+ readonly bypassed: boolean;
29
+ readonly reason: string;
30
+ }
31
+ export declare class RunGate {
32
+ private readonly mode;
33
+ private active;
34
+ private pending;
35
+ constructor(mode: AutomationRunMode);
36
+ get activeRuns(): number;
37
+ get queuedTriggers(): number;
38
+ admit(): RunGateAdmission;
39
+ /** The run holding a slot ended (any outcome). True = a queued trigger took the slot. */
40
+ release(): boolean;
41
+ /** Checked AFTER the conditions pass. Always `ok` outside `cooldown` mode. */
42
+ cooldown(input: CooldownInput): CooldownVerdict;
43
+ }