@camstack/addon-ai 0.4.44 → 0.4.46

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.
package/dist/addon.js CHANGED
@@ -8221,6 +8221,20 @@ var RelocateJobSchema = object({
8221
8221
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8222
8222
  */
8223
8223
  rowsReconciled: number$1().int().nonnegative().optional(),
8224
+ /**
8225
+ * Rows this run FORGOT because the file they name is not on disk.
8226
+ *
8227
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8228
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8229
+ * and the durable row is dropped through the same channel eviction uses. It
8230
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8231
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8232
+ * the same failure as one that quietly skips them (D295).
8233
+ *
8234
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8235
+ * ledger claimed 5.65 GB of footage that no longer existed.
8236
+ */
8237
+ rowsForgotten: number$1().int().nonnegative().optional(),
8224
8238
  startedAt: number$1(),
8225
8239
  finishedAt: number$1().nullable(),
8226
8240
  error: string().nullable()
@@ -8584,6 +8598,91 @@ var RelocateResidueSchema = object({
8584
8598
  segments: number$1().int().nonnegative(),
8585
8599
  bytes: number$1().int().nonnegative()
8586
8600
  }).nullable();
8601
+ /**
8602
+ * Ask one location whether its durable hour rows describe the disk — the walk
8603
+ * (D319).
8604
+ *
8605
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8606
+ * missing tool is the question, and the dry run is how they sanity-check the
8607
+ * destructive run before authorising it.
8608
+ */
8609
+ var LedgerWalkInputSchema = object({
8610
+ locationId: string().min(1),
8611
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8612
+ apply: boolean().optional(),
8613
+ /** Narrow to one camera. */
8614
+ deviceId: number$1().int().positive().optional(),
8615
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8616
+ profiles: array(string().min(1)).optional()
8617
+ });
8618
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8619
+ var LedgerWalkRefusalSchema = _enum([
8620
+ "location-unknown",
8621
+ "source-writable",
8622
+ "no-ledger",
8623
+ "archive-unreadable",
8624
+ "anchor-absent",
8625
+ "anchor-unreadable",
8626
+ "anchor-moved"
8627
+ ]);
8628
+ _enum([
8629
+ "live-tail",
8630
+ "listing-error",
8631
+ "path-mismatch",
8632
+ "durable-refused"
8633
+ ]);
8634
+ /** Every skip reason, always present, always a number — so a reason that never
8635
+ * fired reports as zero rather than absent and the report shape is constant
8636
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8637
+ var LedgerWalkSkipCountsSchema = object({
8638
+ "live-tail": number$1().int().nonnegative(),
8639
+ "listing-error": number$1().int().nonnegative(),
8640
+ "path-mismatch": number$1().int().nonnegative(),
8641
+ "durable-refused": number$1().int().nonnegative()
8642
+ });
8643
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8644
+ var LedgerWalkDeviceReportSchema = object({
8645
+ deviceId: number$1().int(),
8646
+ hoursWalked: number$1().int().nonnegative(),
8647
+ hoursMissing: number$1().int().nonnegative(),
8648
+ ghostSegments: number$1().int().nonnegative(),
8649
+ ghostBytes: number$1().int().nonnegative(),
8650
+ forgottenSegments: number$1().int().nonnegative(),
8651
+ orphanFiles: number$1().int().nonnegative()
8652
+ });
8653
+ /**
8654
+ * What one walk claimed, listed, found and (only when armed) forgot.
8655
+ *
8656
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8657
+ * walk that saw a fraction of the location is visible in its own report rather
8658
+ * than in the absence of one.
8659
+ */
8660
+ var LedgerWalkReportSchema = object({
8661
+ locationId: string(),
8662
+ applied: boolean(),
8663
+ refused: LedgerWalkRefusalSchema.nullable(),
8664
+ archiveSegments: number$1().int().nonnegative().nullable(),
8665
+ archiveBytes: number$1().int().nonnegative().nullable(),
8666
+ hoursClaimed: number$1().int().nonnegative(),
8667
+ hoursWalked: number$1().int().nonnegative(),
8668
+ hoursMissing: number$1().int().nonnegative(),
8669
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8670
+ listings: number$1().int().nonnegative(),
8671
+ segmentsClaimed: number$1().int().nonnegative(),
8672
+ ghostSegments: number$1().int().nonnegative(),
8673
+ ghostBytes: number$1().int().nonnegative(),
8674
+ ghostHoursWhole: number$1().int().nonnegative(),
8675
+ forgottenSegments: number$1().int().nonnegative(),
8676
+ forgottenBytes: number$1().int().nonnegative(),
8677
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8678
+ orphanFiles: number$1().int().nonnegative(),
8679
+ orphanSample: array(string()).readonly(),
8680
+ hoursSkipped: number$1().int().nonnegative(),
8681
+ skippedByReason: LedgerWalkSkipCountsSchema,
8682
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8683
+ bounded: boolean(),
8684
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8685
+ });
8587
8686
  /** How many rows a media pass would still act on against a given target — the
8588
8687
  * media lane's denominator AND its residue, from ONE derivation so the two can
8589
8688
  * never disagree. `null` = the count could not be taken. */
@@ -18951,13 +19050,15 @@ var ListGroupsPageSchema = object({
18951
19050
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18952
19051
  nextCursor: string().nullable()
18953
19052
  });
19053
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19054
+ var KEY_EVENTS_MAX_LIMIT = 200;
18954
19055
  var KeyEventQueryInput = object({
18955
19056
  deviceId: number$1(),
18956
19057
  /** Window lower bound (track firstSeen ≥ since). */
18957
19058
  since: number$1(),
18958
19059
  /** Window upper bound (track firstSeen ≤ until). */
18959
19060
  until: number$1(),
18960
- limit: number$1().int().min(1).max(200).default(50),
19061
+ limit: number$1().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18961
19062
  /** Drop tracks scoring below this importance. */
18962
19063
  minImportance: number$1().min(0).max(1).optional(),
18963
19064
  /** Restrict to a single class (e.g. 'person'). */
@@ -18979,6 +19080,32 @@ var KeyEventSchema = object({
18979
19080
  ...TrackFlagFields,
18980
19081
  ...TrackRetrainFields
18981
19082
  });
19083
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19084
+ var KeyEventBatchQueryInput = object({
19085
+ deviceIds: array(number$1()).min(1).max(200),
19086
+ since: number$1(),
19087
+ until: number$1(),
19088
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19089
+ * across the set, which would let a busy camera starve a quiet one of its
19090
+ * rows and change what the merged feed contains. */
19091
+ limit: number$1().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19092
+ minImportance: number$1().min(0).max(1).optional(),
19093
+ classFilter: string().optional()
19094
+ });
19095
+ /**
19096
+ * One camera's key events in a batch answer.
19097
+ *
19098
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19099
+ * error rather than throwing, so a camera whose store read failed and one with
19100
+ * no events in the window were ALREADY indistinguishable per camera — the
19101
+ * batch does not make that worse, and the row keeps the deviceId the single
19102
+ * method's output never carried (the caller used to stamp it from the fan-out
19103
+ * key, which only worked because there was one query per camera).
19104
+ */
19105
+ var KeyEventsForDeviceSchema = object({
19106
+ deviceId: number$1(),
19107
+ events: array(KeyEventSchema).readonly()
19108
+ });
18982
19109
  object({
18983
19110
  trackId: string(),
18984
19111
  className: string(),
@@ -19198,6 +19325,47 @@ var RebuildStatusSchema = object({
19198
19325
  /** Present when the pass ended by throwing. */
19199
19326
  error: string().nullable()
19200
19327
  });
19328
+ /**
19329
+ * Acknowledgement that a debug-media reclaim STARTED.
19330
+ *
19331
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19332
+ * it runs detached and this returns immediately. Awaiting it is how the
19333
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19334
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19335
+ */
19336
+ var MediaReclaimStartResultSchema = object({
19337
+ started: boolean(),
19338
+ /** True when a pass was already running; the new request is ignored. */
19339
+ alreadyRunning: boolean()
19340
+ });
19341
+ var MediaReclaimInputSchema = object({
19342
+ mode: _enum(["report", "reclaim"]).default("report"),
19343
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19344
+ deviceIds: array(number$1().int()).min(1).optional(),
19345
+ restart: boolean().optional(),
19346
+ pageSize: number$1().int().min(50).max(5e3).optional(),
19347
+ maxRowsPerDevice: number$1().int().min(1).max(5e5).optional(),
19348
+ maxReclaimPerDevice: number$1().int().min(1).max(5e5).optional(),
19349
+ maxBytesPerRun: number$1().int().min(1).optional(),
19350
+ budgetMinutes: number$1().int().min(1).max(720).optional(),
19351
+ throttleBytesPerSec: number$1().int().min(64 * 1024).optional(),
19352
+ graceMinutes: number$1().int().min(1).max(10080).optional()
19353
+ });
19354
+ var MediaReclaimStatusSchema = object({
19355
+ running: boolean(),
19356
+ mode: _enum(["report", "reclaim"]).nullable(),
19357
+ totalExamined: number$1(),
19358
+ totalEligible: number$1(),
19359
+ totalReclaimed: number$1(),
19360
+ totalBytesReclaimed: number$1(),
19361
+ totalRefused: number$1(),
19362
+ /** Device+scope windows finished in this pass. */
19363
+ devicesDone: number$1(),
19364
+ complete: boolean().nullable(),
19365
+ startedAtMs: number$1().nullable(),
19366
+ finishedAtMs: number$1().nullable(),
19367
+ error: string().nullable()
19368
+ });
19201
19369
  var ReplayFrameInputSchema = object({
19202
19370
  timestamp: number$1(),
19203
19371
  frame: PipelineRunResultBridge
@@ -19248,7 +19416,7 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
19248
19416
  until: number$1().optional(),
19249
19417
  kinds: array(string()).optional(),
19250
19418
  limit: number$1().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19251
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19419
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19252
19420
  deviceId: number$1(),
19253
19421
  since: number$1(),
19254
19422
  until: number$1(),
@@ -19339,6 +19507,12 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
19339
19507
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19340
19508
  kind: "query",
19341
19509
  auth: "admin"
19510
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19511
+ kind: "mutation",
19512
+ auth: "admin"
19513
+ }), method(object({}), MediaReclaimStatusSchema, {
19514
+ kind: "query",
19515
+ auth: "admin"
19342
19516
  }), method(object({ deviceIds: array(number$1()).optional() }), TrainingExportSummarySchema, {
19343
19517
  kind: "query",
19344
19518
  auth: "admin"
@@ -26900,6 +27074,9 @@ method(object({
26900
27074
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26901
27075
  kind: "query",
26902
27076
  auth: "admin"
27077
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27078
+ kind: "mutation",
27079
+ auth: "admin"
26903
27080
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26904
27081
  kind: "mutation",
26905
27082
  auth: "admin"
@@ -27291,7 +27468,26 @@ var SceneMonitorStatusSchema = object({
27291
27468
  monitors: array(SceneMonitorSchema),
27292
27469
  lastFetchedAt: number$1()
27293
27470
  });
27294
- DeviceType.Camera, method(object({ deviceId: number$1() }), SceneMonitorStatusSchema), method(object({
27471
+ /**
27472
+ * One camera's row in a `listScenesBatch` answer.
27473
+ *
27474
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27475
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27476
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27477
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27478
+ * configured. Fanned out per camera the difference was visible — one query
27479
+ * errored while the others resolved — and a batch that returned only the rows
27480
+ * it managed would have destroyed it, silently, by making an unreachable camera
27481
+ * indistinguishable from one that answered `monitors: []`.
27482
+ *
27483
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27484
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27485
+ */
27486
+ var SceneMonitorStatusForDeviceSchema = object({
27487
+ deviceId: number$1(),
27488
+ status: SceneMonitorStatusSchema.nullable()
27489
+ });
27490
+ DeviceType.Camera, method(object({ deviceId: number$1() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27295
27491
  deviceId: number$1(),
27296
27492
  label: string(),
27297
27493
  roi: MaskRectShapeSchema,
@@ -28615,6 +28811,27 @@ var CameraOccupancySnapshotSchema = object({
28615
28811
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28616
28812
  });
28617
28813
  /**
28814
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28815
+ *
28816
+ * THREE outcomes, and the single-camera method could only express two of them
28817
+ * because `snapshot: null` was already spoken for:
28818
+ *
28819
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28820
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28821
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28822
+ * - `read: 'unreadable'` — the owner could not answer for this
28823
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28824
+ *
28825
+ * Collapsing the last two is the failure this field exists to prevent: a
28826
+ * hydration that threw would otherwise render as an empty Stationary section,
28827
+ * which is a definite claim about a camera nobody could read.
28828
+ */
28829
+ var CameraOccupancySnapshotForDeviceSchema = object({
28830
+ deviceId: number$1(),
28831
+ read: _enum(["read", "unreadable"]),
28832
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28833
+ });
28834
+ /**
28618
28835
  * Time-series resolution. The history methods return one bucket per
28619
28836
  * step over the requested range. Smaller resolutions cost more
28620
28837
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28638,7 +28855,7 @@ var HistoryPointSchema = object({
28638
28855
  /** Object count averaged over the bucket (rounded to nearest integer). */
28639
28856
  count: number$1().int().nonnegative()
28640
28857
  });
28641
- DeviceType.Camera, method(object({ deviceId: number$1() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28858
+ DeviceType.Camera, method(object({ deviceId: number$1() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28642
28859
  deviceId: number$1(),
28643
28860
  zoneId: string(),
28644
28861
  className: string().optional()
@@ -31943,6 +32160,18 @@ Object.freeze({
31943
32160
  addonId: null,
31944
32161
  access: "view"
31945
32162
  },
32163
+ "pipelineAnalytics.getKeyEventsBatch": {
32164
+ capName: "pipeline-analytics",
32165
+ capScope: "device",
32166
+ addonId: null,
32167
+ access: "view"
32168
+ },
32169
+ "pipelineAnalytics.getMediaReclaimStatus": {
32170
+ capName: "pipeline-analytics",
32171
+ capScope: "device",
32172
+ addonId: null,
32173
+ access: "view"
32174
+ },
31946
32175
  "pipelineAnalytics.getMotionEvents": {
31947
32176
  capName: "pipeline-analytics",
31948
32177
  capScope: "device",
@@ -32117,6 +32346,12 @@ Object.freeze({
32117
32346
  addonId: null,
32118
32347
  access: "create"
32119
32348
  },
32349
+ "pipelineAnalytics.reclaimDebugMedia": {
32350
+ capName: "pipeline-analytics",
32351
+ capScope: "device",
32352
+ addonId: null,
32353
+ access: "create"
32354
+ },
32120
32355
  "pipelineAnalytics.reconcileFromDisk": {
32121
32356
  capName: "pipeline-analytics",
32122
32357
  capScope: "device",
@@ -33119,6 +33354,12 @@ Object.freeze({
33119
33354
  addonId: null,
33120
33355
  access: "view"
33121
33356
  },
33357
+ "recording.reconcileLedgerAgainstDisk": {
33358
+ capName: "recording",
33359
+ capScope: "system",
33360
+ addonId: null,
33361
+ access: "create"
33362
+ },
33122
33363
  "recording.refreshStorageLocationsForMigration": {
33123
33364
  capName: "recording",
33124
33365
  capScope: "system",
@@ -33245,6 +33486,12 @@ Object.freeze({
33245
33486
  addonId: null,
33246
33487
  access: "view"
33247
33488
  },
33489
+ "sceneMonitor.listScenesBatch": {
33490
+ capName: "scene-monitor",
33491
+ capScope: "device",
33492
+ addonId: null,
33493
+ access: "view"
33494
+ },
33248
33495
  "sceneMonitor.recheckNow": {
33249
33496
  capName: "scene-monitor",
33250
33497
  capScope: "device",
@@ -34601,6 +34848,12 @@ Object.freeze({
34601
34848
  addonId: null,
34602
34849
  access: "view"
34603
34850
  },
34851
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34852
+ capName: "zone-analytics",
34853
+ capScope: "device",
34854
+ addonId: null,
34855
+ access: "view"
34856
+ },
34604
34857
  "zoneAnalytics.getUnzonedHistory": {
34605
34858
  capName: "zone-analytics",
34606
34859
  capScope: "device",
@@ -35605,6 +35858,11 @@ Object.freeze({
35605
35858
  form: "single",
35606
35859
  optional: false
35607
35860
  }],
35861
+ "pipelineAnalytics.getKeyEventsBatch": [{
35862
+ name: "deviceIds",
35863
+ form: "array",
35864
+ optional: false
35865
+ }],
35608
35866
  "pipelineAnalytics.getMotionEvents": [{
35609
35867
  name: "deviceId",
35610
35868
  form: "single",
@@ -35710,6 +35968,11 @@ Object.freeze({
35710
35968
  form: "single",
35711
35969
  optional: true
35712
35970
  }],
35971
+ "pipelineAnalytics.reclaimDebugMedia": [{
35972
+ name: "deviceIds",
35973
+ form: "array",
35974
+ optional: true
35975
+ }],
35713
35976
  "pipelineAnalytics.reconcileFromDisk": [{
35714
35977
  name: "deviceId",
35715
35978
  form: "single",
@@ -36045,6 +36308,11 @@ Object.freeze({
36045
36308
  form: "single",
36046
36309
  optional: false
36047
36310
  }],
36311
+ "recording.reconcileLedgerAgainstDisk": [{
36312
+ name: "deviceId",
36313
+ form: "single",
36314
+ optional: true
36315
+ }],
36048
36316
  "recording.relocateFootage": [{
36049
36317
  name: "deviceId",
36050
36318
  form: "single",
@@ -36110,6 +36378,11 @@ Object.freeze({
36110
36378
  form: "single",
36111
36379
  optional: false
36112
36380
  }],
36381
+ "sceneMonitor.listScenesBatch": [{
36382
+ name: "deviceIds",
36383
+ form: "array",
36384
+ optional: false
36385
+ }],
36113
36386
  "sceneMonitor.recheckNow": [{
36114
36387
  name: "deviceId",
36115
36388
  form: "single",
@@ -36371,6 +36644,11 @@ Object.freeze({
36371
36644
  form: "single",
36372
36645
  optional: false
36373
36646
  }],
36647
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36648
+ name: "deviceIds",
36649
+ form: "array",
36650
+ optional: false
36651
+ }],
36374
36652
  "zoneAnalytics.getUnzonedHistory": [{
36375
36653
  name: "deviceId",
36376
36654
  form: "single",
package/dist/addon.mjs CHANGED
@@ -8248,6 +8248,20 @@ var RelocateJobSchema = object({
8248
8248
  * in its row, not in its name, so `MediaRelocateEngine` never reconciles one.
8249
8249
  */
8250
8250
  rowsReconciled: number$1().int().nonnegative().optional(),
8251
+ /**
8252
+ * Rows this run FORGOT because the file they name is not on disk.
8253
+ *
8254
+ * The mover derived the path from the row's own fields and `stat`ed it; an
8255
+ * ENOENT there is a per-path confirmation that the segment is gone (D296),
8256
+ * and the durable row is dropped through the same channel eviction uses. It
8257
+ * is reported for the same reason `rowsReconciled` is: this is a durable
8258
+ * mutation nobody asked for, and a migration that quietly erases hour rows is
8259
+ * the same failure as one that quietly skips them (D295).
8260
+ *
8261
+ * The production drain of 2026-08-30 would have reported 11 074 here — the
8262
+ * ledger claimed 5.65 GB of footage that no longer existed.
8263
+ */
8264
+ rowsForgotten: number$1().int().nonnegative().optional(),
8251
8265
  startedAt: number$1(),
8252
8266
  finishedAt: number$1().nullable(),
8253
8267
  error: string().nullable()
@@ -8611,6 +8625,91 @@ var RelocateResidueSchema = object({
8611
8625
  segments: number$1().int().nonnegative(),
8612
8626
  bytes: number$1().int().nonnegative()
8613
8627
  }).nullable();
8628
+ /**
8629
+ * Ask one location whether its durable hour rows describe the disk — the walk
8630
+ * (D319).
8631
+ *
8632
+ * `apply` DEFAULTS TO FALSE and that default is the product: the operator's
8633
+ * missing tool is the question, and the dry run is how they sanity-check the
8634
+ * destructive run before authorising it.
8635
+ */
8636
+ var LedgerWalkInputSchema = object({
8637
+ locationId: string().min(1),
8638
+ /** Forget the confirmed-absent rows, rather than only counting them. */
8639
+ apply: boolean().optional(),
8640
+ /** Narrow to one camera. */
8641
+ deviceId: number$1().int().positive().optional(),
8642
+ /** Narrow to these recording profiles; empty/absent = every profile. */
8643
+ profiles: array(string().min(1)).optional()
8644
+ });
8645
+ /** Why a whole walk did nothing. Every one leaves the ledger untouched. */
8646
+ var LedgerWalkRefusalSchema = _enum([
8647
+ "location-unknown",
8648
+ "source-writable",
8649
+ "no-ledger",
8650
+ "archive-unreadable",
8651
+ "anchor-absent",
8652
+ "anchor-unreadable",
8653
+ "anchor-moved"
8654
+ ]);
8655
+ _enum([
8656
+ "live-tail",
8657
+ "listing-error",
8658
+ "path-mismatch",
8659
+ "durable-refused"
8660
+ ]);
8661
+ /** Every skip reason, always present, always a number — so a reason that never
8662
+ * fired reports as zero rather than absent and the report shape is constant
8663
+ * between passes. Spelled out rather than `z.record` for exactly that. */
8664
+ var LedgerWalkSkipCountsSchema = object({
8665
+ "live-tail": number$1().int().nonnegative(),
8666
+ "listing-error": number$1().int().nonnegative(),
8667
+ "path-mismatch": number$1().int().nonnegative(),
8668
+ "durable-refused": number$1().int().nonnegative()
8669
+ });
8670
+ /** One camera's share of a walk, so a report names cameras and not rows. */
8671
+ var LedgerWalkDeviceReportSchema = object({
8672
+ deviceId: number$1().int(),
8673
+ hoursWalked: number$1().int().nonnegative(),
8674
+ hoursMissing: number$1().int().nonnegative(),
8675
+ ghostSegments: number$1().int().nonnegative(),
8676
+ ghostBytes: number$1().int().nonnegative(),
8677
+ forgottenSegments: number$1().int().nonnegative(),
8678
+ orphanFiles: number$1().int().nonnegative()
8679
+ });
8680
+ /**
8681
+ * What one walk claimed, listed, found and (only when armed) forgot.
8682
+ *
8683
+ * `archiveSegments` is the **M** and `segmentsClaimed` the **N** (D295), so a
8684
+ * walk that saw a fraction of the location is visible in its own report rather
8685
+ * than in the absence of one.
8686
+ */
8687
+ var LedgerWalkReportSchema = object({
8688
+ locationId: string(),
8689
+ applied: boolean(),
8690
+ refused: LedgerWalkRefusalSchema.nullable(),
8691
+ archiveSegments: number$1().int().nonnegative().nullable(),
8692
+ archiveBytes: number$1().int().nonnegative().nullable(),
8693
+ hoursClaimed: number$1().int().nonnegative(),
8694
+ hoursWalked: number$1().int().nonnegative(),
8695
+ hoursMissing: number$1().int().nonnegative(),
8696
+ /** `readdir` calls issued — the cost, stated in the unit that is paid. */
8697
+ listings: number$1().int().nonnegative(),
8698
+ segmentsClaimed: number$1().int().nonnegative(),
8699
+ ghostSegments: number$1().int().nonnegative(),
8700
+ ghostBytes: number$1().int().nonnegative(),
8701
+ ghostHoursWhole: number$1().int().nonnegative(),
8702
+ forgottenSegments: number$1().int().nonnegative(),
8703
+ forgottenBytes: number$1().int().nonnegative(),
8704
+ /** Files under a claimed hour that no durable row names. Never deleted. */
8705
+ orphanFiles: number$1().int().nonnegative(),
8706
+ orphanSample: array(string()).readonly(),
8707
+ hoursSkipped: number$1().int().nonnegative(),
8708
+ skippedByReason: LedgerWalkSkipCountsSchema,
8709
+ /** The walk stopped at its per-pass hour bound with claims unwalked. */
8710
+ bounded: boolean(),
8711
+ byDevice: array(LedgerWalkDeviceReportSchema).readonly()
8712
+ });
8614
8713
  /** How many rows a media pass would still act on against a given target — the
8615
8714
  * media lane's denominator AND its residue, from ONE derivation so the two can
8616
8715
  * never disagree. `null` = the count could not be taken. */
@@ -18978,13 +19077,15 @@ var ListGroupsPageSchema = object({
18978
19077
  groups: array(AnalyticsGroupRecordSchema).readonly(),
18979
19078
  nextCursor: string().nullable()
18980
19079
  });
19080
+ var KEY_EVENTS_DEFAULT_LIMIT = 50;
19081
+ var KEY_EVENTS_MAX_LIMIT = 200;
18981
19082
  var KeyEventQueryInput = object({
18982
19083
  deviceId: number$1(),
18983
19084
  /** Window lower bound (track firstSeen ≥ since). */
18984
19085
  since: number$1(),
18985
19086
  /** Window upper bound (track firstSeen ≤ until). */
18986
19087
  until: number$1(),
18987
- limit: number$1().int().min(1).max(200).default(50),
19088
+ limit: number$1().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
18988
19089
  /** Drop tracks scoring below this importance. */
18989
19090
  minImportance: number$1().min(0).max(1).optional(),
18990
19091
  /** Restrict to a single class (e.g. 'person'). */
@@ -19006,6 +19107,32 @@ var KeyEventSchema = object({
19006
19107
  ...TrackFlagFields,
19007
19108
  ...TrackRetrainFields
19008
19109
  });
19110
+ /** `getKeyEvents`' window and filters, asked of a SET of cameras at once. */
19111
+ var KeyEventBatchQueryInput = object({
19112
+ deviceIds: array(number$1()).min(1).max(200),
19113
+ since: number$1(),
19114
+ until: number$1(),
19115
+ /** Applied PER CAMERA, exactly as `getKeyEvents.limit` is — never a total
19116
+ * across the set, which would let a busy camera starve a quiet one of its
19117
+ * rows and change what the merged feed contains. */
19118
+ limit: number$1().int().min(1).max(KEY_EVENTS_MAX_LIMIT).default(KEY_EVENTS_DEFAULT_LIMIT),
19119
+ minImportance: number$1().min(0).max(1).optional(),
19120
+ classFilter: string().optional()
19121
+ });
19122
+ /**
19123
+ * One camera's key events in a batch answer.
19124
+ *
19125
+ * The row exists for every requested id. `getKeyEvents` degrades to `[]` on
19126
+ * error rather than throwing, so a camera whose store read failed and one with
19127
+ * no events in the window were ALREADY indistinguishable per camera — the
19128
+ * batch does not make that worse, and the row keeps the deviceId the single
19129
+ * method's output never carried (the caller used to stamp it from the fan-out
19130
+ * key, which only worked because there was one query per camera).
19131
+ */
19132
+ var KeyEventsForDeviceSchema = object({
19133
+ deviceId: number$1(),
19134
+ events: array(KeyEventSchema).readonly()
19135
+ });
19009
19136
  object({
19010
19137
  trackId: string(),
19011
19138
  className: string(),
@@ -19225,6 +19352,47 @@ var RebuildStatusSchema = object({
19225
19352
  /** Present when the pass ended by throwing. */
19226
19353
  error: string().nullable()
19227
19354
  });
19355
+ /**
19356
+ * Acknowledgement that a debug-media reclaim STARTED.
19357
+ *
19358
+ * The pass is paced at ~2 MB/s over a standing 100 GB — tens of minutes — so
19359
+ * it runs detached and this returns immediately. Awaiting it is how the
19360
+ * `addons.custom` door hit the 60 s UDS deadline while the walk carried on
19361
+ * with no status. Poll {@link pipelineAnalyticsCapability.methods.getMediaReclaimStatus}.
19362
+ */
19363
+ var MediaReclaimStartResultSchema = object({
19364
+ started: boolean(),
19365
+ /** True when a pass was already running; the new request is ignored. */
19366
+ alreadyRunning: boolean()
19367
+ });
19368
+ var MediaReclaimInputSchema = object({
19369
+ mode: _enum(["report", "reclaim"]).default("report"),
19370
+ scopes: array(_enum(["motion-still", "track-filmstrip"])).min(1).optional(),
19371
+ deviceIds: array(number$1().int()).min(1).optional(),
19372
+ restart: boolean().optional(),
19373
+ pageSize: number$1().int().min(50).max(5e3).optional(),
19374
+ maxRowsPerDevice: number$1().int().min(1).max(5e5).optional(),
19375
+ maxReclaimPerDevice: number$1().int().min(1).max(5e5).optional(),
19376
+ maxBytesPerRun: number$1().int().min(1).optional(),
19377
+ budgetMinutes: number$1().int().min(1).max(720).optional(),
19378
+ throttleBytesPerSec: number$1().int().min(64 * 1024).optional(),
19379
+ graceMinutes: number$1().int().min(1).max(10080).optional()
19380
+ });
19381
+ var MediaReclaimStatusSchema = object({
19382
+ running: boolean(),
19383
+ mode: _enum(["report", "reclaim"]).nullable(),
19384
+ totalExamined: number$1(),
19385
+ totalEligible: number$1(),
19386
+ totalReclaimed: number$1(),
19387
+ totalBytesReclaimed: number$1(),
19388
+ totalRefused: number$1(),
19389
+ /** Device+scope windows finished in this pass. */
19390
+ devicesDone: number$1(),
19391
+ complete: boolean().nullable(),
19392
+ startedAtMs: number$1().nullable(),
19393
+ finishedAtMs: number$1().nullable(),
19394
+ error: string().nullable()
19395
+ });
19228
19396
  var ReplayFrameInputSchema = object({
19229
19397
  timestamp: number$1(),
19230
19398
  frame: PipelineRunResultBridge
@@ -19275,7 +19443,7 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
19275
19443
  until: number$1().optional(),
19276
19444
  kinds: array(string()).optional(),
19277
19445
  limit: number$1().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
19278
- }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
19446
+ }), array(SensorEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(KeyEventBatchQueryInput, array(KeyEventsForDeviceSchema).readonly()), method(object({
19279
19447
  deviceId: number$1(),
19280
19448
  since: number$1(),
19281
19449
  until: number$1(),
@@ -19366,6 +19534,12 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
19366
19534
  }), method(OpsLogQueryInputSchema, array(OpsLogEntrySchema).readonly(), {
19367
19535
  kind: "query",
19368
19536
  auth: "admin"
19537
+ }), method(MediaReclaimInputSchema, MediaReclaimStartResultSchema, {
19538
+ kind: "mutation",
19539
+ auth: "admin"
19540
+ }), method(object({}), MediaReclaimStatusSchema, {
19541
+ kind: "query",
19542
+ auth: "admin"
19369
19543
  }), method(object({ deviceIds: array(number$1()).optional() }), TrainingExportSummarySchema, {
19370
19544
  kind: "query",
19371
19545
  auth: "admin"
@@ -26927,6 +27101,9 @@ method(object({
26927
27101
  }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26928
27102
  kind: "query",
26929
27103
  auth: "admin"
27104
+ }), method(LedgerWalkInputSchema, LedgerWalkReportSchema, {
27105
+ kind: "mutation",
27106
+ auth: "admin"
26930
27107
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26931
27108
  kind: "mutation",
26932
27109
  auth: "admin"
@@ -27318,7 +27495,26 @@ var SceneMonitorStatusSchema = object({
27318
27495
  monitors: array(SceneMonitorSchema),
27319
27496
  lastFetchedAt: number$1()
27320
27497
  });
27321
- DeviceType.Camera, method(object({ deviceId: number$1() }), SceneMonitorStatusSchema), method(object({
27498
+ /**
27499
+ * One camera's row in a `listScenesBatch` answer.
27500
+ *
27501
+ * `status` is NULLABLE and the nullability is the whole point. `scene-monitor`
27502
+ * is a `defaultActive` wrapper, so every camera is asked; a camera whose
27503
+ * provider is absent (pipeline-analytics not deployed, the post-processing node
27504
+ * down) cannot answer, and that is not the same fact as a camera with no scenes
27505
+ * configured. Fanned out per camera the difference was visible — one query
27506
+ * errored while the others resolved — and a batch that returned only the rows
27507
+ * it managed would have destroyed it, silently, by making an unreachable camera
27508
+ * indistinguishable from one that answered `monitors: []`.
27509
+ *
27510
+ * So: EVERY requested deviceId gets a row. `status: null` means "this camera
27511
+ * could not be read"; `status.monitors: []` means "read, and it has none".
27512
+ */
27513
+ var SceneMonitorStatusForDeviceSchema = object({
27514
+ deviceId: number$1(),
27515
+ status: SceneMonitorStatusSchema.nullable()
27516
+ });
27517
+ DeviceType.Camera, method(object({ deviceId: number$1() }), SceneMonitorStatusSchema), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(SceneMonitorStatusForDeviceSchema).readonly()), method(object({
27322
27518
  deviceId: number$1(),
27323
27519
  label: string(),
27324
27520
  roi: MaskRectShapeSchema,
@@ -28642,6 +28838,27 @@ var CameraOccupancySnapshotSchema = object({
28642
28838
  stationaryObjects: array(StationaryObjectSchema).readonly().optional()
28643
28839
  });
28644
28840
  /**
28841
+ * One camera's row in a `getCurrentSnapshotBatch` answer.
28842
+ *
28843
+ * THREE outcomes, and the single-camera method could only express two of them
28844
+ * because `snapshot: null` was already spoken for:
28845
+ *
28846
+ * - `read: 'read'`, `snapshot` present — the live occupancy reading.
28847
+ * - `read: 'read'`, `snapshot: null` — read, and this camera has no reading
28848
+ * yet: no frame since boot, and no parked-object registry to hydrate from.
28849
+ * - `read: 'unreadable'` — the owner could not answer for this
28850
+ * camera. `snapshot` is null, and it does NOT mean "nothing parked here".
28851
+ *
28852
+ * Collapsing the last two is the failure this field exists to prevent: a
28853
+ * hydration that threw would otherwise render as an empty Stationary section,
28854
+ * which is a definite claim about a camera nobody could read.
28855
+ */
28856
+ var CameraOccupancySnapshotForDeviceSchema = object({
28857
+ deviceId: number$1(),
28858
+ read: _enum(["read", "unreadable"]),
28859
+ snapshot: CameraOccupancySnapshotSchema.nullable()
28860
+ });
28861
+ /**
28645
28862
  * Time-series resolution. The history methods return one bucket per
28646
28863
  * step over the requested range. Smaller resolutions cost more
28647
28864
  * memory + bandwidth; bound to discrete steps so caller cannot ask
@@ -28665,7 +28882,7 @@ var HistoryPointSchema = object({
28665
28882
  /** Object count averaged over the bucket (rounded to nearest integer). */
28666
28883
  count: number$1().int().nonnegative()
28667
28884
  });
28668
- DeviceType.Camera, method(object({ deviceId: number$1() }), CameraOccupancySnapshotSchema.nullable()), method(object({
28885
+ DeviceType.Camera, method(object({ deviceId: number$1() }), CameraOccupancySnapshotSchema.nullable()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(CameraOccupancySnapshotForDeviceSchema).readonly()), method(object({
28669
28886
  deviceId: number$1(),
28670
28887
  zoneId: string(),
28671
28888
  className: string().optional()
@@ -31970,6 +32187,18 @@ Object.freeze({
31970
32187
  addonId: null,
31971
32188
  access: "view"
31972
32189
  },
32190
+ "pipelineAnalytics.getKeyEventsBatch": {
32191
+ capName: "pipeline-analytics",
32192
+ capScope: "device",
32193
+ addonId: null,
32194
+ access: "view"
32195
+ },
32196
+ "pipelineAnalytics.getMediaReclaimStatus": {
32197
+ capName: "pipeline-analytics",
32198
+ capScope: "device",
32199
+ addonId: null,
32200
+ access: "view"
32201
+ },
31973
32202
  "pipelineAnalytics.getMotionEvents": {
31974
32203
  capName: "pipeline-analytics",
31975
32204
  capScope: "device",
@@ -32144,6 +32373,12 @@ Object.freeze({
32144
32373
  addonId: null,
32145
32374
  access: "create"
32146
32375
  },
32376
+ "pipelineAnalytics.reclaimDebugMedia": {
32377
+ capName: "pipeline-analytics",
32378
+ capScope: "device",
32379
+ addonId: null,
32380
+ access: "create"
32381
+ },
32147
32382
  "pipelineAnalytics.reconcileFromDisk": {
32148
32383
  capName: "pipeline-analytics",
32149
32384
  capScope: "device",
@@ -33146,6 +33381,12 @@ Object.freeze({
33146
33381
  addonId: null,
33147
33382
  access: "view"
33148
33383
  },
33384
+ "recording.reconcileLedgerAgainstDisk": {
33385
+ capName: "recording",
33386
+ capScope: "system",
33387
+ addonId: null,
33388
+ access: "create"
33389
+ },
33149
33390
  "recording.refreshStorageLocationsForMigration": {
33150
33391
  capName: "recording",
33151
33392
  capScope: "system",
@@ -33272,6 +33513,12 @@ Object.freeze({
33272
33513
  addonId: null,
33273
33514
  access: "view"
33274
33515
  },
33516
+ "sceneMonitor.listScenesBatch": {
33517
+ capName: "scene-monitor",
33518
+ capScope: "device",
33519
+ addonId: null,
33520
+ access: "view"
33521
+ },
33275
33522
  "sceneMonitor.recheckNow": {
33276
33523
  capName: "scene-monitor",
33277
33524
  capScope: "device",
@@ -34628,6 +34875,12 @@ Object.freeze({
34628
34875
  addonId: null,
34629
34876
  access: "view"
34630
34877
  },
34878
+ "zoneAnalytics.getCurrentSnapshotBatch": {
34879
+ capName: "zone-analytics",
34880
+ capScope: "device",
34881
+ addonId: null,
34882
+ access: "view"
34883
+ },
34631
34884
  "zoneAnalytics.getUnzonedHistory": {
34632
34885
  capName: "zone-analytics",
34633
34886
  capScope: "device",
@@ -35632,6 +35885,11 @@ Object.freeze({
35632
35885
  form: "single",
35633
35886
  optional: false
35634
35887
  }],
35888
+ "pipelineAnalytics.getKeyEventsBatch": [{
35889
+ name: "deviceIds",
35890
+ form: "array",
35891
+ optional: false
35892
+ }],
35635
35893
  "pipelineAnalytics.getMotionEvents": [{
35636
35894
  name: "deviceId",
35637
35895
  form: "single",
@@ -35737,6 +35995,11 @@ Object.freeze({
35737
35995
  form: "single",
35738
35996
  optional: true
35739
35997
  }],
35998
+ "pipelineAnalytics.reclaimDebugMedia": [{
35999
+ name: "deviceIds",
36000
+ form: "array",
36001
+ optional: true
36002
+ }],
35740
36003
  "pipelineAnalytics.reconcileFromDisk": [{
35741
36004
  name: "deviceId",
35742
36005
  form: "single",
@@ -36072,6 +36335,11 @@ Object.freeze({
36072
36335
  form: "single",
36073
36336
  optional: false
36074
36337
  }],
36338
+ "recording.reconcileLedgerAgainstDisk": [{
36339
+ name: "deviceId",
36340
+ form: "single",
36341
+ optional: true
36342
+ }],
36075
36343
  "recording.relocateFootage": [{
36076
36344
  name: "deviceId",
36077
36345
  form: "single",
@@ -36137,6 +36405,11 @@ Object.freeze({
36137
36405
  form: "single",
36138
36406
  optional: false
36139
36407
  }],
36408
+ "sceneMonitor.listScenesBatch": [{
36409
+ name: "deviceIds",
36410
+ form: "array",
36411
+ optional: false
36412
+ }],
36140
36413
  "sceneMonitor.recheckNow": [{
36141
36414
  name: "deviceId",
36142
36415
  form: "single",
@@ -36398,6 +36671,11 @@ Object.freeze({
36398
36671
  form: "single",
36399
36672
  optional: false
36400
36673
  }],
36674
+ "zoneAnalytics.getCurrentSnapshotBatch": [{
36675
+ name: "deviceIds",
36676
+ form: "array",
36677
+ optional: false
36678
+ }],
36401
36679
  "zoneAnalytics.getUnzonedHistory": [{
36402
36680
  name: "deviceId",
36403
36681
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.4.44",
3
+ "version": "0.4.46",
4
4
  "description": "AI addon for CamStack — the `llm` collection provider (cloud, LAN, and camstack-managed local llama.cpp profiles) plus the per-node `llm-runtime` managed executor.",
5
5
  "keywords": [
6
6
  "camstack",