@camstack/types 1.2.72 → 1.2.74

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.
@@ -230,28 +230,36 @@ export declare const NC_AUDIO_DBFS_FLOOR = -96;
230
230
  /**
231
231
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
232
232
  *
233
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
234
- * reference notifier uses, so an operator moving between them re-uses what
235
- * they already know): a rule matches when, over a sampling window of
236
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
237
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
233
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
234
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
235
+ * there is no second switch that can disagree with the first and every rule
236
+ * authored before the decision migrates for free (`audioModeOf`):
238
237
  *
239
- * - `dbThreshold`its level is at or above this many dBFS (see
240
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
241
- * - `labels` the classifier put at least one of these labels on it.
238
+ * - **LABEL mode `labels` present.** The rule fires on the FIRST frame the
239
+ * classifier labels with one of them. No window, no percentage:
240
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
241
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
242
+ * the analyzer's (`classificationMinScore`, per device) — a label only
243
+ * reaches this condition if the classifier was already confident enough.
244
+ * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
245
+ * the condition: at least `hitPercent`% of the samples over
246
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
247
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
248
+ * must be FULL before it can match — a window open for two of its ten
249
+ * seconds is 100% of nothing.
242
250
  *
243
- * Both are OPTIONAL and independent, which is the point of the shape: a
244
- * loudness rule ("something loud at 3am") needs no model to be right, and a
245
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
246
- * is given** a window in which every sample is trivially a hit would fire on
247
- * silence, so the engine refuses such a condition rather than notifying on
248
- * nothing (the schema cannot express "at least one of" without becoming a
249
- * ZodEffects the cap path would have to special-case).
251
+ * **Why label mode has no window.** It had one, and it never fired: the
252
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
253
+ * of them per episode, even through continuous crying. The measured maximum
254
+ * `hitPercent` over the whole live history was 40 under the shipped default
255
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
256
+ * the wrong question to ask of a sparse classifier.
250
257
  *
251
- * `hitPercent` is over the samples the window actually HOLDS, and the window
252
- * must be FULL before it can match a window that has been open for two
253
- * seconds of its ten is 100% of nothing, and firing on it would make
254
- * `samplingSeconds` decorative.
258
+ * **Fail-closed when NEITHER is given** every sample would be a trivial hit
259
+ * and the rule would fire on silence. The schema cannot express "exactly one
260
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
261
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
262
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
255
263
  *
256
264
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
257
265
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -468,11 +476,67 @@ export declare const NcDeviceStateConditionSchema: z.ZodObject<{
468
476
  states: z.ZodArray<z.ZodString>;
469
477
  }, z.core.$strip>;
470
478
  export type NcDeviceStateCondition = z.infer<typeof NcDeviceStateConditionSchema>;
479
+ /**
480
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
481
+ *
482
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
483
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
484
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
485
+ * already has a trigger ("tell me about a person at the front door, but only
486
+ * while the bin is still out"). That is why it composes with every delivery
487
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
488
+ * kind exist for it — see D159.
489
+ *
490
+ * ── Identity ───────────────────────────────────────────────────────────────
491
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
492
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
493
+ * carried as a HINT for the editor and for the log line, never as part of the
494
+ * lookup key: a rule whose hint drifted must still gate correctly.
495
+ *
496
+ * ── Which boolean ──────────────────────────────────────────────────────────
497
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
498
+ * already declares which boolean drives notification rules, and a second knob
499
+ * that could disagree with it is exactly the D62 failure. Set it only to
500
+ * override one rule against the scene's own default.
501
+ *
502
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
503
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
504
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
505
+ * evidence, in either direction.
506
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
507
+ * The latch is a durable fact about the past ("it has diverged since I armed
508
+ * it"), so a camera that has gone dark does not clear it — that is the whole
509
+ * reason the operator asked for a latch.
510
+ *
511
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
512
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
513
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
514
+ * said out loud in the log rather than dropped in silence.
515
+ */
516
+ export declare const NcSceneConditionSchema: z.ZodObject<{
517
+ sceneId: z.ZodString;
518
+ deviceId: z.ZodOptional<z.ZodNumber>;
519
+ requiredState: z.ZodEnum<{
520
+ matched: "matched";
521
+ diverged: "diverged";
522
+ }>;
523
+ latched: z.ZodOptional<z.ZodBoolean>;
524
+ }, z.core.$strip>;
525
+ export type NcSceneCondition = z.infer<typeof NcSceneConditionSchema>;
471
526
  export declare const NcConditionsSchema: z.ZodObject<{
472
527
  deviceState: z.ZodOptional<z.ZodObject<{
473
528
  deviceId: z.ZodNumber;
474
529
  states: z.ZodArray<z.ZodString>;
475
530
  }, z.core.$strip>>;
531
+ scene: z.ZodOptional<z.ZodObject<{
532
+ sceneId: z.ZodString;
533
+ deviceId: z.ZodOptional<z.ZodNumber>;
534
+ requiredState: z.ZodEnum<{
535
+ matched: "matched";
536
+ diverged: "diverged";
537
+ }>;
538
+ latched: z.ZodOptional<z.ZodBoolean>;
539
+ }, z.core.$strip>>;
476
540
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
477
541
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
478
542
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -742,6 +806,15 @@ export declare const NcRuleInputSchema: z.ZodObject<{
742
806
  deviceId: z.ZodNumber;
743
807
  states: z.ZodArray<z.ZodString>;
744
808
  }, z.core.$strip>>;
809
+ scene: z.ZodOptional<z.ZodObject<{
810
+ sceneId: z.ZodString;
811
+ deviceId: z.ZodOptional<z.ZodNumber>;
812
+ requiredState: z.ZodEnum<{
813
+ matched: "matched";
814
+ diverged: "diverged";
815
+ }>;
816
+ latched: z.ZodOptional<z.ZodBoolean>;
817
+ }, z.core.$strip>>;
745
818
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
746
819
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
747
820
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1075,6 +1148,15 @@ export declare const NcRulePatchSchema: z.ZodObject<{
1075
1148
  deviceId: z.ZodNumber;
1076
1149
  states: z.ZodArray<z.ZodString>;
1077
1150
  }, z.core.$strip>>;
1151
+ scene: z.ZodOptional<z.ZodObject<{
1152
+ sceneId: z.ZodString;
1153
+ deviceId: z.ZodOptional<z.ZodNumber>;
1154
+ requiredState: z.ZodEnum<{
1155
+ matched: "matched";
1156
+ diverged: "diverged";
1157
+ }>;
1158
+ latched: z.ZodOptional<z.ZodBoolean>;
1159
+ }, z.core.$strip>>;
1078
1160
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
1079
1161
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1080
1162
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1225,6 +1307,15 @@ export declare const NcRuleSchema: z.ZodObject<{
1225
1307
  deviceId: z.ZodNumber;
1226
1308
  states: z.ZodArray<z.ZodString>;
1227
1309
  }, z.core.$strip>>;
1310
+ scene: z.ZodOptional<z.ZodObject<{
1311
+ sceneId: z.ZodString;
1312
+ deviceId: z.ZodOptional<z.ZodNumber>;
1313
+ requiredState: z.ZodEnum<{
1314
+ matched: "matched";
1315
+ diverged: "diverged";
1316
+ }>;
1317
+ latched: z.ZodOptional<z.ZodBoolean>;
1318
+ }, z.core.$strip>>;
1228
1319
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
1229
1320
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1230
1321
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1489,6 +1580,7 @@ export declare const NcConditionDescriptorSchema: z.ZodObject<{
1489
1580
  audio: "audio";
1490
1581
  deviceState: "deviceState";
1491
1582
  schedule: "schedule";
1583
+ scene: "scene";
1492
1584
  packagePhase: "packagePhase";
1493
1585
  systemEvent: "systemEvent";
1494
1586
  occupancy: "occupancy";
@@ -1890,6 +1982,15 @@ export declare const notificationRulesCapability: {
1890
1982
  deviceId: z.ZodNumber;
1891
1983
  states: z.ZodArray<z.ZodString>;
1892
1984
  }, z.core.$strip>>;
1985
+ scene: z.ZodOptional<z.ZodObject<{
1986
+ sceneId: z.ZodString;
1987
+ deviceId: z.ZodOptional<z.ZodNumber>;
1988
+ requiredState: z.ZodEnum<{
1989
+ matched: "matched";
1990
+ diverged: "diverged";
1991
+ }>;
1992
+ latched: z.ZodOptional<z.ZodBoolean>;
1993
+ }, z.core.$strip>>;
1893
1994
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
1894
1995
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1895
1996
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2134,6 +2235,15 @@ export declare const notificationRulesCapability: {
2134
2235
  deviceId: z.ZodNumber;
2135
2236
  states: z.ZodArray<z.ZodString>;
2136
2237
  }, z.core.$strip>>;
2238
+ scene: z.ZodOptional<z.ZodObject<{
2239
+ sceneId: z.ZodString;
2240
+ deviceId: z.ZodOptional<z.ZodNumber>;
2241
+ requiredState: z.ZodEnum<{
2242
+ matched: "matched";
2243
+ diverged: "diverged";
2244
+ }>;
2245
+ latched: z.ZodOptional<z.ZodBoolean>;
2246
+ }, z.core.$strip>>;
2137
2247
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
2138
2248
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
2139
2249
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2376,6 +2486,15 @@ export declare const notificationRulesCapability: {
2376
2486
  deviceId: z.ZodNumber;
2377
2487
  states: z.ZodArray<z.ZodString>;
2378
2488
  }, z.core.$strip>>;
2489
+ scene: z.ZodOptional<z.ZodObject<{
2490
+ sceneId: z.ZodString;
2491
+ deviceId: z.ZodOptional<z.ZodNumber>;
2492
+ requiredState: z.ZodEnum<{
2493
+ matched: "matched";
2494
+ diverged: "diverged";
2495
+ }>;
2496
+ latched: z.ZodOptional<z.ZodBoolean>;
2497
+ }, z.core.$strip>>;
2379
2498
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
2380
2499
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
2381
2500
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2612,6 +2731,15 @@ export declare const notificationRulesCapability: {
2612
2731
  deviceId: z.ZodNumber;
2613
2732
  states: z.ZodArray<z.ZodString>;
2614
2733
  }, z.core.$strip>>;
2734
+ scene: z.ZodOptional<z.ZodObject<{
2735
+ sceneId: z.ZodString;
2736
+ deviceId: z.ZodOptional<z.ZodNumber>;
2737
+ requiredState: z.ZodEnum<{
2738
+ matched: "matched";
2739
+ diverged: "diverged";
2740
+ }>;
2741
+ latched: z.ZodOptional<z.ZodBoolean>;
2742
+ }, z.core.$strip>>;
2615
2743
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
2616
2744
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
2617
2745
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -2945,6 +3073,15 @@ export declare const notificationRulesCapability: {
2945
3073
  deviceId: z.ZodNumber;
2946
3074
  states: z.ZodArray<z.ZodString>;
2947
3075
  }, z.core.$strip>>;
3076
+ scene: z.ZodOptional<z.ZodObject<{
3077
+ sceneId: z.ZodString;
3078
+ deviceId: z.ZodOptional<z.ZodNumber>;
3079
+ requiredState: z.ZodEnum<{
3080
+ matched: "matched";
3081
+ diverged: "diverged";
3082
+ }>;
3083
+ latched: z.ZodOptional<z.ZodBoolean>;
3084
+ }, z.core.$strip>>;
2948
3085
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
2949
3086
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
2950
3087
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3094,6 +3231,15 @@ export declare const notificationRulesCapability: {
3094
3231
  deviceId: z.ZodNumber;
3095
3232
  states: z.ZodArray<z.ZodString>;
3096
3233
  }, z.core.$strip>>;
3234
+ scene: z.ZodOptional<z.ZodObject<{
3235
+ sceneId: z.ZodString;
3236
+ deviceId: z.ZodOptional<z.ZodNumber>;
3237
+ requiredState: z.ZodEnum<{
3238
+ matched: "matched";
3239
+ diverged: "diverged";
3240
+ }>;
3241
+ latched: z.ZodOptional<z.ZodBoolean>;
3242
+ }, z.core.$strip>>;
3097
3243
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
3098
3244
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
3099
3245
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3384,6 +3530,15 @@ export declare const notificationRulesCapability: {
3384
3530
  deviceId: z.ZodNumber;
3385
3531
  states: z.ZodArray<z.ZodString>;
3386
3532
  }, z.core.$strip>>;
3533
+ scene: z.ZodOptional<z.ZodObject<{
3534
+ sceneId: z.ZodString;
3535
+ deviceId: z.ZodOptional<z.ZodNumber>;
3536
+ requiredState: z.ZodEnum<{
3537
+ matched: "matched";
3538
+ diverged: "diverged";
3539
+ }>;
3540
+ latched: z.ZodOptional<z.ZodBoolean>;
3541
+ }, z.core.$strip>>;
3387
3542
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
3388
3543
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
3389
3544
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -3655,6 +3810,7 @@ export declare const notificationRulesCapability: {
3655
3810
  audio: "audio";
3656
3811
  deviceState: "deviceState";
3657
3812
  schedule: "schedule";
3813
+ scene: "scene";
3658
3814
  packagePhase: "packagePhase";
3659
3815
  systemEvent: "systemEvent";
3660
3816
  occupancy: "occupancy";
@@ -93,6 +93,15 @@ export declare const OsdSlotBindingSchema: z.ZodObject<{
93
93
  deviceId: z.ZodNumber;
94
94
  states: z.ZodArray<z.ZodString>;
95
95
  }, z.core.$strip>>;
96
+ scene: z.ZodOptional<z.ZodObject<{
97
+ sceneId: z.ZodString;
98
+ deviceId: z.ZodOptional<z.ZodNumber>;
99
+ requiredState: z.ZodEnum<{
100
+ matched: "matched";
101
+ diverged: "diverged";
102
+ }>;
103
+ latched: z.ZodOptional<z.ZodBoolean>;
104
+ }, z.core.$strip>>;
96
105
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
97
106
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
98
107
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -243,6 +252,15 @@ export declare const OsdSlotViewSchema: z.ZodObject<{
243
252
  deviceId: z.ZodNumber;
244
253
  states: z.ZodArray<z.ZodString>;
245
254
  }, z.core.$strip>>;
255
+ scene: z.ZodOptional<z.ZodObject<{
256
+ sceneId: z.ZodString;
257
+ deviceId: z.ZodOptional<z.ZodNumber>;
258
+ requiredState: z.ZodEnum<{
259
+ matched: "matched";
260
+ diverged: "diverged";
261
+ }>;
262
+ latched: z.ZodOptional<z.ZodBoolean>;
263
+ }, z.core.$strip>>;
246
264
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
247
265
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
248
266
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -465,6 +483,15 @@ export declare const osdManagerCapability: {
465
483
  deviceId: z.ZodNumber;
466
484
  states: z.ZodArray<z.ZodString>;
467
485
  }, z.core.$strip>>;
486
+ scene: z.ZodOptional<z.ZodObject<{
487
+ sceneId: z.ZodString;
488
+ deviceId: z.ZodOptional<z.ZodNumber>;
489
+ requiredState: z.ZodEnum<{
490
+ matched: "matched";
491
+ diverged: "diverged";
492
+ }>;
493
+ latched: z.ZodOptional<z.ZodBoolean>;
494
+ }, z.core.$strip>>;
468
495
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
469
496
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
470
497
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -617,6 +644,7 @@ export declare const osdManagerCapability: {
617
644
  audio: "audio";
618
645
  deviceState: "deviceState";
619
646
  schedule: "schedule";
647
+ scene: "scene";
620
648
  packagePhase: "packagePhase";
621
649
  systemEvent: "systemEvent";
622
650
  occupancy: "occupancy";
@@ -703,6 +731,15 @@ export declare const osdManagerCapability: {
703
731
  deviceId: z.ZodNumber;
704
732
  states: z.ZodArray<z.ZodString>;
705
733
  }, z.core.$strip>>;
734
+ scene: z.ZodOptional<z.ZodObject<{
735
+ sceneId: z.ZodString;
736
+ deviceId: z.ZodOptional<z.ZodNumber>;
737
+ requiredState: z.ZodEnum<{
738
+ matched: "matched";
739
+ diverged: "diverged";
740
+ }>;
741
+ latched: z.ZodOptional<z.ZodBoolean>;
742
+ }, z.core.$strip>>;
706
743
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
707
744
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
708
745
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -852,6 +889,15 @@ export declare const osdManagerCapability: {
852
889
  deviceId: z.ZodNumber;
853
890
  states: z.ZodArray<z.ZodString>;
854
891
  }, z.core.$strip>>;
892
+ scene: z.ZodOptional<z.ZodObject<{
893
+ sceneId: z.ZodString;
894
+ deviceId: z.ZodOptional<z.ZodNumber>;
895
+ requiredState: z.ZodEnum<{
896
+ matched: "matched";
897
+ diverged: "diverged";
898
+ }>;
899
+ latched: z.ZodOptional<z.ZodBoolean>;
900
+ }, z.core.$strip>>;
855
901
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
856
902
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
857
903
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -1033,6 +1079,15 @@ export declare const osdManagerCapability: {
1033
1079
  deviceId: z.ZodNumber;
1034
1080
  states: z.ZodArray<z.ZodString>;
1035
1081
  }, z.core.$strip>>;
1082
+ scene: z.ZodOptional<z.ZodObject<{
1083
+ sceneId: z.ZodString;
1084
+ deviceId: z.ZodOptional<z.ZodNumber>;
1085
+ requiredState: z.ZodEnum<{
1086
+ matched: "matched";
1087
+ diverged: "diverged";
1088
+ }>;
1089
+ latched: z.ZodOptional<z.ZodBoolean>;
1090
+ }, z.core.$strip>>;
1036
1091
  devices: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
1037
1092
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1038
1093
  classesExclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -9,10 +9,22 @@ import { type InferNativeProvider } from './capability-definition.js';
9
9
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
10
10
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
11
11
  *
12
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
13
- * derives the device-detail contribution; the provider carries NO hand-written
14
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
15
- * every hysteresis flip / availability change; consumers never poll.
12
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
13
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
14
+ * for the thing: a scene is a standing question about the property ("is the bin
15
+ * still out"), and the operator's question is "which of my scenes have tripped",
16
+ * across every camera at once — not "what does camera 617 think". Buried one
17
+ * camera deep it also could not be found. The surface is now a top-level admin
18
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
19
+ * picks the camera inside the create flow, the same shape Events and Faces have.
20
+ *
21
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
22
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
23
+ * directions, so a registration nobody declares fails exactly as loudly as a
24
+ * declaration nobody registers. The editor is imported directly by the page.
25
+ *
26
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
27
+ * availability change; consumers never poll.
16
28
  */
17
29
  /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
18
30
  * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
@@ -317,14 +329,6 @@ export declare const sceneMonitorCapability: {
317
329
  readonly kind: "wrapper";
318
330
  readonly defaultActive: true;
319
331
  readonly deviceTypes: readonly [DeviceType.Camera];
320
- readonly deviceConfig: {
321
- readonly ui: {
322
- readonly kind: "widget";
323
- readonly widgetId: "host/scene-monitor-editor";
324
- readonly tab: "scenes";
325
- readonly label: "Scenes";
326
- };
327
- };
328
332
  readonly methods: {
329
333
  readonly listScenes: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
330
334
  deviceId: z.ZodNumber;
@@ -39,6 +39,69 @@ declare const NetworkAddressSchema: z.ZodObject<{
39
39
  family: z.ZodString;
40
40
  internal: z.ZodBoolean;
41
41
  }, z.core.$strip>;
42
+ /**
43
+ * Provenance of the site coordinates, and the whole reason this is not just two
44
+ * numbers.
45
+ *
46
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
47
+ * nothing overwrites it.
48
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
49
+ * default that is right to a few kilometres beats the coarse UTC clock split
50
+ * the sun-times consumers otherwise fall back to.
51
+ *
52
+ * The UI shows which one it is. An operator who cannot tell a guess from their
53
+ * own input will eventually trust the guess.
54
+ */
55
+ declare const SiteLocationSourceSchema: z.ZodEnum<{
56
+ "operator-set": "operator-set";
57
+ "derived-from-ip": "derived-from-ip";
58
+ }>;
59
+ /**
60
+ * Where the installation physically is — a fact of the SITE, not of any addon.
61
+ *
62
+ * It used to live in `pipeline-analytics`' global settings, which made a
63
+ * property of the building a property of one analytics addon. Anything that
64
+ * needs sun-times (scene condition variants today; anything solar tomorrow)
65
+ * reads it from here.
66
+ */
67
+ declare const SiteLocationSchema: z.ZodObject<{
68
+ latitude: z.ZodNumber;
69
+ longitude: z.ZodNumber;
70
+ source: z.ZodEnum<{
71
+ "operator-set": "operator-set";
72
+ "derived-from-ip": "derived-from-ip";
73
+ }>;
74
+ updatedAt: z.ZodNumber;
75
+ label: z.ZodOptional<z.ZodString>;
76
+ }, z.core.$strip>;
77
+ /**
78
+ * The read shape: the location plus the honest state of the one-shot derivation.
79
+ *
80
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
81
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
82
+ * failed; the hub will NOT try again on its own — the fallback is declared
83
+ * (consumers degrade to their own last resort) and the operator either types the
84
+ * coordinates or presses detect.
85
+ */
86
+ declare const SiteLocationStatusSchema: z.ZodObject<{
87
+ location: z.ZodNullable<z.ZodObject<{
88
+ latitude: z.ZodNumber;
89
+ longitude: z.ZodNumber;
90
+ source: z.ZodEnum<{
91
+ "operator-set": "operator-set";
92
+ "derived-from-ip": "derived-from-ip";
93
+ }>;
94
+ updatedAt: z.ZodNumber;
95
+ label: z.ZodOptional<z.ZodString>;
96
+ }, z.core.$strip>>;
97
+ derivationAttemptedAt: z.ZodNullable<z.ZodNumber>;
98
+ derivationError: z.ZodNullable<z.ZodString>;
99
+ }, z.core.$strip>;
100
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
101
+ declare const SetSiteLocationInputSchema: z.ZodNullable<z.ZodObject<{
102
+ latitude: z.ZodNumber;
103
+ longitude: z.ZodNumber;
104
+ }, z.core.$strip>>;
42
105
  export declare const systemCapability: {
43
106
  readonly name: "system";
44
107
  readonly scope: "system";
@@ -79,6 +142,68 @@ export declare const systemCapability: {
79
142
  readonly getRetentionConfig: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodNullable<z.ZodUnknown>, import("./capability-definition.js").CapabilityMethodKind>;
80
143
  readonly setRetentionConfig: import("./capability-definition.js").CapabilityMethodSchema<z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodNull, "mutation">;
81
144
  readonly forceRetentionCleanup: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodVoid, "mutation">;
145
+ /**
146
+ * The site coordinates, deriving a default from the hub's public IP on the
147
+ * FIRST read that finds nothing stored.
148
+ *
149
+ * The derivation is one-shot and bounded: one outbound request, a few
150
+ * seconds, its outcome persisted either way. A hub with no internet pays it
151
+ * once and never again, and neither boot nor any consumer is blocked on it —
152
+ * the caller gets `location: null` and degrades exactly as it did before this
153
+ * method existed.
154
+ */
155
+ readonly getSiteLocation: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
156
+ location: z.ZodNullable<z.ZodObject<{
157
+ latitude: z.ZodNumber;
158
+ longitude: z.ZodNumber;
159
+ source: z.ZodEnum<{
160
+ "operator-set": "operator-set";
161
+ "derived-from-ip": "derived-from-ip";
162
+ }>;
163
+ updatedAt: z.ZodNumber;
164
+ label: z.ZodOptional<z.ZodString>;
165
+ }, z.core.$strip>>;
166
+ derivationAttemptedAt: z.ZodNullable<z.ZodNumber>;
167
+ derivationError: z.ZodNullable<z.ZodString>;
168
+ }, z.core.$strip>, import("./capability-definition.js").CapabilityMethodKind>;
169
+ /** Operator input. Always lands as `source: 'operator-set'`. */
170
+ readonly setSiteLocation: import("./capability-definition.js").CapabilityMethodSchema<z.ZodNullable<z.ZodObject<{
171
+ latitude: z.ZodNumber;
172
+ longitude: z.ZodNumber;
173
+ }, z.core.$strip>>, z.ZodObject<{
174
+ location: z.ZodNullable<z.ZodObject<{
175
+ latitude: z.ZodNumber;
176
+ longitude: z.ZodNumber;
177
+ source: z.ZodEnum<{
178
+ "operator-set": "operator-set";
179
+ "derived-from-ip": "derived-from-ip";
180
+ }>;
181
+ updatedAt: z.ZodNumber;
182
+ label: z.ZodOptional<z.ZodString>;
183
+ }, z.core.$strip>>;
184
+ derivationAttemptedAt: z.ZodNullable<z.ZodNumber>;
185
+ derivationError: z.ZodNullable<z.ZodString>;
186
+ }, z.core.$strip>, "mutation">;
187
+ /**
188
+ * Re-run the geo-IP derivation now. The ONLY way a spent or failed
189
+ * derivation is retried — there is no timer, and no read path retries.
190
+ * Overwrites an existing `derived-from-ip` value; refuses to clobber an
191
+ * `operator-set` one.
192
+ */
193
+ readonly detectSiteLocation: import("./capability-definition.js").CapabilityMethodSchema<z.ZodVoid, z.ZodObject<{
194
+ location: z.ZodNullable<z.ZodObject<{
195
+ latitude: z.ZodNumber;
196
+ longitude: z.ZodNumber;
197
+ source: z.ZodEnum<{
198
+ "operator-set": "operator-set";
199
+ "derived-from-ip": "derived-from-ip";
200
+ }>;
201
+ updatedAt: z.ZodNumber;
202
+ label: z.ZodOptional<z.ZodString>;
203
+ }, z.core.$strip>>;
204
+ derivationAttemptedAt: z.ZodNullable<z.ZodNumber>;
205
+ derivationError: z.ZodNullable<z.ZodString>;
206
+ }, z.core.$strip>, "mutation">;
82
207
  };
83
208
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
84
209
  readonly mount: {
@@ -86,4 +211,8 @@ export declare const systemCapability: {
86
211
  };
87
212
  };
88
213
  export type ISystemProvider = InferProvider<typeof systemCapability>;
89
- export { FeatureManifestSchema, HealthStatusSchema, NetworkAddressSchema };
214
+ export type SiteLocation = z.infer<typeof SiteLocationSchema>;
215
+ export type SiteLocationSource = z.infer<typeof SiteLocationSourceSchema>;
216
+ export type SiteLocationStatus = z.infer<typeof SiteLocationStatusSchema>;
217
+ export type SetSiteLocationInput = z.infer<typeof SetSiteLocationInputSchema>;
218
+ export { FeatureManifestSchema, HealthStatusSchema, NetworkAddressSchema, SetSiteLocationInputSchema, SiteLocationSchema, SiteLocationSourceSchema, SiteLocationStatusSchema, };
@@ -3354,6 +3354,13 @@ export type AppRouter = TrpcCoreRouter<{
3354
3354
  output: z.infer<typeof llmCapability.methods.generateVision.output>;
3355
3355
  meta: object;
3356
3356
  }>;
3357
+ cancel: TRPCMutationProcedure<{
3358
+ input: {
3359
+ [x: string]: unknown;
3360
+ } & z.input<typeof llmCapability.methods.cancel.input>;
3361
+ output: z.infer<typeof llmCapability.methods.cancel.output>;
3362
+ meta: object;
3363
+ }>;
3357
3364
  listProfileKinds: TRPCQueryProcedure<{
3358
3365
  input: {
3359
3366
  [x: string]: unknown;
@@ -7494,6 +7501,25 @@ export type AppRouter = TrpcCoreRouter<{
7494
7501
  output: z.infer<typeof systemCapability.methods.forceRetentionCleanup.output>;
7495
7502
  meta: object;
7496
7503
  }>;
7504
+ getSiteLocation: TRPCQueryProcedure<{
7505
+ input: {
7506
+ nodeId?: string | undefined;
7507
+ } | undefined;
7508
+ output: z.infer<typeof systemCapability.methods.getSiteLocation.output>;
7509
+ meta: object;
7510
+ }>;
7511
+ setSiteLocation: TRPCMutationProcedure<{
7512
+ input: z.input<typeof systemCapability.methods.setSiteLocation.input>;
7513
+ output: z.infer<typeof systemCapability.methods.setSiteLocation.output>;
7514
+ meta: object;
7515
+ }>;
7516
+ detectSiteLocation: TRPCMutationProcedure<{
7517
+ input: {
7518
+ nodeId?: string | undefined;
7519
+ } | undefined;
7520
+ output: z.infer<typeof systemCapability.methods.detectSiteLocation.output>;
7521
+ meta: object;
7522
+ }>;
7497
7523
  }>>;
7498
7524
  tamper: TRPCBuiltRouter<{
7499
7525
  ctx: TrpcContext;
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 934 method paths across 123 capabilities.
9
+ * Coverage: 938 method paths across 123 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {