@camstack/types 1.2.163 → 1.2.165

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.
@@ -93,6 +93,7 @@ export declare const dataStoreProviderCapability: {
93
93
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
94
94
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
95
95
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
96
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
96
97
  orderBy: z.ZodOptional<z.ZodObject<{
97
98
  field: z.ZodString;
98
99
  direction: z.ZodEnum<{
@@ -150,6 +151,7 @@ export declare const dataStoreProviderCapability: {
150
151
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
151
152
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
152
153
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
154
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
153
155
  }, z.core.$strip>;
154
156
  }, z.core.$strip>, z.ZodObject<{
155
157
  deleted: z.ZodNumber;
@@ -163,6 +165,7 @@ export declare const dataStoreProviderCapability: {
163
165
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
164
166
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
165
167
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
168
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
166
169
  }, z.core.$strip>;
167
170
  data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
168
171
  }, z.core.$strip>, z.ZodObject<{
@@ -177,6 +180,7 @@ export declare const dataStoreProviderCapability: {
177
180
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
178
181
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
179
182
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
183
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
180
184
  orderBy: z.ZodOptional<z.ZodObject<{
181
185
  field: z.ZodString;
182
186
  direction: z.ZodEnum<{
@@ -208,6 +212,7 @@ export declare const dataStoreProviderCapability: {
208
212
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
209
213
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
210
214
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
215
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
211
216
  orderBy: z.ZodOptional<z.ZodObject<{
212
217
  field: z.ZodString;
213
218
  direction: z.ZodEnum<{
@@ -234,6 +239,7 @@ export declare const dataStoreProviderCapability: {
234
239
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
235
240
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
236
241
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
242
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
237
243
  orderBy: z.ZodOptional<z.ZodObject<{
238
244
  field: z.ZodString;
239
245
  direction: z.ZodEnum<{
@@ -32,6 +32,7 @@ export declare const MotionOnMotionChangedDataSchema: z.ZodObject<{
32
32
  source: z.ZodEnum<{
33
33
  onboard: "onboard";
34
34
  analyzer: "analyzer";
35
+ "device-activity": "device-activity";
35
36
  }>;
36
37
  regions: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
37
38
  bbox: z.ZodObject<{
@@ -96,6 +97,7 @@ export declare const motionCapability: {
96
97
  source: z.ZodEnum<{
97
98
  onboard: "onboard";
98
99
  analyzer: "analyzer";
100
+ "device-activity": "device-activity";
99
101
  }>;
100
102
  regions: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
101
103
  bbox: z.ZodObject<{
@@ -247,6 +247,44 @@ export declare const TrackSourceSchema: z.ZodEnum<{
247
247
  pipeline: "pipeline";
248
248
  }>;
249
249
  export type TrackSource = z.infer<typeof TrackSourceSchema>;
250
+ /**
251
+ * The `excludeSources` filter on `listTracks` / `listRecentTracks`: track
252
+ * SOURCES to leave out. ABSENT or EMPTY excludes nothing.
253
+ *
254
+ * ## Why the query needs a source dimension at all
255
+ *
256
+ * `classes` cannot express it, and trying made a filter that lies. A synthetic
257
+ * row carries no accumulated `classes[]`, and `whereJsonArrayAny` is a
258
+ * deliberate SUPERSET — an unreadable column keeps its row. So on the live hub
259
+ * every class selection returned every synthetic row: `classes: ['person']`
260
+ * came back with 66 contact markers and 29 audio markers attached, and a class
261
+ * that exists nowhere at all still answered 116 rows, all of them synthetic.
262
+ * "Sensor" and "Audio" selected the identical set, because neither selected
263
+ * anything — they merely failed to exclude the rows the escape hatch keeps.
264
+ *
265
+ * Provenance is `source`, never a class name (D189 put every audio row on
266
+ * `className: 'audio'` precisely so the audio VOCABULARY could stop colliding
267
+ * with the detection one). This is the filter that speaks it.
268
+ *
269
+ * ## Why EXCLUDE and not an allow-list
270
+ *
271
+ * A stored client preference outlives this enum. An allow-list persisted by a
272
+ * viewer that has never heard of a source added next year would hide it
273
+ * forever, silently — the same shape as the `!== 'sensor'` check this file
274
+ * already warns about. An exclusion list can only ever hide what it names.
275
+ *
276
+ * ## NULL is `pipeline`
277
+ *
278
+ * Pipeline rows carry no `source` at all (the column is nullable and the
279
+ * completed-track persist omits it), so the provider compiles this as a
280
+ * NULL-safe `whereNotIn` when `pipeline` is NOT excluded, and as a positive
281
+ * `whereIn` over the surviving sources when it is. Both are exact; neither
282
+ * leaves a row's fate to a default.
283
+ *
284
+ * It is a REAL query filter, not a post-read drop: paging must tell the truth
285
+ * about how much is left.
286
+ */
287
+ export type ExcludeSourcesDoc = readonly TrackSource[];
250
288
  /**
251
289
  * Where a track sits in the RETRAIN lifecycle (D81).
252
290
  *
@@ -261,10 +299,12 @@ export type TrackSource = z.infer<typeof TrackSourceSchema>;
261
299
  * Terminal for the plain `markForTrain` toggle: returning it to `staging` is
262
300
  * a deliberate action of the retrain page, not a side effect of a checkbox.
263
301
  *
264
- * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` because
265
- * the store's filter language has only positive equality and `whereIn` — no
266
- * negation, no IS NULL — so a NULL would be unselectable by ANY predicate and
267
- * would make the entire pre-column history immortal in one deploy.
302
+ * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` so that
303
+ * every row is selectable by a positive predicate. (The filter language has
304
+ * since grown the NULL-safe `whereNot` / `whereNotIn` and an `IS NULL` reading
305
+ * of `where: { f: null }`, which is how {@link TrackSourceSchema} can be
306
+ * filtered on a NULLABLE column — see `excludeSources`. It did not when this
307
+ * column was designed, and a NOT NULL column is still the better shape.)
268
308
  */
269
309
  export declare const RetrainStatusSchema: z.ZodEnum<{
270
310
  none: "none";
@@ -1121,6 +1161,11 @@ declare const RecentTracksQueryInput: z.ZodObject<{
1121
1161
  }>>;
1122
1162
  includeStationary: z.ZodOptional<z.ZodBoolean>;
1123
1163
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1164
+ excludeSources: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1165
+ sensor: "sensor";
1166
+ audio: "audio";
1167
+ pipeline: "pipeline";
1168
+ }>>>;
1124
1169
  }, z.core.$strip>;
1125
1170
  export type RecentTracksQuery = z.infer<typeof RecentTracksQueryInput>;
1126
1171
  /**
@@ -1854,6 +1899,11 @@ export declare const pipelineAnalyticsCapability: {
1854
1899
  }>>;
1855
1900
  includeStationary: z.ZodOptional<z.ZodBoolean>;
1856
1901
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
1902
+ excludeSources: z.ZodOptional<z.ZodArray<z.ZodEnum<{
1903
+ sensor: "sensor";
1904
+ audio: "audio";
1905
+ pipeline: "pipeline";
1906
+ }>>>;
1857
1907
  }, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
1858
1908
  retrainStatus: z.ZodOptional<z.ZodEnum<{
1859
1909
  none: "none";
@@ -1972,6 +2022,11 @@ export declare const pipelineAnalyticsCapability: {
1972
2022
  }>>;
1973
2023
  includeStationary: z.ZodOptional<z.ZodBoolean>;
1974
2024
  classes: z.ZodOptional<z.ZodArray<z.ZodString>>;
2025
+ excludeSources: z.ZodOptional<z.ZodArray<z.ZodEnum<{
2026
+ sensor: "sensor";
2027
+ audio: "audio";
2028
+ pipeline: "pipeline";
2029
+ }>>>;
1975
2030
  }, z.core.$strip>, z.ZodObject<{
1976
2031
  tracks: z.ZodReadonly<z.ZodArray<z.ZodObject<{
1977
2032
  retrainStatus: z.ZodOptional<z.ZodEnum<{
@@ -93,10 +93,24 @@ export type StatelessStepRefusal = z.infer<typeof StatelessStepRefusalSchema>;
93
93
  * (analyzer attaches detected `regions[]`; onboard does not — the
94
94
  * camera typically only reports a binary signal plus an optional
95
95
  * channel/AI class which lives in dedicated event channels).
96
+ *
97
+ * - `onboard` — the camera's firmware said something moved.
98
+ * - `analyzer` — this runner's frame-diff said so, and attaches `regions[]`.
99
+ * - `device-activity` — the DEVICE said it is doing its job: the
100
+ * `recording-signal` LEVEL the same device raises for the recorder
101
+ * ([D380](../../../../docs/decisions/adr-0380-a-device-decided-recording-is-a-mode-with-no-schedule-seeded-once.md)),
102
+ * republished as a motion source. It attaches **nothing** — no regions, no
103
+ * class: the only fact it carries is that the device is active, and a robot
104
+ * vacuum that is itself the moving object has no region worth sending. It is
105
+ * a LEVEL, so unlike `onboard` it has a real falling edge, and unlike
106
+ * `analyzer` it must not open the frame-diff side-channel — the runner's
107
+ * `handleOnboardMotionAnalyzer` gate is `source === 'onboard'` and stays that
108
+ * way ([D392](../../../../docs/decisions/adr-0392-a-device-that-says-it-is-working-is-a-motion-source-of-its-own.md)).
96
109
  */
97
110
  export declare const MotionSourceEnum: z.ZodEnum<{
98
111
  onboard: "onboard";
99
112
  analyzer: "analyzer";
113
+ "device-activity": "device-activity";
100
114
  }>;
101
115
  export type MotionSource = z.infer<typeof MotionSourceEnum>;
102
116
  /**
@@ -116,6 +130,7 @@ export type MotionSource = z.infer<typeof MotionSourceEnum>;
116
130
  export declare const MotionSourcesSchema: z.ZodArray<z.ZodEnum<{
117
131
  onboard: "onboard";
118
132
  analyzer: "analyzer";
133
+ "device-activity": "device-activity";
119
134
  }>>;
120
135
  export type MotionSources = z.infer<typeof MotionSourcesSchema>;
121
136
  /**
@@ -130,6 +145,7 @@ export declare const ReportMotionInputSchema: z.ZodObject<{
130
145
  source: z.ZodDefault<z.ZodEnum<{
131
146
  onboard: "onboard";
132
147
  analyzer: "analyzer";
148
+ "device-activity": "device-activity";
133
149
  }>>;
134
150
  regions: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
135
151
  bbox: z.ZodObject<{
@@ -218,6 +234,7 @@ declare const RunnerCameraConfigSchema: z.ZodObject<{
218
234
  motionSources: z.ZodDefault<z.ZodArray<z.ZodEnum<{
219
235
  onboard: "onboard";
220
236
  analyzer: "analyzer";
237
+ "device-activity": "device-activity";
221
238
  }>>>;
222
239
  pipelineEnabled: z.ZodDefault<z.ZodBoolean>;
223
240
  steps: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodType<import("./pipeline-executor.cap.js").PipelineStepInputOutput, unknown, z.core.$ZodTypeInternals<import("./pipeline-executor.cap.js").PipelineStepInputOutput, unknown>>>>>;
@@ -477,6 +494,7 @@ export declare const pipelineRunnerCapability: {
477
494
  motionSources: z.ZodDefault<z.ZodArray<z.ZodEnum<{
478
495
  onboard: "onboard";
479
496
  analyzer: "analyzer";
497
+ "device-activity": "device-activity";
480
498
  }>>>;
481
499
  pipelineEnabled: z.ZodDefault<z.ZodBoolean>;
482
500
  steps: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodType<import("./pipeline-executor.cap.js").PipelineStepInputOutput, unknown, z.core.$ZodTypeInternals<import("./pipeline-executor.cap.js").PipelineStepInputOutput, unknown>>>>>;
@@ -545,6 +563,7 @@ export declare const pipelineRunnerCapability: {
545
563
  source: z.ZodDefault<z.ZodEnum<{
546
564
  onboard: "onboard";
547
565
  analyzer: "analyzer";
566
+ "device-activity": "device-activity";
548
567
  }>>;
549
568
  regions: z.ZodOptional<z.ZodReadonly<z.ZodArray<z.ZodObject<{
550
569
  bbox: z.ZodObject<{
@@ -8,6 +8,7 @@ declare const QueryFilterSchema: z.ZodObject<{
8
8
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
9
9
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
10
10
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
11
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
11
12
  orderBy: z.ZodOptional<z.ZodObject<{
12
13
  field: z.ZodString;
13
14
  direction: z.ZodEnum<{
@@ -32,6 +33,7 @@ declare const MutationFilterSchema: z.ZodObject<{
32
33
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
33
34
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
34
35
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
36
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
35
37
  }, z.core.$strip>;
36
38
  /**
37
39
  * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
@@ -177,6 +179,7 @@ export declare const settingsStoreCapability: {
177
179
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
178
180
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
179
181
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
182
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
180
183
  orderBy: z.ZodOptional<z.ZodObject<{
181
184
  field: z.ZodString;
182
185
  direction: z.ZodEnum<{
@@ -269,6 +272,7 @@ export declare const settingsStoreCapability: {
269
272
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
270
273
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
271
274
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
275
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
272
276
  }, z.core.$strip>;
273
277
  }, z.core.$strip>, z.ZodObject<{
274
278
  deleted: z.ZodNumber;
@@ -286,6 +290,7 @@ export declare const settingsStoreCapability: {
286
290
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
287
291
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
288
292
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
293
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
289
294
  }, z.core.$strip>;
290
295
  data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
291
296
  }, z.core.$strip>, z.ZodObject<{
@@ -300,6 +305,7 @@ export declare const settingsStoreCapability: {
300
305
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
301
306
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
302
307
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
308
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
303
309
  orderBy: z.ZodOptional<z.ZodObject<{
304
310
  field: z.ZodString;
305
311
  direction: z.ZodEnum<{
@@ -348,6 +354,7 @@ export declare const settingsStoreCapability: {
348
354
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
349
355
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
350
356
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
357
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
351
358
  orderBy: z.ZodOptional<z.ZodObject<{
352
359
  field: z.ZodString;
353
360
  direction: z.ZodEnum<{
@@ -374,6 +381,7 @@ export declare const settingsStoreCapability: {
374
381
  whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
375
382
  whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
376
383
  whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
384
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
377
385
  orderBy: z.ZodOptional<z.ZodObject<{
378
386
  field: z.ZodString;
379
387
  direction: z.ZodEnum<{
@@ -105,7 +105,7 @@ export interface DeviceProfileDefaults {
105
105
  * explicitly to enable SW motion). PIR-sourced motion currently
106
106
  * flows through `'onboard'` because Reolink's accessory PIRs feed
107
107
  * the parent camera's firmware motion event channel. */
108
- readonly motionSources?: readonly ('onboard' | 'analyzer')[];
108
+ readonly motionSources?: import('../capabilities/pipeline-runner.cap.js').MotionSources;
109
109
  readonly audioMode?: PipelinePhaseMode;
110
110
  readonly detectionMode?: PipelinePhaseMode;
111
111
  }
package/dist/index.js CHANGED
@@ -9137,6 +9137,9 @@ var QueryFilterSchema = zod.z.object({
9137
9137
  whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional(),
9138
9138
  /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
9139
9139
  whereNot: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
9140
+ /** NULL-safe exclusion of a SET — `whereNot` for more than one value. An
9141
+ * empty list excludes nothing. See `QueryFilter.whereNotIn`. */
9142
+ whereNotIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional(),
9140
9143
  orderBy: zod.z.object({
9141
9144
  field: zod.z.string(),
9142
9145
  direction: zod.z.enum(["asc", "desc"])
@@ -9157,7 +9160,8 @@ var MutationFilterSchema = zod.z.object({
9157
9160
  where: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
9158
9161
  whereIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional(),
9159
9162
  whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional(),
9160
- whereNot: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
9163
+ whereNot: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
9164
+ whereNotIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional()
9161
9165
  });
9162
9166
  /**
9163
9167
  * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
@@ -18649,10 +18653,12 @@ var TrackSourceSchema = zod.z.enum([
18649
18653
  * Terminal for the plain `markForTrain` toggle: returning it to `staging` is
18650
18654
  * a deliberate action of the retrain page, not a side effect of a checkbox.
18651
18655
  *
18652
- * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` because
18653
- * the store's filter language has only positive equality and `whereIn` — no
18654
- * negation, no IS NULL — so a NULL would be unselectable by ANY predicate and
18655
- * would make the entire pre-column history immortal in one deploy.
18656
+ * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` so that
18657
+ * every row is selectable by a positive predicate. (The filter language has
18658
+ * since grown the NULL-safe `whereNot` / `whereNotIn` and an `IS NULL` reading
18659
+ * of `where: { f: null }`, which is how {@link TrackSourceSchema} can be
18660
+ * filtered on a NULLABLE column — see `excludeSources`. It did not when this
18661
+ * column was designed, and a NOT NULL column is still the better shape.)
18656
18662
  */
18657
18663
  var RetrainStatusSchema = zod.z.enum([
18658
18664
  "none",
@@ -19433,7 +19439,9 @@ var RecentTracksQueryInput = zod.z.object({
19433
19439
  * whose class list is unreadable are kept, and the client's rule stays the
19434
19440
  * exact one.
19435
19441
  */
19436
- classes: zod.z.array(zod.z.string()).optional()
19442
+ classes: zod.z.array(zod.z.string()).optional(),
19443
+ /** See {@link ExcludeSourcesDoc}. */
19444
+ excludeSources: zod.z.array(TrackSourceSchema).optional()
19437
19445
  });
19438
19446
  /**
19439
19447
  * A Summary (D359): the per-camera session envelope that references tracks.
@@ -19893,7 +19901,9 @@ var pipelineAnalyticsCapability = {
19893
19901
  * selected is an operator who has not narrowed anything, and an empty
19894
19902
  * timeline would read as a camera that saw nothing.
19895
19903
  */
19896
- classes: zod.z.array(zod.z.string()).optional()
19904
+ classes: zod.z.array(zod.z.string()).optional(),
19905
+ /** See {@link ExcludeSourcesDoc}. */
19906
+ excludeSources: zod.z.array(TrackSourceSchema).optional()
19897
19907
  }), zod.z.array(TrackSchema).readonly()),
19898
19908
  /**
19899
19909
  * Batched cluster-wide track listing — ONE call for the events page /
@@ -20939,8 +20949,25 @@ var occupancyRecheckFramesField = {
20939
20949
  * (analyzer attaches detected `regions[]`; onboard does not — the
20940
20950
  * camera typically only reports a binary signal plus an optional
20941
20951
  * channel/AI class which lives in dedicated event channels).
20942
- */
20943
- var MotionSourceEnum = zod.z.enum(["onboard", "analyzer"]);
20952
+ *
20953
+ * - `onboard` — the camera's firmware said something moved.
20954
+ * - `analyzer` — this runner's frame-diff said so, and attaches `regions[]`.
20955
+ * - `device-activity` — the DEVICE said it is doing its job: the
20956
+ * `recording-signal` LEVEL the same device raises for the recorder
20957
+ * ([D380](../../../../docs/decisions/adr-0380-a-device-decided-recording-is-a-mode-with-no-schedule-seeded-once.md)),
20958
+ * republished as a motion source. It attaches **nothing** — no regions, no
20959
+ * class: the only fact it carries is that the device is active, and a robot
20960
+ * vacuum that is itself the moving object has no region worth sending. It is
20961
+ * a LEVEL, so unlike `onboard` it has a real falling edge, and unlike
20962
+ * `analyzer` it must not open the frame-diff side-channel — the runner's
20963
+ * `handleOnboardMotionAnalyzer` gate is `source === 'onboard'` and stays that
20964
+ * way ([D392](../../../../docs/decisions/adr-0392-a-device-that-says-it-is-working-is-a-motion-source-of-its-own.md)).
20965
+ */
20966
+ var MotionSourceEnum = zod.z.enum([
20967
+ "onboard",
20968
+ "analyzer",
20969
+ "device-activity"
20970
+ ]);
20944
20971
  /**
20945
20972
  * List of motion sources active on a camera. Empty array is valid:
20946
20973
  * "no source" — happens for battery cams without firmware motion when
@@ -21204,13 +21231,20 @@ var RunnerCameraDeviceUIFields = [
21204
21231
  type: "multiselect",
21205
21232
  label: "Motion Sources",
21206
21233
  default: ["analyzer"],
21207
- options: [{
21208
- value: "analyzer",
21209
- label: "Frame-diff Analyzer (motion addon)"
21210
- }, {
21211
- value: "onboard",
21212
- label: "Camera Onboard Sensor"
21213
- }]
21234
+ options: [
21235
+ {
21236
+ value: "analyzer",
21237
+ label: "Frame-diff Analyzer (motion addon)"
21238
+ },
21239
+ {
21240
+ value: "onboard",
21241
+ label: "Camera Onboard Sensor"
21242
+ },
21243
+ {
21244
+ value: "device-activity",
21245
+ label: "Device activity (the device says it is working)"
21246
+ }
21247
+ ]
21214
21248
  },
21215
21249
  {
21216
21250
  key: "motionFps",
package/dist/index.mjs CHANGED
@@ -9136,6 +9136,9 @@ var QueryFilterSchema = z.object({
9136
9136
  whereBetween: z.record(z.string(), z.tuple([z.unknown(), z.unknown()])).optional(),
9137
9137
  /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
9138
9138
  whereNot: z.record(z.string(), z.unknown()).optional(),
9139
+ /** NULL-safe exclusion of a SET — `whereNot` for more than one value. An
9140
+ * empty list excludes nothing. See `QueryFilter.whereNotIn`. */
9141
+ whereNotIn: z.record(z.string(), z.array(z.unknown())).optional(),
9139
9142
  orderBy: z.object({
9140
9143
  field: z.string(),
9141
9144
  direction: z.enum(["asc", "desc"])
@@ -9156,7 +9159,8 @@ var MutationFilterSchema = z.object({
9156
9159
  where: z.record(z.string(), z.unknown()).optional(),
9157
9160
  whereIn: z.record(z.string(), z.array(z.unknown())).optional(),
9158
9161
  whereBetween: z.record(z.string(), z.tuple([z.unknown(), z.unknown()])).optional(),
9159
- whereNot: z.record(z.string(), z.unknown()).optional()
9162
+ whereNot: z.record(z.string(), z.unknown()).optional(),
9163
+ whereNotIn: z.record(z.string(), z.array(z.unknown())).optional()
9160
9164
  });
9161
9165
  /**
9162
9166
  * One scalar an {@link settingsStoreCapability.methods.aggregate} call asks for.
@@ -18648,10 +18652,12 @@ var TrackSourceSchema = z.enum([
18648
18652
  * Terminal for the plain `markForTrain` toggle: returning it to `staging` is
18649
18653
  * a deliberate action of the retrain page, not a side effect of a checkbox.
18650
18654
  *
18651
- * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` because
18652
- * the store's filter language has only positive equality and `whereIn` — no
18653
- * negation, no IS NULL — so a NULL would be unselectable by ANY predicate and
18654
- * would make the entire pre-column history immortal in one deploy.
18655
+ * There is no `null`. The state is stored `TEXT NOT NULL DEFAULT 'none'` so that
18656
+ * every row is selectable by a positive predicate. (The filter language has
18657
+ * since grown the NULL-safe `whereNot` / `whereNotIn` and an `IS NULL` reading
18658
+ * of `where: { f: null }`, which is how {@link TrackSourceSchema} can be
18659
+ * filtered on a NULLABLE column — see `excludeSources`. It did not when this
18660
+ * column was designed, and a NOT NULL column is still the better shape.)
18655
18661
  */
18656
18662
  var RetrainStatusSchema = z.enum([
18657
18663
  "none",
@@ -19432,7 +19438,9 @@ var RecentTracksQueryInput = z.object({
19432
19438
  * whose class list is unreadable are kept, and the client's rule stays the
19433
19439
  * exact one.
19434
19440
  */
19435
- classes: z.array(z.string()).optional()
19441
+ classes: z.array(z.string()).optional(),
19442
+ /** See {@link ExcludeSourcesDoc}. */
19443
+ excludeSources: z.array(TrackSourceSchema).optional()
19436
19444
  });
19437
19445
  /**
19438
19446
  * A Summary (D359): the per-camera session envelope that references tracks.
@@ -19892,7 +19900,9 @@ var pipelineAnalyticsCapability = {
19892
19900
  * selected is an operator who has not narrowed anything, and an empty
19893
19901
  * timeline would read as a camera that saw nothing.
19894
19902
  */
19895
- classes: z.array(z.string()).optional()
19903
+ classes: z.array(z.string()).optional(),
19904
+ /** See {@link ExcludeSourcesDoc}. */
19905
+ excludeSources: z.array(TrackSourceSchema).optional()
19896
19906
  }), z.array(TrackSchema).readonly()),
19897
19907
  /**
19898
19908
  * Batched cluster-wide track listing — ONE call for the events page /
@@ -20938,8 +20948,25 @@ var occupancyRecheckFramesField = {
20938
20948
  * (analyzer attaches detected `regions[]`; onboard does not — the
20939
20949
  * camera typically only reports a binary signal plus an optional
20940
20950
  * channel/AI class which lives in dedicated event channels).
20941
- */
20942
- var MotionSourceEnum = z.enum(["onboard", "analyzer"]);
20951
+ *
20952
+ * - `onboard` — the camera's firmware said something moved.
20953
+ * - `analyzer` — this runner's frame-diff said so, and attaches `regions[]`.
20954
+ * - `device-activity` — the DEVICE said it is doing its job: the
20955
+ * `recording-signal` LEVEL the same device raises for the recorder
20956
+ * ([D380](../../../../docs/decisions/adr-0380-a-device-decided-recording-is-a-mode-with-no-schedule-seeded-once.md)),
20957
+ * republished as a motion source. It attaches **nothing** — no regions, no
20958
+ * class: the only fact it carries is that the device is active, and a robot
20959
+ * vacuum that is itself the moving object has no region worth sending. It is
20960
+ * a LEVEL, so unlike `onboard` it has a real falling edge, and unlike
20961
+ * `analyzer` it must not open the frame-diff side-channel — the runner's
20962
+ * `handleOnboardMotionAnalyzer` gate is `source === 'onboard'` and stays that
20963
+ * way ([D392](../../../../docs/decisions/adr-0392-a-device-that-says-it-is-working-is-a-motion-source-of-its-own.md)).
20964
+ */
20965
+ var MotionSourceEnum = z.enum([
20966
+ "onboard",
20967
+ "analyzer",
20968
+ "device-activity"
20969
+ ]);
20943
20970
  /**
20944
20971
  * List of motion sources active on a camera. Empty array is valid:
20945
20972
  * "no source" — happens for battery cams without firmware motion when
@@ -21203,13 +21230,20 @@ var RunnerCameraDeviceUIFields = [
21203
21230
  type: "multiselect",
21204
21231
  label: "Motion Sources",
21205
21232
  default: ["analyzer"],
21206
- options: [{
21207
- value: "analyzer",
21208
- label: "Frame-diff Analyzer (motion addon)"
21209
- }, {
21210
- value: "onboard",
21211
- label: "Camera Onboard Sensor"
21212
- }]
21233
+ options: [
21234
+ {
21235
+ value: "analyzer",
21236
+ label: "Frame-diff Analyzer (motion addon)"
21237
+ },
21238
+ {
21239
+ value: "onboard",
21240
+ label: "Camera Onboard Sensor"
21241
+ },
21242
+ {
21243
+ value: "device-activity",
21244
+ label: "Device activity (the device says it is working)"
21245
+ }
21246
+ ]
21213
21247
  },
21214
21248
  {
21215
21249
  key: "motionFps",
@@ -409,8 +409,10 @@ export interface PhaseTransitionPayload {
409
409
  from: 'watching' | 'active';
410
410
  to: 'watching' | 'active';
411
411
  reason: 'motion_detected' | 'cooldown_expired';
412
- /** Motion source that armed the cooldown — present on both ON and OFF. */
413
- source?: 'onboard' | 'analyzer';
412
+ /** Motion source that armed the cooldown — present on both ON and OFF.
413
+ * The enum itself, never a hand-copied union: this was a second list of
414
+ * the sources and it silently excluded every variant added after it. */
415
+ source?: import('../capabilities/pipeline-runner.cap.js').MotionSource;
414
416
  /** Cooldown window in ms that was/is active for this transition. */
415
417
  cooldownMs?: number;
416
418
  /** Ms epoch of the transition. */
@@ -208,6 +208,25 @@ export interface QueryFilter {
208
208
  * kind-exclusion must KEEP).
209
209
  */
210
210
  whereNot?: Record<string, unknown>;
211
+ /**
212
+ * NULL-safe exclusion of SEVERAL values — `whereNot` for a set.
213
+ *
214
+ * A row matches when its field is NULL OR not one of the listed values, for
215
+ * the same reason `whereNot` is NULL-safe: on a nullable column the NULL
216
+ * rows are the ones written before the column existed, and a "not these
217
+ * kinds" filter must keep them.
218
+ *
219
+ * It exists because `whereNot` is a `Record` keyed by field, so ONE field
220
+ * can carry exactly one excluded value. Excluding two members of a small
221
+ * enum (the case that produced it: `Track.source`, hiding the synthetic
222
+ * `audio` and `sensor` projections at once) was otherwise inexpressible —
223
+ * and the alternative, dropping the rows in the caller after the read, makes
224
+ * paging lie about how much is left.
225
+ *
226
+ * An EMPTY list is no filter, never "matches nothing": it is a chip row with
227
+ * nothing selected. Same rule as `whereJsonArrayAny`.
228
+ */
229
+ whereNotIn?: Record<string, unknown[]>;
211
230
  /**
212
231
  * "This JSON array column contains at least one of these values."
213
232
  *
@@ -395,6 +395,21 @@ export interface CameraDetectionConfig {
395
395
  readonly detectionStreamId: string;
396
396
  readonly motionFps: number;
397
397
  readonly detectionFps: number;
398
+ /**
399
+ * The detection rate a session opened by the `device-activity` source is
400
+ * capped to (D392). A device-activity session is LONG — a cleaning run is
401
+ * tens of minutes of continuous activity — so it is analysed at a rate that
402
+ * can be sustained for that long, while a session any other trigger opens
403
+ * keeps `detectionFps`. Resolved by the orchestrator and applied at dispatch
404
+ * (`detectionFpsForTrigger`), so what reaches a runner is still a plain
405
+ * `detectionFps` and no runner needs to know this field exists.
406
+ *
407
+ * OPTIONAL because this config is built at several sites, not all of which
408
+ * resolve the device store. Absent means "not resolved here" and the cap
409
+ * still applies at `ACTIVITY_DETECTION_FPS_DEFAULT` — the safe direction: a
410
+ * long session runs slow rather than at full rate for an hour.
411
+ */
412
+ readonly activityDetectionFps?: number;
398
413
  readonly motionCooldownMs: number;
399
414
  /**
400
415
  * Orchestrator on-motion session-hold cap (ms). While the session is active
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/types",
3
- "version": "1.2.163",
3
+ "version": "1.2.165",
4
4
  "description": "Shared types, interfaces, and model catalogs for the CamStack detection ecosystem",
5
5
  "keywords": [
6
6
  "camstack",