@camstack/addon-ai 0.4.46 → 0.4.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +173 -76
  2. package/dist/addon.mjs +173 -76
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -8299,6 +8299,13 @@ var MediaRelocateModeSchema = _enum([
8299
8299
  ]);
8300
8300
  var RelocateMediaInputSchema = object({
8301
8301
  toLocationId: string(),
8302
+ /**
8303
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8304
+ * every row that is not already on `toLocationId` (the historical
8305
+ * behaviour). A named source is what a from→to migration needs: without it
8306
+ * "move events off disk 2" also emptied disk 1.
8307
+ */
8308
+ fromLocationId: string().optional(),
8302
8309
  throttleMbps: number$1().min(1).max(1e3).optional(),
8303
8310
  /** Omitted = `move`, the pre-existing behaviour. */
8304
8311
  mode: MediaRelocateModeSchema.optional()
@@ -8366,6 +8373,19 @@ var StorageMigrationDestinationsSchema = object({
8366
8373
  galleryMedia: string().min(1).optional()
8367
8374
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8368
8375
  /**
8376
+ * Optional named source per class. Omitted = the class's current default
8377
+ * (the historical behaviour). A named source that is NOT the default is a
8378
+ * drain of that disk: bytes move, the default stays, and the source is
8379
+ * disabled when the move finishes.
8380
+ */
8381
+ var StorageMigrationSourcesSchema = object({
8382
+ recordings: string().min(1).optional(),
8383
+ recordingsLow: string().min(1).optional(),
8384
+ eventMedia: string().min(1).optional(),
8385
+ backups: string().min(1).optional(),
8386
+ galleryMedia: string().min(1).optional()
8387
+ }).optional();
8388
+ /**
8369
8389
  * How a migration sequences the cutover against the byte move.
8370
8390
  *
8371
8391
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8387,6 +8407,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8387
8407
  /** Shared input for planning and starting an orchestrated storage migration. */
8388
8408
  var StorageMigrationInputSchema = object({
8389
8409
  destinations: StorageMigrationDestinationsSchema,
8410
+ /** Omitted = each class's current default. */
8411
+ sources: StorageMigrationSourcesSchema,
8390
8412
  throttleMbps: number$1().min(1).max(1e3).optional(),
8391
8413
  /** Omitted = `blocking`, which stays the default. */
8392
8414
  mode: StorageMigrationModeSchema.optional()
@@ -8466,6 +8488,13 @@ var StorageMigrationMoveSchema = object({
8466
8488
  storageClass: StorageMigrationClassSchema,
8467
8489
  fromLocationId: string(),
8468
8490
  toLocationId: string(),
8491
+ /**
8492
+ * True when `from` was NOT the class default at plan time. The move still
8493
+ * copies bytes, but the default is left alone and the source is disabled
8494
+ * once the copy verifies. Absent on jobs planned before this field existed
8495
+ * — those jobs always repointed, which is `false`.
8496
+ */
8497
+ freezeSource: boolean().optional(),
8469
8498
  moverJobId: string().nullable(),
8470
8499
  state: RelocateJobStateSchema.nullable(),
8471
8500
  error: string().nullable(),
@@ -8479,6 +8508,7 @@ var StorageMigrationJobSchema = object({
8479
8508
  * can tell a seconds-long cutover from a thirty-hour one. */
8480
8509
  mode: StorageMigrationModeSchema,
8481
8510
  destinations: StorageMigrationDestinationsSchema,
8511
+ sources: StorageMigrationSourcesSchema,
8482
8512
  throttleMbps: number$1(),
8483
8513
  moves: array(StorageMigrationMoveSchema),
8484
8514
  pauseLeaseId: string().nullable(),
@@ -8504,6 +8534,7 @@ var StorageMigrationFindingSchema = object({
8504
8534
  });
8505
8535
  var StorageMigrationPlanSchema = object({
8506
8536
  destinations: StorageMigrationDestinationsSchema,
8537
+ sources: StorageMigrationSourcesSchema,
8507
8538
  /** The mode this plan was built for. A plan is only valid for its mode: the
8508
8539
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8509
8540
  * it. */
@@ -8511,7 +8542,8 @@ var StorageMigrationPlanSchema = object({
8511
8542
  moves: array(object({
8512
8543
  storageClass: StorageMigrationClassSchema,
8513
8544
  fromLocationId: string(),
8514
- toLocationId: string()
8545
+ toLocationId: string(),
8546
+ freezeSource: boolean().optional()
8515
8547
  })),
8516
8548
  findings: array(StorageMigrationFindingSchema)
8517
8549
  });
@@ -8689,10 +8721,51 @@ var LedgerWalkReportSchema = object({
8689
8721
  var RelocatableMediaCountSchema = object({ rows: number$1().int().nonnegative() }).nullable();
8690
8722
  var RelocatableMediaCountInputSchema = object({
8691
8723
  toLocationId: string().min(1),
8724
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8725
+ fromLocationId: string().optional(),
8692
8726
  /** Omitted = `move`. */
8693
8727
  mode: MediaRelocateModeSchema.optional()
8694
8728
  });
8695
8729
  /**
8730
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8731
+ * ghost ledger entries on frozen footage locations.
8732
+ *
8733
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8734
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8735
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8736
+ * with no operator-visible status.
8737
+ */
8738
+ var StorageCleanupPhaseSchema = _enum([
8739
+ "orphans",
8740
+ "debug-media",
8741
+ "ghost-ledger",
8742
+ "done",
8743
+ "failed",
8744
+ "cancelled"
8745
+ ]);
8746
+ var StorageCleanupInputSchema = object({
8747
+ /** Also walk motion stills / track filmstrips. Off by default. */
8748
+ includeDebugMedia: boolean().optional() });
8749
+ var StorageCleanupJobSchema = object({
8750
+ jobId: string(),
8751
+ phase: StorageCleanupPhaseSchema,
8752
+ includeDebugMedia: boolean(),
8753
+ orphansReclaimed: number$1().int().nonnegative(),
8754
+ orphanBytesReclaimed: number$1().int().nonnegative(),
8755
+ debugMediaReclaimed: number$1().int().nonnegative(),
8756
+ debugMediaBytesReclaimed: number$1().int().nonnegative(),
8757
+ ghostsForgotten: number$1().int().nonnegative(),
8758
+ ghostBytesForgotten: number$1().int().nonnegative(),
8759
+ /** Short operator-facing line: current collection, pass, or location. */
8760
+ detail: string().nullable(),
8761
+ cancelRequested: boolean(),
8762
+ startedAt: number$1(),
8763
+ updatedAt: number$1(),
8764
+ finishedAt: number$1().nullable(),
8765
+ error: string().nullable()
8766
+ });
8767
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8768
+ /**
8696
8769
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8697
8770
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8698
8771
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8720,11 +8793,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8720
8793
  * The default location for a type uses `id === <type>:default` by
8721
8794
  * convention (the bare type ref like `'backups'` resolves to it).
8722
8795
  *
8723
- * `isSystem: true` marks a location as orchestrator-seeded and
8724
- * undeletable. The bootstrap-installed defaults (one per type) carry
8725
- * this flag; operator-added locations don't. Editing the config of
8726
- * a system location is allowed (path migration, provider swap) but
8727
- * deleting it is rejected at the cap level.
8796
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8797
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8798
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8799
+ * / last-enabled, not on this bit.
8728
8800
  */
8729
8801
  var StorageLocationSchema = object({
8730
8802
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -12986,6 +13058,12 @@ method(object({
12986
13058
  }), _void(), {
12987
13059
  kind: "mutation",
12988
13060
  auth: "admin"
13061
+ }), method(object({
13062
+ from: string(),
13063
+ to: string()
13064
+ }), object({ moved: number$1() }), {
13065
+ kind: "mutation",
13066
+ auth: "admin"
12989
13067
  }), method(object({
12990
13068
  deviceId: number$1(),
12991
13069
  disabled: boolean()
@@ -19010,46 +19088,6 @@ var RecentTracksPageSchema = object({
19010
19088
  /** Cursor for the next page, or null when this page is the last. */
19011
19089
  nextCursor: string().nullable()
19012
19090
  });
19013
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19014
- var LIST_GROUPS_MAX_LIMIT = 100;
19015
- var AnalyticsGroupRecordSchema = object({
19016
- id: string(),
19017
- deviceId: number$1().int(),
19018
- openedAt: number$1().int(),
19019
- closedAt: number$1().int(),
19020
- timestamp: number$1().int(),
19021
- memberCount: number$1().int(),
19022
- memberTrackIds: array(string()).readonly(),
19023
- className: string(),
19024
- classes: array(string()).readonly(),
19025
- /** Relative event-media path, or null when the group has no picture yet. */
19026
- mediaUrl: string().nullable(),
19027
- singleton: boolean()
19028
- });
19029
- var AnalyticsGroupMemberSchema = object({
19030
- trackId: string(),
19031
- deviceId: number$1().int(),
19032
- className: string(),
19033
- firstSeen: number$1().int(),
19034
- lastSeen: number$1().int(),
19035
- mediaUrl: string().nullable()
19036
- });
19037
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19038
- var ListGroupsQueryInput = object({
19039
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19040
- deviceIds: array(number$1()),
19041
- /** Window lower bound on `closedAt` (inclusive). */
19042
- since: number$1().optional(),
19043
- /** Window upper bound on `openedAt` (inclusive). */
19044
- until: number$1().optional(),
19045
- limit: number$1().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19046
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19047
- cursor: string().optional()
19048
- });
19049
- var ListGroupsPageSchema = object({
19050
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19051
- nextCursor: string().nullable()
19052
- });
19053
19091
  var KEY_EVENTS_DEFAULT_LIMIT = 50;
19054
19092
  var KEY_EVENTS_MAX_LIMIT = 200;
19055
19093
  var KeyEventQueryInput = object({
@@ -19153,9 +19191,7 @@ var TrackCascadeCountsSchema = object({
19153
19191
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19154
19192
  plates: number$1().int(),
19155
19193
  /** Per-track CLIP search vectors removed (best-effort). */
19156
- embeddings: number$1().int(),
19157
- /** Group membership + group rows removed with their last member (best-effort). */
19158
- groups: number$1().int()
19194
+ embeddings: number$1().int()
19159
19195
  });
19160
19196
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19161
19197
  var DiskReconcileCountsSchema = object({
@@ -19404,10 +19440,7 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
19404
19440
  * stationary registry). Default false: the timeline lists passages,
19405
19441
  * not parking records (operator decision, 2026-08-15). */
19406
19442
  includeStationary: boolean().optional()
19407
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19408
- deviceId: number$1(),
19409
- groupId: string().min(1)
19410
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
19443
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number$1() }), _void(), {
19411
19444
  kind: "mutation",
19412
19445
  auth: "admin"
19413
19446
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number$1() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -21483,7 +21516,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21483
21516
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21484
21517
  kind: "mutation",
21485
21518
  auth: "admin"
21486
- });
21519
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21520
+ kind: "mutation",
21521
+ auth: "admin"
21522
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21523
+ kind: "mutation",
21524
+ auth: "admin"
21525
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21487
21526
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21488
21527
  providerId: string().min(1),
21489
21528
  displayName: string().min(1),
@@ -26861,6 +26900,33 @@ var RecordingRebalanceInputSchema = object({
26861
26900
  minMoveGb: number$1().min(0).optional()
26862
26901
  });
26863
26902
  /**
26903
+ * Operator-facing placement of one camera onto a recordings location.
26904
+ *
26905
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26906
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26907
+ * currently write — the plan, which may disagree with the pin when Auto.
26908
+ */
26909
+ var RecordingDevicePlacementSchema = object({
26910
+ deviceId: number$1().int(),
26911
+ profile: string(),
26912
+ locationId: string()
26913
+ });
26914
+ var RecordingDevicePinSchema = object({
26915
+ deviceId: number$1().int(),
26916
+ /** Recordings-class location this camera is pinned to. */
26917
+ locationId: string()
26918
+ });
26919
+ var RecordingPlacementViewSchema = object({
26920
+ assignments: array(RecordingDevicePlacementSchema),
26921
+ pins: array(RecordingDevicePinSchema),
26922
+ defaultLocations: record(string(), string())
26923
+ });
26924
+ var RecordingSetDevicePlacementInputSchema = object({
26925
+ deviceId: number$1().int(),
26926
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26927
+ locationId: string().nullable()
26928
+ });
26929
+ /**
26864
26930
  * Result of locating footage at a wall-clock instant for one device/profile.
26865
26931
  * `segment` carries the covering segment's window; `gap` reports the forward
26866
26932
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -27086,6 +27152,12 @@ method(object({
27086
27152
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
27087
27153
  kind: "mutation",
27088
27154
  auth: "admin"
27155
+ }), method(object({}), RecordingPlacementViewSchema, {
27156
+ kind: "query",
27157
+ auth: "admin"
27158
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27159
+ kind: "mutation",
27160
+ auth: "admin"
27089
27161
  });
27090
27162
  /**
27091
27163
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -30456,6 +30528,12 @@ Object.freeze({
30456
30528
  addonId: null,
30457
30529
  access: "delete"
30458
30530
  },
30531
+ "deviceManager.renameLocation": {
30532
+ capName: "device-manager",
30533
+ capScope: "system",
30534
+ addonId: null,
30535
+ access: "create"
30536
+ },
30459
30537
  "deviceManager.runDeviceAction": {
30460
30538
  capName: "device-manager",
30461
30539
  capScope: "system",
@@ -32148,12 +32226,6 @@ Object.freeze({
32148
32226
  addonId: null,
32149
32227
  access: "view"
32150
32228
  },
32151
- "pipelineAnalytics.getGroup": {
32152
- capName: "pipeline-analytics",
32153
- capScope: "device",
32154
- addonId: null,
32155
- access: "view"
32156
- },
32157
32229
  "pipelineAnalytics.getKeyEvents": {
32158
32230
  capName: "pipeline-analytics",
32159
32231
  capScope: "device",
@@ -32256,12 +32328,6 @@ Object.freeze({
32256
32328
  addonId: null,
32257
32329
  access: "view"
32258
32330
  },
32259
- "pipelineAnalytics.listGroups": {
32260
- capName: "pipeline-analytics",
32261
- capScope: "device",
32262
- addonId: null,
32263
- access: "view"
32264
- },
32265
32331
  "pipelineAnalytics.listOpsLog": {
32266
32332
  capName: "pipeline-analytics",
32267
32333
  capScope: "device",
@@ -33276,6 +33342,12 @@ Object.freeze({
33276
33342
  addonId: null,
33277
33343
  access: "view"
33278
33344
  },
33345
+ "recording.getPlacement": {
33346
+ capName: "recording",
33347
+ capScope: "system",
33348
+ addonId: null,
33349
+ access: "view"
33350
+ },
33279
33351
  "recording.getPlaybackManifest": {
33280
33352
  capName: "recording",
33281
33353
  capScope: "system",
@@ -33402,6 +33474,12 @@ Object.freeze({
33402
33474
  addonId: null,
33403
33475
  access: "create"
33404
33476
  },
33477
+ "recording.setDevicePlacement": {
33478
+ capName: "recording",
33479
+ capScope: "system",
33480
+ addonId: null,
33481
+ access: "create"
33482
+ },
33405
33483
  "recording.startStorageMigrationMove": {
33406
33484
  capName: "recording",
33407
33485
  capScope: "system",
@@ -33846,12 +33924,36 @@ Object.freeze({
33846
33924
  addonId: null,
33847
33925
  access: "create"
33848
33926
  },
33927
+ "storageMigration.cleanupCancel": {
33928
+ capName: "storage-migration",
33929
+ capScope: "system",
33930
+ addonId: null,
33931
+ access: "create"
33932
+ },
33933
+ "storageMigration.cleanupStart": {
33934
+ capName: "storage-migration",
33935
+ capScope: "system",
33936
+ addonId: null,
33937
+ access: "create"
33938
+ },
33939
+ "storageMigration.cleanupStatus": {
33940
+ capName: "storage-migration",
33941
+ capScope: "system",
33942
+ addonId: null,
33943
+ access: "view"
33944
+ },
33849
33945
  "storageMigration.drain": {
33850
33946
  capName: "storage-migration",
33851
33947
  capScope: "system",
33852
33948
  addonId: null,
33853
33949
  access: "create"
33854
33950
  },
33951
+ "storageMigration.history": {
33952
+ capName: "storage-migration",
33953
+ capScope: "system",
33954
+ addonId: null,
33955
+ access: "view"
33956
+ },
33855
33957
  "storageMigration.movers": {
33856
33958
  capName: "storage-migration",
33857
33959
  capScope: "system",
@@ -35848,11 +35950,6 @@ Object.freeze({
35848
35950
  form: "single",
35849
35951
  optional: true
35850
35952
  }],
35851
- "pipelineAnalytics.getGroup": [{
35852
- name: "deviceId",
35853
- form: "single",
35854
- optional: false
35855
- }],
35856
35953
  "pipelineAnalytics.getKeyEvents": [{
35857
35954
  name: "deviceId",
35858
35955
  form: "single",
@@ -35918,11 +36015,6 @@ Object.freeze({
35918
36015
  form: "single",
35919
36016
  optional: false
35920
36017
  }],
35921
- "pipelineAnalytics.listGroups": [{
35922
- name: "deviceIds",
35923
- form: "array",
35924
- optional: false
35925
- }],
35926
36018
  "pipelineAnalytics.listOpsLog": [{
35927
36019
  name: "deviceId",
35928
36020
  form: "single",
@@ -36338,6 +36430,11 @@ Object.freeze({
36338
36430
  form: "single",
36339
36431
  optional: false
36340
36432
  }],
36433
+ "recording.setDevicePlacement": [{
36434
+ name: "deviceId",
36435
+ form: "single",
36436
+ optional: false
36437
+ }],
36341
36438
  "recording.startStorageMigrationMove": [{
36342
36439
  name: "deviceId",
36343
36440
  form: "single",
package/dist/addon.mjs CHANGED
@@ -8326,6 +8326,13 @@ var MediaRelocateModeSchema = _enum([
8326
8326
  ]);
8327
8327
  var RelocateMediaInputSchema = object({
8328
8328
  toLocationId: string(),
8329
+ /**
8330
+ * Restrict the pass to rows currently on this location. Omitted / `'*'` =
8331
+ * every row that is not already on `toLocationId` (the historical
8332
+ * behaviour). A named source is what a from→to migration needs: without it
8333
+ * "move events off disk 2" also emptied disk 1.
8334
+ */
8335
+ fromLocationId: string().optional(),
8329
8336
  throttleMbps: number$1().min(1).max(1e3).optional(),
8330
8337
  /** Omitted = `move`, the pre-existing behaviour. */
8331
8338
  mode: MediaRelocateModeSchema.optional()
@@ -8393,6 +8400,19 @@ var StorageMigrationDestinationsSchema = object({
8393
8400
  galleryMedia: string().min(1).optional()
8394
8401
  }).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
8395
8402
  /**
8403
+ * Optional named source per class. Omitted = the class's current default
8404
+ * (the historical behaviour). A named source that is NOT the default is a
8405
+ * drain of that disk: bytes move, the default stays, and the source is
8406
+ * disabled when the move finishes.
8407
+ */
8408
+ var StorageMigrationSourcesSchema = object({
8409
+ recordings: string().min(1).optional(),
8410
+ recordingsLow: string().min(1).optional(),
8411
+ eventMedia: string().min(1).optional(),
8412
+ backups: string().min(1).optional(),
8413
+ galleryMedia: string().min(1).optional()
8414
+ }).optional();
8415
+ /**
8396
8416
  * How a migration sequences the cutover against the byte move.
8397
8417
  *
8398
8418
  * - `blocking` — the historical order: pause, move every byte, repoint,
@@ -8414,6 +8434,8 @@ var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
8414
8434
  /** Shared input for planning and starting an orchestrated storage migration. */
8415
8435
  var StorageMigrationInputSchema = object({
8416
8436
  destinations: StorageMigrationDestinationsSchema,
8437
+ /** Omitted = each class's current default. */
8438
+ sources: StorageMigrationSourcesSchema,
8417
8439
  throttleMbps: number$1().min(1).max(1e3).optional(),
8418
8440
  /** Omitted = `blocking`, which stays the default. */
8419
8441
  mode: StorageMigrationModeSchema.optional()
@@ -8493,6 +8515,13 @@ var StorageMigrationMoveSchema = object({
8493
8515
  storageClass: StorageMigrationClassSchema,
8494
8516
  fromLocationId: string(),
8495
8517
  toLocationId: string(),
8518
+ /**
8519
+ * True when `from` was NOT the class default at plan time. The move still
8520
+ * copies bytes, but the default is left alone and the source is disabled
8521
+ * once the copy verifies. Absent on jobs planned before this field existed
8522
+ * — those jobs always repointed, which is `false`.
8523
+ */
8524
+ freezeSource: boolean().optional(),
8496
8525
  moverJobId: string().nullable(),
8497
8526
  state: RelocateJobStateSchema.nullable(),
8498
8527
  error: string().nullable(),
@@ -8506,6 +8535,7 @@ var StorageMigrationJobSchema = object({
8506
8535
  * can tell a seconds-long cutover from a thirty-hour one. */
8507
8536
  mode: StorageMigrationModeSchema,
8508
8537
  destinations: StorageMigrationDestinationsSchema,
8538
+ sources: StorageMigrationSourcesSchema,
8509
8539
  throttleMbps: number$1(),
8510
8540
  moves: array(StorageMigrationMoveSchema),
8511
8541
  pauseLeaseId: string().nullable(),
@@ -8531,6 +8561,7 @@ var StorageMigrationFindingSchema = object({
8531
8561
  });
8532
8562
  var StorageMigrationPlanSchema = object({
8533
8563
  destinations: StorageMigrationDestinationsSchema,
8564
+ sources: StorageMigrationSourcesSchema,
8534
8565
  /** The mode this plan was built for. A plan is only valid for its mode: the
8535
8566
  * `eventMedia` seal gate and the single-cardinality refusal both depend on
8536
8567
  * it. */
@@ -8538,7 +8569,8 @@ var StorageMigrationPlanSchema = object({
8538
8569
  moves: array(object({
8539
8570
  storageClass: StorageMigrationClassSchema,
8540
8571
  fromLocationId: string(),
8541
- toLocationId: string()
8572
+ toLocationId: string(),
8573
+ freezeSource: boolean().optional()
8542
8574
  })),
8543
8575
  findings: array(StorageMigrationFindingSchema)
8544
8576
  });
@@ -8716,10 +8748,51 @@ var LedgerWalkReportSchema = object({
8716
8748
  var RelocatableMediaCountSchema = object({ rows: number$1().int().nonnegative() }).nullable();
8717
8749
  var RelocatableMediaCountInputSchema = object({
8718
8750
  toLocationId: string().min(1),
8751
+ /** Restrict the count to one source; omitted / `'*'` = every non-target row. */
8752
+ fromLocationId: string().optional(),
8719
8753
  /** Omitted = `move`. */
8720
8754
  mode: MediaRelocateModeSchema.optional()
8721
8755
  });
8722
8756
  /**
8757
+ * Operator cleanup of leftover analytics rows, optional debug media, and
8758
+ * ghost ledger entries on frozen footage locations.
8759
+ *
8760
+ * A pass is tens of minutes on a standing backlog. The hub method RETURNS
8761
+ * `{ jobId }` immediately; progress is `cleanupStatus`. Awaiting the work
8762
+ * is how `addons.custom` hit the 60 s UDS deadline while reclaim continued
8763
+ * with no operator-visible status.
8764
+ */
8765
+ var StorageCleanupPhaseSchema = _enum([
8766
+ "orphans",
8767
+ "debug-media",
8768
+ "ghost-ledger",
8769
+ "done",
8770
+ "failed",
8771
+ "cancelled"
8772
+ ]);
8773
+ var StorageCleanupInputSchema = object({
8774
+ /** Also walk motion stills / track filmstrips. Off by default. */
8775
+ includeDebugMedia: boolean().optional() });
8776
+ var StorageCleanupJobSchema = object({
8777
+ jobId: string(),
8778
+ phase: StorageCleanupPhaseSchema,
8779
+ includeDebugMedia: boolean(),
8780
+ orphansReclaimed: number$1().int().nonnegative(),
8781
+ orphanBytesReclaimed: number$1().int().nonnegative(),
8782
+ debugMediaReclaimed: number$1().int().nonnegative(),
8783
+ debugMediaBytesReclaimed: number$1().int().nonnegative(),
8784
+ ghostsForgotten: number$1().int().nonnegative(),
8785
+ ghostBytesForgotten: number$1().int().nonnegative(),
8786
+ /** Short operator-facing line: current collection, pass, or location. */
8787
+ detail: string().nullable(),
8788
+ cancelRequested: boolean(),
8789
+ startedAt: number$1(),
8790
+ updatedAt: number$1(),
8791
+ finishedAt: number$1().nullable(),
8792
+ error: string().nullable()
8793
+ });
8794
+ var StorageCleanupStatusInputSchema = object({ jobId: string().optional() });
8795
+ /**
8723
8796
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8724
8797
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8725
8798
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8747,11 +8820,10 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8747
8820
  * The default location for a type uses `id === <type>:default` by
8748
8821
  * convention (the bare type ref like `'backups'` resolves to it).
8749
8822
  *
8750
- * `isSystem: true` marks a location as orchestrator-seeded and
8751
- * undeletable. The bootstrap-installed defaults (one per type) carry
8752
- * this flag; operator-added locations don't. Editing the config of
8753
- * a system location is allowed (path migration, provider swap) but
8754
- * deleting it is rejected at the cap level.
8823
+ * `isSystem` is a legacy persisted flag. Seed still creates the initial
8824
+ * `<type>:default` locations; the flag is no longer a lock, a badge, or a
8825
+ * prune selector. New writes leave it false. Deletion is gated on uniqueness
8826
+ * / last-enabled, not on this bit.
8755
8827
  */
8756
8828
  var StorageLocationSchema = object({
8757
8829
  id: string().regex(/^[a-z][a-zA-Z0-9-]*:[a-zA-Z0-9-]+$/),
@@ -13013,6 +13085,12 @@ method(object({
13013
13085
  }), _void(), {
13014
13086
  kind: "mutation",
13015
13087
  auth: "admin"
13088
+ }), method(object({
13089
+ from: string(),
13090
+ to: string()
13091
+ }), object({ moved: number$1() }), {
13092
+ kind: "mutation",
13093
+ auth: "admin"
13016
13094
  }), method(object({
13017
13095
  deviceId: number$1(),
13018
13096
  disabled: boolean()
@@ -19037,46 +19115,6 @@ var RecentTracksPageSchema = object({
19037
19115
  /** Cursor for the next page, or null when this page is the last. */
19038
19116
  nextCursor: string().nullable()
19039
19117
  });
19040
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
19041
- var LIST_GROUPS_MAX_LIMIT = 100;
19042
- var AnalyticsGroupRecordSchema = object({
19043
- id: string(),
19044
- deviceId: number$1().int(),
19045
- openedAt: number$1().int(),
19046
- closedAt: number$1().int(),
19047
- timestamp: number$1().int(),
19048
- memberCount: number$1().int(),
19049
- memberTrackIds: array(string()).readonly(),
19050
- className: string(),
19051
- classes: array(string()).readonly(),
19052
- /** Relative event-media path, or null when the group has no picture yet. */
19053
- mediaUrl: string().nullable(),
19054
- singleton: boolean()
19055
- });
19056
- var AnalyticsGroupMemberSchema = object({
19057
- trackId: string(),
19058
- deviceId: number$1().int(),
19059
- className: string(),
19060
- firstSeen: number$1().int(),
19061
- lastSeen: number$1().int(),
19062
- mediaUrl: string().nullable()
19063
- });
19064
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: array(AnalyticsGroupMemberSchema).readonly() });
19065
- var ListGroupsQueryInput = object({
19066
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
19067
- deviceIds: array(number$1()),
19068
- /** Window lower bound on `closedAt` (inclusive). */
19069
- since: number$1().optional(),
19070
- /** Window upper bound on `openedAt` (inclusive). */
19071
- until: number$1().optional(),
19072
- limit: number$1().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
19073
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
19074
- cursor: string().optional()
19075
- });
19076
- var ListGroupsPageSchema = object({
19077
- groups: array(AnalyticsGroupRecordSchema).readonly(),
19078
- nextCursor: string().nullable()
19079
- });
19080
19118
  var KEY_EVENTS_DEFAULT_LIMIT = 50;
19081
19119
  var KEY_EVENTS_MAX_LIMIT = 200;
19082
19120
  var KeyEventQueryInput = object({
@@ -19180,9 +19218,7 @@ var TrackCascadeCountsSchema = object({
19180
19218
  /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
19181
19219
  plates: number$1().int(),
19182
19220
  /** Per-track CLIP search vectors removed (best-effort). */
19183
- embeddings: number$1().int(),
19184
- /** Group membership + group rows removed with their last member (best-effort). */
19185
- groups: number$1().int()
19221
+ embeddings: number$1().int()
19186
19222
  });
19187
19223
  /** Disk-wins reconcile: media rows whose blobs are gone, then empty tracks. */
19188
19224
  var DiskReconcileCountsSchema = object({
@@ -19431,10 +19467,7 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
19431
19467
  * stationary registry). Default false: the timeline lists passages,
19432
19468
  * not parking records (operator decision, 2026-08-15). */
19433
19469
  includeStationary: boolean().optional()
19434
- }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(ListGroupsQueryInput, ListGroupsPageSchema), method(object({
19435
- deviceId: number$1(),
19436
- groupId: string().min(1)
19437
- }), AnalyticsGroupDetailSchema.nullable()), method(object({ deviceId: number$1() }), _void(), {
19470
+ }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number$1() }), _void(), {
19438
19471
  kind: "mutation",
19439
19472
  auth: "admin"
19440
19473
  }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({ deviceId: number$1() }), array(EventKindDescriptorSchema).readonly()), method(object({ deviceIds: array(number$1()).min(1).max(200) }), array(EventKindsForDeviceSchema).readonly()), method(object({
@@ -21510,7 +21543,13 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
21510
21543
  }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
21511
21544
  kind: "mutation",
21512
21545
  auth: "admin"
21513
- });
21546
+ }), method(StorageCleanupInputSchema, object({ jobId: string() }), {
21547
+ kind: "mutation",
21548
+ auth: "admin"
21549
+ }), method(StorageCleanupStatusInputSchema, StorageCleanupJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
21550
+ kind: "mutation",
21551
+ auth: "admin"
21552
+ }), method(object({}), array(StorageMigrationJobSchema).readonly(), { auth: "admin" });
21514
21553
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
21515
21554
  providerId: string().min(1),
21516
21555
  displayName: string().min(1),
@@ -26888,6 +26927,33 @@ var RecordingRebalanceInputSchema = object({
26888
26927
  minMoveGb: number$1().min(0).optional()
26889
26928
  });
26890
26929
  /**
26930
+ * Operator-facing placement of one camera onto a recordings location.
26931
+ *
26932
+ * `pinLocationId` is the operator instruction: a location id, or `null` for
26933
+ * Auto (the planner may move this camera). `locationId` is where high/mid
26934
+ * currently write — the plan, which may disagree with the pin when Auto.
26935
+ */
26936
+ var RecordingDevicePlacementSchema = object({
26937
+ deviceId: number$1().int(),
26938
+ profile: string(),
26939
+ locationId: string()
26940
+ });
26941
+ var RecordingDevicePinSchema = object({
26942
+ deviceId: number$1().int(),
26943
+ /** Recordings-class location this camera is pinned to. */
26944
+ locationId: string()
26945
+ });
26946
+ var RecordingPlacementViewSchema = object({
26947
+ assignments: array(RecordingDevicePlacementSchema),
26948
+ pins: array(RecordingDevicePinSchema),
26949
+ defaultLocations: record(string(), string())
26950
+ });
26951
+ var RecordingSetDevicePlacementInputSchema = object({
26952
+ deviceId: number$1().int(),
26953
+ /** `null` = Auto. Otherwise a `recordings:*` location id. */
26954
+ locationId: string().nullable()
26955
+ });
26956
+ /**
26891
26957
  * Result of locating footage at a wall-clock instant for one device/profile.
26892
26958
  * `segment` carries the covering segment's window; `gap` reports the forward
26893
26959
  * nearest covered edge (`null` past the end of footage / when none exists)
@@ -27113,6 +27179,12 @@ method(object({
27113
27179
  }), method(RecordingRebalanceInputSchema, RecordingRebalancePlanSchema, {
27114
27180
  kind: "mutation",
27115
27181
  auth: "admin"
27182
+ }), method(object({}), RecordingPlacementViewSchema, {
27183
+ kind: "query",
27184
+ auth: "admin"
27185
+ }), method(RecordingSetDevicePlacementInputSchema, object({ ok: literal(true) }), {
27186
+ kind: "mutation",
27187
+ auth: "admin"
27116
27188
  });
27117
27189
  /**
27118
27190
  * `recording-export` cap — render a footage time range into a single downloadable
@@ -30483,6 +30555,12 @@ Object.freeze({
30483
30555
  addonId: null,
30484
30556
  access: "delete"
30485
30557
  },
30558
+ "deviceManager.renameLocation": {
30559
+ capName: "device-manager",
30560
+ capScope: "system",
30561
+ addonId: null,
30562
+ access: "create"
30563
+ },
30486
30564
  "deviceManager.runDeviceAction": {
30487
30565
  capName: "device-manager",
30488
30566
  capScope: "system",
@@ -32175,12 +32253,6 @@ Object.freeze({
32175
32253
  addonId: null,
32176
32254
  access: "view"
32177
32255
  },
32178
- "pipelineAnalytics.getGroup": {
32179
- capName: "pipeline-analytics",
32180
- capScope: "device",
32181
- addonId: null,
32182
- access: "view"
32183
- },
32184
32256
  "pipelineAnalytics.getKeyEvents": {
32185
32257
  capName: "pipeline-analytics",
32186
32258
  capScope: "device",
@@ -32283,12 +32355,6 @@ Object.freeze({
32283
32355
  addonId: null,
32284
32356
  access: "view"
32285
32357
  },
32286
- "pipelineAnalytics.listGroups": {
32287
- capName: "pipeline-analytics",
32288
- capScope: "device",
32289
- addonId: null,
32290
- access: "view"
32291
- },
32292
32358
  "pipelineAnalytics.listOpsLog": {
32293
32359
  capName: "pipeline-analytics",
32294
32360
  capScope: "device",
@@ -33303,6 +33369,12 @@ Object.freeze({
33303
33369
  addonId: null,
33304
33370
  access: "view"
33305
33371
  },
33372
+ "recording.getPlacement": {
33373
+ capName: "recording",
33374
+ capScope: "system",
33375
+ addonId: null,
33376
+ access: "view"
33377
+ },
33306
33378
  "recording.getPlaybackManifest": {
33307
33379
  capName: "recording",
33308
33380
  capScope: "system",
@@ -33429,6 +33501,12 @@ Object.freeze({
33429
33501
  addonId: null,
33430
33502
  access: "create"
33431
33503
  },
33504
+ "recording.setDevicePlacement": {
33505
+ capName: "recording",
33506
+ capScope: "system",
33507
+ addonId: null,
33508
+ access: "create"
33509
+ },
33432
33510
  "recording.startStorageMigrationMove": {
33433
33511
  capName: "recording",
33434
33512
  capScope: "system",
@@ -33873,12 +33951,36 @@ Object.freeze({
33873
33951
  addonId: null,
33874
33952
  access: "create"
33875
33953
  },
33954
+ "storageMigration.cleanupCancel": {
33955
+ capName: "storage-migration",
33956
+ capScope: "system",
33957
+ addonId: null,
33958
+ access: "create"
33959
+ },
33960
+ "storageMigration.cleanupStart": {
33961
+ capName: "storage-migration",
33962
+ capScope: "system",
33963
+ addonId: null,
33964
+ access: "create"
33965
+ },
33966
+ "storageMigration.cleanupStatus": {
33967
+ capName: "storage-migration",
33968
+ capScope: "system",
33969
+ addonId: null,
33970
+ access: "view"
33971
+ },
33876
33972
  "storageMigration.drain": {
33877
33973
  capName: "storage-migration",
33878
33974
  capScope: "system",
33879
33975
  addonId: null,
33880
33976
  access: "create"
33881
33977
  },
33978
+ "storageMigration.history": {
33979
+ capName: "storage-migration",
33980
+ capScope: "system",
33981
+ addonId: null,
33982
+ access: "view"
33983
+ },
33882
33984
  "storageMigration.movers": {
33883
33985
  capName: "storage-migration",
33884
33986
  capScope: "system",
@@ -35875,11 +35977,6 @@ Object.freeze({
35875
35977
  form: "single",
35876
35978
  optional: true
35877
35979
  }],
35878
- "pipelineAnalytics.getGroup": [{
35879
- name: "deviceId",
35880
- form: "single",
35881
- optional: false
35882
- }],
35883
35980
  "pipelineAnalytics.getKeyEvents": [{
35884
35981
  name: "deviceId",
35885
35982
  form: "single",
@@ -35945,11 +36042,6 @@ Object.freeze({
35945
36042
  form: "single",
35946
36043
  optional: false
35947
36044
  }],
35948
- "pipelineAnalytics.listGroups": [{
35949
- name: "deviceIds",
35950
- form: "array",
35951
- optional: false
35952
- }],
35953
36045
  "pipelineAnalytics.listOpsLog": [{
35954
36046
  name: "deviceId",
35955
36047
  form: "single",
@@ -36365,6 +36457,11 @@ Object.freeze({
36365
36457
  form: "single",
36366
36458
  optional: false
36367
36459
  }],
36460
+ "recording.setDevicePlacement": [{
36461
+ name: "deviceId",
36462
+ form: "single",
36463
+ optional: false
36464
+ }],
36368
36465
  "recording.startStorageMigrationMove": [{
36369
36466
  name: "deviceId",
36370
36467
  form: "single",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.4.46",
3
+ "version": "0.4.47",
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",