@camstack/types 1.2.451 → 1.2.453

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 (47) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/addon/base-addon.d.ts +27 -0
  3. package/dist/addon.js +8 -3
  4. package/dist/addon.mjs +8 -3
  5. package/dist/automation/condition-eval.d.ts +20 -5
  6. package/dist/automation/detection-subject.d.ts +154 -0
  7. package/dist/automation/detection-trigger.d.ts +16 -6
  8. package/dist/automation/index.d.ts +10 -4
  9. package/dist/automation/legacy-recipe.d.ts +130 -0
  10. package/dist/automation/normalize-units.d.ts +4 -3
  11. package/dist/automation/recipe.d.ts +286 -142
  12. package/dist/automation/stationary-trigger.d.ts +4 -4
  13. package/dist/automation/step-runner.d.ts +3 -1
  14. package/dist/automation/trace.d.ts +51 -6
  15. package/dist/automation/when.d.ts +295 -0
  16. package/dist/capabilities/actions.cap.d.ts +10 -0
  17. package/dist/capabilities/addon-settings.cap.d.ts +18 -0
  18. package/dist/capabilities/core-blocks.cap.d.ts +2546 -51
  19. package/dist/capabilities/device-discovery.cap.d.ts +2 -0
  20. package/dist/capabilities/device-manager.cap.d.ts +16 -1
  21. package/dist/capabilities/device-provider.cap.d.ts +165 -0
  22. package/dist/capabilities/index.d.ts +3 -3
  23. package/dist/capabilities/integrations.cap.d.ts +1 -0
  24. package/dist/capabilities/keychain.cap.d.ts +12 -0
  25. package/dist/capabilities/local-network.cap.d.ts +198 -0
  26. package/dist/capabilities/storage-admin.cap.d.ts +155 -0
  27. package/dist/capabilities/terminal-session.cap.d.ts +7 -0
  28. package/dist/client.js +8 -1
  29. package/dist/client.mjs +8 -1
  30. package/dist/device/base-device-provider.d.ts +17 -0
  31. package/dist/generated/addon-api.d.ts +84 -0
  32. package/dist/generated/collection-array-methods.d.ts +1 -1
  33. package/dist/generated/method-access-map.d.ts +1 -1
  34. package/dist/generated/system-proxy.d.ts +3 -3
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +1969 -706
  37. package/dist/index.mjs +1934 -703
  38. package/dist/interfaces/addon.d.ts +16 -0
  39. package/dist/interfaces/config-ui-secrets.d.ts +5 -3
  40. package/dist/interfaces/config-ui.d.ts +34 -1
  41. package/dist/{sleep-BSPhInRm.js → sleep-CewR0zRz.js} +125 -6
  42. package/dist/{sleep-63gM_1JR.mjs → sleep-CeyhrcOk.mjs} +125 -6
  43. package/dist/vendor-login-proxy/cookie-jar.d.ts +30 -0
  44. package/dist/vendor-login-proxy/index.d.ts +1 -0
  45. package/dist/vendor-login-proxy/login-proxy.d.ts +6 -0
  46. package/dist/vendor-login-proxy/url-rewrite.d.ts +9 -2
  47. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.2.453] - 2026-10-30
11
+
12
+ ### Changed
13
+
14
+ - Terminal instances report nodeInCluster and updateInstance accepts targetNodeId to re-point an instance.
15
+ - Notification targets pick a broker and list their options inside the add/edit form; the page-wide Find devices dialog is gone
16
+
17
+ ## [1.2.452] - 2026-10-30
18
+
19
+ ### Added
20
+
21
+ - Discovery modes: a device provider can declare how it is found on the network and classify the hits the scan engine returns; candidates can say they set up an integration or sit behind a gateway
22
+
23
+ ### Changed
24
+
25
+ - An editable-array config field can now hold keychain secrets per row
26
+ - A discovered child device can name the gateway it sits behind (parentStableId)
27
+ - The vendor login proxy can keep a sign-in's vendor cookies server-side (serverCookieJar)
28
+ - Hub: a stale-data audit finds record kinds and settings keys an addon no longer declares, read-only, with an operator-triggered cleanup
29
+ - Automations are one 'when' tree: an event starts a run, states must hold at that instant, and a detection is a macro class with an optional label (dog, a known face, a plate); stored recipes convert on read, and step traces now show the arguments they sent.
30
+
10
31
  ## [1.2.451] - 2026-10-30
11
32
 
12
33
  ### Changed
@@ -3,6 +3,17 @@ import type { AddonContext, AddonInitResult, CapabilitiesAccess, ProviderRegistr
3
3
  import type { ConfigField, ConfigSection, ConfigUISchema, ConfigUISchemaWithValues } from '../interfaces/config-ui.js';
4
4
  import { type DurableState } from './durable-state.js';
5
5
  import { type UnresolvedSecret } from './secret-settings-view.js';
6
+ export interface DeclaredSettingsKeys {
7
+ readonly keys: readonly string[];
8
+ }
9
+ export interface UnsetSettingsKeysOutcome {
10
+ readonly removed: readonly string[];
11
+ readonly refused: ReadonlyArray<{
12
+ readonly key: string;
13
+ readonly reason: string;
14
+ }>;
15
+ readonly secretsCleared: number;
16
+ }
6
17
  export type { CapabilitiesAccess };
7
18
  /**
8
19
  * Utility type that constrains a ConfigField's `key` to only accept
@@ -427,6 +438,22 @@ export declare abstract class BaseAddon<TConfig extends object = Record<string,
427
438
  * a warning and falls back rather than crashing boot.
428
439
  */
429
440
  protected state<T>(key: string, schema: ZodType<T>, fallback: T): DurableState<T>;
441
+ /** Durable-state keys this process registered through {@link state}. */
442
+ private readonly _declaredStateKeys;
443
+ /**
444
+ * Stale-data audit: every global-settings key this addon still declares —
445
+ * constructor defaults, settings-schema fields (layout containers included),
446
+ * durable-state keys and the root of each `secretStatePaths` entry. Answered
447
+ * here, in the addon's own process, so the hub keeps no copy of it.
448
+ */
449
+ getDeclaredSettingsKeys(): Promise<DeclaredSettingsKeys>;
450
+ private declaredKeySet;
451
+ /**
452
+ * Stale-data audit: remove stored global-settings keys the addon no longer
453
+ * declares, and clear the keychain values their markers held — except a value
454
+ * a surviving key still points at. A declared or absent key is refused.
455
+ */
456
+ unsetStoredSettingsKeys(keys: readonly string[]): Promise<UnsetSettingsKeysOutcome>;
430
457
  /** Per-device variant of {@link state}, backed by the per-device store. */
431
458
  protected deviceState<T>(deviceId: number, key: string, schema: ZodType<T>, fallback: T): DurableState<T>;
432
459
  /** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
package/dist/addon.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-BSPhInRm.js");
2
+ const require_sleep = require("./sleep-CewR0zRz.js");
3
3
  const require_event_category = require("./event-category-E17zaHjN.js");
4
4
  const require_err_msg = require("./err-msg-DkPZXb-g.js");
5
5
  //#region src/generated/cap-input-defaults.ts
@@ -367,7 +367,7 @@ function materialise(defaults) {
367
367
  * barrel (`ALL_CAPABILITY_DEFINITIONS`), which would cost ~144MB RSS per
368
368
  * forked runner. See docs/decisions/adr-0028.md.
369
369
  *
370
- * Coverage: 29 collection caps, 49 array methods.
370
+ * Coverage: 29 collection caps, 51 array methods.
371
371
  */
372
372
  var COLLECTION_ARRAY_METHODS = Object.freeze({
373
373
  "action-source": ["listActions"],
@@ -385,7 +385,12 @@ var COLLECTION_ARRAY_METHODS = Object.freeze({
385
385
  "listSupportedEntityKinds"
386
386
  ],
387
387
  "device-extension": ["listSlots", "listSources"],
388
- "device-provider": ["discoverDevices", "getDevices"],
388
+ "device-provider": [
389
+ "classifyDiscoveryHits",
390
+ "discoverDevices",
391
+ "getDevices",
392
+ "getDiscoveryModes"
393
+ ],
389
394
  "device-removal-hook": ["listDeviceData"],
390
395
  "failure-contribution": ["list"],
391
396
  "llm": [
package/dist/addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $n as RECORD_SECRET_COLLECTION_PREFIX, $r as SECRET_CLEAR, $t as entryBody, A as HF_REPO, An as RECORDS_ERROR_CODES, B as DEVICE_CHILDREN_BATCH_MAX, Bn as asRecordsError, C as addonSliceHandleApi, Cn as BatchTooLargeError, Dn as InvalidRecordError, En as ForeignSecretMarkerError, Fn as RecordsError, Fr as formatSecretScope, G as describeCustomActions, Gn as RECORD_DATABASES, H as withoutCallerStamp, Hn as RECORD_BATCH_MAX, Hr as createEvent, I as deviceOpsCapability, In as RevConflictError, Ir as globalSecretScope, Jn as RECORD_ID_MAX_LENGTH, Kn as RECORD_DEFAULT_MAX_DATA_BYTES, L as viewerUiCapability, Ln as TransactRefusedError, Lr as isSecretRef, M as CAP_ADVANCE_FIELDS, Mn as RecordSecretUnavailableError, Mr as SECRET_REF_SCHEME, Nn as RecordTooLargeError, Nr as arrayItemFieldPath, On as NoCallerError, P as CAP_STATE_LEAVES, Pn as RecordUniqueViolationError, Pr as buildSecretRef, Q as readNodePin, Qn as RECORD_QUERY_MAX, Qt as defineEntryKind, R as PipelineRunResultBridge, Rn as UnindexedFilterError, Rr as normalizeSecretOwner, Sn as isZodSecret, Sr as normalizeAddonInitResult, Tn as FilterRequiredError, U as customAction, Un as RECORD_BATCH_MAX_BYTES, Ur as emitReadiness, V as CALLER_STAMPED_CAPABILITIES, Vn as recordsErrorFromMessage, W as defineCustomActions, Wn as RECORD_CLASSES, Xn as RECORD_MAX_INDEXES, Y as addonPagesSourceCapability, Yn as RECORD_MAX_DATA_BYTES, Z as nodePin, Zn as RECORD_MAX_INDEX_FIELDS, Zt as makeInMemoryRecords, _ as readErrorI18n, _n as recordSecretCollection, _r as DATAPLANE_SECRET_HEADER, ai as makeSecretMarker, an as RecordKindDeclarationSchema, ar as MIN_SCRUBBED_LENGTH, b as isDeviceScopedCap, br as BaseAddon, c as asString, cn as StoredRecordKindDeclarationSchema, cr as knownSecretCount, d as parseJsonUnknown, dn as defineRecordKinds, dr as shortSecretRefHash, ei as classifySecretWrite, en as createRecordsAccess, er as RECORD_SETTINGS_MAX_ROWS, f as hfModelUrl, fn as REMOVE_LEAF, fr as PEER_BYTES_CHUNK_BYTES, g as ErrorI18nPayloadSchema, gn as recordArrayItemPath, gt as ReadinessTimeoutError, hn as parseSecretPath, hr as PEER_BYTES_TICKET_TTL_MS, ht as ReadinessRegistry, ii as isSecretMarkerLike, in as RecordIndexSchema, ir as createSecretsAccess, jn as RecordNotFoundError, kn as PlaintextSecretError, ln as assertValidDeclaration, lr as registerKnownSecret, mn as mapSecretLeaves, mr as PEER_BYTES_STALL_MS, mt as MODEL_FORMATS, ni as isSecretFieldView, nn as EntryDescriptorSchema, nr as resolveSecretViewsIn, o as asJsonObject, oi as toSecretFieldView, on as RecordKindNameSchema, or as clearKnownSecrets, p as BOOT_RECOVERY_BACKOFF_MS, pn as collectSecretLeaves, pr as PEER_BYTES_MAX_OUTSTANDING, q as adminUiCapability, qn as RECORD_DELETE_BATCH, r as maskUrlCredentials, ri as isSecretMarker, rn as RecordFieldPathSchema, rr as REVALIDATE_AFTER_MS, sn as RecordRetentionSchema, sr as forgetKnownSecret, st as expandCapMethods, t as sleep, ti as isSecretClear, tn as assertTransactScope, tr as RECORD_SWEEP_INTERVAL_MS, un as declarationsOf, ur as scrubKnownSecrets, vn as recordSecretRef, w as createEventBusSliceSource, wn as DeclarationRefusedError, x as createDeviceProxy, xn as collectZodSecretPaths, y as DEVICE_SCOPED_CAPS, yn as secretArrayPrefixes, yr as DisposerChain, yt as scopeKey, z as PipelineStepInputSchema, zn as UnknownRecordKindError, zr as parseSecretRef } from "./sleep-63gM_1JR.mjs";
1
+ import { $n as RECORD_SECRET_COLLECTION_PREFIX, $r as SECRET_CLEAR, $t as entryBody, A as HF_REPO, An as RECORDS_ERROR_CODES, B as DEVICE_CHILDREN_BATCH_MAX, Bn as asRecordsError, C as addonSliceHandleApi, Cn as BatchTooLargeError, Dn as InvalidRecordError, En as ForeignSecretMarkerError, Fn as RecordsError, Fr as formatSecretScope, G as describeCustomActions, Gn as RECORD_DATABASES, H as withoutCallerStamp, Hn as RECORD_BATCH_MAX, Hr as createEvent, I as deviceOpsCapability, In as RevConflictError, Ir as globalSecretScope, Jn as RECORD_ID_MAX_LENGTH, Kn as RECORD_DEFAULT_MAX_DATA_BYTES, L as viewerUiCapability, Ln as TransactRefusedError, Lr as isSecretRef, M as CAP_ADVANCE_FIELDS, Mn as RecordSecretUnavailableError, Mr as SECRET_REF_SCHEME, Nn as RecordTooLargeError, Nr as arrayItemFieldPath, On as NoCallerError, P as CAP_STATE_LEAVES, Pn as RecordUniqueViolationError, Pr as buildSecretRef, Q as readNodePin, Qn as RECORD_QUERY_MAX, Qt as defineEntryKind, R as PipelineRunResultBridge, Rn as UnindexedFilterError, Rr as normalizeSecretOwner, Sn as isZodSecret, Sr as normalizeAddonInitResult, Tn as FilterRequiredError, U as customAction, Un as RECORD_BATCH_MAX_BYTES, Ur as emitReadiness, V as CALLER_STAMPED_CAPABILITIES, Vn as recordsErrorFromMessage, W as defineCustomActions, Wn as RECORD_CLASSES, Xn as RECORD_MAX_INDEXES, Y as addonPagesSourceCapability, Yn as RECORD_MAX_DATA_BYTES, Z as nodePin, Zn as RECORD_MAX_INDEX_FIELDS, Zt as makeInMemoryRecords, _ as readErrorI18n, _n as recordSecretCollection, _r as DATAPLANE_SECRET_HEADER, ai as makeSecretMarker, an as RecordKindDeclarationSchema, ar as MIN_SCRUBBED_LENGTH, b as isDeviceScopedCap, br as BaseAddon, c as asString, cn as StoredRecordKindDeclarationSchema, cr as knownSecretCount, d as parseJsonUnknown, dn as defineRecordKinds, dr as shortSecretRefHash, ei as classifySecretWrite, en as createRecordsAccess, er as RECORD_SETTINGS_MAX_ROWS, f as hfModelUrl, fn as REMOVE_LEAF, fr as PEER_BYTES_CHUNK_BYTES, g as ErrorI18nPayloadSchema, gn as recordArrayItemPath, gt as ReadinessTimeoutError, hn as parseSecretPath, hr as PEER_BYTES_TICKET_TTL_MS, ht as ReadinessRegistry, ii as isSecretMarkerLike, in as RecordIndexSchema, ir as createSecretsAccess, jn as RecordNotFoundError, kn as PlaintextSecretError, ln as assertValidDeclaration, lr as registerKnownSecret, mn as mapSecretLeaves, mr as PEER_BYTES_STALL_MS, mt as MODEL_FORMATS, ni as isSecretFieldView, nn as EntryDescriptorSchema, nr as resolveSecretViewsIn, o as asJsonObject, oi as toSecretFieldView, on as RecordKindNameSchema, or as clearKnownSecrets, p as BOOT_RECOVERY_BACKOFF_MS, pn as collectSecretLeaves, pr as PEER_BYTES_MAX_OUTSTANDING, q as adminUiCapability, qn as RECORD_DELETE_BATCH, r as maskUrlCredentials, ri as isSecretMarker, rn as RecordFieldPathSchema, rr as REVALIDATE_AFTER_MS, sn as RecordRetentionSchema, sr as forgetKnownSecret, st as expandCapMethods, t as sleep, ti as isSecretClear, tn as assertTransactScope, tr as RECORD_SWEEP_INTERVAL_MS, un as declarationsOf, ur as scrubKnownSecrets, vn as recordSecretRef, w as createEventBusSliceSource, wn as DeclarationRefusedError, x as createDeviceProxy, xn as collectZodSecretPaths, y as DEVICE_SCOPED_CAPS, yn as secretArrayPrefixes, yr as DisposerChain, yt as scopeKey, z as PipelineStepInputSchema, zn as UnknownRecordKindError, zr as parseSecretRef } from "./sleep-CeyhrcOk.mjs";
2
2
  import { t as EventCategory } from "./event-category-DmZDPDf_.mjs";
3
3
  import { a as DeviceType, t as errMsg } from "./err-msg-BD5buxvT.mjs";
4
4
  //#region src/generated/cap-input-defaults.ts
@@ -366,7 +366,7 @@ function materialise(defaults) {
366
366
  * barrel (`ALL_CAPABILITY_DEFINITIONS`), which would cost ~144MB RSS per
367
367
  * forked runner. See docs/decisions/adr-0028.md.
368
368
  *
369
- * Coverage: 29 collection caps, 49 array methods.
369
+ * Coverage: 29 collection caps, 51 array methods.
370
370
  */
371
371
  var COLLECTION_ARRAY_METHODS = Object.freeze({
372
372
  "action-source": ["listActions"],
@@ -384,7 +384,12 @@ var COLLECTION_ARRAY_METHODS = Object.freeze({
384
384
  "listSupportedEntityKinds"
385
385
  ],
386
386
  "device-extension": ["listSlots", "listSources"],
387
- "device-provider": ["discoverDevices", "getDevices"],
387
+ "device-provider": [
388
+ "classifyDiscoveryHits",
389
+ "discoverDevices",
390
+ "getDevices",
391
+ "getDiscoveryModes"
392
+ ],
388
393
  "device-removal-hook": ["listDeviceData"],
389
394
  "failure-contribution": ["list"],
390
395
  "llm": [
@@ -12,7 +12,8 @@
12
12
  * ones a short-circuit never reached) with a stable path and a human reason, so
13
13
  * the editor can paint each block.
14
14
  */
15
- import type { AutomationCondition } from './recipe.js';
15
+ import { type LegacyCondition } from './legacy-recipe.js';
16
+ import { type AutomationWhen } from './when.js';
16
17
  import type { AutomationConditionTraceNode } from './trace.js';
17
18
  /** One device cap whose slice the mirror must hold. */
18
19
  export type MirrorSource = {
@@ -24,8 +25,10 @@ export type MirrorSource = {
24
25
  readonly stableId: string;
25
26
  readonly cap: string;
26
27
  };
27
- /** Every (device, cap) a tree reads, deduplicated. */
28
- export declare function collectMirrorSources(cond: AutomationCondition | undefined): MirrorSource[];
28
+ /** Every (device, cap) a `when` tree's STATE leaves read, deduplicated. */
29
+ export declare function collectWhenMirrorSources(cond: AutomationWhen | undefined): MirrorSource[];
30
+ /** Legacy name, for blocks generated before the single tree: an old condition tree. */
31
+ export declare function collectMirrorSources(cond: LegacyCondition | undefined): MirrorSource[];
29
32
  export interface ConditionReader {
30
33
  /** `undefined` = unknown. */
31
34
  read(deviceId: number, cap: string, fieldPath: string): unknown;
@@ -57,8 +60,20 @@ export declare class ConditionMirror implements ConditionReader {
57
60
  }
58
61
  export interface ConditionEvaluation {
59
62
  readonly pass: boolean;
63
+ /** A state leaf could not be read (the mirror held nothing for it). A false
64
+ * that comes with this is "not known", and a `not` over it is not to be trusted. */
65
+ readonly unknown: boolean;
60
66
  /** Pre-order: a container, then its children. */
61
67
  readonly nodes: readonly AutomationConditionTraceNode[];
62
68
  }
63
- /** No condition tree is a pass with no nodes. */
64
- export declare function evaluateConditionTree(cond: AutomationCondition | undefined, reader: ConditionReader, now: number): ConditionEvaluation;
69
+ /** Options of one evaluation of a `when` tree. */
70
+ export interface WhenEvaluationOptions {
71
+ /** The path of the event leaf that occurred (`when.0`); absent for a state-only tree. */
72
+ readonly firedPath?: string;
73
+ /** The root path (default `when`; the legacy wrapper keeps `conditions`). */
74
+ readonly rootPath?: string;
75
+ }
76
+ /** Evaluate a `when` tree: the fired event leaf is true, every other event leaf false. */
77
+ export declare function evaluateWhenTree(when: AutomationWhen, reader: ConditionReader, now: number, options?: WhenEvaluationOptions): ConditionEvaluation;
78
+ /** Legacy name, for blocks generated before the single tree. No tree is a pass with no nodes. */
79
+ export declare function evaluateConditionTree(cond: LegacyCondition | undefined, reader: ConditionReader, now: number): ConditionEvaluation;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * WHAT a detection-shaped leaf is about: one MACRO class (person, vehicle,
3
+ * animal, package) plus an OPTIONAL label that narrows it.
4
+ *
5
+ * The taxonomy is `COCO_TO_MACRO` (and `CLASS_MAP_MACRO_TARGETS`) — never a
6
+ * second list. The label that fits depends on the macro class:
7
+ *
8
+ * - `animal` → a sub-class (`dog`, `cat`, …);
9
+ * - `person` → a known face (an identity of the `face-gallery`);
10
+ * - `vehicle` → a plate (its text) or a sub-class (`car`, `truck`, …);
11
+ * - `package` → a sub-class.
12
+ *
13
+ * `other` exists only so a class the taxonomy does not know (a custom model's
14
+ * `forklift`) survives the conversion of a stored recipe without widening.
15
+ *
16
+ * A label is decided LATE (D683): face recognition and LPR land after the
17
+ * detection starts. The track-lifecycle `update` event carries the newly
18
+ * resolved `label` / `plateText`, which is why a leaf with a label fires when
19
+ * the subject GETS it — not at the first detection.
20
+ */
21
+ import { z } from 'zod';
22
+ declare const SubClassLabelSchema: z.ZodObject<{
23
+ kind: z.ZodLiteral<"sub-class">;
24
+ className: z.ZodString;
25
+ }, z.core.$strip>;
26
+ declare const IdentityLabelSchema: z.ZodObject<{
27
+ kind: z.ZodLiteral<"identity">;
28
+ identityId: z.ZodString;
29
+ name: z.ZodString;
30
+ }, z.core.$strip>;
31
+ declare const PlateLabelSchema: z.ZodObject<{
32
+ kind: z.ZodLiteral<"plate">;
33
+ text: z.ZodString;
34
+ }, z.core.$strip>;
35
+ export type SubClassLabel = z.infer<typeof SubClassLabelSchema>;
36
+ export type IdentityLabel = z.infer<typeof IdentityLabelSchema>;
37
+ export type PlateLabel = z.infer<typeof PlateLabelSchema>;
38
+ export declare const DetectionSubjectSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
39
+ macro: z.ZodLiteral<"animal">;
40
+ label: z.ZodOptional<z.ZodObject<{
41
+ kind: z.ZodLiteral<"sub-class">;
42
+ className: z.ZodString;
43
+ }, z.core.$strip>>;
44
+ }, z.core.$strip>, z.ZodObject<{
45
+ macro: z.ZodLiteral<"person">;
46
+ label: z.ZodOptional<z.ZodObject<{
47
+ kind: z.ZodLiteral<"identity">;
48
+ identityId: z.ZodString;
49
+ name: z.ZodString;
50
+ }, z.core.$strip>>;
51
+ }, z.core.$strip>, z.ZodObject<{
52
+ macro: z.ZodLiteral<"vehicle">;
53
+ label: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
54
+ kind: z.ZodLiteral<"sub-class">;
55
+ className: z.ZodString;
56
+ }, z.core.$strip>, z.ZodObject<{
57
+ kind: z.ZodLiteral<"plate">;
58
+ text: z.ZodString;
59
+ }, z.core.$strip>], "kind">>;
60
+ }, z.core.$strip>, z.ZodObject<{
61
+ macro: z.ZodLiteral<"package">;
62
+ label: z.ZodOptional<z.ZodObject<{
63
+ kind: z.ZodLiteral<"sub-class">;
64
+ className: z.ZodString;
65
+ }, z.core.$strip>>;
66
+ }, z.core.$strip>, z.ZodObject<{
67
+ macro: z.ZodLiteral<"other">;
68
+ className: z.ZodString;
69
+ }, z.core.$strip>], "macro">;
70
+ export type DetectionSubject = z.infer<typeof DetectionSubjectSchema>;
71
+ /**
72
+ * The subject of a PARKED object. The stationary registry's event carries a
73
+ * class and a zone, no face and no plate — an identity or plate label there
74
+ * could never match, so the schema refuses it instead of letting it sit dead.
75
+ */
76
+ export declare const StationarySubjectSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
77
+ macro: z.ZodLiteral<"animal">;
78
+ label: z.ZodOptional<z.ZodObject<{
79
+ kind: z.ZodLiteral<"sub-class">;
80
+ className: z.ZodString;
81
+ }, z.core.$strip>>;
82
+ }, z.core.$strip>, z.ZodObject<{
83
+ macro: z.ZodLiteral<"person">;
84
+ label: z.ZodOptional<z.ZodObject<{
85
+ kind: z.ZodLiteral<"identity">;
86
+ identityId: z.ZodString;
87
+ name: z.ZodString;
88
+ }, z.core.$strip>>;
89
+ }, z.core.$strip>, z.ZodObject<{
90
+ macro: z.ZodLiteral<"vehicle">;
91
+ label: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
92
+ kind: z.ZodLiteral<"sub-class">;
93
+ className: z.ZodString;
94
+ }, z.core.$strip>, z.ZodObject<{
95
+ kind: z.ZodLiteral<"plate">;
96
+ text: z.ZodString;
97
+ }, z.core.$strip>], "kind">>;
98
+ }, z.core.$strip>, z.ZodObject<{
99
+ macro: z.ZodLiteral<"package">;
100
+ label: z.ZodOptional<z.ZodObject<{
101
+ kind: z.ZodLiteral<"sub-class">;
102
+ className: z.ZodString;
103
+ }, z.core.$strip>>;
104
+ }, z.core.$strip>, z.ZodObject<{
105
+ macro: z.ZodLiteral<"other">;
106
+ className: z.ZodString;
107
+ }, z.core.$strip>], "macro">;
108
+ /** What the macro-class picker offers, and where a label's choices come from. */
109
+ export type DetectionLabelSource = {
110
+ readonly kind: 'sub-class';
111
+ } | {
112
+ readonly kind: 'cap';
113
+ readonly cap: string;
114
+ readonly method: string;
115
+ readonly label: 'identity' | 'plate';
116
+ };
117
+ /**
118
+ * Where an editor lists the labels of a macro class. Sub-classes come from the
119
+ * taxonomy (`subClassesOf`); identities and plates come from the galleries'
120
+ * own methods, so a picker reads the cap and never a copy of it.
121
+ */
122
+ export declare const DETECTION_LABEL_SOURCES: Readonly<Record<'animal' | 'person' | 'vehicle' | 'package', readonly DetectionLabelSource[]>>;
123
+ /** The raw classes the taxonomy files under a macro class. */
124
+ export declare function subClassesOf(macro: string): readonly string[];
125
+ /**
126
+ * A stored class string as a subject: a macro name stays a macro; a raw class
127
+ * the taxonomy files under a macro becomes that macro plus the sub-class label
128
+ * (`dog` → animal / dog); anything else is `other`, kept verbatim.
129
+ */
130
+ export declare function subjectFromClassName(className: string): DetectionSubject;
131
+ /** What an event says about the subject — all optional, off the bus. */
132
+ export interface SubjectObservation {
133
+ readonly className: string;
134
+ /** Every class the track was observed as. */
135
+ readonly classes?: readonly string[];
136
+ /** The display label (an identity name, a plate text) once resolved. */
137
+ readonly label?: string;
138
+ readonly identityId?: string;
139
+ readonly plateText?: string;
140
+ }
141
+ export type SubjectVerdict = {
142
+ readonly matches: true;
143
+ readonly label: string;
144
+ } | {
145
+ readonly matches: false;
146
+ readonly reason: string;
147
+ };
148
+ /** A human name for a subject, for a reason line. */
149
+ export declare function describeSubject(subject: DetectionSubject): string;
150
+ /** Does one observation satisfy one subject, and which label did it match? */
151
+ export declare function subjectMatches(subject: DetectionSubject, observed: SubjectObservation): SubjectVerdict;
152
+ /** The first subject an observation satisfies, else why the last one did not. */
153
+ export declare function anySubjectMatches(subjects: readonly DetectionSubject[], observed: SubjectObservation): SubjectVerdict;
154
+ export {};
@@ -9,15 +9,17 @@
9
9
  * `source`, so the filter reads `data.deviceId`.
10
10
  *
11
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
12
+ * the subject, score and zone conditions (which may be on an `update`, once its
13
+ * best confidence has risen, or once its face or plate has been resolved — D683
14
+ * decides identity late, so a leaf with a label fires on the update that
15
+ * carries it, never on the first detection), and never again. The fired set is bounded and
14
16
  * forgets a track on its `end` or after {@link FIRED_TTL_MS}, so a track whose
15
17
  * `end` was dropped (events are telemetry, D8) cannot pin memory.
16
18
  */
17
- import type { AutomationTrigger } from './recipe.js';
18
- export type DetectionTrigger = Extract<AutomationTrigger, {
19
- kind: 'detection';
20
- }>;
19
+ import type { LegacyDetectionTrigger } from './legacy-recipe.js';
20
+ import type { DetectionLeaf } from './when.js';
21
+ /** A detection leaf — or the old trigger a block generated before the single tree still hands over. */
22
+ export type DetectionTrigger = DetectionLeaf | LegacyDetectionTrigger;
21
23
  /** A track that never ended is forgotten after this long. */
22
24
  export declare const FIRED_TTL_MS: number;
23
25
  /** Hard bound on the fired set. */
@@ -28,6 +30,11 @@ export interface TrackLifecycleInput {
28
30
  readonly trackId?: unknown;
29
31
  readonly phase?: unknown;
30
32
  readonly bestClassName?: unknown;
33
+ readonly classes?: unknown;
34
+ /** The display label once resolved: an identity name, a plate text. */
35
+ readonly label?: unknown;
36
+ readonly identityId?: unknown;
37
+ readonly plateText?: unknown;
31
38
  readonly bestConfidence?: unknown;
32
39
  readonly zonesVisited?: unknown;
33
40
  }
@@ -37,6 +44,8 @@ export interface DetectionVars {
37
44
  readonly className: string;
38
45
  readonly trackId: string;
39
46
  readonly score: number;
47
+ /** The label the subject matched (`dog`, a face name, a plate); empty when the leaf names none. */
48
+ readonly label: string;
40
49
  }
41
50
  export type DetectionDecision =
42
51
  /** Not this trigger's business (other device, malformed, an `end`): neither fired nor traced. */
@@ -58,6 +67,7 @@ export declare class DetectionTriggerFilter {
58
67
  private readonly trigger;
59
68
  /** `${deviceId}|${trackId}` → fired at. Insertion-ordered, so the oldest is first. */
60
69
  private readonly fired;
70
+ private readonly subjects;
61
71
  constructor(trigger: DetectionTrigger);
62
72
  get firedCount(): number;
63
73
  evaluate(event: TrackLifecycleInput, now: number): DetectionDecision;
@@ -11,7 +11,7 @@
11
11
  export type { GenerateAutomationBlockInput } from './codegen.js';
12
12
  export { generateAutomationBlock } from './codegen.js';
13
13
  export type { ConditionEvaluation, ConditionReader, MirrorRefreshFetchers, MirrorRefreshReport, MirrorSource, } from './condition-eval.js';
14
- export { collectMirrorSources, ConditionMirror, evaluateConditionTree, } from './condition-eval.js';
14
+ export { collectMirrorSources, collectWhenMirrorSources, ConditionMirror, evaluateConditionTree, evaluateWhenTree, } from './condition-eval.js';
15
15
  export type { DetectionDecision, DetectionTrigger, DetectionVars, TrackLifecycleInput, } from './detection-trigger.js';
16
16
  export { DetectionTriggerFilter, detectionClassMatches, FIRED_MAX, FIRED_TTL_MS, } from './detection-trigger.js';
17
17
  export type { StationaryChangedInput, StationaryDecision, StationaryTrigger, StationaryVars, } from './stationary-trigger.js';
@@ -19,9 +19,15 @@ export { evaluateStationaryTrigger } from './stationary-trigger.js';
19
19
  export type { CooldownInput, CooldownVerdict, RunGateAdmission } from './run-gate.js';
20
20
  export { RunGate } from './run-gate.js';
21
21
  export type { AutomationConditionTraceNode, AutomationRunOutcome, AutomationRunTrace, AutomationRunVerdict, AutomationStateDoc, AutomationStepTrace, AutomationTriggerTrace, TraceNodeOutcome, TraceValue, } from './trace.js';
22
- export { AutomationConditionTraceNodeSchema, EMPTY_AUTOMATION_STATE, mergeSkipTraces, AutomationRunOutcomeSchema, AutomationRunTraceSchema, AutomationRunVerdictSchema, AutomationStateDocSchema, AutomationStepTraceSchema, AutomationTriggerTraceSchema, appendTrace, buildTriggerTrace, isSkippedTrace, skipSignature, TraceNodeOutcomeSchema, TraceValueSchema, } from './trace.js';
23
- export type { ActionStep, AutomationAction, AutomationCapStep, AutomationCondition, AutomationConditionAll, AutomationConditionAny, AutomationConditionExpression, AutomationConditionLeaf, AutomationConditionNot, AutomationConditionOperator, AutomationConditionStationaryPresent, AutomationRecipe, AutomationRunMode, AutomationTrigger, } from './recipe.js';
24
- 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';
22
+ export { AutomationConditionTraceNodeSchema, EMPTY_AUTOMATION_STATE, mergeSkipTraces, AutomationRunOutcomeSchema, AutomationRunTraceSchema, AutomationRunVerdictSchema, AutomationStateDocSchema, AutomationStepTraceSchema, AutomationTriggerTraceSchema, appendTrace, buildStepArgsTrace, buildTriggerTrace, isSkippedTrace, MAX_TRACE_ARG_LENGTH, MAX_TRACE_ARGS, REDACTED_ARG, skipSignature, TraceNodeOutcomeSchema, TraceValueSchema, } from './trace.js';
23
+ export type { ActionStep, AutomationAction, AutomationCapStep, AutomationRecipe, AutomationRunMode, LegacyAutomationRecipe, } from './recipe.js';
24
+ export { ActionStepSchema, AutomationActionSchema, AutomationCapStepSchema, AutomationWaitStepSchema, AutomationRecipeSchema, AutomationRunModeSchema, CurrentAutomationRecipeSchema, convertLegacyRecipe, DEFAULT_TRACE_KEEP, LegacyAutomationRecipeSchema, MAX_TRACE_KEEP, MAX_CONDITION_DEPTH, MAX_CONDITION_LEAVES, validateRecipeBounds, } from './recipe.js';
25
+ export type { AutomationAll, AutomationAny, AutomationConditionOperator, AutomationEventLeaf, AutomationLeaf, AutomationNot, AutomationStateLeaf, AutomationWhen, DetectionLeaf, DeviceBecomesLeaf, DeviceIsLeaf, ExpressionLeaf, StationaryLeaf, StationaryPresentLeaf, } from './when.js';
26
+ export { AutomationConditionOperatorSchema, AutomationWhenSchema, countWhenLeaves, forEachLeaf, hasEventLeaves, isEventLeaf, isLeaf, MAX_WHEN_DEPTH, MAX_WHEN_LEAVES, validateWhenTree, whenDepth, } from './when.js';
27
+ export type { LegacyCondition, LegacyTrigger, } from './legacy-recipe.js';
28
+ export { convertLegacyCondition, convertLegacyWhen, LegacyConditionSchema, LegacyTriggerSchema, } from './legacy-recipe.js';
29
+ export type { DetectionLabelSource, DetectionSubject, IdentityLabel, PlateLabel, SubClassLabel, SubjectObservation, SubjectVerdict, } from './detection-subject.js';
30
+ export { anySubjectMatches, DETECTION_LABEL_SOURCES, DetectionSubjectSchema, describeSubject, StationarySubjectSchema, subClassesOf, subjectFromClassName, subjectMatches, } from './detection-subject.js';
25
31
  export * from './action.js';
26
32
  export * from './step-runner.js';
27
33
  export { UnitWarningSchema, capUnitResolver, normalizeActionUnits, normalizeConditionUnits, normalizeRecipeUnits, } from './normalize-units.js';
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The recipe shape before the single `when` tree: a flat list of triggers
3
+ * (OR'd, each an instant) and an optional condition tree. Stored recipes —
4
+ * the `AUTOMATION_RECIPE` literal of every block saved so far, and whatever an
5
+ * older client still posts — are in this shape, so it stays READABLE here, and
6
+ * {@link convertLegacyRecipe} turns it into the new one, losslessly and one
7
+ * way. Nothing writes this shape any more.
8
+ *
9
+ * `{ triggers: [t1…tn], conditions?: C }`
10
+ * → `when = all(any(t1…tn), C)`, or `any(t1…tn)` without `C`;
11
+ * a single trigger is not wrapped in `any`.
12
+ * a `condition` leaf → a `device-is` leaf (the "is" verb);
13
+ * a `device-state` trigger → a `device-becomes` leaf (the "becomes" verb);
14
+ * `classes: ['dog']` → the macro `animal` with the label `dog`.
15
+ */
16
+ import { z } from 'zod';
17
+ import { type AutomationWhen } from './when.js';
18
+ export declare const LegacyTriggerSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
19
+ kind: z.ZodLiteral<"device-state">;
20
+ deviceId: z.ZodNumber;
21
+ cap: z.ZodString;
22
+ fieldPath: z.ZodString;
23
+ becomes: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
24
+ edge: z.ZodOptional<z.ZodBoolean>;
25
+ forMs: z.ZodOptional<z.ZodNumber>;
26
+ debounceMs: z.ZodOptional<z.ZodNumber>;
27
+ }, z.core.$strip>, z.ZodObject<{
28
+ kind: z.ZodLiteral<"device-event">;
29
+ category: z.ZodString;
30
+ deviceId: z.ZodOptional<z.ZodNumber>;
31
+ }, z.core.$strip>, z.ZodObject<{
32
+ kind: z.ZodLiteral<"schedule">;
33
+ cron: z.ZodString;
34
+ }, z.core.$strip>, z.ZodObject<{
35
+ kind: z.ZodLiteral<"manual">;
36
+ }, z.core.$strip>, z.ZodObject<{
37
+ kind: z.ZodLiteral<"detection">;
38
+ classes: z.ZodArray<z.ZodString>;
39
+ deviceIds: z.ZodArray<z.ZodNumber>;
40
+ zones: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
+ minScore: z.ZodOptional<z.ZodNumber>;
42
+ }, z.core.$strip>, z.ZodObject<{
43
+ kind: z.ZodLiteral<"stationary">;
44
+ deviceIds: z.ZodArray<z.ZodNumber>;
45
+ classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
46
+ zones: z.ZodOptional<z.ZodArray<z.ZodString>>;
47
+ phase: z.ZodEnum<{
48
+ appeared: "appeared";
49
+ departed: "departed";
50
+ }>;
51
+ }, z.core.$strip>, z.ZodObject<{
52
+ kind: z.ZodLiteral<"interval">;
53
+ minutes: z.ZodNumber;
54
+ }, z.core.$strip>], "kind">;
55
+ export type LegacyTrigger = z.infer<typeof LegacyTriggerSchema>;
56
+ export type LegacyDetectionTrigger = Extract<LegacyTrigger, {
57
+ kind: 'detection';
58
+ }>;
59
+ export type LegacyStationaryTrigger = Extract<LegacyTrigger, {
60
+ kind: 'stationary';
61
+ }>;
62
+ declare const LegacyConditionLeafSchema: z.ZodObject<{
63
+ kind: z.ZodLiteral<"condition">;
64
+ deviceId: z.ZodNumber;
65
+ cap: z.ZodString;
66
+ fieldPath: z.ZodString;
67
+ operator: z.ZodEnum<{
68
+ contains: "contains";
69
+ eq: "eq";
70
+ gt: "gt";
71
+ gte: "gte";
72
+ in: "in";
73
+ lt: "lt";
74
+ lte: "lte";
75
+ ne: "ne";
76
+ }>;
77
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>, z.ZodObject<{
78
+ value: z.ZodNumber;
79
+ unit: z.ZodString;
80
+ }, z.core.$strip>]>;
81
+ unit: z.ZodOptional<z.ZodString>;
82
+ }, z.core.$strip>;
83
+ declare const LegacyStationaryPresentSchema: z.ZodObject<{
84
+ kind: z.ZodLiteral<"stationary-present">;
85
+ deviceId: z.ZodNumber;
86
+ classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
87
+ zones: z.ZodOptional<z.ZodArray<z.ZodString>>;
88
+ }, z.core.$strip>;
89
+ declare const LegacyExpressionSchema: z.ZodObject<{
90
+ kind: z.ZodLiteral<"expression">;
91
+ expr: z.ZodString;
92
+ bindings: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
93
+ kind: z.ZodOptional<z.ZodLiteral<"field">>;
94
+ sourceKey: z.ZodString;
95
+ cap: z.ZodString;
96
+ fieldPath: z.ZodString;
97
+ }, z.core.$strip>, z.ZodObject<{
98
+ kind: z.ZodLiteral<"literal">;
99
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>;
100
+ }, z.core.$strip>, z.ZodObject<{
101
+ kind: z.ZodLiteral<"global">;
102
+ sourceStableId: z.ZodString;
103
+ cap: z.ZodString;
104
+ fieldPath: z.ZodString;
105
+ }, z.core.$strip>]>>;
106
+ }, z.core.$strip>;
107
+ type LegacyLeaf = z.infer<typeof LegacyConditionLeafSchema> | z.infer<typeof LegacyStationaryPresentSchema> | z.infer<typeof LegacyExpressionSchema>;
108
+ export type LegacyCondition = {
109
+ readonly kind: 'all';
110
+ readonly children: readonly LegacyCondition[];
111
+ } | {
112
+ readonly kind: 'any';
113
+ readonly children: readonly LegacyCondition[];
114
+ } | {
115
+ readonly kind: 'not';
116
+ readonly child: LegacyCondition;
117
+ } | LegacyLeaf;
118
+ export declare const LegacyConditionSchema: z.ZodType<LegacyCondition>;
119
+ /** The recipe without its `actions` / `runMode` / `keepTraces` half, which did not change. */
120
+ export interface LegacyWhenHalf {
121
+ readonly triggers: readonly LegacyTrigger[];
122
+ readonly conditions?: LegacyCondition | undefined;
123
+ }
124
+ /** An old condition tree as a `when` subtree: `condition` → "is", classes → subjects. */
125
+ export declare function convertLegacyCondition(condition: LegacyCondition): AutomationWhen;
126
+ /** One legacy trigger as a leaf — kept for readers that hold a single old trigger. */
127
+ export declare function convertLegacyTrigger(trigger: LegacyTrigger): AutomationWhen;
128
+ /** `all(any(t1…tn), C)`, or `any(t1…tn)`; a single trigger is not wrapped in `any`. */
129
+ export declare function convertLegacyWhen(half: LegacyWhenHalf): AutomationWhen;
130
+ export {};
@@ -24,7 +24,8 @@ import type { CapabilityDefinition } from '../capabilities/capability-definition
24
24
  import { type ParamMeta } from '../units/quantity-input.js';
25
25
  import type { ReadingQuantity } from '../units/quantity.js';
26
26
  import { z } from 'zod';
27
- import type { AutomationAction, AutomationCapStep, AutomationCondition, AutomationRecipe } from './recipe.js';
27
+ import type { AutomationAction, AutomationCapStep, AutomationRecipe } from './recipe.js';
28
+ import type { AutomationWhen } from './when.js';
28
29
  /** What the boundary needs to know about caps. */
29
30
  export interface UnitResolver {
30
31
  /** The quantity of a status field, or `undefined` when it is not a quantity. */
@@ -49,11 +50,11 @@ export interface UnitsNormalized<T> {
49
50
  /** A resolver over capability definitions (the generated registry, or a fake). */
50
51
  export declare function capUnitResolver(defs: readonly CapabilityDefinition[]): UnitResolver;
51
52
  /** Normalise every leaf of a condition tree. Throws {@link QuantityUnitError}. */
52
- export declare function normalizeConditionUnits(condition: AutomationCondition, resolver: UnitResolver, path?: string): UnitsNormalized<AutomationCondition>;
53
+ export declare function normalizeConditionUnits(condition: AutomationWhen, resolver: UnitResolver, path?: string): UnitsNormalized<AutomationWhen>;
53
54
  /**
54
55
  * Normalise the cap steps of an action list (an internal Action's steps, or a
55
56
  * recipe's actions). Other step kinds pass through. Throws {@link QuantityUnitError}.
56
57
  */
57
58
  export declare function normalizeActionUnits<A extends AutomationAction>(actions: readonly A[], resolver: UnitResolver, path?: string): UnitsNormalized<(A | AutomationCapStep)[]>;
58
- /** Normalise a whole recipe: its condition tree and its actions. */
59
+ /** Normalise a whole recipe: its `when` tree and its actions. */
59
60
  export declare function normalizeRecipeUnits(recipe: AutomationRecipe, resolver: UnitResolver): UnitsNormalized<AutomationRecipe>;