@camstack/system 1.2.314 → 1.2.315

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 (68) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  11. package/dist/builtins/camera-grid/index.js +1 -1
  12. package/dist/builtins/camera-grid/index.mjs +1 -1
  13. package/dist/builtins/composer/composer-plan.d.ts +8 -1
  14. package/dist/builtins/composer/composer.addon.js +191 -29
  15. package/dist/builtins/composer/composer.addon.mjs +195 -33
  16. package/dist/builtins/composer/composer.d.ts +5 -1
  17. package/dist/builtins/composer/composition-runtime.d.ts +10 -1
  18. package/dist/builtins/composer/existing-target.d.ts +9 -0
  19. package/dist/builtins/composer/field-record.d.ts +12 -3
  20. package/dist/builtins/console-logging/index.js +1 -1
  21. package/dist/builtins/console-logging/index.mjs +1 -1
  22. package/dist/builtins/core-blocks/composition-sources.d.ts +3 -1
  23. package/dist/builtins/core-blocks/core-blocks.addon.js +3 -103
  24. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -102
  25. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  26. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  27. package/dist/builtins/doorbell/binding-mirror.d.ts +1 -0
  28. package/dist/builtins/doorbell/doorbell-composition-migration.d.ts +59 -0
  29. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +3 -0
  30. package/dist/builtins/doorbell/virtual-doorbell.addon.js +315 -8
  31. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +315 -8
  32. package/dist/builtins/hub-forwarder/index.js +1 -1
  33. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  34. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  35. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  36. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  37. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  38. package/dist/builtins/local-network/local-network.addon.js +1 -1
  39. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  40. package/dist/builtins/loki-logging/index.js +1 -1
  41. package/dist/builtins/loki-logging/index.mjs +1 -1
  42. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  43. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  44. package/dist/builtins/platform-probe/index.js +1 -1
  45. package/dist/builtins/platform-probe/index.mjs +1 -1
  46. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  47. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  48. package/dist/builtins/snapshot/index.js +1 -1
  49. package/dist/builtins/snapshot/index.mjs +1 -1
  50. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  51. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  52. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  53. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  54. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  55. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  56. package/dist/builtins/system-config/system-config.addon.js +1 -1
  57. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  58. package/dist/builtins/winston-logging/index.js +1 -1
  59. package/dist/builtins/winston-logging/index.mjs +1 -1
  60. package/dist/composition-sources-DYqQLRzF.js +122 -0
  61. package/dist/composition-sources-yonsRneP.mjs +111 -0
  62. package/dist/{dist-BtNkk3Qz.js → dist-DVKv5i-e.js} +611 -299
  63. package/dist/{dist-cH4t1ZEm.mjs → dist-MzYJVCLE.mjs} +582 -300
  64. package/dist/index.js +1 -1
  65. package/dist/index.mjs +1 -1
  66. package/dist/{retired-settings-keys-DQYHfZ0S.mjs → retired-settings-keys-8yY2sTc-.mjs} +1 -1
  67. package/dist/{retired-settings-keys-B7imb-qH.js → retired-settings-keys-iY5I0nKM.js} +1 -1
  68. package/package.json +1 -1
@@ -701,7 +701,7 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
701
701
  return EventCategory;
702
702
  }({});
703
703
  //#endregion
704
- //#region ../types/dist/sleep-jkRQgtSc.mjs
704
+ //#region ../types/dist/sleep-E6eD4yQS.mjs
705
705
  /**
706
706
  * The audio chunk plane's byte format, and the ONE expansion from a coded
707
707
  * window to float samples (D455).
@@ -2777,6 +2777,10 @@ function toNodeId(idOrRunnerId) {
2777
2777
  const head = idOrRunnerId.split("/")[0];
2778
2778
  return head === void 0 || head.length === 0 ? idOrRunnerId : head;
2779
2779
  }
2780
+ z.object({
2781
+ field: z.string().min(1),
2782
+ on: z.literal("advance")
2783
+ });
2780
2784
  /**
2781
2785
  * Resolve the EFFECTIVE mount hint for a cap — the explicit `mount` when
2782
2786
  * present, else the scope/mode/deviceNative-derived default. Single source
@@ -3508,7 +3512,7 @@ function errMsg(err) {
3508
3512
  return String(err);
3509
3513
  }
3510
3514
  //#endregion
3511
- //#region ../types/dist/composition-CPFIlFfw.mjs
3515
+ //#region ../types/dist/composition-D5BQywfe.mjs
3512
3516
  var AUDIO_ENCODER_BY_CODEC = {
3513
3517
  opus: "libopus",
3514
3518
  aac: "aac",
@@ -3902,6 +3906,173 @@ function buildFfmpegArgs(inv) {
3902
3906
  ];
3903
3907
  }
3904
3908
  /**
3909
+ * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
3910
+ * valutatore puro del suo FRONTE.
3911
+ *
3912
+ * Viveva dentro il builtin virtual-doorbell
3913
+ * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
3914
+ * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
3915
+ * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
3916
+ * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
3917
+ * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
3918
+ * SPOSTA qui e il doorbell la ri-esporta.
3919
+ *
3920
+ * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
3921
+ * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
3922
+ * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
3923
+ * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
3924
+ */
3925
+ /**
3926
+ * Known binary / switch source caps → the boolean slice field whose
3927
+ * false→true rise counts as ACTIVE. Every entry is "fire on active".
3928
+ * Sensors whose "active" reading is not a plain boolean (presence's string
3929
+ * state, connectivity's connected flag) are deliberately excluded — a
3930
+ * reconnect is not a doorbell press, and it is not a recording either.
3931
+ */
3932
+ var SOURCE_CAP_ACTIVE_FIELD = {
3933
+ contact: "entryOpen",
3934
+ binary: "on",
3935
+ switch: "on",
3936
+ motion: "detected",
3937
+ flood: "flooded",
3938
+ gas: "detected",
3939
+ smoke: "detected",
3940
+ "carbon-monoxide": "detected",
3941
+ vibration: "detected",
3942
+ tamper: "tampered"
3943
+ };
3944
+ /**
3945
+ * The same caps → the slice field carrying the ms-epoch timestamp of the
3946
+ * last transition. Every source cap MUST appear here (guarded by a spec):
3947
+ * without a transition timestamp the evaluator cannot tell a genuine rise
3948
+ * from a boot-time hydration when the FIRST slice it ever sees is already
3949
+ * active, and errs towards silence — swallowing the rise.
3950
+ *
3951
+ * These timestamps are UPSTREAM ones, not ingest ones: the Home Assistant
3952
+ * provider derives them from `state.last_changed`, so they survive our own
3953
+ * restarts and correctly read as "hours ago" for a state that has been
3954
+ * active for hours. `motion` names its rise timestamp `lastDetectedAt`.
3955
+ */
3956
+ var SOURCE_CAP_CHANGED_AT_FIELD = {
3957
+ contact: "lastChangedAt",
3958
+ binary: "lastChangedAt",
3959
+ switch: "lastChangedAt",
3960
+ motion: "lastDetectedAt",
3961
+ flood: "lastChangedAt",
3962
+ gas: "lastChangedAt",
3963
+ smoke: "lastChangedAt",
3964
+ "carbon-monoxide": "lastChangedAt",
3965
+ vibration: "lastChangedAt",
3966
+ tamper: "lastChangedAt"
3967
+ };
3968
+ /** Cap names whose presence in a device's bindings qualify it as a source. */
3969
+ var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
3970
+ /** True when a cap is a recognised binary/switch source. */
3971
+ function isSourceCap(capName) {
3972
+ return Object.prototype.hasOwnProperty.call(SOURCE_CAP_ACTIVE_FIELD, capName);
3973
+ }
3974
+ /** Extract the "active" boolean a source cap's slice carries, or null when
3975
+ * the cap is unknown or the field is missing / non-boolean. */
3976
+ function sliceActiveValue(capName, slice) {
3977
+ const field = SOURCE_CAP_ACTIVE_FIELD[capName];
3978
+ if (field === void 0) return null;
3979
+ const raw = slice[field];
3980
+ return typeof raw === "boolean" ? raw : null;
3981
+ }
3982
+ /** Ms-epoch transition timestamp a source cap's slice carries, or null when
3983
+ * it is absent, non-numeric or the zero "never observed" sentinel. */
3984
+ function sliceChangedAt(capName, slice) {
3985
+ const field = SOURCE_CAP_CHANGED_AT_FIELD[capName];
3986
+ if (field === void 0) return null;
3987
+ const raw = slice[field];
3988
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return null;
3989
+ return raw;
3990
+ }
3991
+ /**
3992
+ * How recent a source's own transition timestamp must be for a FIRST
3993
+ * sighting that is already active to count as a genuine rise rather than a
3994
+ * hydration of long-standing state.
3995
+ */
3996
+ var DEFAULT_FIRST_SIGHTING_FRESHNESS_MS = 3e4;
3997
+ /**
3998
+ * The source-independent edge rule. The caller supplies an already extracted
3999
+ * boolean level and source timestamp; this function owns first-sighting
4000
+ * freshness and false-to-true detection.
4001
+ */
4002
+ function evaluateSensorLevelEdge(input) {
4003
+ const timestamp = input.changedAt;
4004
+ const freshFloor = Math.max(input.startedAtMs, input.nowMs - input.firstSightingFreshnessMs);
4005
+ const firstRise = input.priorLevel === void 0 && input.level && timestamp !== null && Number.isFinite(timestamp) && timestamp >= freshFloor;
4006
+ const transitionRise = input.priorLevel === false && input.level && timestamp !== null && Number.isFinite(timestamp);
4007
+ if ((firstRise || transitionRise) && timestamp !== null) return {
4008
+ kind: "rise",
4009
+ timestamp
4010
+ };
4011
+ return {
4012
+ kind: "none",
4013
+ nextPriorLevel: input.level
4014
+ };
4015
+ }
4016
+ /**
4017
+ * IL fronte. Puro: nessun orologio proprio, nessuna memoria — il chiamante
4018
+ * porta `prior`, `nowMs` e il proprio `startedAtMs`.
4019
+ *
4020
+ * Il caso della PRIMA slice già attiva è trattato esplicitamente e vale come
4021
+ * fronte solo se il timestamp UPSTREAM della transizione è posteriore a
4022
+ * `startedAtMs` **e** entro `firstSightingFreshnessMs`. Entrambe le metà
4023
+ * servono: la sola freschezza scatterebbe su un'idratazione al boot di uno
4024
+ * stato flippato pochi secondi prima del riavvio, e il solo "dopo che abbiamo
4025
+ * iniziato" scatterebbe, su un processo di lunga vita, per una sorgente
4026
+ * adottata oggi il cui stato è cambiato ieri. Il caso ambiguo ERRA VERSO IL
4027
+ * SILENZIO e lo dichiara (`baseline-seeded-stale-active`).
4028
+ */
4029
+ function evaluateSensorEdge(input) {
4030
+ const value = sliceActiveValue(input.capName, input.slice);
4031
+ if (value === null) return {
4032
+ edge: "none",
4033
+ value: null,
4034
+ reason: isSourceCap(input.capName) ? "non-boolean-value" : "unknown-cap"
4035
+ };
4036
+ if (evaluateSensorLevelEdge({
4037
+ level: value,
4038
+ changedAt: sliceChangedAt(input.capName, input.slice),
4039
+ priorLevel: input.prior,
4040
+ startedAtMs: input.startedAtMs,
4041
+ nowMs: input.nowMs,
4042
+ firstSightingFreshnessMs: input.firstSightingFreshnessMs
4043
+ }).kind === "rise") return {
4044
+ edge: "rising",
4045
+ value: true
4046
+ };
4047
+ if (input.prior === void 0) {
4048
+ if (!value) return {
4049
+ edge: "none",
4050
+ value,
4051
+ reason: "baseline-seeded-inactive"
4052
+ };
4053
+ return {
4054
+ edge: "none",
4055
+ value,
4056
+ reason: "baseline-seeded-stale-active"
4057
+ };
4058
+ }
4059
+ if (input.prior === value) return {
4060
+ edge: "none",
4061
+ value,
4062
+ reason: "no-change"
4063
+ };
4064
+ if (!value) return {
4065
+ edge: "none",
4066
+ value,
4067
+ reason: "falling-edge"
4068
+ };
4069
+ return {
4070
+ edge: "none",
4071
+ value,
4072
+ reason: "missing-timestamp"
4073
+ };
4074
+ }
4075
+ /**
3905
4076
  * Error types for the safe expression engine. Two distinct classes so callers
3906
4077
  * can tell a compile-time (grammar) failure from a runtime (evaluation)
3907
4078
  * failure — both are non-fatal to the host: read paths degrade to "skip link".
@@ -3924,6 +4095,9 @@ var ExpressionEvalError = class extends Error {
3924
4095
  this.name = "ExpressionEvalError";
3925
4096
  }
3926
4097
  };
4098
+ function isStatefulBuiltin(name) {
4099
+ return name === "rose" || name === "count";
4100
+ }
3927
4101
  function asFiniteNumber(value, name, index) {
3928
4102
  if (typeof value !== "number" || !Number.isFinite(value)) throw new ExpressionEvalError(`${name}: argument ${index + 1} must be a finite number`);
3929
4103
  return value;
@@ -3959,6 +4133,37 @@ function toNumberOrNull(value) {
3959
4133
  const parsed = Number(trimmed);
3960
4134
  return Number.isFinite(parsed) ? parsed : null;
3961
4135
  }
4136
+ var DEFAULT_ROSE_DEBOUNCE_MS = 2e3;
4137
+ function evaluateRose(level, changedAt, debounceMs, call, field, env) {
4138
+ const active = asBoolean(level, "rose", 0);
4139
+ const timestamp = typeof changedAt === "number" && Number.isFinite(changedAt) ? changedAt : null;
4140
+ const debounce = debounceMs === void 0 ? DEFAULT_ROSE_DEBOUNCE_MS : asFiniteNumber(debounceMs, "rose", 2);
4141
+ if (debounce < 0) throw new ExpressionEvalError("rose: debounce must be a non-negative finite number");
4142
+ const verdict = evaluateSensorLevelEdge({
4143
+ level: active,
4144
+ changedAt: timestamp,
4145
+ priorLevel: call.priorLevel,
4146
+ startedAtMs: env.startedAtMs,
4147
+ nowMs: env.nowMs,
4148
+ firstSightingFreshnessMs: DEFAULT_FIRST_SIGHTING_FRESHNESS_MS
4149
+ });
4150
+ call.priorLevel = verdict.kind === "rise" ? true : verdict.nextPriorLevel;
4151
+ if (verdict.kind === "none") return env.currentOutput;
4152
+ if (field.acceptedEdgeAt !== null && verdict.timestamp - field.acceptedEdgeAt < debounce) return env.currentOutput;
4153
+ field.acceptedEdgeAt = verdict.timestamp;
4154
+ return verdict.timestamp;
4155
+ }
4156
+ function evaluateCount(input, call, currentOutput) {
4157
+ const currentCount = typeof currentOutput === "number" && Number.isFinite(currentOutput) && currentOutput >= 0 ? currentOutput : 0;
4158
+ if (typeof input !== "number" || !Number.isFinite(input)) return currentCount;
4159
+ if (call.highWater === null) {
4160
+ call.highWater = input;
4161
+ return currentCount;
4162
+ }
4163
+ if (input <= call.highWater) return currentCount;
4164
+ call.highWater = input;
4165
+ return currentCount + 1;
4166
+ }
3962
4167
  var INF = Number.POSITIVE_INFINITY;
3963
4168
  var table = {
3964
4169
  min: {
@@ -4098,6 +4303,20 @@ var table = {
4098
4303
  const present = args.flatMap((a, i) => a === null ? [] : [asFiniteNumber(a, "latest", i)]);
4099
4304
  return present.length === 0 ? null : finiteResult(Math.max(...present), "latest");
4100
4305
  }
4306
+ },
4307
+ rose: {
4308
+ minArgs: 2,
4309
+ maxArgs: 3,
4310
+ apply: () => {
4311
+ throw new ExpressionEvalError("rose: stateful evaluation context is required");
4312
+ }
4313
+ },
4314
+ count: {
4315
+ minArgs: 1,
4316
+ maxArgs: 1,
4317
+ apply: () => {
4318
+ throw new ExpressionEvalError("count: stateful evaluation context is required");
4319
+ }
4101
4320
  }
4102
4321
  };
4103
4322
  /** Frozen, null-prototype builtin table. */
@@ -4551,22 +4770,28 @@ function step(ctx) {
4551
4770
  ctx.steps += 1;
4552
4771
  if (ctx.steps > ctx.maxSteps) throw new ExpressionEvalError("expression evaluation step budget exceeded");
4553
4772
  }
4554
- function evalNode(node, ctx) {
4773
+ function childPath(path, child) {
4774
+ return `${path}/${child}`;
4775
+ }
4776
+ function evalNode(node, ctx, path) {
4555
4777
  step(ctx);
4556
4778
  switch (node.kind) {
4557
4779
  case "literal": return node.value;
4558
4780
  case "identifier":
4559
4781
  if (!(node.name in ctx.scope)) throw new ExpressionEvalError(`unknown identifier: ${node.name}`);
4560
4782
  return ctx.scope[node.name];
4561
- case "unary": return evalUnary(node.op, evalNode(node.operand, ctx));
4562
- case "binary": return evalBinary(node.op, evalNode(node.left, ctx), evalNode(node.right, ctx));
4783
+ case "unary": return evalUnary(node.op, evalNode(node.operand, ctx, childPath(path, "operand")));
4784
+ case "binary": return evalBinary(node.op, evalNode(node.left, ctx, childPath(path, "left")), evalNode(node.right, ctx, childPath(path, "right")));
4563
4785
  case "logical": {
4564
- const left = evalNode(node.left, ctx);
4565
- if (node.op === "&&") return truthy(left) ? evalNode(node.right, ctx) : left;
4566
- return truthy(left) ? left : evalNode(node.right, ctx);
4786
+ const left = evalNode(node.left, ctx, childPath(path, "left"));
4787
+ if (node.op === "&&") return truthy(left) ? evalNode(node.right, ctx, childPath(path, "right")) : left;
4788
+ return truthy(left) ? left : evalNode(node.right, ctx, childPath(path, "right"));
4789
+ }
4790
+ case "conditional": return truthy(evalNode(node.test, ctx, childPath(path, "test"))) ? evalNode(node.consequent, ctx, childPath(path, "consequent")) : evalNode(node.alternate, ctx, childPath(path, "alternate"));
4791
+ case "call": {
4792
+ const args = node.args.map((arg, index) => evalNode(arg, ctx, childPath(path, `arg:${index}`)));
4793
+ return invokeCall(node.callee, args, path, ctx);
4567
4794
  }
4568
- case "conditional": return truthy(evalNode(node.test, ctx)) ? evalNode(node.consequent, ctx) : evalNode(node.alternate, ctx);
4569
- case "call": return evalCall(node.callee, node.args.map((a) => evalNode(a, ctx)), ctx.hooks);
4570
4795
  }
4571
4796
  }
4572
4797
  function evalUnary(op, operand) {
@@ -4597,11 +4822,19 @@ function evalBinary(op, left, right) {
4597
4822
  throw new ExpressionEvalError(`"${op}" requires two numbers or two strings`);
4598
4823
  }
4599
4824
  }
4600
- function evalCall(callee, args, hooks) {
4825
+ function invokeCall(callee, args, path, ctx) {
4601
4826
  if (!Object.prototype.hasOwnProperty.call(EXPRESSION_BUILTINS, callee)) throw new ExpressionEvalError(`unknown function: ${callee}`);
4602
4827
  const builtin = EXPRESSION_BUILTINS[callee];
4603
4828
  if (args.length < builtin.minArgs || args.length > builtin.maxArgs) throw new ExpressionEvalError(`${callee}: wrong number of arguments (${args.length})`);
4604
- return builtin.apply(args, hooks);
4829
+ if (isStatefulBuiltin(callee)) {
4830
+ if (ctx.invokeStateful === void 0) throw new ExpressionEvalError(`${callee}: stateful evaluation context is required`);
4831
+ return ctx.invokeStateful({
4832
+ name: callee,
4833
+ path,
4834
+ args
4835
+ });
4836
+ }
4837
+ return builtin.apply(args, ctx.hooks);
4605
4838
  }
4606
4839
  /** Evaluate an AST node against a scope. Throws `ExpressionEvalError` on any
4607
4840
  * runtime failure (unknown identifier, type mismatch, non-finite result,
@@ -4611,8 +4844,44 @@ function evaluateAst(node, scope, opts) {
4611
4844
  scope,
4612
4845
  hooks: opts?.hooks ?? EMPTY_HOOKS,
4613
4846
  maxSteps: opts?.maxSteps ?? 4096,
4847
+ ...opts?.invokeStateful === void 0 ? {} : { invokeStateful: opts.invokeStateful },
4614
4848
  steps: 0
4615
- });
4849
+ }, "root");
4850
+ }
4851
+ function createStatefulExpressionMemory() {
4852
+ return {
4853
+ calls: /* @__PURE__ */ new Map(),
4854
+ acceptedEdgeAt: null
4855
+ };
4856
+ }
4857
+ function cloneStatefulExpressionMemory(memory) {
4858
+ return {
4859
+ calls: new Map([...memory.calls].map(([path, call]) => [path, { ...call }])),
4860
+ acceptedEdgeAt: memory.acceptedEdgeAt
4861
+ };
4862
+ }
4863
+ function evaluateStatefulExpressionCall(invocation, memory, env) {
4864
+ const existing = memory.calls.get(invocation.path);
4865
+ switch (invocation.name) {
4866
+ case "rose": {
4867
+ const call = existing?.kind === "rose" ? existing : { kind: "rose" };
4868
+ memory.calls.set(invocation.path, call);
4869
+ const currentOutput = env.currentOutput === null || typeof env.currentOutput === "number" && Number.isFinite(env.currentOutput) ? env.currentOutput : null;
4870
+ return evaluateRose(invocation.args[0], invocation.args[1], invocation.args[2], call, memory, {
4871
+ currentOutput,
4872
+ startedAtMs: env.startedAtMs,
4873
+ nowMs: env.nowMs
4874
+ });
4875
+ }
4876
+ case "count": {
4877
+ const call = existing?.kind === "count" ? existing : {
4878
+ kind: "count",
4879
+ highWater: null
4880
+ };
4881
+ memory.calls.set(invocation.path, call);
4882
+ return evaluateCount(invocation.args[0], call, env.currentOutput);
4883
+ }
4884
+ }
4616
4885
  }
4617
4886
  /**
4618
4887
  * Coerce an untrusted `getByPath` / mirror read to an `ExpressionValue`.
@@ -4653,9 +4922,10 @@ function validateExpressionSource(src) {
4653
4922
  /**
4654
4923
  * Shared read-path evaluation. Builds a null-proto scope from `bindingValues`
4655
4924
  * plus the injected `now` (supplied by the caller for determinism and
4656
- * testability), compiles via the LRU, and evaluates. Any failure (parse or
4657
- * eval) returns `{ ok: false }` — the caller treats that as "skip this
4658
- * derivation", never as a throw that takes the pass down.
4925
+ * testability), compiles via the LRU, and evaluates. Stateful calls are
4926
+ * delegated through `opts.invokeStateful` with their stable AST path. Any
4927
+ * failure (parse or eval) returns `{ ok: false }` — the caller treats that as
4928
+ * "skip this derivation", never as a throw that takes the pass down.
4659
4929
  */
4660
4930
  function evaluateExpressionSource(expr, bindingValues, now, opts) {
4661
4931
  const compiled = compileExpressionSafe(expr);
@@ -4787,6 +5057,7 @@ var CompositionItemEntrySchema = z.object({
4787
5057
  var CompositionFieldsFeatureSchema = z.object({
4788
5058
  kind: z.literal("fields"),
4789
5059
  cap: z.string().min(1),
5060
+ enabled: z.boolean().optional(),
4790
5061
  fields: z.record(z.string().min(1), CompositionFieldSourceSchema).refine((fields) => Object.keys(fields).length <= 32, { message: `at most 32 fields per capability` }),
4791
5062
  /**
4792
5063
  * Items of the cap's item array, keyed by item key (D663). Naming `items`
@@ -4800,9 +5071,14 @@ var CompositionFieldsFeatureSchema = z.object({
4800
5071
  var CompositionPassthroughFeatureSchema = z.object({
4801
5072
  kind: z.literal("passthrough"),
4802
5073
  cap: z.string().min(1),
5074
+ enabled: z.boolean().optional(),
4803
5075
  source: CompositionSourceRefSchema
4804
5076
  });
4805
5077
  var CompositionFeatureSchema = z.discriminatedUnion("kind", [CompositionFieldsFeatureSchema, CompositionPassthroughFeatureSchema]);
5078
+ /** Only an explicit `false` disables a feature; omitted preserves the existing wire default. */
5079
+ function compositionFeatureEnabled(feature) {
5080
+ return feature.enabled !== false;
5081
+ }
4806
5082
  /**
4807
5083
  * A NEW device. It has no `name` of its own: the block's name IS the device's
4808
5084
  * name (2026-08-08). A second copy here would be a second writer of one string (D62).
@@ -11009,6 +11285,190 @@ function procedureAuthKey(auth) {
11009
11285
  function kebabToCamel(s) {
11010
11286
  return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
11011
11287
  }
11288
+ /** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
11289
+ * Zod v4 exposes `.unwrap()` on all three wrapper classes. */
11290
+ function unwrapSchema(schema) {
11291
+ let s = schema;
11292
+ while (s instanceof z.ZodNullable || s instanceof z.ZodOptional || s instanceof z.ZodDefault) s = s.unwrap();
11293
+ return s;
11294
+ }
11295
+ function leafKind(schema) {
11296
+ const s = unwrapSchema(schema);
11297
+ if (s instanceof z.ZodEnum) return {
11298
+ kind: "enum",
11299
+ enumValues: s.options
11300
+ };
11301
+ if (s instanceof z.ZodNumber) return { kind: "number" };
11302
+ if (s instanceof z.ZodString) return { kind: "string" };
11303
+ if (s instanceof z.ZodBoolean) return { kind: "boolean" };
11304
+ return null;
11305
+ }
11306
+ /** Walk a cap status schema into flat wireable leaf fields (dotted paths).
11307
+ * Recurses into nested ZodObject (unwrapping nullable/optional/default first),
11308
+ * so fields with null live values are still offered. Arrays / unknown shapes
11309
+ * are skipped — never throws. */
11310
+ function enumerateSchemaFields(schema, prefix = "") {
11311
+ const root = unwrapSchema(schema);
11312
+ if (!(root instanceof z.ZodObject)) return [];
11313
+ const out = [];
11314
+ const shape = root.shape;
11315
+ for (const [key, field] of Object.entries(shape)) {
11316
+ const path = prefix ? `${prefix}.${key}` : key;
11317
+ const inner = unwrapSchema(field);
11318
+ if (inner instanceof z.ZodObject) {
11319
+ out.push(...enumerateSchemaFields(inner, path));
11320
+ continue;
11321
+ }
11322
+ const leaf = leafKind(field);
11323
+ if (leaf) out.push({
11324
+ path,
11325
+ kind: leaf.kind,
11326
+ ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
11327
+ });
11328
+ }
11329
+ return out;
11330
+ }
11331
+ /** Enumerate the per-item wireable fields of an item-array cap (see
11332
+ * `CapabilityStatusItemArray`): the item schema's leaf fields, minus the
11333
+ * `keyField` (the key comes from the link's `itemKey`, never from a wired
11334
+ * source), each tagged `item: true` so the authoring UI collects an
11335
+ * `itemKey` alongside the field. Never throws. */
11336
+ function enumerateItemArrayFields(itemArray) {
11337
+ return enumerateSchemaFields(itemArray.itemSchema).filter((f) => f.path !== itemArray.keyField).map((f) => ({
11338
+ ...f,
11339
+ item: true
11340
+ }));
11341
+ }
11342
+ /**
11343
+ * What a capability's state field IS: kind, nullability, whether required. The
11344
+ * validator needs nullability, which `enumerateSchemaFields` does not report,
11345
+ * and that decides a field's unavailable mode (D393).
11346
+ */
11347
+ function fieldAt(schema, path) {
11348
+ let current = schema;
11349
+ for (const segment of path.split(".")) {
11350
+ const container = unwrapSchema(current);
11351
+ if (!(container instanceof z.ZodObject)) return null;
11352
+ const next = container.shape[segment];
11353
+ if (!(next instanceof z.ZodType)) return null;
11354
+ current = next;
11355
+ }
11356
+ return current;
11357
+ }
11358
+ function describeStateField(schema, path) {
11359
+ const leaf = enumerateSchemaFields(schema).find((f) => f.path === path);
11360
+ if (leaf === void 0) return null;
11361
+ const field = fieldAt(schema, path);
11362
+ if (field === null) return null;
11363
+ return {
11364
+ path,
11365
+ kind: leaf.kind,
11366
+ nullable: field.safeParse(null).success,
11367
+ required: !field.safeParse(void 0).success,
11368
+ ...leaf.enumValues === void 0 ? {} : { enumValues: leaf.enumValues }
11369
+ };
11370
+ }
11371
+ function describeTopLevelStateFields(schema) {
11372
+ return new Map(enumerateSchemaFields(schema).filter((f) => !f.path.includes(".")).flatMap((f) => {
11373
+ const described = describeStateField(schema, f.path);
11374
+ return described === null ? [] : [[f.path, described]];
11375
+ }));
11376
+ }
11377
+ /** Top-level keys a value MUST carry: those whose schema refuses `undefined`. */
11378
+ function requiredTopLevelKeys(schema) {
11379
+ const root = unwrapSchema(schema);
11380
+ if (!(root instanceof z.ZodObject)) return [];
11381
+ return Object.entries(root.shape).flatMap(([key, field]) => field instanceof z.ZodType && !field.safeParse(void 0).success ? [key] : []);
11382
+ }
11383
+ /**
11384
+ * The proper prefixes of `path` whose schema accepts `undefined`: a required
11385
+ * leaf under one of them is required only once something writes that parent
11386
+ * (`remaining.unit` is required only when `remaining` is present).
11387
+ */
11388
+ function optionalAncestors(schema, path) {
11389
+ const segments = path.split(".");
11390
+ return segments.slice(0, -1).flatMap((_, i) => {
11391
+ const prefix = segments.slice(0, i + 1).join(".");
11392
+ const field = fieldAt(schema, prefix);
11393
+ return field !== null && field.safeParse(void 0).success ? [prefix] : [];
11394
+ });
11395
+ }
11396
+ var EVENT_CATEGORIES = { "doorbell.onPressed": EventCategory.DoorbellOnPressed };
11397
+ var CompositionEventPlanSchema = z.object({
11398
+ featureIndex: z.number().int().nonnegative(),
11399
+ cap: z.string(),
11400
+ eventName: z.string(),
11401
+ category: z.enum(EventCategory),
11402
+ stateField: z.string(),
11403
+ fieldKey: z.string()
11404
+ });
11405
+ function eventProblem(code, path, message) {
11406
+ return {
11407
+ code,
11408
+ path,
11409
+ message
11410
+ };
11411
+ }
11412
+ function payloadReason(error) {
11413
+ return error.issues.map((issue) => `${issue.path.length === 0 ? "payload" : issue.path.join(".")}: ${issue.message}`).join("; ");
11414
+ }
11415
+ /**
11416
+ * Plan one capability's state-derived events after its feature mode and fields
11417
+ * are known. A native replacement never becomes a second event authority.
11418
+ */
11419
+ function planCompositionEvents(def, feature, fields) {
11420
+ if (feature.mode === "replace") return {
11421
+ problems: [],
11422
+ events: []
11423
+ };
11424
+ const problems = [];
11425
+ const events = [];
11426
+ for (const [eventName, declaration] of Object.entries(def.events ?? {})) {
11427
+ const fromState = declaration.fromState;
11428
+ if (fromState === void 0) continue;
11429
+ const statePath = `features[${feature.featureIndex}].fields.${fromState.field}`;
11430
+ const state = def.runtimeState ? describeStateField(def.runtimeState, fromState.field) : null;
11431
+ if (state === null) {
11432
+ problems.push(eventProblem("unknown-field", statePath, `\`${def.name}.${eventName}\` derives from missing state field \`${fromState.field}\``));
11433
+ continue;
11434
+ }
11435
+ if (state.kind !== "number") {
11436
+ problems.push(eventProblem("kind-mismatch", statePath, `\`${def.name}.${eventName}\` needs a numeric state field; \`${fromState.field}\` is ${state.kind}`));
11437
+ continue;
11438
+ }
11439
+ const eventKey = `${def.name}.${eventName}`;
11440
+ const category = EVENT_CATEGORIES[eventKey];
11441
+ if (category === void 0) {
11442
+ problems.push(eventProblem("not-composable", `features[${feature.featureIndex}].cap`, `\`${eventKey}\` has no explicit composition event-category mapping`));
11443
+ continue;
11444
+ }
11445
+ const payload = declaration.data.safeParse({
11446
+ deviceId: 1,
11447
+ timestamp: 1
11448
+ });
11449
+ if (!payload.success) {
11450
+ problems.push(eventProblem("not-composable", `features[${feature.featureIndex}].cap`, `\`${eventKey}\` cannot be derived from { deviceId, timestamp }: ${payloadReason(payload.error)}`));
11451
+ continue;
11452
+ }
11453
+ const field = fields.find((candidate) => candidate.featureIndex === feature.featureIndex && candidate.cap === def.name && candidate.path === fromState.field && candidate.item === void 0);
11454
+ if (field === void 0) {
11455
+ problems.push(eventProblem("unknown-field", statePath, `\`${eventKey}\` derives from \`${fromState.field}\`, but that field is not composed`));
11456
+ continue;
11457
+ }
11458
+ events.push({
11459
+ featureIndex: feature.featureIndex,
11460
+ cap: def.name,
11461
+ eventName,
11462
+ category,
11463
+ stateField: fromState.field,
11464
+ fieldKey: `${field.cap}.${field.path}`
11465
+ });
11466
+ }
11467
+ return {
11468
+ problems,
11469
+ events
11470
+ };
11471
+ }
11012
11472
  /**
11013
11473
  * What the validator says about a composition and what the composer says about
11014
11474
  * a running one: the wire shapes the API returns (D659).
@@ -11063,12 +11523,14 @@ var CompositionUnavailableModeSchema = z.enum([
11063
11523
  ]);
11064
11524
  /**
11065
11525
  * `derived` reads a source · `clock` reads only `now`, recomputed when a sibling
11066
- * field of its cap changes value · `constant` reads nothing, computed once.
11526
+ * field of its cap changes value · `constant` reads nothing, computed once ·
11527
+ * `accumulator` advances from the target's seed/current output.
11067
11528
  */
11068
11529
  var CompositionFieldRoleSchema = z.enum([
11069
11530
  "derived",
11070
11531
  "clock",
11071
- "constant"
11532
+ "constant",
11533
+ "accumulator"
11072
11534
  ]);
11073
11535
  /** The item a planned field belongs to; its `path` is then the dotted path INSIDE the item. */
11074
11536
  var CompositionFieldItemSchema = z.object({
@@ -11109,6 +11571,7 @@ var CompositionFeatureModeSchema = z.enum([
11109
11571
  "replace"
11110
11572
  ]);
11111
11573
  var CompositionFeaturePlanSchema = z.object({
11574
+ /** Index in the stored feature array; disabled features are omitted rather than re-indexed. */
11112
11575
  featureIndex: z.number().int().nonnegative(),
11113
11576
  cap: z.string(),
11114
11577
  mode: CompositionFeatureModeSchema
@@ -11117,7 +11580,8 @@ var CompositionValidationSchema = z.object({
11117
11580
  ok: z.boolean(),
11118
11581
  problems: z.array(CompositionProblemSchema),
11119
11582
  fields: z.array(CompositionFieldPlanSchema),
11120
- features: z.array(CompositionFeaturePlanSchema)
11583
+ features: z.array(CompositionFeaturePlanSchema),
11584
+ events: z.array(CompositionEventPlanSchema)
11121
11585
  });
11122
11586
  /**
11123
11587
  * Mirrors `CoreBlockStatus`, pinned by `core-blocks-source.spec.ts`. `degraded`:
@@ -29024,148 +29488,6 @@ var videoclipsCapability = {
29024
29488
  }
29025
29489
  };
29026
29490
  /**
29027
- * LA tabella "quale booleano di questo tipo di device conta come ALTO", e il
29028
- * valutatore puro del suo FRONTE.
29029
- *
29030
- * Viveva dentro il builtin virtual-doorbell
29031
- * (`@camstack/system` — `builtins/doorbell/trigger-engine.ts`) e i suoi
29032
- * predicati erano privati al modulo. Il recorder ne ha bisogno per il trigger
29033
- * `RecordingTriggers.sensorDeviceIds`: copiarla avrebbe creato la SECONDA
29034
- * tabella, che diverge alla prima cap aggiunta e il cui sintomo — "il sensore
29035
- * fa suonare il campanello ma non registra" — è esattamente D62. Quindi si
29036
- * SPOSTA qui e il doorbell la ri-esporta.
29037
- *
29038
- * ⚠ NON è `DEVICE_STATE_READERS` (`catalogs/device-state-vocabulary.ts`), e le
29039
- * due non vanno unificate: quella risponde a "qual è la PAROLA di stato per una
29040
- * regola" (e include `presence`, `cover`, `alarm-panel`), questa a "qual è il
29041
- * booleano il cui FRONTE conta". Vocabolari deliberatamente diversi.
29042
- */
29043
- /**
29044
- * Known binary / switch source caps → the boolean slice field whose
29045
- * false→true rise counts as ACTIVE. Every entry is "fire on active".
29046
- * Sensors whose "active" reading is not a plain boolean (presence's string
29047
- * state, connectivity's connected flag) are deliberately excluded — a
29048
- * reconnect is not a doorbell press, and it is not a recording either.
29049
- */
29050
- var SOURCE_CAP_ACTIVE_FIELD = {
29051
- contact: "entryOpen",
29052
- binary: "on",
29053
- switch: "on",
29054
- motion: "detected",
29055
- flood: "flooded",
29056
- gas: "detected",
29057
- smoke: "detected",
29058
- "carbon-monoxide": "detected",
29059
- vibration: "detected",
29060
- tamper: "tampered"
29061
- };
29062
- /**
29063
- * The same caps → the slice field carrying the ms-epoch timestamp of the
29064
- * last transition. Every source cap MUST appear here (guarded by a spec):
29065
- * without a transition timestamp the evaluator cannot tell a genuine rise
29066
- * from a boot-time hydration when the FIRST slice it ever sees is already
29067
- * active, and errs towards silence — swallowing the rise.
29068
- *
29069
- * These timestamps are UPSTREAM ones, not ingest ones: the Home Assistant
29070
- * provider derives them from `state.last_changed`, so they survive our own
29071
- * restarts and correctly read as "hours ago" for a state that has been
29072
- * active for hours. `motion` names its rise timestamp `lastDetectedAt`.
29073
- */
29074
- var SOURCE_CAP_CHANGED_AT_FIELD = {
29075
- contact: "lastChangedAt",
29076
- binary: "lastChangedAt",
29077
- switch: "lastChangedAt",
29078
- motion: "lastDetectedAt",
29079
- flood: "lastChangedAt",
29080
- gas: "lastChangedAt",
29081
- smoke: "lastChangedAt",
29082
- "carbon-monoxide": "lastChangedAt",
29083
- vibration: "lastChangedAt",
29084
- tamper: "lastChangedAt"
29085
- };
29086
- /** Cap names whose presence in a device's bindings qualify it as a source. */
29087
- var SOURCE_CAPS = Object.keys(SOURCE_CAP_ACTIVE_FIELD);
29088
- /** True when a cap is a recognised binary/switch source. */
29089
- function isSourceCap(capName) {
29090
- return Object.prototype.hasOwnProperty.call(SOURCE_CAP_ACTIVE_FIELD, capName);
29091
- }
29092
- /** Extract the "active" boolean a source cap's slice carries, or null when
29093
- * the cap is unknown or the field is missing / non-boolean. */
29094
- function sliceActiveValue(capName, slice) {
29095
- const field = SOURCE_CAP_ACTIVE_FIELD[capName];
29096
- if (field === void 0) return null;
29097
- const raw = slice[field];
29098
- return typeof raw === "boolean" ? raw : null;
29099
- }
29100
- /** Ms-epoch transition timestamp a source cap's slice carries, or null when
29101
- * it is absent, non-numeric or the zero "never observed" sentinel. */
29102
- function sliceChangedAt(capName, slice) {
29103
- const field = SOURCE_CAP_CHANGED_AT_FIELD[capName];
29104
- if (field === void 0) return null;
29105
- const raw = slice[field];
29106
- if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return null;
29107
- return raw;
29108
- }
29109
- /**
29110
- * How recent a source's own transition timestamp must be for a FIRST
29111
- * sighting that is already active to count as a genuine rise rather than a
29112
- * hydration of long-standing state.
29113
- */
29114
- var DEFAULT_FIRST_SIGHTING_FRESHNESS_MS = 3e4;
29115
- /**
29116
- * IL fronte. Puro: nessun orologio proprio, nessuna memoria — il chiamante
29117
- * porta `prior`, `nowMs` e il proprio `startedAtMs`.
29118
- *
29119
- * Il caso della PRIMA slice già attiva è trattato esplicitamente e vale come
29120
- * fronte solo se il timestamp UPSTREAM della transizione è posteriore a
29121
- * `startedAtMs` **e** entro `firstSightingFreshnessMs`. Entrambe le metà
29122
- * servono: la sola freschezza scatterebbe su un'idratazione al boot di uno
29123
- * stato flippato pochi secondi prima del riavvio, e il solo "dopo che abbiamo
29124
- * iniziato" scatterebbe, su un processo di lunga vita, per una sorgente
29125
- * adottata oggi il cui stato è cambiato ieri. Il caso ambiguo ERRA VERSO IL
29126
- * SILENZIO e lo dichiara (`baseline-seeded-stale-active`).
29127
- */
29128
- function evaluateSensorEdge(input) {
29129
- const value = sliceActiveValue(input.capName, input.slice);
29130
- if (value === null) return {
29131
- edge: "none",
29132
- value: null,
29133
- reason: isSourceCap(input.capName) ? "non-boolean-value" : "unknown-cap"
29134
- };
29135
- if (input.prior === void 0) {
29136
- if (!value) return {
29137
- edge: "none",
29138
- value,
29139
- reason: "baseline-seeded-inactive"
29140
- };
29141
- const changedAt = sliceChangedAt(input.capName, input.slice);
29142
- const floor = Math.max(input.startedAtMs, input.nowMs - input.firstSightingFreshnessMs);
29143
- if (changedAt === null || changedAt < floor) return {
29144
- edge: "none",
29145
- value,
29146
- reason: "baseline-seeded-stale-active"
29147
- };
29148
- return {
29149
- edge: "rising",
29150
- value: true
29151
- };
29152
- }
29153
- if (input.prior === value) return {
29154
- edge: "none",
29155
- value,
29156
- reason: "no-change"
29157
- };
29158
- if (!value) return {
29159
- edge: "none",
29160
- value,
29161
- reason: "falling-edge"
29162
- };
29163
- return {
29164
- edge: "rising",
29165
- value: true
29166
- };
29167
- }
29168
- /**
29169
29491
  * Doorbell button cap. Two kinds of providers coexist behind this cap
29170
29492
  * name (same pattern as `snapshot`):
29171
29493
  *
@@ -29238,7 +29560,13 @@ var doorbellCapability = {
29238
29560
  * push (`ReolinkSimpleEvent.type === 'doorbell'`). There is no
29239
29561
  * release/duration — it's a pulse.
29240
29562
  */
29241
- onPressed: { data: DoorbellPressEventSchema } },
29563
+ onPressed: {
29564
+ data: DoorbellPressEventSchema,
29565
+ fromState: {
29566
+ field: "lastPressedAt",
29567
+ on: "advance"
29568
+ }
29569
+ } },
29242
29570
  status: {
29243
29571
  schema: DoorbellStatusSchema,
29244
29572
  kind: "push"
@@ -42640,7 +42968,7 @@ function createDeviceExtensionProvider(options) {
42640
42968
  var CompositionGraphError = class extends Error {};
42641
42969
  /** `"cap.field"` of every field a peer writes — the same spelling as `compositionFieldKey`. */
42642
42970
  function writtenFields(peer) {
42643
- return new Set(peer.composition.features.flatMap((feature) => feature.kind !== "fields" ? [] : featureFieldEntries(feature).map(({ fieldPath }) => `${feature.cap}.${fieldPath}`)));
42971
+ return new Set(peer.composition.features.flatMap((feature) => !compositionFeatureEnabled(feature) || feature.kind !== "fields" ? [] : featureFieldEntries(feature).map(({ fieldPath }) => `${feature.cap}.${fieldPath}`)));
42644
42972
  }
42645
42973
  /** A read of the peer's OWN device is an edge only when it reads the peer's own output (rule 5, D663). */
42646
42974
  function readsComposedValue(peer, written, read) {
@@ -42650,7 +42978,7 @@ function readsComposedValue(peer, written, read) {
42650
42978
  }
42651
42979
  function peerEdges(peer) {
42652
42980
  const written = writtenFields(peer);
42653
- return peer.composition.features.flatMap((feature) => feature.kind !== "fields" ? [] : featureFieldEntries(feature).map(({ fieldPath, source }) => [compositionReadKey({
42981
+ return peer.composition.features.flatMap((feature) => !compositionFeatureEnabled(feature) || feature.kind !== "fields" ? [] : featureFieldEntries(feature).map(({ fieldPath, source }) => [compositionReadKey({
42654
42982
  source: peer.target,
42655
42983
  cap: feature.cap,
42656
42984
  fieldPath
@@ -42755,114 +43083,6 @@ function findCandidateCycles(candidate, stored) {
42755
43083
  }];
42756
43084
  });
42757
43085
  }
42758
- /** Unwrap ZodNullable / ZodOptional / ZodDefault wrappers to reach the inner type.
42759
- * Zod v4 exposes `.unwrap()` on all three wrapper classes. */
42760
- function unwrapSchema(schema) {
42761
- let s = schema;
42762
- while (s instanceof z.ZodNullable || s instanceof z.ZodOptional || s instanceof z.ZodDefault) s = s.unwrap();
42763
- return s;
42764
- }
42765
- function leafKind(schema) {
42766
- const s = unwrapSchema(schema);
42767
- if (s instanceof z.ZodEnum) return {
42768
- kind: "enum",
42769
- enumValues: s.options
42770
- };
42771
- if (s instanceof z.ZodNumber) return { kind: "number" };
42772
- if (s instanceof z.ZodString) return { kind: "string" };
42773
- if (s instanceof z.ZodBoolean) return { kind: "boolean" };
42774
- return null;
42775
- }
42776
- /** Walk a cap status schema into flat wireable leaf fields (dotted paths).
42777
- * Recurses into nested ZodObject (unwrapping nullable/optional/default first),
42778
- * so fields with null live values are still offered. Arrays / unknown shapes
42779
- * are skipped — never throws. */
42780
- function enumerateSchemaFields(schema, prefix = "") {
42781
- const root = unwrapSchema(schema);
42782
- if (!(root instanceof z.ZodObject)) return [];
42783
- const out = [];
42784
- const shape = root.shape;
42785
- for (const [key, field] of Object.entries(shape)) {
42786
- const path = prefix ? `${prefix}.${key}` : key;
42787
- const inner = unwrapSchema(field);
42788
- if (inner instanceof z.ZodObject) {
42789
- out.push(...enumerateSchemaFields(inner, path));
42790
- continue;
42791
- }
42792
- const leaf = leafKind(field);
42793
- if (leaf) out.push({
42794
- path,
42795
- kind: leaf.kind,
42796
- ...leaf.enumValues ? { enumValues: leaf.enumValues } : {}
42797
- });
42798
- }
42799
- return out;
42800
- }
42801
- /** Enumerate the per-item wireable fields of an item-array cap (see
42802
- * `CapabilityStatusItemArray`): the item schema's leaf fields, minus the
42803
- * `keyField` (the key comes from the link's `itemKey`, never from a wired
42804
- * source), each tagged `item: true` so the authoring UI collects an
42805
- * `itemKey` alongside the field. Never throws. */
42806
- function enumerateItemArrayFields(itemArray) {
42807
- return enumerateSchemaFields(itemArray.itemSchema).filter((f) => f.path !== itemArray.keyField).map((f) => ({
42808
- ...f,
42809
- item: true
42810
- }));
42811
- }
42812
- /**
42813
- * What a capability's state field IS: kind, nullability, whether required. The
42814
- * validator needs nullability, which `enumerateSchemaFields` does not report,
42815
- * and that decides a field's unavailable mode (D393).
42816
- */
42817
- function fieldAt(schema, path) {
42818
- let current = schema;
42819
- for (const segment of path.split(".")) {
42820
- const container = unwrapSchema(current);
42821
- if (!(container instanceof z.ZodObject)) return null;
42822
- const next = container.shape[segment];
42823
- if (!(next instanceof z.ZodType)) return null;
42824
- current = next;
42825
- }
42826
- return current;
42827
- }
42828
- function describeStateField(schema, path) {
42829
- const leaf = enumerateSchemaFields(schema).find((f) => f.path === path);
42830
- if (leaf === void 0) return null;
42831
- const field = fieldAt(schema, path);
42832
- if (field === null) return null;
42833
- return {
42834
- path,
42835
- kind: leaf.kind,
42836
- nullable: field.safeParse(null).success,
42837
- required: !field.safeParse(void 0).success,
42838
- ...leaf.enumValues === void 0 ? {} : { enumValues: leaf.enumValues }
42839
- };
42840
- }
42841
- function describeTopLevelStateFields(schema) {
42842
- return new Map(enumerateSchemaFields(schema).filter((f) => !f.path.includes(".")).flatMap((f) => {
42843
- const described = describeStateField(schema, f.path);
42844
- return described === null ? [] : [[f.path, described]];
42845
- }));
42846
- }
42847
- /** Top-level keys a value MUST carry: those whose schema refuses `undefined`. */
42848
- function requiredTopLevelKeys(schema) {
42849
- const root = unwrapSchema(schema);
42850
- if (!(root instanceof z.ZodObject)) return [];
42851
- return Object.entries(root.shape).flatMap(([key, field]) => field instanceof z.ZodType && !field.safeParse(void 0).success ? [key] : []);
42852
- }
42853
- /**
42854
- * The proper prefixes of `path` whose schema accepts `undefined`: a required
42855
- * leaf under one of them is required only once something writes that parent
42856
- * (`remaining.unit` is required only when `remaining` is present).
42857
- */
42858
- function optionalAncestors(schema, path) {
42859
- const segments = path.split(".");
42860
- return segments.slice(0, -1).flatMap((_, i) => {
42861
- const prefix = segments.slice(0, i + 1).join(".");
42862
- const field = fieldAt(schema, prefix);
42863
- return field !== null && field.safeParse(void 0).success ? [prefix] : [];
42864
- });
42865
- }
42866
43086
  function problem(code, path, message) {
42867
43087
  return {
42868
43088
  code,
@@ -42874,7 +43094,8 @@ function refused(...problems) {
42874
43094
  return {
42875
43095
  problems,
42876
43096
  fields: [],
42877
- features: []
43097
+ features: [],
43098
+ events: []
42878
43099
  };
42879
43100
  }
42880
43101
  /** Where a planned field sits in the composition: `features[N].fields.<path>` or `features[N].items.<key>.fields.<path>`. */
@@ -42916,7 +43137,12 @@ var BUILTIN_RESULT_KINDS = Object.freeze({
42916
43137
  number: {
42917
43138
  kind: "fixed",
42918
43139
  result: ["number", "null"]
42919
- }
43140
+ },
43141
+ rose: {
43142
+ kind: "fixed",
43143
+ result: ["number", "null"]
43144
+ },
43145
+ count: NUMBER
42920
43146
  });
42921
43147
  var KindInferenceError = class extends Error {};
42922
43148
  function kindOfValue(value) {
@@ -42975,6 +43201,21 @@ function kindsOf(node, bindings) {
42975
43201
  case "call": return callKinds(node.callee, node.args.map((a) => kindsOf(a, bindings)));
42976
43202
  }
42977
43203
  }
43204
+ function nodeContainsCall(node, callee) {
43205
+ switch (node.kind) {
43206
+ case "literal":
43207
+ case "identifier": return false;
43208
+ case "unary": return nodeContainsCall(node.operand, callee);
43209
+ case "binary":
43210
+ case "logical": return nodeContainsCall(node.left, callee) || nodeContainsCall(node.right, callee);
43211
+ case "conditional": return nodeContainsCall(node.test, callee) || nodeContainsCall(node.consequent, callee) || nodeContainsCall(node.alternate, callee);
43212
+ case "call": return node.callee === callee || node.args.some((arg) => nodeContainsCall(arg, callee));
43213
+ }
43214
+ }
43215
+ function expressionContainsBuiltinCall(expr, callee) {
43216
+ const compiled = compileExpressionSafe(expr);
43217
+ return compiled.ok && nodeContainsCall(compiled.parsed.ast, callee);
43218
+ }
42978
43219
  function inferExpressionKinds(input) {
42979
43220
  const compiled = compileExpressionSafe(input.expr);
42980
43221
  if (!compiled.ok) return {
@@ -43095,6 +43336,7 @@ function enumLiteralProblem(cap, target, source, at) {
43095
43336
  }
43096
43337
  function roleOf(source) {
43097
43338
  if (source.kind !== "expression") return "derived";
43339
+ if (expressionContainsBuiltinCall(source.expr, "count")) return "accumulator";
43098
43340
  if (fieldReads(source).length > 0) return "derived";
43099
43341
  const compiled = compileExpressionSafe(source.expr);
43100
43342
  return compiled.ok && compiled.parsed.identifiers.has("now") ? "clock" : "constant";
@@ -43106,13 +43348,13 @@ function roleOf(source) {
43106
43348
  * field that READS a source never gets `offline`.
43107
43349
  */
43108
43350
  function unavailableFor(tf, role, mode, cap, at) {
43109
- if (mode === "new" || role !== "derived") return {
43351
+ if (mode === "replace" && (role === "derived" || role === "accumulator")) return {
43110
43352
  ok: true,
43111
- unavailable: tf.nullable ? "null" : "offline"
43353
+ unavailable: "release"
43112
43354
  };
43113
- if (mode === "replace") return {
43355
+ if (mode === "new" || role !== "derived") return {
43114
43356
  ok: true,
43115
- unavailable: "release"
43357
+ unavailable: tf.nullable ? "null" : "offline"
43116
43358
  };
43117
43359
  if (tf.nullable) return {
43118
43360
  ok: true,
@@ -43185,7 +43427,8 @@ function planItemField(ctx, leaf) {
43185
43427
  return {
43186
43428
  problems: [...compatibility(capName, tf, kinds.kinds, at), ...enumProblem === null ? [] : [enumProblem]],
43187
43429
  fields: [plan],
43188
- features: []
43430
+ features: [],
43431
+ events: []
43189
43432
  };
43190
43433
  }
43191
43434
  function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
@@ -43204,7 +43447,8 @@ function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
43204
43447
  const results = Object.entries(feature.items ?? {}).flatMap(([key, item]) => [{
43205
43448
  problems: missingLeaves(ctx, item, `${at}.${key}`),
43206
43449
  fields: [],
43207
- features: []
43450
+ features: [],
43451
+ events: []
43208
43452
  }, ...Object.entries(item.fields).map(([path, source]) => planItemField(ctx, {
43209
43453
  key,
43210
43454
  label: item.label,
@@ -43214,7 +43458,8 @@ function planItems(def, feature, featureIndex, lookupCap, mode = "new") {
43214
43458
  return {
43215
43459
  problems: results.flatMap((r) => r.problems),
43216
43460
  fields: results.flatMap((r) => r.fields),
43217
- features: []
43461
+ features: [],
43462
+ events: []
43218
43463
  };
43219
43464
  }
43220
43465
  function describeRead(read) {
@@ -43238,7 +43483,7 @@ function fromReading(reading, read) {
43238
43483
  deadline: null
43239
43484
  };
43240
43485
  }
43241
- function expressionOutcome(source, read, now) {
43486
+ function expressionOutcome(source, read, now, stateful) {
43242
43487
  const values = {};
43243
43488
  const staleReasons = [];
43244
43489
  let fatalUnavailable = null;
@@ -43267,10 +43512,31 @@ function expressionOutcome(source, read, now) {
43267
43512
  values[name] = reading.value;
43268
43513
  if (reading.state === "stale") staleReasons.push(`\`${name}\`: ${reading.reason}`);
43269
43514
  }
43270
- if (fatalUnavailable !== null) return fatalUnavailable;
43271
- if (nonFatalUnavailable !== null) return nonFatalUnavailable;
43515
+ const unavailable = fatalUnavailable ?? nonFatalUnavailable;
43516
+ if (unavailable !== null) {
43517
+ if (stateful !== void 0 && stateful.currentOutput !== void 0 && expressionContainsBuiltinCall(source.expr, "count")) return {
43518
+ state: "stale",
43519
+ value: evaluateCount(null, {
43520
+ kind: "count",
43521
+ highWater: null
43522
+ }, stateful.currentOutput),
43523
+ reason: `${unavailable.reason}; holding the last accumulator value`,
43524
+ deadline: null
43525
+ };
43526
+ return unavailable;
43527
+ }
43272
43528
  const deadlines = [];
43273
- const result = evaluateExpressionSource(source.expr, values, now, { hooks: { noteDeadline: (at) => deadlines.push(at) } });
43529
+ const result = evaluateExpressionSource(source.expr, values, now, {
43530
+ hooks: { noteDeadline: (at) => deadlines.push(at) },
43531
+ ...stateful === void 0 ? {} : { invokeStateful: (invocation) => evaluateStatefulExpressionCall(stateful.callPathPrefix === void 0 ? invocation : {
43532
+ ...invocation,
43533
+ path: `${stateful.callPathPrefix}@${invocation.path}`
43534
+ }, stateful.expressionMemory, {
43535
+ currentOutput: stateful.currentOutput,
43536
+ startedAtMs: stateful.startedAtMs,
43537
+ nowMs: now
43538
+ }) }
43539
+ });
43274
43540
  if (!result.ok) return {
43275
43541
  state: "unavailable",
43276
43542
  value: null,
@@ -43289,7 +43555,7 @@ function evaluateCompositionField(input) {
43289
43555
  const { source } = input;
43290
43556
  switch (source.kind) {
43291
43557
  case "from": return fromReading(input.read(source), source);
43292
- case "expression": return expressionOutcome(source, input.read, input.now);
43558
+ case "expression": return expressionOutcome(source, input.read, input.now, input.stateful);
43293
43559
  case "code": return {
43294
43560
  state: "unavailable",
43295
43561
  value: null,
@@ -43367,7 +43633,8 @@ function planTopLevelField({ def, fields }, field, mode, lookupCap) {
43367
43633
  return {
43368
43634
  problems: [...compatibility(def.name, tf, kinds.kinds, fieldAt), ...enumProblem === null ? [] : [enumProblem]],
43369
43635
  fields: [plan],
43370
- features: []
43636
+ features: [],
43637
+ events: []
43371
43638
  };
43372
43639
  }
43373
43640
  function planFeature(feature, featureIndex, target, lookupCap) {
@@ -43383,14 +43650,22 @@ function planFeature(feature, featureIndex, target, lookupCap) {
43383
43650
  }, target.mode, lookupCap));
43384
43651
  const items = feature.items === void 0 ? [] : [planItems(cap.def, feature, featureIndex, lookupCap, target.mode)];
43385
43652
  const all = [...perField, ...items];
43653
+ const fields = all.flatMap((result) => result.fields);
43654
+ const featurePlan = {
43655
+ featureIndex,
43656
+ cap: cap.def.name,
43657
+ mode: target.mode
43658
+ };
43659
+ const eventPlan = planCompositionEvents(cap.def, featurePlan, fields);
43386
43660
  return {
43387
- problems: [...capProblems, ...all.flatMap((r) => r.problems)],
43388
- fields: all.flatMap((r) => r.fields),
43389
- features: [{
43390
- featureIndex,
43391
- cap: cap.def.name,
43392
- mode: target.mode
43393
- }]
43661
+ problems: [
43662
+ ...capProblems,
43663
+ ...all.flatMap((r) => r.problems),
43664
+ ...eventPlan.problems
43665
+ ],
43666
+ fields,
43667
+ features: [featurePlan],
43668
+ events: eventPlan.events
43394
43669
  };
43395
43670
  }
43396
43671
  /** A cap the device serves itself; one the composer grafted (an earlier ADD) is not native. */
@@ -43432,17 +43707,23 @@ function planComposition(composition, lookupCap, target) {
43432
43707
  const planTarget = planTargetOf(composition, target);
43433
43708
  if ("code" in planTarget) return refused(planTarget);
43434
43709
  const results = composition.features.map((feature, index) => {
43435
- if (feature.kind === "passthrough") return refused(problem("reserved-variant", `features[${index}]`, `whole-capability passthrough of \`${feature.cap}\` arrives in slice 3`));
43436
- return planFeature(feature, index, featureTargetOf(planTarget, feature.cap), lookupCap);
43710
+ const result = feature.kind === "passthrough" ? refused(problem("reserved-variant", `features[${index}]`, `whole-capability passthrough of \`${feature.cap}\` arrives in slice 3`)) : planFeature(feature, index, featureTargetOf(planTarget, feature.cap), lookupCap);
43711
+ return compositionFeatureEnabled(feature) ? result : {
43712
+ problems: result.problems,
43713
+ fields: [],
43714
+ features: [],
43715
+ events: []
43716
+ };
43437
43717
  });
43438
43718
  return {
43439
43719
  problems: results.flatMap((r) => r.problems),
43440
43720
  fields: results.flatMap((r) => r.fields),
43441
- features: results.flatMap((r) => r.features)
43721
+ features: results.flatMap((r) => r.features),
43722
+ events: results.flatMap((r) => r.events)
43442
43723
  };
43443
43724
  }
43444
43725
  function compositionSourceRefs(composition) {
43445
- const refs = composition.features.flatMap((f) => f.kind === "fields" ? featureSources(f).flatMap(fieldReads).map((r) => r.source) : []);
43726
+ const refs = composition.features.flatMap((f) => compositionFeatureEnabled(f) && f.kind === "fields" ? featureSources(f).flatMap(fieldReads).map((r) => r.source) : []);
43446
43727
  return [...new Map(refs.map((r) => [compositionSourceKey(r), r])).values()];
43447
43728
  }
43448
43729
  /**
@@ -43577,7 +43858,8 @@ function validateComposition(composition, ctx) {
43577
43858
  ok: problems.length === 0,
43578
43859
  problems,
43579
43860
  fields: [...plan.fields],
43580
- features: [...plan.features]
43861
+ features: [...plan.features],
43862
+ events: [...plan.events]
43581
43863
  };
43582
43864
  }
43583
43865
  /**
@@ -57112,4 +57394,4 @@ function enumerateInferenceDevices(hw) {
57112
57394
  return out;
57113
57395
  }
57114
57396
  //#endregion
57115
- export { compositionFieldKey as $, expandCapMethods as $n, parseStreamParamsFormPatch as $t, RecordingStatusSchema as A, composedDeviceRef as An, isCollectionArrayMethod as At, addonPagesCapability as B, BaseAddon as Bn, localNetworkCapability as Bt, LocationIconIdSchema as C, validateComposition as Cn, expandContainer as Ct, RECORDING_SOURCE_CAMSTACK_ADDON as D, withLocationMode as Dn, formatMinutes as Dt, MOTION_CLOSE_AFTER_MS as E, vectorStoreCapability as En, foldSnapshotByFunction as Et, StorageCleanupJobSchema as F, toExpressionValue as Fn, isVoidInput as Ft, backupCapability as G, ReadinessTimeoutError as Gn, mayReadLocation as Gt, addonWidgetsCapability as H, DEVICE_SETTINGS_CONTRIBUTION_METHODS as Hn, logDestinationCapability as Ht, StorageLocationModeSchema as I, DeviceFeature as In, kebabToCamel as It, buildCapabilityLookup as J, asNumber as Jn, modeMayWrite as Jt, bareAddonId as K, WELL_KNOWN_TAB_MAP as Kn, mayWriteToLocation as Kt, StorageLocationTypeSchema as L, DeviceRole as Ln, legacyAutoLinkIds as Lt, STORAGE_ACCESS_FALLBACK as M, compositionItemFieldPath as Mn, isObjectInput as Mt, STREAM_PROFILE_META as N, compositionSliceKey as Nn, isSameAddonId as Nt, REDACTED_SECRET as O, COMPOSER_ADDON_ID as On, gbToBytes as Ot, ScopedTokenSchema as P, compositionSourceKey as Pn, isSourceCap as Pt, composerCapability as Q, emitReadiness as Qn, objectInputDeclaresRequiredDeviceId as Qt, StorageMigrationJobSchema as R, DeviceType as Rn, legacyModeOf as Rt, LegacyStorageLocationDefaultSchema as S, userManagementCapability as Sn, evictionPolicyOfLocation as St, MINUTES_PER_DAY as T, vectorIndexDimConflictError as Tn, filesystemBrowseCapability as Tt, alertsCapability as U, DEVICE_STATUS_METHOD as Un, logLevelAtMost as Ut, addonSettingsCapability as V, DATAPLANE_SECRET_HEADER as Vn, locationIconKey as Vt, authProviderCapability as W, ReadinessRegistry as Wn, looseSchema as Wt, cameraGridLayoutCapability as X, createEvent as Xn, normalizeUnit as Xt, buildStreamParamsConfigSchema as Y, asString$1 as Yn, motionCapability as Yt, collectSecretConfigKeys as Z, emitDownForOwnedCaps as Zn, objectInputDeclaresAddonId as Zt, DeviceStatusSchema as _, storageMigrationCapability as _n, enumerateInferenceDevices as _t, BatteryStatusSchema as a, replacedFieldKeys as an, parseJsonUnknown as ar, coreBlocksCapability as at, FieldClaimSchema as b, streamCatalogCapability as bn, evaluateCompositionField as bt, COMPOSED_ITEM_FIXED_FIELDS as c, resolveMethodAuth as cn, scopeKey as cr, declarationOwnerNodeId as ct, CORE_BLOCK_ADDON_PREFIX as d, scopesAllowDeviceCap as dn, deriveBatteryPresence as dt, planComposition as en, hydrateSchema as er, compositionSourceRefs as et, CoreBlockSchema as f, scoreRuntimes as fn, deviceExtensionCapability as ft, DeclaredDevices as g, storageCapability as gn, doorbellCapability as gt, DERIVED_CAMERA_SILENCED_CAP_NAMES as h, snapshotCapability as hn, deviceStatusCapability as ht, BaseDevice as i, reducePoints as in, parseJsonObject as ir, coreBlockSourceKind as it, SOURCE_CAPS as j, composedDeviceStableId as jn, isLocationEnabled as jt, RUNTIME_DEFAULTS as k, buildFfmpegArgs as kn, isArrayOutputSchema as kt, COMPOSER_RUNNER_ID as l, runtimeStatePolicyFor as ln, sleep as lr, decodeVectorBase64 as lt, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as m, siteZoneKey as mn, deviceStateCapability as mt, AlertSchema as n, procedureAuthKey as nn, isEvent as nr, coreBlockComposition as nt, CAMERA_SWITCH_ORDER as o, resolveBucketMs as on, readinessKey as or, createDeviceExtensionProvider as ot, DAY_NAMES as p, settingsStoreCapability as pn, deviceManagerCapability as pt, batteryCapability as q, asJsonObject as qn, metricsProviderCapability as qt, ApiKeyRecordSchema as r, ptzAutotrackCapability as rn, nodePin as rr, coreBlockIdFromAddonId as rt, CAP_NAMES_WITH_STATUS as s, resolveLocationMode as sn, resolveCapMount as sr, dataStoreProviderCapability as st, ALL_CAPABILITY_DEFINITIONS as t, platformProbeCapability as tn, isDeviceConfigCap as tr, coreBlockAddonId as tt, CORE_BLOCKS_ADDON_ID as u, scopesAllowAddon as un, EventCategory as ur, defineExtensionSlotProvider as ut, FIRST_LEVEL_MACRO_CLASSES as v, storageOccupancyCapability as vn, enumerateItemArrayFields as vt, METHOD_ACCESS_MAP as w, vectorDimFromBase64 as wn, extractNestedAddonId as wt, LOG_LEVEL_RANK as x, streamQualityLabel as xn, evaluateSensorEdge as xt, FieldClaimModeSchema as y, storageProviderCapability as yn, enumerateSchemaFields as yt, UserRecordSchema as z, errMsg as zn, lifecycleJobSchema as zt };
57397
+ export { coreBlockAddonId as $, asNumber as $n, ptzAutotrackCapability as $t, STORAGE_ACCESS_FALLBACK as A, cloneStatefulExpressionMemory as An, isSameAddonId as At, addonWidgetsCapability as B, toExpressionValue as Bn, looseSchema as Bt, METHOD_ACCESS_MAP as C, withLocationMode as Cn, foldSnapshotByFunction as Ct, REDACTED_SECRET as D, SOURCE_CAP_ACTIVE_FIELD as Dn, isCollectionArrayMethod as Dt, RECORDING_SOURCE_CAMSTACK_ADDON as E, SOURCE_CAPS as En, isArrayOutputSchema as Et, StorageLocationTypeSchema as F, compositionSliceKey as Fn, lifecycleJobSchema as Ft, batteryCapability as G, BaseAddon as Gn, motionCapability as Gt, authProviderCapability as H, DeviceRole as Hn, mayWriteToLocation as Ht, StorageMigrationJobSchema as I, compositionSourceKey as In, localNetworkCapability as It, cameraGridLayoutCapability as J, DEVICE_STATUS_METHOD as Jn, objectInputDeclaresRequiredDeviceId as Jt, buildCapabilityLookup as K, DATAPLANE_SECRET_HEADER as Kn, normalizeUnit as Kt, UserRecordSchema as L, createStatefulExpressionMemory as Ln, locationIconKey as Lt, ScopedTokenSchema as M, composedDeviceStableId as Mn, kebabToCamel as Mt, StorageCleanupJobSchema as N, compositionFeatureEnabled as Nn, legacyAutoLinkIds as Nt, RUNTIME_DEFAULTS as O, SOURCE_CAP_CHANGED_AT_FIELD as On, isLocationEnabled as Ot, StorageLocationModeSchema as P, compositionItemFieldPath as Pn, legacyModeOf as Pt, compositionSourceRefs as Q, asJsonObject as Qn, procedureAuthKey as Qt, addonPagesCapability as R, evaluateSensorEdge as Rn, logDestinationCapability as Rt, LocationIconIdSchema as S, vectorStoreCapability as Sn, filesystemBrowseCapability as St, MOTION_CLOSE_AFTER_MS as T, DEFAULT_FIRST_SIGHTING_FRESHNESS_MS as Tn, gbToBytes as Tt, backupCapability as U, DeviceType as Un, metricsProviderCapability as Ut, alertsCapability as V, DeviceFeature as Vn, mayReadLocation as Vt, bareAddonId as W, errMsg as Wn, modeMayWrite as Wt, composerCapability as X, ReadinessTimeoutError as Xn, planComposition as Xt, collectSecretConfigKeys as Y, ReadinessRegistry as Yn, parseStreamParamsFormPatch as Yt, compositionFieldKey as Z, WELL_KNOWN_TAB_MAP as Zn, platformProbeCapability as Zt, FIRST_LEVEL_MACRO_CLASSES as _, streamQualityLabel as _n, enumerateSchemaFields as _t, BatteryStatusSchema as a, runtimeStatePolicyFor as an, hydrateSchema as ar, dataStoreProviderCapability as at, LOG_LEVEL_RANK as b, vectorDimFromBase64 as bn, expandContainer as bt, COMPOSED_ITEM_FIXED_FIELDS as c, scoreRuntimes as cn, nodePin as cr, defineExtensionSlotProvider as ct, CORE_BLOCK_ADDON_PREFIX as d, snapshotCapability as dn, readinessKey as dr, deviceManagerCapability as dt, reducePoints as en, asString$1 as er, coreBlockComposition as et, CoreBlockSchema as f, storageCapability as fn, resolveCapMount as fr, deviceStateCapability as ft, DeviceStatusSchema as g, streamCatalogCapability as gn, enumerateItemArrayFields as gt, DeclaredDevices as h, storageProviderCapability as hn, EventCategory as hr, enumerateInferenceDevices as ht, BaseDevice as i, resolveMethodAuth as in, expandCapMethods as ir, createDeviceExtensionProvider as it, STREAM_PROFILE_META as j, composedDeviceRef as jn, isVoidInput as jt, RecordingStatusSchema as k, buildFfmpegArgs as kn, isObjectInput as kt, COMPOSER_RUNNER_ID as l, settingsStoreCapability as ln, parseJsonObject as lr, deriveBatteryPresence as lt, DERIVED_CAMERA_SILENCED_CAP_NAMES as m, storageOccupancyCapability as mn, sleep as mr, doorbellCapability as mt, AlertSchema as n, resolveBucketMs as nn, emitDownForOwnedCaps as nr, coreBlockSourceKind as nt, CAMERA_SWITCH_ORDER as o, scopesAllowAddon as on, isDeviceConfigCap as or, declarationOwnerNodeId as ot, DAY_NAMES as p, storageMigrationCapability as pn, scopeKey as pr, deviceStatusCapability as pt, buildStreamParamsConfigSchema as q, DEVICE_SETTINGS_CONTRIBUTION_METHODS as qn, objectInputDeclaresAddonId as qt, ApiKeyRecordSchema as r, resolveLocationMode as rn, emitReadiness as rr, coreBlocksCapability as rt, CAP_NAMES_WITH_STATUS as s, scopesAllowDeviceCap as sn, isEvent as sr, decodeVectorBase64 as st, ALL_CAPABILITY_DEFINITIONS as t, replacedFieldKeys as tn, createEvent as tr, coreBlockIdFromAddonId as tt, CORE_BLOCKS_ADDON_ID as u, siteZoneKey as un, parseJsonUnknown as ur, deviceExtensionCapability as ut, FieldClaimModeSchema as v, userManagementCapability as vn, evaluateCompositionField as vt, MINUTES_PER_DAY as w, COMPOSER_ADDON_ID as wn, formatMinutes as wt, LegacyStorageLocationDefaultSchema as x, vectorIndexDimConflictError as xn, extractNestedAddonId as xt, FieldClaimSchema as y, validateComposition as yn, evictionPolicyOfLocation as yt, addonSettingsCapability as z, isSourceCap as zn, logLevelAtMost as zt };