@camstack/types 1.2.245 → 1.2.247

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.
@@ -0,0 +1,1123 @@
1
+ /**
2
+ * `recording-archive` — OUR archive, and the intent that fills it.
3
+ *
4
+ * The system-singleton half of the 2026-09-24 cut (D625). `recording` used to
5
+ * be one 33-method system singleton holding two unrelated subjects: three
6
+ * per-camera READS about coverage and playback, and everything else — storage
7
+ * locations, retention, relocation, rebalance, the ops log, the placement
8
+ * table and the byte-plane primitives our scrub and export are built on.
9
+ *
10
+ * The reads became a device-scoped COLLECTION, so a camera's own card can be a
11
+ * source beside ours (`recording.cap.ts`). Everything that is about OUR store,
12
+ * or unimplementable by a camera, stayed here.
13
+ *
14
+ * ## On the name
15
+ *
16
+ * `recording-storage` was the obvious choice and is wrong: this cap also holds
17
+ * `getDeviceConfig`/`setDeviceConfig`, which are recording INTENT — bands,
18
+ * retention, the D62 switch authority — and a name that says "storage" invites
19
+ * the next reader to move them out again. An archive is a thing we keep, and
20
+ * what we keep it under is a policy; the name covers both halves honestly and
21
+ * sits in the existing family (`recording-onboard`, `recording-export`,
22
+ * `recording-signal`).
23
+ *
24
+ * ## What must NOT happen to it
25
+ *
26
+ * It stays a SINGLETON. It is registered by `recorder`, which is
27
+ * `placement: 'any-node'` and runs on every recording node; the hub dispatches
28
+ * to one of them. Putting the ledger, the placement table or the relocation
29
+ * jobs behind a fan-out is the one genuinely dangerous move in this cut.
30
+ *
31
+ * `getDeviceConfig` / `setDeviceConfig` in particular are the D62 recording
32
+ * authority (`CameraSwitch.authority`). If a write reached a different provider
33
+ * than the read — which a collection fan-out permits — two authorities would
34
+ * decide when one camera records, and the symptom (recording silently off, or
35
+ * a `bands` array clobbered by a partial write) is durable and silent. Keeping
36
+ * them here means the worst case during a rollout is a 412: the switch refuses
37
+ * to flip and SAYS so. **Do not move them into the collection, at any point,
38
+ * for any reason.**
39
+ *
40
+ * ## The two batch reads
41
+ *
42
+ * `getAvailabilityBatch` / `getDaysWithRecordingsBatch` take `deviceIds:
43
+ * number[]` with no single `deviceId`, and a device-scoped mount routes
44
+ * through `getProviderForDevice(deviceId)` — there is nothing for it to route
45
+ * on. They stay here, and on this cap the batch is explicitly OURS: a grid has
46
+ * no per-camera picker, and a caller that wants another source's coverage asks
47
+ * `recording.getAvailability` per device with that source's `provider`.
48
+ */
49
+ import { z } from 'zod';
50
+ import { type InferProvider } from './capability-definition.js';
51
+ export declare const RecordingStatusSchema: z.ZodObject<{
52
+ deviceId: z.ZodNumber;
53
+ enabled: z.ZodBoolean;
54
+ activeMode: z.ZodEnum<{
55
+ events: "events";
56
+ off: "off";
57
+ continuous: "continuous";
58
+ "on-device-decision": "on-device-decision";
59
+ }>;
60
+ nodeId: z.ZodString;
61
+ storageBytes: z.ZodNumber;
62
+ }, z.core.$strip>;
63
+ export type RecordingStatus = z.infer<typeof RecordingStatusSchema>;
64
+ /**
65
+ * Most cameras one `getAvailabilityBatch` / `getDaysWithRecordingsBatch` may
66
+ * name.
67
+ *
68
+ * Matches the ceiling the per-camera batches elsewhere in the system carry
69
+ * (`pipelineAnalytics.getKeyEventsBatch`, `sceneMonitor.listScenesBatch`): a
70
+ * grid is tens of cameras, never hundreds, and each id costs the recorder one
71
+ * calendar walk whose answer crosses a process boundary.
72
+ *
73
+ * The ceiling is on the SCHEMA. An over-eager ask is REFUSED, never truncated
74
+ * to the first {@link RECORDING_TIMELINE_BATCH_MAX} ids — a truncated answer
75
+ * looks exactly like a complete one, and the caller would draw an empty
76
+ * timeline for the cameras it never learned were dropped.
77
+ */
78
+ export declare const RECORDING_TIMELINE_BATCH_MAX = 200;
79
+ /**
80
+ * One camera's row in a `getAvailabilityBatch` answer.
81
+ *
82
+ * `ranges` is EXACTLY what `recording.getAvailability` returns for that camera
83
+ * at OUR source — the batch collapses the transport, not the work — plus the
84
+ * `read` mark the singular answer now carries too (D625):
85
+ *
86
+ * - `read: 'read'` — answered. `ranges: []` means "read, and this camera has
87
+ * no footage in the window", which is a real claim.
88
+ * - `read: 'unreadable'` — the recorder could not answer for this camera (its
89
+ * calendar rung threw, its location is unmounted). `ranges` is empty and
90
+ * that emptiness means NOTHING.
91
+ *
92
+ * A timeline that renders the second as the first tells an operator there is no
93
+ * footage when the truth is that nobody looked.
94
+ */
95
+ export declare const RecordingAvailabilityForDeviceSchema: z.ZodObject<{
96
+ deviceId: z.ZodNumber;
97
+ read: z.ZodEnum<{
98
+ read: "read";
99
+ unreadable: "unreadable";
100
+ }>;
101
+ ranges: z.ZodReadonly<z.ZodArray<z.ZodObject<{
102
+ profile: z.ZodString;
103
+ startMs: z.ZodNumber;
104
+ endMs: z.ZodNumber;
105
+ }, z.core.$strip>>>;
106
+ profilesWithFootage: z.ZodReadonly<z.ZodArray<z.ZodString>>;
107
+ }, z.core.$strip>;
108
+ export type RecordingAvailabilityForDevice = z.infer<typeof RecordingAvailabilityForDeviceSchema>;
109
+ /**
110
+ * One camera's row in a `getDaysWithRecordingsBatch` answer. Same rule as
111
+ * {@link RecordingAvailabilityForDeviceSchema}: `days: []` on a `'read'` row is
112
+ * "no footage in this month", `read: 'unreadable'` is "nobody could look", and
113
+ * the date-picker's day dots must not spell them the same.
114
+ */
115
+ export declare const RecordingDaysForDeviceSchema: z.ZodObject<{
116
+ deviceId: z.ZodNumber;
117
+ read: z.ZodEnum<{
118
+ read: "read";
119
+ unreadable: "unreadable";
120
+ }>;
121
+ days: z.ZodReadonly<z.ZodArray<z.ZodNumber>>;
122
+ }, z.core.$strip>;
123
+ export type RecordingDaysForDevice = z.infer<typeof RecordingDaysForDeviceSchema>;
124
+ /**
125
+ * Recording storage usage for one camera — what the ARCHIVE holds for it,
126
+ * across every profile and every resolvable location on this node.
127
+ *
128
+ * ## Archive vs residency, which is the whole point of this shape (D418)
129
+ *
130
+ * `usedBytes` / `oldestMs` / `segments` are the durable hour ledger's numbers:
131
+ * the same quantity `RecordingLocationUsage.usedBytes` reports for the volume
132
+ * this camera writes to, so the two can be added up and compared.
133
+ *
134
+ * They were not always. Until D418 this row came from the recorder's in-RAM
135
+ * `RecordingIndex`, which since D287 holds only the live tail plus the hours
136
+ * somebody hydrated — so a hub with 1.9 TB and 3.3 days of footage reported
137
+ * 60.5 GB and "oldest: three hours ago" on the same page as the volume row
138
+ * that said otherwise, and two people read it as retention eating recent
139
+ * footage. Residency is still reported, under `residentBytes` /
140
+ * `residentOldestMs`, which answer a different question and say so.
141
+ */
142
+ export declare const RecordingDeviceUsageSchema: z.ZodObject<{
143
+ deviceId: z.ZodNumber;
144
+ usedBytes: z.ZodNumber;
145
+ oldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
146
+ segments: z.ZodOptional<z.ZodNumber>;
147
+ residentBytes: z.ZodOptional<z.ZodNumber>;
148
+ residentOldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
149
+ }, z.core.$strip>;
150
+ export type RecordingDeviceUsage = z.infer<typeof RecordingDeviceUsageSchema>;
151
+ /** Recording storage usage + capacity for one storage location. */
152
+ export declare const RecordingLocationUsageSchema: z.ZodObject<{
153
+ locationId: z.ZodNullable<z.ZodString>;
154
+ locationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
155
+ root: z.ZodOptional<z.ZodString>;
156
+ usedBytes: z.ZodNumber;
157
+ availableBytes: z.ZodNullable<z.ZodNumber>;
158
+ totalBytes: z.ZodNullable<z.ZodNumber>;
159
+ }, z.core.$strip>;
160
+ export type RecordingLocationUsage = z.infer<typeof RecordingLocationUsageSchema>;
161
+ /** Aggregate recordings storage usage for a node: global total + per-camera +
162
+ * per-location used/available. Stat-based (accurate even for detached cameras),
163
+ * computed on demand — not from the name-only playback index. */
164
+ export declare const RecordingStorageUsageSchema: z.ZodObject<{
165
+ nodeId: z.ZodString;
166
+ totalUsedBytes: z.ZodNumber;
167
+ devices: z.ZodArray<z.ZodObject<{
168
+ deviceId: z.ZodNumber;
169
+ usedBytes: z.ZodNumber;
170
+ oldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
171
+ segments: z.ZodOptional<z.ZodNumber>;
172
+ residentBytes: z.ZodOptional<z.ZodNumber>;
173
+ residentOldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
174
+ }, z.core.$strip>>;
175
+ devicesRead: z.ZodOptional<z.ZodEnum<{
176
+ unavailable: "unavailable";
177
+ archive: "archive";
178
+ }>>;
179
+ locations: z.ZodOptional<z.ZodArray<z.ZodObject<{
180
+ locationId: z.ZodNullable<z.ZodString>;
181
+ locationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
182
+ root: z.ZodOptional<z.ZodString>;
183
+ usedBytes: z.ZodNumber;
184
+ availableBytes: z.ZodNullable<z.ZodNumber>;
185
+ totalBytes: z.ZodNullable<z.ZodNumber>;
186
+ }, z.core.$strip>>>;
187
+ }, z.core.$strip>;
188
+ export type RecordingStorageUsage = z.infer<typeof RecordingStorageUsageSchema>;
189
+ /**
190
+ * The OPERATOR-ARMED half of multi-location recordings (D116).
191
+ *
192
+ * The placement plan decides where NEW writes go and moves nothing. A rebalance
193
+ * is the operator asking for the EXISTING archive to be brought into line with
194
+ * that plan: one relocate job per (camera, profile) pile that sits on the wrong
195
+ * location, run FIFO behind the single-flight mover.
196
+ *
197
+ * `plan…` and `start…` return the SAME shape deliberately — what the operator
198
+ * confirms is exactly what gets enqueued, and `jobIds` is the only difference
199
+ * (empty on the plan).
200
+ */
201
+ export declare const RecordingRebalanceMoveSchema: z.ZodObject<{
202
+ deviceId: z.ZodNumber;
203
+ profile: z.ZodString;
204
+ fromLocationId: z.ZodString;
205
+ toLocationId: z.ZodString;
206
+ bytes: z.ZodNumber;
207
+ files: z.ZodNumber;
208
+ }, z.core.$strip>;
209
+ export type RecordingRebalanceMove = z.infer<typeof RecordingRebalanceMoveSchema>;
210
+ /** Why a pile that is out of place is staying there. Every refusal is
211
+ * reported: a rebalance that silently drops a camera reads exactly like one
212
+ * that had nothing to do. */
213
+ export declare const RecordingRebalanceSkipReasonSchema: z.ZodEnum<{
214
+ unassigned: "unassigned";
215
+ "below-threshold": "below-threshold";
216
+ "target-not-writable": "target-not-writable";
217
+ "no-headroom": "no-headroom";
218
+ }>;
219
+ export type RecordingRebalanceSkipReason = z.infer<typeof RecordingRebalanceSkipReasonSchema>;
220
+ export declare const RecordingRebalanceSkipSchema: z.ZodObject<{
221
+ deviceId: z.ZodNumber;
222
+ profile: z.ZodString;
223
+ fromLocationId: z.ZodString;
224
+ toLocationId: z.ZodNullable<z.ZodString>;
225
+ bytes: z.ZodNumber;
226
+ reason: z.ZodEnum<{
227
+ unassigned: "unassigned";
228
+ "below-threshold": "below-threshold";
229
+ "target-not-writable": "target-not-writable";
230
+ "no-headroom": "no-headroom";
231
+ }>;
232
+ }, z.core.$strip>;
233
+ export type RecordingRebalanceSkip = z.infer<typeof RecordingRebalanceSkipSchema>;
234
+ export declare const RecordingRebalancePlanSchema: z.ZodObject<{
235
+ moves: z.ZodArray<z.ZodObject<{
236
+ deviceId: z.ZodNumber;
237
+ profile: z.ZodString;
238
+ fromLocationId: z.ZodString;
239
+ toLocationId: z.ZodString;
240
+ bytes: z.ZodNumber;
241
+ files: z.ZodNumber;
242
+ }, z.core.$strip>>;
243
+ skipped: z.ZodArray<z.ZodObject<{
244
+ deviceId: z.ZodNumber;
245
+ profile: z.ZodString;
246
+ fromLocationId: z.ZodString;
247
+ toLocationId: z.ZodNullable<z.ZodString>;
248
+ bytes: z.ZodNumber;
249
+ reason: z.ZodEnum<{
250
+ unassigned: "unassigned";
251
+ "below-threshold": "below-threshold";
252
+ "target-not-writable": "target-not-writable";
253
+ "no-headroom": "no-headroom";
254
+ }>;
255
+ }, z.core.$strip>>;
256
+ bytesToMove: z.ZodNumber;
257
+ jobIds: z.ZodArray<z.ZodString>;
258
+ }, z.core.$strip>;
259
+ export type RecordingRebalancePlan = z.infer<typeof RecordingRebalancePlanSchema>;
260
+ export declare const RecordingRebalanceInputSchema: z.ZodObject<{
261
+ throttleMbps: z.ZodOptional<z.ZodNumber>;
262
+ minMoveGb: z.ZodOptional<z.ZodNumber>;
263
+ }, z.core.$strip>;
264
+ export type RecordingRebalanceInput = z.infer<typeof RecordingRebalanceInputSchema>;
265
+ /**
266
+ * Operator-facing placement of one camera onto a recordings location.
267
+ *
268
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
269
+ * Auto (the planner may move this camera). `locationId` is where high/mid
270
+ * currently write — the plan, which may disagree with the pin when Auto.
271
+ */
272
+ export declare const RecordingDevicePlacementSchema: z.ZodObject<{
273
+ deviceId: z.ZodNumber;
274
+ profile: z.ZodString;
275
+ locationId: z.ZodString;
276
+ }, z.core.$strip>;
277
+ export type RecordingDevicePlacement = z.infer<typeof RecordingDevicePlacementSchema>;
278
+ export declare const RecordingDevicePinSchema: z.ZodObject<{
279
+ deviceId: z.ZodNumber;
280
+ locationId: z.ZodString;
281
+ }, z.core.$strip>;
282
+ export type RecordingDevicePin = z.infer<typeof RecordingDevicePinSchema>;
283
+ export declare const RecordingPlacementViewSchema: z.ZodObject<{
284
+ assignments: z.ZodArray<z.ZodObject<{
285
+ deviceId: z.ZodNumber;
286
+ profile: z.ZodString;
287
+ locationId: z.ZodString;
288
+ }, z.core.$strip>>;
289
+ pins: z.ZodArray<z.ZodObject<{
290
+ deviceId: z.ZodNumber;
291
+ locationId: z.ZodString;
292
+ }, z.core.$strip>>;
293
+ defaultLocations: z.ZodRecord<z.ZodString, z.ZodString>;
294
+ }, z.core.$strip>;
295
+ export type RecordingPlacementView = z.infer<typeof RecordingPlacementViewSchema>;
296
+ export declare const RecordingSetDevicePlacementInputSchema: z.ZodObject<{
297
+ deviceId: z.ZodNumber;
298
+ locationId: z.ZodNullable<z.ZodString>;
299
+ }, z.core.$strip>;
300
+ export type RecordingSetDevicePlacementInput = z.infer<typeof RecordingSetDevicePlacementInputSchema>;
301
+ /**
302
+ * Result of locating footage at a wall-clock instant for one device/profile.
303
+ * `segment` carries the covering segment's window; `gap` reports the forward
304
+ * nearest covered edge (`null` past the end of footage / when none exists)
305
+ * and the backward covered edge `prevEndMs` (exclusive end of the nearest
306
+ * footage behind the epoch; `null` when none — optional so older providers
307
+ * that omit it stay valid). `prevEndMs` lets a backward frame-step hop the
308
+ * small inter-segment cracks (durMs under-covers the span to the next
309
+ * startMs by ~11-17 ms) instead of no-opping at a segment head.
310
+ */
311
+ export declare const LocateSegmentResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
312
+ kind: z.ZodLiteral<"segment">;
313
+ startMs: z.ZodNumber;
314
+ durMs: z.ZodNumber;
315
+ bytes: z.ZodNumber;
316
+ }, z.core.$strip>, z.ZodObject<{
317
+ kind: z.ZodLiteral<"gap">;
318
+ nearestEdgeMs: z.ZodNullable<z.ZodNumber>;
319
+ prevEndMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
320
+ }, z.core.$strip>], "kind">;
321
+ export type LocateSegmentResult = z.infer<typeof LocateSegmentResultSchema>;
322
+ /** Raw bytes of one finalized footage segment (read off disk on the recording node). */
323
+ export declare const ReadSegmentBytesResultSchema: z.ZodObject<{
324
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
325
+ }, z.core.$strip>;
326
+ export type ReadSegmentBytesResult = z.infer<typeof ReadSegmentBytesResultSchema>;
327
+ /**
328
+ * One GOP of a finalized segment, cut by byte range through the segment's own
329
+ * `mfra` (D31 on the D42 feeder path). `data` is the `ftyp`+`moov` head plus
330
+ * the single `moof`+`mdat` covering the requested instant — standalone-
331
+ * demuxable, never the whole file. When the segment's index cannot be parsed
332
+ * the provider degrades INSIDE the mechanism to the whole segment (still one
333
+ * `data`, `gopStartMs` = the segment start) — a worse read, not another path.
334
+ */
335
+ export declare const ReadGopBytesResultSchema: z.ZodObject<{
336
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
337
+ gopStartMs: z.ZodNumber;
338
+ gopDurMs: z.ZodNumber;
339
+ }, z.core.$strip>;
340
+ export type ReadGopBytesResult = z.infer<typeof ReadGopBytesResultSchema>;
341
+ /**
342
+ * A time WINDOW of one finalized segment, cut by byte range — the multi-GOP
343
+ * twin of {@link ReadGopBytesResultSchema}'s single instant. Built for the
344
+ * replay clip's `recording` source (`docs/design/plans/2026-08-26-replay-clip-su-pipeline.md`):
345
+ * a replay needs several seconds of native pixels, not one frame.
346
+ *
347
+ * `ok.data` is standalone-demuxable, same as a GOP read. `ok.reachesRequestedEnd`
348
+ * is `false` when the returned bytes were cut short by the read's own safety
349
+ * byte cap before covering `[fromMs, toMs)` — a truncation, reported, not a
350
+ * silently shorter answer. `spans-multiple-segments` is a REFUSAL, not a
351
+ * degradation: a window whose end falls past the covering segment would need
352
+ * bytes stitched from a second segment file (its own `ftyp`+`moov`), which is
353
+ * not one standalone-demuxable stream — the caller's answer is to request a
354
+ * shorter window or one aligned to a single segment, not to receive spliced
355
+ * bytes nothing has proven decodable.
356
+ */
357
+ export declare const ReadWindowBytesResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
358
+ kind: z.ZodLiteral<"ok">;
359
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
360
+ gopStartMs: z.ZodNumber;
361
+ gopDurMs: z.ZodNumber;
362
+ reachesRequestedEnd: z.ZodBoolean;
363
+ }, z.core.$strip>, z.ZodObject<{
364
+ kind: z.ZodLiteral<"spans-multiple-segments">;
365
+ segmentEndMs: z.ZodNumber;
366
+ }, z.core.$strip>], "kind">;
367
+ export type ReadWindowBytesResult = z.infer<typeof ReadWindowBytesResultSchema>;
368
+ export declare const recordingArchiveCapability: {
369
+ readonly name: "recording-archive";
370
+ readonly scope: "system";
371
+ readonly mode: "singleton";
372
+ /** Moves here with the config pair — the derived Recording settings
373
+ * section is a view over `getDeviceConfig`/`setDeviceConfig` (D14), and
374
+ * those did not move (D625). */
375
+ readonly exposesDeviceSettings: true;
376
+ readonly status: {
377
+ readonly schema: z.ZodObject<{
378
+ deviceId: z.ZodNumber;
379
+ enabled: z.ZodBoolean;
380
+ activeMode: z.ZodEnum<{
381
+ events: "events";
382
+ off: "off";
383
+ continuous: "continuous";
384
+ "on-device-decision": "on-device-decision";
385
+ }>;
386
+ nodeId: z.ZodString;
387
+ storageBytes: z.ZodNumber;
388
+ }, z.core.$strip>;
389
+ readonly kind: "command-driven";
390
+ };
391
+ readonly methods: {
392
+ /**
393
+ * `recording.getAvailability` for a SET of cameras, in one round trip, at
394
+ * OUR source.
395
+ *
396
+ * A multi-camera timeline re-asks availability for every camera in the grid
397
+ * on every day change; fanned out that is one request per camera for N
398
+ * independent calendar walks. The per-camera work is IDENTICAL (the same
399
+ * `availabilityProfileFor` + `rangesIn`, run concurrently inside the
400
+ * recorder) — only the transport collapses.
401
+ *
402
+ * It lives on the ARCHIVE, not on the `recording` collection, because it
403
+ * takes `deviceIds` with no single `deviceId` and a device-scoped mount has
404
+ * nothing to route on (D625 §10.2). The consequence is stated rather than
405
+ * hidden: the batch answers for OURS. A caller that needs another source's
406
+ * coverage asks `recording.getAvailability` per device, naming that
407
+ * source's provider.
408
+ *
409
+ * `protected` for the same reason the singular method is: every id in
410
+ * `deviceIds` is a device reference, so the F1 #3 gate refuses any camera
411
+ * outside the caller's scope — one id out of scope refuses the CALL, it
412
+ * does not quietly drop a row.
413
+ *
414
+ * Every requested id gets a row, marked — see
415
+ * {@link RecordingAvailabilityForDeviceSchema}. `deviceIds` is capped at
416
+ * {@link RECORDING_TIMELINE_BATCH_MAX} and an over-cap ask is refused.
417
+ */
418
+ readonly getAvailabilityBatch: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
419
+ deviceIds: z.ZodArray<z.ZodNumber>;
420
+ fromMs: z.ZodNumber;
421
+ toMs: z.ZodNumber;
422
+ }, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
423
+ deviceId: z.ZodNumber;
424
+ read: z.ZodEnum<{
425
+ read: "read";
426
+ unreadable: "unreadable";
427
+ }>;
428
+ ranges: z.ZodReadonly<z.ZodArray<z.ZodObject<{
429
+ profile: z.ZodString;
430
+ startMs: z.ZodNumber;
431
+ endMs: z.ZodNumber;
432
+ }, z.core.$strip>>>;
433
+ profilesWithFootage: z.ZodReadonly<z.ZodArray<z.ZodString>>;
434
+ }, z.core.$strip>>>, "query">;
435
+ /**
436
+ * `recording.getDaysWithRecordings` for a SET of cameras, in one round
437
+ * trip, at OUR source. Same placement argument as
438
+ * {@link getAvailabilityBatch}.
439
+ *
440
+ * The cheapest question in the product, asked once per camera per month
441
+ * change. One directory read per day per camera at the owner, unchanged;
442
+ * what collapses is the N requests it took to ask.
443
+ *
444
+ * Every requested id gets a row, marked — see
445
+ * {@link RecordingDaysForDeviceSchema}. `deviceIds` is capped at
446
+ * {@link RECORDING_TIMELINE_BATCH_MAX} and an over-cap ask is refused.
447
+ */
448
+ readonly getDaysWithRecordingsBatch: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
449
+ deviceIds: z.ZodArray<z.ZodNumber>;
450
+ fromMs: z.ZodNumber;
451
+ toMs: z.ZodNumber;
452
+ tzOffsetMinutes: z.ZodNumber;
453
+ }, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
454
+ deviceId: z.ZodNumber;
455
+ read: z.ZodEnum<{
456
+ read: "read";
457
+ unreadable: "unreadable";
458
+ }>;
459
+ days: z.ZodReadonly<z.ZodArray<z.ZodNumber>>;
460
+ }, z.core.$strip>>>, "query">;
461
+ readonly getStorageUsage: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
462
+ nodeId: z.ZodString;
463
+ totalUsedBytes: z.ZodNumber;
464
+ devices: z.ZodArray<z.ZodObject<{
465
+ deviceId: z.ZodNumber;
466
+ usedBytes: z.ZodNumber;
467
+ oldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
468
+ segments: z.ZodOptional<z.ZodNumber>;
469
+ residentBytes: z.ZodOptional<z.ZodNumber>;
470
+ residentOldestMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
471
+ }, z.core.$strip>>;
472
+ devicesRead: z.ZodOptional<z.ZodEnum<{
473
+ unavailable: "unavailable";
474
+ archive: "archive";
475
+ }>>;
476
+ locations: z.ZodOptional<z.ZodArray<z.ZodObject<{
477
+ locationId: z.ZodNullable<z.ZodString>;
478
+ locationIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
479
+ root: z.ZodOptional<z.ZodString>;
480
+ usedBytes: z.ZodNumber;
481
+ availableBytes: z.ZodNullable<z.ZodNumber>;
482
+ totalBytes: z.ZodNullable<z.ZodNumber>;
483
+ }, z.core.$strip>>>;
484
+ }, z.core.$strip>, "query">;
485
+ /**
486
+ * READ the camera's recording intent.
487
+ *
488
+ * `protected`, not `admin`, since 2026-09-21. It is the only per-camera
489
+ * source of the recording mode the viewer has, and the camera status dot
490
+ * asks for it on a screen every read-only user sees. As `admin` it answered
491
+ * FORBIDDEN, the viewer collapsed that to `null` and `null` to `'off'`, and
492
+ * a recording camera was drawn GREEN — a failed measurement rendered as a
493
+ * value (D315, D393, D590). It carries schedules and retention, no secret,
494
+ * and the per-device gate in `scope-access.ts` still applies; `setDeviceConfig`
495
+ * stays `admin`.
496
+ *
497
+ * It is a SINGLETON method and stays one (D625 §16.2): it is the D62
498
+ * recording authority, and an authority that several providers could answer
499
+ * is the "two knobs over one decision" D62 forbids.
500
+ */
501
+ readonly getDeviceConfig: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
502
+ deviceId: z.ZodNumber;
503
+ }, z.core.$strip>, z.ZodObject<{
504
+ enabled: z.ZodBoolean;
505
+ mode: z.ZodOptional<z.ZodEnum<{
506
+ events: "events";
507
+ off: "off";
508
+ continuous: "continuous";
509
+ "on-device-decision": "on-device-decision";
510
+ }>>;
511
+ profiles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
512
+ high: "high";
513
+ mid: "mid";
514
+ low: "low";
515
+ }>>>;
516
+ segmentSeconds: z.ZodOptional<z.ZodNumber>;
517
+ bands: z.ZodDefault<z.ZodArray<z.ZodObject<{
518
+ days: z.ZodArray<z.ZodNumber>;
519
+ start: z.ZodString;
520
+ end: z.ZodString;
521
+ mode: z.ZodEnum<{
522
+ events: "events";
523
+ continuous: "continuous";
524
+ }>;
525
+ audio: z.ZodOptional<z.ZodEnum<{
526
+ muted: "muted";
527
+ on: "on";
528
+ }>>;
529
+ triggers: z.ZodOptional<z.ZodObject<{
530
+ motion: z.ZodOptional<z.ZodBoolean>;
531
+ audioThresholdDbfs: z.ZodOptional<z.ZodNumber>;
532
+ objectClasses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
533
+ person: "person";
534
+ vehicle: "vehicle";
535
+ animal: "animal";
536
+ }>>>;
537
+ sensorDeviceIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
538
+ }, z.core.$strip>>;
539
+ preBufferSec: z.ZodOptional<z.ZodNumber>;
540
+ postBufferSec: z.ZodOptional<z.ZodNumber>;
541
+ }, z.core.$strip>>>;
542
+ deviceDecision: z.ZodOptional<z.ZodBoolean>;
543
+ retention: z.ZodOptional<z.ZodObject<{
544
+ maxAgeDays: z.ZodOptional<z.ZodNumber>;
545
+ maxSizeGb: z.ZodOptional<z.ZodNumber>;
546
+ }, z.core.$strip>>;
547
+ }, z.core.$strict>, "query">;
548
+ /** Locate footage at a wall-clock instant: the covering segment's window,
549
+ * or a gap with the forward nearest covered edge. Used by a feeder running
550
+ * in another addon process to seek recorded footage over tRPC.
551
+ *
552
+ * BYTE PLANE: defined by `mfra` byte ranges over OUR own MP4 segment tree.
553
+ * There is no vendor-neutral statement of it, its only callers are feeders
554
+ * in other addon processes (the stream broker, the replay-clip source),
555
+ * and an onboard source that ever needs bytes delegates to the
556
+ * `videoclips` transport its vendor already has (D625 §9.3, §10.5). */
557
+ readonly locateSegment: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
558
+ deviceId: z.ZodNumber;
559
+ profile: z.ZodString;
560
+ epochMs: z.ZodNumber;
561
+ }, z.core.$strip>, z.ZodDiscriminatedUnion<[z.ZodObject<{
562
+ kind: z.ZodLiteral<"segment">;
563
+ startMs: z.ZodNumber;
564
+ durMs: z.ZodNumber;
565
+ bytes: z.ZodNumber;
566
+ }, z.core.$strip>, z.ZodObject<{
567
+ kind: z.ZodLiteral<"gap">;
568
+ nearestEdgeMs: z.ZodNullable<z.ZodNumber>;
569
+ prevEndMs: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
570
+ }, z.core.$strip>], "kind">, "query">;
571
+ /** Read one finalized segment's raw bytes by its exact `startMs`. Used by a
572
+ * feeder running in another addon process to pull recorded footage over tRPC. */
573
+ readonly readSegmentBytes: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
574
+ deviceId: z.ZodNumber;
575
+ profile: z.ZodString;
576
+ startMs: z.ZodNumber;
577
+ }, z.core.$strip>, z.ZodObject<{
578
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
579
+ }, z.core.$strip>, "query">;
580
+ /** Read the single GOP of segment `startMs` covering `epochMs`, by mfra
581
+ * byte range — the scrub-granular read (D31 letter on the feeder path).
582
+ * See {@link ReadGopBytesResultSchema} for the degradation contract. */
583
+ readonly readGopBytes: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
584
+ deviceId: z.ZodNumber;
585
+ profile: z.ZodString;
586
+ startMs: z.ZodNumber;
587
+ epochMs: z.ZodNumber;
588
+ }, z.core.$strip>, z.ZodObject<{
589
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
590
+ gopStartMs: z.ZodNumber;
591
+ gopDurMs: z.ZodNumber;
592
+ }, z.core.$strip>, "query">;
593
+ /** Read a WINDOW `[fromMs, toMs)` of segment `startMs`, by mfra byte
594
+ * range — the multi-GOP twin of `readGopBytes`. Refuses (does not
595
+ * degrade) when the window runs past the covering segment. Used by the
596
+ * replay clip's `recording` source to fetch several seconds of native
597
+ * footage for its single ffmpeg decode pass. */
598
+ readonly readWindowBytes: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
599
+ deviceId: z.ZodNumber;
600
+ profile: z.ZodString;
601
+ startMs: z.ZodNumber;
602
+ fromMs: z.ZodNumber;
603
+ toMs: z.ZodNumber;
604
+ }, z.core.$strip>, z.ZodDiscriminatedUnion<[z.ZodObject<{
605
+ kind: z.ZodLiteral<"ok">;
606
+ data: z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>;
607
+ gopStartMs: z.ZodNumber;
608
+ gopDurMs: z.ZodNumber;
609
+ reachesRequestedEnd: z.ZodBoolean;
610
+ }, z.core.$strip>, z.ZodObject<{
611
+ kind: z.ZodLiteral<"spans-multiple-segments">;
612
+ segmentEndMs: z.ZodNumber;
613
+ }, z.core.$strip>], "kind">, "query">;
614
+ readonly setDeviceConfig: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
615
+ deviceId: z.ZodNumber;
616
+ config: z.ZodObject<{
617
+ enabled: z.ZodBoolean;
618
+ mode: z.ZodOptional<z.ZodEnum<{
619
+ events: "events";
620
+ off: "off";
621
+ continuous: "continuous";
622
+ "on-device-decision": "on-device-decision";
623
+ }>>;
624
+ profiles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
625
+ high: "high";
626
+ mid: "mid";
627
+ low: "low";
628
+ }>>>;
629
+ segmentSeconds: z.ZodOptional<z.ZodNumber>;
630
+ bands: z.ZodDefault<z.ZodArray<z.ZodObject<{
631
+ days: z.ZodArray<z.ZodNumber>;
632
+ start: z.ZodString;
633
+ end: z.ZodString;
634
+ mode: z.ZodEnum<{
635
+ events: "events";
636
+ continuous: "continuous";
637
+ }>;
638
+ audio: z.ZodOptional<z.ZodEnum<{
639
+ muted: "muted";
640
+ on: "on";
641
+ }>>;
642
+ triggers: z.ZodOptional<z.ZodObject<{
643
+ motion: z.ZodOptional<z.ZodBoolean>;
644
+ audioThresholdDbfs: z.ZodOptional<z.ZodNumber>;
645
+ objectClasses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
646
+ person: "person";
647
+ vehicle: "vehicle";
648
+ animal: "animal";
649
+ }>>>;
650
+ sensorDeviceIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
651
+ }, z.core.$strip>>;
652
+ preBufferSec: z.ZodOptional<z.ZodNumber>;
653
+ postBufferSec: z.ZodOptional<z.ZodNumber>;
654
+ }, z.core.$strip>>>;
655
+ deviceDecision: z.ZodOptional<z.ZodBoolean>;
656
+ retention: z.ZodOptional<z.ZodObject<{
657
+ maxAgeDays: z.ZodOptional<z.ZodNumber>;
658
+ maxSizeGb: z.ZodOptional<z.ZodNumber>;
659
+ }, z.core.$strip>>;
660
+ }, z.core.$strict>;
661
+ }, z.core.$strip>, z.ZodObject<{
662
+ enabled: z.ZodBoolean;
663
+ mode: z.ZodOptional<z.ZodEnum<{
664
+ events: "events";
665
+ off: "off";
666
+ continuous: "continuous";
667
+ "on-device-decision": "on-device-decision";
668
+ }>>;
669
+ profiles: z.ZodOptional<z.ZodArray<z.ZodEnum<{
670
+ high: "high";
671
+ mid: "mid";
672
+ low: "low";
673
+ }>>>;
674
+ segmentSeconds: z.ZodOptional<z.ZodNumber>;
675
+ bands: z.ZodDefault<z.ZodArray<z.ZodObject<{
676
+ days: z.ZodArray<z.ZodNumber>;
677
+ start: z.ZodString;
678
+ end: z.ZodString;
679
+ mode: z.ZodEnum<{
680
+ events: "events";
681
+ continuous: "continuous";
682
+ }>;
683
+ audio: z.ZodOptional<z.ZodEnum<{
684
+ muted: "muted";
685
+ on: "on";
686
+ }>>;
687
+ triggers: z.ZodOptional<z.ZodObject<{
688
+ motion: z.ZodOptional<z.ZodBoolean>;
689
+ audioThresholdDbfs: z.ZodOptional<z.ZodNumber>;
690
+ objectClasses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
691
+ person: "person";
692
+ vehicle: "vehicle";
693
+ animal: "animal";
694
+ }>>>;
695
+ sensorDeviceIds: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
696
+ }, z.core.$strip>>;
697
+ preBufferSec: z.ZodOptional<z.ZodNumber>;
698
+ postBufferSec: z.ZodOptional<z.ZodNumber>;
699
+ }, z.core.$strip>>>;
700
+ deviceDecision: z.ZodOptional<z.ZodBoolean>;
701
+ retention: z.ZodOptional<z.ZodObject<{
702
+ maxAgeDays: z.ZodOptional<z.ZodNumber>;
703
+ maxSizeGb: z.ZodOptional<z.ZodNumber>;
704
+ }, z.core.$strip>>;
705
+ }, z.core.$strict>, "mutation">;
706
+ /** Re-scan this device's footage from disk (stat sizes) and reseed the
707
+ * index, then return the fresh status. */
708
+ readonly rescanStorage: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
709
+ deviceId: z.ZodNumber;
710
+ }, z.core.$strip>, z.ZodObject<{
711
+ deviceId: z.ZodNumber;
712
+ enabled: z.ZodBoolean;
713
+ activeMode: z.ZodEnum<{
714
+ events: "events";
715
+ off: "off";
716
+ continuous: "continuous";
717
+ "on-device-decision": "on-device-decision";
718
+ }>;
719
+ nodeId: z.ZodString;
720
+ storageBytes: z.ZodNumber;
721
+ }, z.core.$strip>, "mutation">;
722
+ /** Apply this device's retention policy to footage now; returns the oldest
723
+ * surviving footage start (the retention floor) or null if no footage. The
724
+ * prune is logged to the recordings ops-log with `reason` (default
725
+ * `'retention'` — the policy-driven prune; `'quota'` when disk-pressure
726
+ * triggered). */
727
+ readonly pruneFootage: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
728
+ deviceId: z.ZodNumber;
729
+ reason: z.ZodOptional<z.ZodEnum<{
730
+ retention: "retention";
731
+ quota: "quota";
732
+ manual: "manual";
733
+ operator: "operator";
734
+ maintenance: "maintenance";
735
+ "orphaned-device": "orphaned-device";
736
+ }>>;
737
+ }, z.core.$strip>, z.ZodObject<{
738
+ floorMs: z.ZodNullable<z.ZodNumber>;
739
+ deletedBuckets: z.ZodNumber;
740
+ reclaimedBytes: z.ZodNumber;
741
+ }, z.core.$strip>, "mutation">;
742
+ /**
743
+ * Manually delete a camera's footage — the whole footprint, or a
744
+ * `[fromMs, toMs)` window when either bound is given. Logged to the
745
+ * recordings ops-log as `op:'manual-delete', reason:'manual'`. Destructive:
746
+ * the admin UI guards it behind a confirm.
747
+ */
748
+ readonly deleteFootprint: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
749
+ deviceId: z.ZodNumber;
750
+ fromMs: z.ZodOptional<z.ZodNumber>;
751
+ toMs: z.ZodOptional<z.ZodNumber>;
752
+ }, z.core.$strip>, z.ZodObject<{
753
+ deletedBuckets: z.ZodNumber;
754
+ reclaimedBytes: z.ZodNumber;
755
+ }, z.core.$strip>, "mutation">;
756
+ /** The recordings ops-log rows (newest-first), optionally scoped to one
757
+ * camera. Backed by the recorder's bounded DurableState ring. */
758
+ readonly listOpsLog: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
759
+ deviceId: z.ZodOptional<z.ZodNumber>;
760
+ limit: z.ZodOptional<z.ZodNumber>;
761
+ }, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
762
+ id: z.ZodString;
763
+ at: z.ZodNumber;
764
+ domain: z.ZodEnum<{
765
+ recording: "recording";
766
+ events: "events";
767
+ }>;
768
+ op: z.ZodEnum<{
769
+ prune: "prune";
770
+ "manual-delete": "manual-delete";
771
+ rescan: "rescan";
772
+ "retention-run": "retention-run";
773
+ relocate: "relocate";
774
+ "orphan-audit": "orphan-audit";
775
+ }>;
776
+ reason: z.ZodEnum<{
777
+ retention: "retention";
778
+ quota: "quota";
779
+ manual: "manual";
780
+ operator: "operator";
781
+ maintenance: "maintenance";
782
+ "orphaned-device": "orphaned-device";
783
+ }>;
784
+ deviceId: z.ZodNullable<z.ZodNumber>;
785
+ nodeId: z.ZodString;
786
+ itemsAffected: z.ZodNumber;
787
+ bytesReclaimed: z.ZodNumber;
788
+ detail: z.ZodNullable<z.ZodString>;
789
+ actor: z.ZodString;
790
+ }, z.core.$strip>>>, "query">;
791
+ /**
792
+ * Render a short GIF from recorded footage around `aroundMs` (low profile,
793
+ * palette-free ffmpeg gif). Synchronous — the window is a few seconds of
794
+ * `low`, rendering in ~1-2s. Built for notification attachments: the
795
+ * caller gets bytes, nothing persists. Fails when no footage covers the
796
+ * window (camera not recording → caller skips the attachment).
797
+ */
798
+ readonly renderGif: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
799
+ deviceId: z.ZodNumber;
800
+ aroundMs: z.ZodNumber;
801
+ preRollSec: z.ZodDefault<z.ZodNumber>;
802
+ postRollSec: z.ZodDefault<z.ZodNumber>;
803
+ maxWidth: z.ZodDefault<z.ZodNumber>;
804
+ fps: z.ZodDefault<z.ZodNumber>;
805
+ }, z.core.$strip>, z.ZodObject<{
806
+ gifBase64: z.ZodString;
807
+ fromMs: z.ZodNumber;
808
+ toMs: z.ZodNumber;
809
+ }, z.core.$strip>, "mutation">;
810
+ /**
811
+ * Render a short MP4 clip from recorded footage around `aroundMs` — the
812
+ * "send a clip, not a gif" notification attachment. Same window, source
813
+ * and fail-closed contract as {@link renderGif}: no footage covering the
814
+ * window ⇒ it throws and the caller ships no attachment. Re-encoded (never
815
+ * stream-copied) so a concat across segments with different encoder
816
+ * parameters still plays. Silent by contract.
817
+ *
818
+ * The returned window is SEGMENT-GRANULAR: the source playlist is built
819
+ * from whole segments, so the clip starts at or before `fromMs` and ends
820
+ * at or after `toMs` — overshoot bounded by one segment per side (an 8 s
821
+ * ask over 4 s segments measured 12 s). `fromMs`/`toMs` report the window
822
+ * ASKED for, not the encoded duration.
823
+ */
824
+ readonly renderClip: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
825
+ deviceId: z.ZodNumber;
826
+ aroundMs: z.ZodNumber;
827
+ preRollSec: z.ZodDefault<z.ZodNumber>;
828
+ postRollSec: z.ZodDefault<z.ZodNumber>;
829
+ maxWidth: z.ZodDefault<z.ZodNumber>;
830
+ }, z.core.$strip>, z.ZodObject<{
831
+ clipBase64: z.ZodString;
832
+ mime: z.ZodString;
833
+ fromMs: z.ZodNumber;
834
+ toMs: z.ZodNumber;
835
+ bytes: z.ZodNumber;
836
+ }, z.core.$strip>, "mutation">;
837
+ /** Internal migration-participant lease. Blocks new recorder attaches and
838
+ * waits for writers to exit while their watchers index final segments. */
839
+ readonly pauseForStorageMigration: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
840
+ leaseId: z.ZodString;
841
+ }, z.core.$strip>, z.ZodObject<{
842
+ paused: z.ZodLiteral<true>;
843
+ }, z.core.$strip>, "mutation">;
844
+ readonly resumeForStorageMigration: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
845
+ leaseId: z.ZodString;
846
+ }, z.core.$strip>, z.ZodObject<{
847
+ resumed: z.ZodLiteral<true>;
848
+ }, z.core.$strip>, "mutation">;
849
+ /** Re-resolve location roots after the coordinator changes defaults. */
850
+ readonly refreshStorageLocationsForMigration: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
851
+ leaseId: z.ZodString;
852
+ }, z.core.$strip>, z.ZodObject<{
853
+ refreshed: z.ZodLiteral<true>;
854
+ }, z.core.$strip>, "mutation">;
855
+ /** Internal mover, callable only by the storage-migration coordinator. */
856
+ readonly startStorageMigrationMove: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
857
+ fromLocationId: z.ZodString;
858
+ toLocationId: z.ZodString;
859
+ entities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
860
+ segments: "segments";
861
+ }>>>;
862
+ footageClass: z.ZodOptional<z.ZodEnum<{
863
+ recordings: "recordings";
864
+ recordingsLow: "recordingsLow";
865
+ }>>;
866
+ deviceId: z.ZodOptional<z.ZodNumber>;
867
+ profiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
868
+ throttleMbps: z.ZodOptional<z.ZodNumber>;
869
+ sinceMs: z.ZodOptional<z.ZodNumber>;
870
+ leaseId: z.ZodString;
871
+ }, z.core.$strip>, z.ZodObject<{
872
+ jobId: z.ZodString;
873
+ }, z.core.$strip>, "mutation">;
874
+ readonly getStorageMigrationMoveStatus: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
875
+ jobId: z.ZodString;
876
+ }, z.core.$strip>, z.ZodNullable<z.ZodObject<{
877
+ jobId: z.ZodString;
878
+ state: z.ZodEnum<{
879
+ queued: "queued";
880
+ running: "running";
881
+ done: "done";
882
+ failed: "failed";
883
+ cancelled: "cancelled";
884
+ }>;
885
+ fromLocationId: z.ZodString;
886
+ toLocationId: z.ZodString;
887
+ deviceId: z.ZodNullable<z.ZodNumber>;
888
+ entities: z.ZodArray<z.ZodString>;
889
+ filesMoved: z.ZodNumber;
890
+ bytesMoved: z.ZodNumber;
891
+ filesTotal: z.ZodNullable<z.ZodNumber>;
892
+ rowsReconciled: z.ZodOptional<z.ZodNumber>;
893
+ rowsForgotten: z.ZodOptional<z.ZodNumber>;
894
+ startedAt: z.ZodNumber;
895
+ finishedAt: z.ZodNullable<z.ZodNumber>;
896
+ error: z.ZodNullable<z.ZodString>;
897
+ }, z.core.$strip>>, import("./capability-definition.js").CapabilityMethodKind>;
898
+ readonly cancelStorageMigrationMove: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
899
+ jobId: z.ZodString;
900
+ }, z.core.$strip>, z.ZodObject<{
901
+ cancelled: z.ZodBoolean;
902
+ }, z.core.$strip>, "mutation">;
903
+ /**
904
+ * Move footage between locations — the OPERATOR's mover, scoped to one
905
+ * camera (and optionally to specific profiles) rather than a whole disk.
906
+ * Queued FIFO behind the single-flight engine, so arming several is safe.
907
+ */
908
+ readonly relocateFootage: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
909
+ fromLocationId: z.ZodString;
910
+ toLocationId: z.ZodString;
911
+ entities: z.ZodOptional<z.ZodArray<z.ZodEnum<{
912
+ segments: "segments";
913
+ }>>>;
914
+ footageClass: z.ZodOptional<z.ZodEnum<{
915
+ recordings: "recordings";
916
+ recordingsLow: "recordingsLow";
917
+ }>>;
918
+ deviceId: z.ZodOptional<z.ZodNumber>;
919
+ profiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
920
+ throttleMbps: z.ZodOptional<z.ZodNumber>;
921
+ sinceMs: z.ZodOptional<z.ZodNumber>;
922
+ }, z.core.$strip>, z.ZodObject<{
923
+ jobId: z.ZodString;
924
+ }, z.core.$strip>, "mutation">;
925
+ /** Every relocate job this recorder knows about, newest first (in RAM: the
926
+ * move is resumable, so a lost list costs nothing but the display). */
927
+ readonly listRelocateJobs: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{}, z.core.$strip>, z.ZodReadonly<z.ZodArray<z.ZodObject<{
928
+ jobId: z.ZodString;
929
+ state: z.ZodEnum<{
930
+ queued: "queued";
931
+ running: "running";
932
+ done: "done";
933
+ failed: "failed";
934
+ cancelled: "cancelled";
935
+ }>;
936
+ fromLocationId: z.ZodString;
937
+ toLocationId: z.ZodString;
938
+ deviceId: z.ZodNullable<z.ZodNumber>;
939
+ entities: z.ZodArray<z.ZodString>;
940
+ filesMoved: z.ZodNumber;
941
+ bytesMoved: z.ZodNumber;
942
+ filesTotal: z.ZodNullable<z.ZodNumber>;
943
+ rowsReconciled: z.ZodOptional<z.ZodNumber>;
944
+ rowsForgotten: z.ZodOptional<z.ZodNumber>;
945
+ startedAt: z.ZodNumber;
946
+ finishedAt: z.ZodNullable<z.ZodNumber>;
947
+ error: z.ZodNullable<z.ZodString>;
948
+ }, z.core.$strip>>>, "query">;
949
+ /**
950
+ * What a location STILL holds, asked of the durable hour ledger.
951
+ *
952
+ * The number behind "drain remaining": segments and bytes that would still
953
+ * have to move off `fromLocationId`. It is a ledger aggregate — the archive
954
+ * — because the resident index is not the archive (D295), and a drain sized
955
+ * off the index is exactly what reported `done` over 80.3 GB on 2026-08-29.
956
+ * `null` means the archive could not be asked (no ledger on this node, or
957
+ * the aggregate failed) and is never conflated with an empty source.
958
+ */
959
+ readonly getRelocateResidue: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
960
+ fromLocationId: z.ZodString;
961
+ footageClass: z.ZodOptional<z.ZodEnum<{
962
+ recordings: "recordings";
963
+ recordingsLow: "recordingsLow";
964
+ }>>;
965
+ }, z.core.$strip>, z.ZodNullable<z.ZodObject<{
966
+ segments: z.ZodNumber;
967
+ bytes: z.ZodNumber;
968
+ }, z.core.$strip>>, "query">;
969
+ /**
970
+ * Does this location's LEDGER tell the truth about its disk? (D319)
971
+ *
972
+ * One `readdir` per claimed hour, diffed both ways: durable rows whose file
973
+ * is not there, and files no durable row names. `apply` defaults to FALSE —
974
+ * the report is the product, and the dry run is how an operator
975
+ * sanity-checks the destructive run before authorising it.
976
+ *
977
+ * REFUSES a source that is still a write target: a listing of a live
978
+ * location is a lower bound, which is not the strong evidence that lets
979
+ * this pass forget without a budget. A live location is reconciled by the
980
+ * mover, under D318's bound.
981
+ */
982
+ readonly reconcileLedgerAgainstDisk: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
983
+ locationId: z.ZodString;
984
+ apply: z.ZodOptional<z.ZodBoolean>;
985
+ deviceId: z.ZodOptional<z.ZodNumber>;
986
+ profiles: z.ZodOptional<z.ZodArray<z.ZodString>>;
987
+ }, z.core.$strip>, z.ZodObject<{
988
+ locationId: z.ZodString;
989
+ applied: z.ZodBoolean;
990
+ refused: z.ZodNullable<z.ZodEnum<{
991
+ "location-unknown": "location-unknown";
992
+ "source-writable": "source-writable";
993
+ "no-ledger": "no-ledger";
994
+ "archive-unreadable": "archive-unreadable";
995
+ "anchor-absent": "anchor-absent";
996
+ "anchor-unreadable": "anchor-unreadable";
997
+ "anchor-moved": "anchor-moved";
998
+ }>>;
999
+ archiveSegments: z.ZodNullable<z.ZodNumber>;
1000
+ archiveBytes: z.ZodNullable<z.ZodNumber>;
1001
+ hoursClaimed: z.ZodNumber;
1002
+ hoursWalked: z.ZodNumber;
1003
+ hoursMissing: z.ZodNumber;
1004
+ listings: z.ZodNumber;
1005
+ segmentsClaimed: z.ZodNumber;
1006
+ ghostSegments: z.ZodNumber;
1007
+ ghostBytes: z.ZodNumber;
1008
+ ghostHoursWhole: z.ZodNumber;
1009
+ forgottenSegments: z.ZodNumber;
1010
+ forgottenBytes: z.ZodNumber;
1011
+ orphanFiles: z.ZodNumber;
1012
+ orphanSample: z.ZodReadonly<z.ZodArray<z.ZodString>>;
1013
+ hoursSkipped: z.ZodNumber;
1014
+ skippedByReason: z.ZodObject<{
1015
+ 'live-tail': z.ZodNumber;
1016
+ 'listing-error': z.ZodNumber;
1017
+ 'path-mismatch': z.ZodNumber;
1018
+ 'durable-refused': z.ZodNumber;
1019
+ }, z.core.$strip>;
1020
+ bounded: z.ZodBoolean;
1021
+ byDevice: z.ZodReadonly<z.ZodArray<z.ZodObject<{
1022
+ deviceId: z.ZodNumber;
1023
+ hoursWalked: z.ZodNumber;
1024
+ hoursMissing: z.ZodNumber;
1025
+ ghostSegments: z.ZodNumber;
1026
+ ghostBytes: z.ZodNumber;
1027
+ forgottenSegments: z.ZodNumber;
1028
+ orphanFiles: z.ZodNumber;
1029
+ }, z.core.$strip>>>;
1030
+ }, z.core.$strip>, "mutation">;
1031
+ /** Cancel a running or queued relocate job. A queued job never runs. */
1032
+ readonly cancelRelocateJob: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
1033
+ jobId: z.ZodString;
1034
+ }, z.core.$strip>, z.ZodObject<{
1035
+ cancelled: z.ZodBoolean;
1036
+ }, z.core.$strip>, "mutation">;
1037
+ /** What a rebalance WOULD move, and what it would refuse. Moves nothing. */
1038
+ readonly planStorageRebalance: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
1039
+ throttleMbps: z.ZodOptional<z.ZodNumber>;
1040
+ minMoveGb: z.ZodOptional<z.ZodNumber>;
1041
+ }, z.core.$strip>, z.ZodObject<{
1042
+ moves: z.ZodArray<z.ZodObject<{
1043
+ deviceId: z.ZodNumber;
1044
+ profile: z.ZodString;
1045
+ fromLocationId: z.ZodString;
1046
+ toLocationId: z.ZodString;
1047
+ bytes: z.ZodNumber;
1048
+ files: z.ZodNumber;
1049
+ }, z.core.$strip>>;
1050
+ skipped: z.ZodArray<z.ZodObject<{
1051
+ deviceId: z.ZodNumber;
1052
+ profile: z.ZodString;
1053
+ fromLocationId: z.ZodString;
1054
+ toLocationId: z.ZodNullable<z.ZodString>;
1055
+ bytes: z.ZodNumber;
1056
+ reason: z.ZodEnum<{
1057
+ unassigned: "unassigned";
1058
+ "below-threshold": "below-threshold";
1059
+ "target-not-writable": "target-not-writable";
1060
+ "no-headroom": "no-headroom";
1061
+ }>;
1062
+ }, z.core.$strip>>;
1063
+ bytesToMove: z.ZodNumber;
1064
+ jobIds: z.ZodArray<z.ZodString>;
1065
+ }, z.core.$strip>, "query">;
1066
+ /** Arm the rebalance: enqueue one relocate job per planned move. */
1067
+ readonly startStorageRebalance: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
1068
+ throttleMbps: z.ZodOptional<z.ZodNumber>;
1069
+ minMoveGb: z.ZodOptional<z.ZodNumber>;
1070
+ }, z.core.$strip>, z.ZodObject<{
1071
+ moves: z.ZodArray<z.ZodObject<{
1072
+ deviceId: z.ZodNumber;
1073
+ profile: z.ZodString;
1074
+ fromLocationId: z.ZodString;
1075
+ toLocationId: z.ZodString;
1076
+ bytes: z.ZodNumber;
1077
+ files: z.ZodNumber;
1078
+ }, z.core.$strip>>;
1079
+ skipped: z.ZodArray<z.ZodObject<{
1080
+ deviceId: z.ZodNumber;
1081
+ profile: z.ZodString;
1082
+ fromLocationId: z.ZodString;
1083
+ toLocationId: z.ZodNullable<z.ZodString>;
1084
+ bytes: z.ZodNumber;
1085
+ reason: z.ZodEnum<{
1086
+ unassigned: "unassigned";
1087
+ "below-threshold": "below-threshold";
1088
+ "target-not-writable": "target-not-writable";
1089
+ "no-headroom": "no-headroom";
1090
+ }>;
1091
+ }, z.core.$strip>>;
1092
+ bytesToMove: z.ZodNumber;
1093
+ jobIds: z.ZodArray<z.ZodString>;
1094
+ }, z.core.$strip>, "mutation">;
1095
+ /**
1096
+ * The placement plan in force plus operator pins. Drives the Locations
1097
+ * admin page: Auto vs a named recordings location, per camera.
1098
+ */
1099
+ readonly getPlacement: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
1100
+ assignments: z.ZodArray<z.ZodObject<{
1101
+ deviceId: z.ZodNumber;
1102
+ profile: z.ZodString;
1103
+ locationId: z.ZodString;
1104
+ }, z.core.$strip>>;
1105
+ pins: z.ZodArray<z.ZodObject<{
1106
+ deviceId: z.ZodNumber;
1107
+ locationId: z.ZodString;
1108
+ }, z.core.$strip>>;
1109
+ defaultLocations: z.ZodRecord<z.ZodString, z.ZodString>;
1110
+ }, z.core.$strip>, "query">;
1111
+ /**
1112
+ * Pin a camera to a recordings location, or clear the pin (Auto). High and
1113
+ * mid follow the pin; low stays with the recordingsLow planner.
1114
+ */
1115
+ readonly setDevicePlacement: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
1116
+ deviceId: z.ZodNumber;
1117
+ locationId: z.ZodNullable<z.ZodString>;
1118
+ }, z.core.$strip>, z.ZodObject<{
1119
+ ok: z.ZodLiteral<true>;
1120
+ }, z.core.$strip>, "mutation">;
1121
+ };
1122
+ };
1123
+ export type IRecordingArchiveProvider = InferProvider<typeof recordingArchiveCapability>;