@camstack/addon-provider-petkit 0.2.43 → 0.2.45

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 +333 -12
  2. package/dist/addon.mjs +333 -12
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -9266,13 +9266,49 @@ var StorageMigrationParticipantSchema = _enum([
9266
9266
  "recorder",
9267
9267
  "analytics"
9268
9268
  ]);
9269
+ /**
9270
+ * The mover's own numbers, folded onto the coordinator's durable move record.
9271
+ *
9272
+ * The long half of a non-blocking migration is `draining`, and it is measured
9273
+ * in hours: 136 885 files at ~4 MB/s is about five of them. Before this shape
9274
+ * existed the only place those numbers appeared was a Loki line, so an operator
9275
+ * watching the Admin UI saw `phase: draining` and nothing else for a whole
9276
+ * afternoon.
9277
+ *
9278
+ * It is POLLED, never pushed. Events are telemetry and may be dropped
9279
+ * (D8/D11), and a dropped progress event is indistinguishable from a stalled
9280
+ * mover — which is the exact failure this is meant to end. The coordinator's
9281
+ * `waitForMoves` already fetches the whole {@link RelocateJob} on every tick to
9282
+ * read `state`; folding the counters costs no extra read and makes the durable
9283
+ * record say afterwards how far a move actually got.
9284
+ *
9285
+ * `filesTotal` is `null` for "no honest denominator" and is never zero-filled:
9286
+ * a windowed footage job (`sinceMs`) and a node with no ledger both genuinely
9287
+ * cannot say M, and a 0 there would render as "100 % done".
9288
+ */
9289
+ var StorageMigrationMoveProgressSchema = object({
9290
+ filesMoved: number().int().nonnegative(),
9291
+ /** The archive census — the **M** of "N of M" (D295). `null` = unknowable. */
9292
+ filesTotal: number().int().nonnegative().nullable(),
9293
+ bytesMoved: number().int().nonnegative(),
9294
+ /** The MOVER's start, not the migration's: a drain restarted after an addon
9295
+ * crash gets a new mover, and a rate computed from the migration's start
9296
+ * would silently average in the time nothing was running. */
9297
+ startedAt: number(),
9298
+ /** When the coordinator last read these numbers. Paired with `startedAt` it
9299
+ * is the only honest rate: both clocks are the hub's, so a UI never has to
9300
+ * subtract its own. */
9301
+ observedAt: number()
9302
+ });
9269
9303
  var StorageMigrationMoveSchema = object({
9270
9304
  storageClass: StorageMigrationClassSchema,
9271
9305
  fromLocationId: string(),
9272
9306
  toLocationId: string(),
9273
9307
  moverJobId: string().nullable(),
9274
9308
  state: RelocateJobStateSchema.nullable(),
9275
- error: string().nullable()
9309
+ error: string().nullable(),
9310
+ /** Last observed mover counters; `null` until the mover has been polled once. */
9311
+ progress: StorageMigrationMoveProgressSchema.nullable()
9276
9312
  });
9277
9313
  var StorageMigrationJobSchema = object({
9278
9314
  jobId: string(),
@@ -9318,6 +9354,98 @@ var StorageMigrationPlanSchema = object({
9318
9354
  findings: array(StorageMigrationFindingSchema)
9319
9355
  });
9320
9356
  /**
9357
+ * A mover as it exists RIGHT NOW, whether or not a migration job owns it.
9358
+ *
9359
+ * The coordinator's job record is the state of record for a migration, and its
9360
+ * moves carry {@link StorageMigrationMoveProgress}. But the movers are usable
9361
+ * standalone — `recording.relocateFootage` and `pipelineAnalytics.relocateMedia`
9362
+ * are both operator-callable, and on 2026-08-29 a five-hour drain was armed that
9363
+ * way because no supported UI path existed. A mover armed like that has no job
9364
+ * to fold progress into, so it has to be readable on its own or it is invisible.
9365
+ *
9366
+ * `migrationJobId` is what tells the two apart: `null` means nothing here
9367
+ * orchestrated it.
9368
+ */
9369
+ var StorageMigrationMoverSchema = object({
9370
+ lane: _enum(["footage", "media"]),
9371
+ job: RelocateJobSchema,
9372
+ /** The coordinator job that armed this mover, or `null` for a mover armed
9373
+ * directly against the owning addon. */
9374
+ migrationJobId: string().nullable(),
9375
+ /** When the hub read these counters. Stamped here so a rate is `bytesMoved`
9376
+ * over (`observedAt` − `job.startedAt`) with BOTH ends on the hub's clock —
9377
+ * a browser subtracting its own `Date.now()` from a server `startedAt` is a
9378
+ * rate made of two different clocks. */
9379
+ observedAt: number()
9380
+ });
9381
+ /**
9382
+ * What a SOURCE still holds for one storage class — the number that makes a
9383
+ * "drain remaining" action honest rather than hopeful.
9384
+ *
9385
+ * It comes from the archive (`SegmentHourLedger.census` for footage, the media
9386
+ * engine's own selection count for media), never from the resident index: a
9387
+ * drain sized off `RecordingIndex` is what reported `done` over 80.3 GB it had
9388
+ * never been told about (D295).
9389
+ *
9390
+ * `items`/`bytes` are `null` for "the archive could not be asked", which is
9391
+ * deliberately NOT zero: a drain is still offered for an unknown residue,
9392
+ * because refusing on an unanswerable read would hide exactly the case an
9393
+ * operator needs to act on.
9394
+ */
9395
+ var StorageMigrationResidueSchema = object({
9396
+ storageClass: StorageMigrationClassSchema,
9397
+ /** The location still holding the data. `'*'` for the media lane, whose rows
9398
+ * move from wherever they are rather than from one named source. */
9399
+ fromLocationId: string(),
9400
+ /** Where a drain would move it — the class's CURRENT default. */
9401
+ toLocationId: string(),
9402
+ /** Segments (footage lane) or rows (media lane) still on the source. */
9403
+ items: number().int().nonnegative().nullable(),
9404
+ /** Bytes on the source; `null` when the lane counts rows rather than bytes. */
9405
+ bytes: number().int().nonnegative().nullable()
9406
+ });
9407
+ /**
9408
+ * Run the DRAIN half and nothing else.
9409
+ *
9410
+ * A migration that reached `done` has already repointed, so `start` correctly
9411
+ * refuses its destination ("already the default") — there is nothing left to
9412
+ * repoint. But the drain can fail, be cancelled, be interrupted by a restart,
9413
+ * or finish against a work list that was a tenth of the archive (D295), and
9414
+ * before this there was no supported way to run only that half: the only way
9415
+ * through was calling `recording.relocateFootage` by hand over admin tRPC.
9416
+ *
9417
+ * `drain` NEVER calls `setDefaultLocations`. That is what keeps `start`'s
9418
+ * refusal meaningful: the two verbs are disjoint, so nothing here can silently
9419
+ * re-repoint a class that is already migrated.
9420
+ */
9421
+ var StorageMigrationDrainInputSchema = object({
9422
+ /** The classes to drain. Each must appear in `storageMigration.residue`, so
9423
+ * a class whose source is already empty is refused rather than started. */
9424
+ classes: array(StorageMigrationClassSchema).min(1),
9425
+ throttleMbps: number().min(1).max(1e3).optional()
9426
+ });
9427
+ /** What a footage source still holds, asked of the durable hour ledger. */
9428
+ var RelocateResidueInputSchema = object({
9429
+ fromLocationId: string().min(1),
9430
+ /** Narrow to one logical class; omit for every profile on the location. */
9431
+ footageClass: RelocateFootageClassSchema.optional()
9432
+ });
9433
+ /** `null` = the archive could not answer (no ledger on this node, or the
9434
+ * aggregate failed). Never conflated with an empty source. */
9435
+ var RelocateResidueSchema = object({
9436
+ segments: number().int().nonnegative(),
9437
+ bytes: number().int().nonnegative()
9438
+ }).nullable();
9439
+ /** How many rows a media pass would still act on against a given target — the
9440
+ * media lane's denominator AND its residue, from ONE derivation so the two can
9441
+ * never disagree. `null` = the count could not be taken. */
9442
+ var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9443
+ var RelocatableMediaCountInputSchema = object({
9444
+ toLocationId: string().min(1),
9445
+ /** Omitted = `move`. */
9446
+ mode: MediaRelocateModeSchema.optional()
9447
+ });
9448
+ /**
9321
9449
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9322
9450
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9323
9451
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9421,6 +9549,32 @@ var StorageLocationRefSchema = union([StorageLocationTypeSchema, string().regex(
9421
9549
  * two addons declaring the same `id` must agree on `cardinality` (validated
9422
9550
  * at kernel aggregation time, not here).
9423
9551
  */
9552
+ /**
9553
+ * `StorageAccess` — how the service that DECLARED a storage-location kind
9554
+ * actually reaches the bytes. It is the constraint that decides which
9555
+ * `storage-provider`s may back a location of that kind.
9556
+ *
9557
+ * - `'local-path'` — the service asks `storage.resolve` for a path string and
9558
+ * then does its own `node:fs` I/O on it (the recorder's segment writer, the
9559
+ * post-analysis media roots). Only a provider that serves a genuine local
9560
+ * filesystem (`getProviderInfo().nodeLocal === true`) can satisfy that: a
9561
+ * remote provider's `resolve` returns a path on the REMOTE host, and
9562
+ * `fs.readdir` of it on this node either fails or — far worse — succeeds
9563
+ * against a same-named local directory that is something else entirely.
9564
+ *
9565
+ * - `'cap-mediated'` — every byte travels through the `storage` cap
9566
+ * (`read`/`write`, or `beginUpload`/`writeChunk`/`finalizeUpload`). The
9567
+ * service never sees a path, so any provider can back it. `backups` is the
9568
+ * one kind that qualifies today.
9569
+ *
9570
+ * Before this existed, `recordings` was unreachable by SFTP/S3/WebDAV only as
9571
+ * an EMERGENT property of how the recorder happened to be written. Nothing
9572
+ * refused the configuration; the first write simply went somewhere wrong, and
9573
+ * a recording write that goes wrong surfaces as a silent black window rather
9574
+ * than an error (the read path does not `stat`). This turns that accident into
9575
+ * a declared, enforced, testable refusal.
9576
+ */
9577
+ var StorageAccessSchema = _enum(["local-path", "cap-mediated"]);
9424
9578
  var StorageLocationDeclarationSchema = object({
9425
9579
  /**
9426
9580
  * Global location identifier, e.g. `recordings` or `recordingsLow`.
@@ -9440,6 +9594,19 @@ var StorageLocationDeclarationSchema = object({
9440
9594
  */
9441
9595
  cardinality: _enum(["single", "multi"]),
9442
9596
  /**
9597
+ * HOW the declaring service reaches the bytes — and therefore WHICH
9598
+ * providers may back a location of this kind. See {@link StorageAccessSchema}
9599
+ * and {@link STORAGE_ACCESS_FALLBACK}.
9600
+ *
9601
+ * Absent means `'local-path'`. That default is FAIL-CLOSED on purpose: it
9602
+ * can only over-restrict (refuse a remote provider for a kind that might
9603
+ * have coped) and never under-restrict. Declaring `'cap-mediated'` is the
9604
+ * permissive direction and is therefore never inferred — a repo guard
9605
+ * (`scripts/check-storage-access-declarations.ts`) refuses to let it be
9606
+ * reached by omission.
9607
+ */
9608
+ access: StorageAccessSchema.optional(),
9609
+ /**
9443
9610
  * When set, the default instance for this location inherits its resolved
9444
9611
  * root from the named location's default instance. Useful for derivative
9445
9612
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
@@ -19324,8 +19491,10 @@ var TrackSchema = object({
19324
19491
  lastSeen: number(),
19325
19492
  /** Frame-rate position history (subject to maxPositionHistory cap). */
19326
19493
  positions: array(TrackPositionSchema).readonly(),
19327
- /** Periodic snapshots at snapshotIntervalMs cadence (subject to
19328
- * saveThumbnails policy). */
19494
+ /** Periodic snapshots at snapshotIntervalMs cadence DEBUG media, produced
19495
+ * only while `MediaSettings.debugMediaEnabled` is on for the camera (D299;
19496
+ * the retired `saveThumbnails` used to gate this and the rolling
19497
+ * `lastFrame` together). Empty is the healthy default, not a capture gap. */
19329
19498
  snapshots: array(TrackSnapshotSchema).readonly(),
19330
19499
  /** Deduplicated zones the track has entered at least once. Zone IDS. */
19331
19500
  zonesVisited: array(string()).readonly(),
@@ -20185,7 +20354,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20185
20354
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
20186
20355
  kind: "mutation",
20187
20356
  auth: "admin"
20188
- }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
20357
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
20358
+ kind: "query",
20359
+ auth: "admin"
20360
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
20189
20361
  kind: "query",
20190
20362
  auth: "admin"
20191
20363
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -22144,6 +22316,9 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
22144
22316
  }), method(object({ jobId: string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22145
22317
  kind: "mutation",
22146
22318
  auth: "admin"
22319
+ }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
22320
+ kind: "mutation",
22321
+ auth: "admin"
22147
22322
  });
22148
22323
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
22149
22324
  providerId: string().min(1),
@@ -22542,12 +22717,38 @@ response: record(string(), unknown()) }), object({
22542
22717
  *
22543
22718
  * ## Why this is a capability and not a helper
22544
22719
  *
22545
- * Six stores in `addon-post-analysis` already hold vectors object CLIP, face,
22546
- * plate, vehicle, identity, and the event store's derivativesand every one of
22547
- * them keeps its vectors in a `JSON` settings-store column and ranks them by
22548
- * brute-force cosine in JS. Measured on the live hub that costs ~11.7 KB per row
22549
- * (512 floats as TEXT, `JSON.parse`d on every search) and made semantic search
22550
- * load 5,000 rows before ranking anything.
22720
+ * This capability was introduced with the claim that SIX stores in
22721
+ * `addon-post-analysis` held vectors in a `JSON` settings-store columnobject
22722
+ * CLIP, face, plate, vehicle, identity, and the event store's derivatives. That
22723
+ * claim was never true, and leaving it here made five stores look like pending
22724
+ * work when three of them have no vector at all. Counted column by column on
22725
+ * 2026-08-30, exactly THREE ever held one:
22726
+ *
22727
+ * - `object-clip` — 512-dim CLIP image embedding, migrated 2026-08-06.
22728
+ * - `faces.embedding` — 512-dim ArcFace face embedding, migrated 2026-08-30.
22729
+ * - `identity-samples.embedding` — the same ArcFace vector for an ENROLLED
22730
+ * face, migrated 2026-08-30 into its OWN index (see below).
22731
+ *
22732
+ * `plates` and `vehicle-samples` store a plate STRING and a score; `vehicles`
22733
+ * and `identities` store a name; the event store stores no derivative vector.
22734
+ * They are not migration candidates and never were.
22735
+ *
22736
+ * Measured on the live hub the JSON encoding cost ~11.7 KB per row (512 floats
22737
+ * as TEXT, `JSON.parse`d on every search) and made semantic search load 5,000
22738
+ * rows before ranking anything.
22739
+ *
22740
+ * ## One index per COMPARISON, never per encoder
22741
+ *
22742
+ * `faces` and `identity-samples` hold the same 512 ArcFace dims from the same
22743
+ * model, and they still get two indexes. An index is a set of things that are
22744
+ * ranked against each other and that live and die together, and these two are
22745
+ * neither: a `faces` row is TRACK-OWNED and cascades away with its track under
22746
+ * a per-camera capacity cap, an `identity-samples` row is retention-EXEMPT
22747
+ * forever and is the gallery every recognition ranks against. One index would
22748
+ * mean every gallery load and every reconcile carried a filter whose failure
22749
+ * mode is either ranking a candidate against itself or reclaiming an enrolled
22750
+ * person's only sample. The dimension they share is not a reason to share an
22751
+ * index; the question they answer is, and it differs.
22551
22752
  *
22552
22753
  * The fix is not a faster loop, it is a different backend — and the backend
22553
22754
  * should be replaceable without touching six callers. So: a singleton
@@ -22652,7 +22853,20 @@ var VectorQueryResultSchema = object({
22652
22853
  */
22653
22854
  scanned: number(),
22654
22855
  /** True when the backend could not consider every row that passed the filter. */
22655
- truncated: boolean()
22856
+ truncated: boolean(),
22857
+ /**
22858
+ * The `topK` the backend actually ran with.
22859
+ *
22860
+ * Every backend has a ceiling — sqlite-vec's is 4,096 — and a caller asking
22861
+ * past it used to learn nothing but a boolean, from a WARN in the provider's
22862
+ * own log rather than in its answer. That is how an audit asking for 20,000
22863
+ * consumed 4,096 and reported `examined: 4096` as if it had walked the index,
22864
+ * for weeks. `truncated` says THAT the answer was short; this says BY HOW
22865
+ * MUCH, in the return value, where the caller cannot fail to see it.
22866
+ *
22867
+ * Equals the requested `topK` whenever nothing was lowered.
22868
+ */
22869
+ effectiveTopK: number().int().positive()
22656
22870
  });
22657
22871
  var VectorDeleteInputSchema = object({
22658
22872
  index: string(),
@@ -22681,6 +22895,68 @@ var VectorGetResultSchema = object({ items: array(object({
22681
22895
  id: string(),
22682
22896
  metadata: VectorMetadataSchema
22683
22897
  })) });
22898
+ /**
22899
+ * Ids to read back WITH their vectors.
22900
+ *
22901
+ * The sibling of {@link VectorGetResultSchema}, and deliberately a separate
22902
+ * method rather than a flag on it: `getByIds` promises no vectors and its one
22903
+ * caller depends on that promise. This one promises the opposite.
22904
+ *
22905
+ * It exists because a store cannot put its vectors here otherwise. An ArcFace
22906
+ * gallery is ranked IN PROCESS, per detection, against every enrolled sample —
22907
+ * a per-face cross-process KNN would be a network round trip inside the
22908
+ * recognition loop. So the gallery is loaded once and held in RAM, and loading
22909
+ * it requires the index to hand the floats back. Without this method the only
22910
+ * way to keep a readable vector is a JSON column, which is the thing this
22911
+ * capability exists to delete.
22912
+ *
22913
+ * BOUNDED BY THE CALLER: ids are named, never "everything". Enumerating an
22914
+ * index is {@link VectorScanInputSchema}'s job, and it returns no vectors.
22915
+ */
22916
+ var VectorFetchInputSchema = object({
22917
+ index: string(),
22918
+ ids: array(string())
22919
+ });
22920
+ var VectorFetchResultSchema = object({ items: array(object({
22921
+ id: string(),
22922
+ /** base64 Float32LE — the same wire form `upsert` accepts. */
22923
+ vector: string(),
22924
+ metadata: VectorMetadataSchema
22925
+ })) });
22926
+ /**
22927
+ * ENUMERATE an index: one page of rows in a stable order, no ranking.
22928
+ *
22929
+ * A reconcile does not want the nearest rows, it wants ALL of them, and asking
22930
+ * a KNN for "all" is the wrong question twice over. It hits the backend's `k`
22931
+ * ceiling — 4,096 on sqlite-vec against a 22,128-row index — and it needs a
22932
+ * probe vector it does not have, so the audit passed a ZERO vector whose cosine
22933
+ * distance to every row is degenerate. `examined: 4096` then read as "we
22934
+ * looked" for as long as anyone cared to read it.
22935
+ *
22936
+ * This is the primitive that question actually needs: a bounded page, ordered
22937
+ * by the backend's own row order, costing no distance computation at all.
22938
+ * Vectors are NOT returned — an enumeration that shipped 2 KB per row would be
22939
+ * the full-table read this capability was built to stop.
22940
+ */
22941
+ var VectorScanInputSchema = object({
22942
+ index: string(),
22943
+ /** Opaque resume point. `0` starts at the top; pass back `nextCursor`. */
22944
+ cursor: number().int().nonnegative().default(0),
22945
+ limit: number().int().positive()
22946
+ });
22947
+ var VectorScanResultSchema = object({
22948
+ items: array(object({
22949
+ id: string(),
22950
+ metadata: VectorMetadataSchema
22951
+ })),
22952
+ /**
22953
+ * Where the next page starts, or `null` when the walk reached the end.
22954
+ *
22955
+ * `null` is the ONLY end-of-index signal. A caller must not infer the end
22956
+ * from a short page: a backend is free to return fewer rows than asked.
22957
+ */
22958
+ nextCursor: number().int().nonnegative().nullable()
22959
+ });
22684
22960
  var VectorStatsInputSchema = object({ index: string() });
22685
22961
  var VectorStatsResultSchema = object({
22686
22962
  /** Provider id, so an operator can tell brute force from an ANN index. */
@@ -22699,7 +22975,7 @@ method(VectorDeclareIndexInputSchema, _void(), {
22699
22975
  }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
22700
22976
  kind: "mutation",
22701
22977
  auth: "admin"
22702
- }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
22978
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorFetchInputSchema, VectorFetchResultSchema, { auth: "admin" }), method(VectorScanInputSchema, VectorScanResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
22703
22979
  kind: "mutation",
22704
22980
  auth: "admin"
22705
22981
  }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
@@ -29326,6 +29602,9 @@ method(object({
29326
29602
  }), method(object({}), array(RelocateJobSchema).readonly(), {
29327
29603
  kind: "query",
29328
29604
  auth: "admin"
29605
+ }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29606
+ kind: "query",
29607
+ auth: "admin"
29329
29608
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29330
29609
  kind: "mutation",
29331
29610
  auth: "admin"
@@ -35960,6 +36239,12 @@ Object.freeze({
35960
36239
  addonId: null,
35961
36240
  access: "create"
35962
36241
  },
36242
+ "pipelineAnalytics.countRelocatableMedia": {
36243
+ capName: "pipeline-analytics",
36244
+ capScope: "device",
36245
+ addonId: null,
36246
+ access: "view"
36247
+ },
35963
36248
  "pipelineAnalytics.countUnstampedEventMedia": {
35964
36249
  capName: "pipeline-analytics",
35965
36250
  capScope: "device",
@@ -37124,6 +37409,12 @@ Object.freeze({
37124
37409
  addonId: null,
37125
37410
  access: "view"
37126
37411
  },
37412
+ "recording.getRelocateResidue": {
37413
+ capName: "recording",
37414
+ capScope: "system",
37415
+ addonId: null,
37416
+ access: "view"
37417
+ },
37127
37418
  "recording.getStorageMigrationMoveStatus": {
37128
37419
  capName: "recording",
37129
37420
  capScope: "system",
@@ -37670,12 +37961,30 @@ Object.freeze({
37670
37961
  addonId: null,
37671
37962
  access: "create"
37672
37963
  },
37964
+ "storageMigration.drain": {
37965
+ capName: "storage-migration",
37966
+ capScope: "system",
37967
+ addonId: null,
37968
+ access: "create"
37969
+ },
37970
+ "storageMigration.movers": {
37971
+ capName: "storage-migration",
37972
+ capScope: "system",
37973
+ addonId: null,
37974
+ access: "view"
37975
+ },
37673
37976
  "storageMigration.plan": {
37674
37977
  capName: "storage-migration",
37675
37978
  capScope: "system",
37676
37979
  addonId: null,
37677
37980
  access: "view"
37678
37981
  },
37982
+ "storageMigration.residue": {
37983
+ capName: "storage-migration",
37984
+ capScope: "system",
37985
+ addonId: null,
37986
+ access: "view"
37987
+ },
37679
37988
  "storageMigration.start": {
37680
37989
  capName: "storage-migration",
37681
37990
  capScope: "system",
@@ -38510,6 +38819,12 @@ Object.freeze({
38510
38819
  addonId: null,
38511
38820
  access: "delete"
38512
38821
  },
38822
+ "vectorStore.fetchByIds": {
38823
+ capName: "vector-store",
38824
+ capScope: "system",
38825
+ addonId: null,
38826
+ access: "view"
38827
+ },
38513
38828
  "vectorStore.getByIds": {
38514
38829
  capName: "vector-store",
38515
38830
  capScope: "system",
@@ -38522,6 +38837,12 @@ Object.freeze({
38522
38837
  addonId: null,
38523
38838
  access: "view"
38524
38839
  },
38840
+ "vectorStore.scan": {
38841
+ capName: "vector-store",
38842
+ capScope: "system",
38843
+ addonId: null,
38844
+ access: "view"
38845
+ },
38525
38846
  "vectorStore.stats": {
38526
38847
  capName: "vector-store",
38527
38848
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -9265,13 +9265,49 @@ var StorageMigrationParticipantSchema = _enum([
9265
9265
  "recorder",
9266
9266
  "analytics"
9267
9267
  ]);
9268
+ /**
9269
+ * The mover's own numbers, folded onto the coordinator's durable move record.
9270
+ *
9271
+ * The long half of a non-blocking migration is `draining`, and it is measured
9272
+ * in hours: 136 885 files at ~4 MB/s is about five of them. Before this shape
9273
+ * existed the only place those numbers appeared was a Loki line, so an operator
9274
+ * watching the Admin UI saw `phase: draining` and nothing else for a whole
9275
+ * afternoon.
9276
+ *
9277
+ * It is POLLED, never pushed. Events are telemetry and may be dropped
9278
+ * (D8/D11), and a dropped progress event is indistinguishable from a stalled
9279
+ * mover — which is the exact failure this is meant to end. The coordinator's
9280
+ * `waitForMoves` already fetches the whole {@link RelocateJob} on every tick to
9281
+ * read `state`; folding the counters costs no extra read and makes the durable
9282
+ * record say afterwards how far a move actually got.
9283
+ *
9284
+ * `filesTotal` is `null` for "no honest denominator" and is never zero-filled:
9285
+ * a windowed footage job (`sinceMs`) and a node with no ledger both genuinely
9286
+ * cannot say M, and a 0 there would render as "100 % done".
9287
+ */
9288
+ var StorageMigrationMoveProgressSchema = object({
9289
+ filesMoved: number().int().nonnegative(),
9290
+ /** The archive census — the **M** of "N of M" (D295). `null` = unknowable. */
9291
+ filesTotal: number().int().nonnegative().nullable(),
9292
+ bytesMoved: number().int().nonnegative(),
9293
+ /** The MOVER's start, not the migration's: a drain restarted after an addon
9294
+ * crash gets a new mover, and a rate computed from the migration's start
9295
+ * would silently average in the time nothing was running. */
9296
+ startedAt: number(),
9297
+ /** When the coordinator last read these numbers. Paired with `startedAt` it
9298
+ * is the only honest rate: both clocks are the hub's, so a UI never has to
9299
+ * subtract its own. */
9300
+ observedAt: number()
9301
+ });
9268
9302
  var StorageMigrationMoveSchema = object({
9269
9303
  storageClass: StorageMigrationClassSchema,
9270
9304
  fromLocationId: string(),
9271
9305
  toLocationId: string(),
9272
9306
  moverJobId: string().nullable(),
9273
9307
  state: RelocateJobStateSchema.nullable(),
9274
- error: string().nullable()
9308
+ error: string().nullable(),
9309
+ /** Last observed mover counters; `null` until the mover has been polled once. */
9310
+ progress: StorageMigrationMoveProgressSchema.nullable()
9275
9311
  });
9276
9312
  var StorageMigrationJobSchema = object({
9277
9313
  jobId: string(),
@@ -9317,6 +9353,98 @@ var StorageMigrationPlanSchema = object({
9317
9353
  findings: array(StorageMigrationFindingSchema)
9318
9354
  });
9319
9355
  /**
9356
+ * A mover as it exists RIGHT NOW, whether or not a migration job owns it.
9357
+ *
9358
+ * The coordinator's job record is the state of record for a migration, and its
9359
+ * moves carry {@link StorageMigrationMoveProgress}. But the movers are usable
9360
+ * standalone — `recording.relocateFootage` and `pipelineAnalytics.relocateMedia`
9361
+ * are both operator-callable, and on 2026-08-29 a five-hour drain was armed that
9362
+ * way because no supported UI path existed. A mover armed like that has no job
9363
+ * to fold progress into, so it has to be readable on its own or it is invisible.
9364
+ *
9365
+ * `migrationJobId` is what tells the two apart: `null` means nothing here
9366
+ * orchestrated it.
9367
+ */
9368
+ var StorageMigrationMoverSchema = object({
9369
+ lane: _enum(["footage", "media"]),
9370
+ job: RelocateJobSchema,
9371
+ /** The coordinator job that armed this mover, or `null` for a mover armed
9372
+ * directly against the owning addon. */
9373
+ migrationJobId: string().nullable(),
9374
+ /** When the hub read these counters. Stamped here so a rate is `bytesMoved`
9375
+ * over (`observedAt` − `job.startedAt`) with BOTH ends on the hub's clock —
9376
+ * a browser subtracting its own `Date.now()` from a server `startedAt` is a
9377
+ * rate made of two different clocks. */
9378
+ observedAt: number()
9379
+ });
9380
+ /**
9381
+ * What a SOURCE still holds for one storage class — the number that makes a
9382
+ * "drain remaining" action honest rather than hopeful.
9383
+ *
9384
+ * It comes from the archive (`SegmentHourLedger.census` for footage, the media
9385
+ * engine's own selection count for media), never from the resident index: a
9386
+ * drain sized off `RecordingIndex` is what reported `done` over 80.3 GB it had
9387
+ * never been told about (D295).
9388
+ *
9389
+ * `items`/`bytes` are `null` for "the archive could not be asked", which is
9390
+ * deliberately NOT zero: a drain is still offered for an unknown residue,
9391
+ * because refusing on an unanswerable read would hide exactly the case an
9392
+ * operator needs to act on.
9393
+ */
9394
+ var StorageMigrationResidueSchema = object({
9395
+ storageClass: StorageMigrationClassSchema,
9396
+ /** The location still holding the data. `'*'` for the media lane, whose rows
9397
+ * move from wherever they are rather than from one named source. */
9398
+ fromLocationId: string(),
9399
+ /** Where a drain would move it — the class's CURRENT default. */
9400
+ toLocationId: string(),
9401
+ /** Segments (footage lane) or rows (media lane) still on the source. */
9402
+ items: number().int().nonnegative().nullable(),
9403
+ /** Bytes on the source; `null` when the lane counts rows rather than bytes. */
9404
+ bytes: number().int().nonnegative().nullable()
9405
+ });
9406
+ /**
9407
+ * Run the DRAIN half and nothing else.
9408
+ *
9409
+ * A migration that reached `done` has already repointed, so `start` correctly
9410
+ * refuses its destination ("already the default") — there is nothing left to
9411
+ * repoint. But the drain can fail, be cancelled, be interrupted by a restart,
9412
+ * or finish against a work list that was a tenth of the archive (D295), and
9413
+ * before this there was no supported way to run only that half: the only way
9414
+ * through was calling `recording.relocateFootage` by hand over admin tRPC.
9415
+ *
9416
+ * `drain` NEVER calls `setDefaultLocations`. That is what keeps `start`'s
9417
+ * refusal meaningful: the two verbs are disjoint, so nothing here can silently
9418
+ * re-repoint a class that is already migrated.
9419
+ */
9420
+ var StorageMigrationDrainInputSchema = object({
9421
+ /** The classes to drain. Each must appear in `storageMigration.residue`, so
9422
+ * a class whose source is already empty is refused rather than started. */
9423
+ classes: array(StorageMigrationClassSchema).min(1),
9424
+ throttleMbps: number().min(1).max(1e3).optional()
9425
+ });
9426
+ /** What a footage source still holds, asked of the durable hour ledger. */
9427
+ var RelocateResidueInputSchema = object({
9428
+ fromLocationId: string().min(1),
9429
+ /** Narrow to one logical class; omit for every profile on the location. */
9430
+ footageClass: RelocateFootageClassSchema.optional()
9431
+ });
9432
+ /** `null` = the archive could not answer (no ledger on this node, or the
9433
+ * aggregate failed). Never conflated with an empty source. */
9434
+ var RelocateResidueSchema = object({
9435
+ segments: number().int().nonnegative(),
9436
+ bytes: number().int().nonnegative()
9437
+ }).nullable();
9438
+ /** How many rows a media pass would still act on against a given target — the
9439
+ * media lane's denominator AND its residue, from ONE derivation so the two can
9440
+ * never disagree. `null` = the count could not be taken. */
9441
+ var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
9442
+ var RelocatableMediaCountInputSchema = object({
9443
+ toLocationId: string().min(1),
9444
+ /** Omitted = `move`. */
9445
+ mode: MediaRelocateModeSchema.optional()
9446
+ });
9447
+ /**
9320
9448
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
9321
9449
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
9322
9450
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -9420,6 +9548,32 @@ var StorageLocationRefSchema = union([StorageLocationTypeSchema, string().regex(
9420
9548
  * two addons declaring the same `id` must agree on `cardinality` (validated
9421
9549
  * at kernel aggregation time, not here).
9422
9550
  */
9551
+ /**
9552
+ * `StorageAccess` — how the service that DECLARED a storage-location kind
9553
+ * actually reaches the bytes. It is the constraint that decides which
9554
+ * `storage-provider`s may back a location of that kind.
9555
+ *
9556
+ * - `'local-path'` — the service asks `storage.resolve` for a path string and
9557
+ * then does its own `node:fs` I/O on it (the recorder's segment writer, the
9558
+ * post-analysis media roots). Only a provider that serves a genuine local
9559
+ * filesystem (`getProviderInfo().nodeLocal === true`) can satisfy that: a
9560
+ * remote provider's `resolve` returns a path on the REMOTE host, and
9561
+ * `fs.readdir` of it on this node either fails or — far worse — succeeds
9562
+ * against a same-named local directory that is something else entirely.
9563
+ *
9564
+ * - `'cap-mediated'` — every byte travels through the `storage` cap
9565
+ * (`read`/`write`, or `beginUpload`/`writeChunk`/`finalizeUpload`). The
9566
+ * service never sees a path, so any provider can back it. `backups` is the
9567
+ * one kind that qualifies today.
9568
+ *
9569
+ * Before this existed, `recordings` was unreachable by SFTP/S3/WebDAV only as
9570
+ * an EMERGENT property of how the recorder happened to be written. Nothing
9571
+ * refused the configuration; the first write simply went somewhere wrong, and
9572
+ * a recording write that goes wrong surfaces as a silent black window rather
9573
+ * than an error (the read path does not `stat`). This turns that accident into
9574
+ * a declared, enforced, testable refusal.
9575
+ */
9576
+ var StorageAccessSchema = _enum(["local-path", "cap-mediated"]);
9423
9577
  var StorageLocationDeclarationSchema = object({
9424
9578
  /**
9425
9579
  * Global location identifier, e.g. `recordings` or `recordingsLow`.
@@ -9439,6 +9593,19 @@ var StorageLocationDeclarationSchema = object({
9439
9593
  */
9440
9594
  cardinality: _enum(["single", "multi"]),
9441
9595
  /**
9596
+ * HOW the declaring service reaches the bytes — and therefore WHICH
9597
+ * providers may back a location of this kind. See {@link StorageAccessSchema}
9598
+ * and {@link STORAGE_ACCESS_FALLBACK}.
9599
+ *
9600
+ * Absent means `'local-path'`. That default is FAIL-CLOSED on purpose: it
9601
+ * can only over-restrict (refuse a remote provider for a kind that might
9602
+ * have coped) and never under-restrict. Declaring `'cap-mediated'` is the
9603
+ * permissive direction and is therefore never inferred — a repo guard
9604
+ * (`scripts/check-storage-access-declarations.ts`) refuses to let it be
9605
+ * reached by omission.
9606
+ */
9607
+ access: StorageAccessSchema.optional(),
9608
+ /**
9442
9609
  * When set, the default instance for this location inherits its resolved
9443
9610
  * root from the named location's default instance. Useful for derivative
9444
9611
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
@@ -19323,8 +19490,10 @@ var TrackSchema = object({
19323
19490
  lastSeen: number(),
19324
19491
  /** Frame-rate position history (subject to maxPositionHistory cap). */
19325
19492
  positions: array(TrackPositionSchema).readonly(),
19326
- /** Periodic snapshots at snapshotIntervalMs cadence (subject to
19327
- * saveThumbnails policy). */
19493
+ /** Periodic snapshots at snapshotIntervalMs cadence DEBUG media, produced
19494
+ * only while `MediaSettings.debugMediaEnabled` is on for the camera (D299;
19495
+ * the retired `saveThumbnails` used to gate this and the rolling
19496
+ * `lastFrame` together). Empty is the healthy default, not a capture gap. */
19328
19497
  snapshots: array(TrackSnapshotSchema).readonly(),
19329
19498
  /** Deduplicated zones the track has entered at least once. Zone IDS. */
19330
19499
  zonesVisited: array(string()).readonly(),
@@ -20184,7 +20353,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20184
20353
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
20185
20354
  kind: "mutation",
20186
20355
  auth: "admin"
20187
- }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
20356
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
20357
+ kind: "query",
20358
+ auth: "admin"
20359
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
20188
20360
  kind: "query",
20189
20361
  auth: "admin"
20190
20362
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -22143,6 +22315,9 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
22143
22315
  }), method(object({ jobId: string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
22144
22316
  kind: "mutation",
22145
22317
  auth: "admin"
22318
+ }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
22319
+ kind: "mutation",
22320
+ auth: "admin"
22146
22321
  });
22147
22322
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
22148
22323
  providerId: string().min(1),
@@ -22541,12 +22716,38 @@ response: record(string(), unknown()) }), object({
22541
22716
  *
22542
22717
  * ## Why this is a capability and not a helper
22543
22718
  *
22544
- * Six stores in `addon-post-analysis` already hold vectors object CLIP, face,
22545
- * plate, vehicle, identity, and the event store's derivativesand every one of
22546
- * them keeps its vectors in a `JSON` settings-store column and ranks them by
22547
- * brute-force cosine in JS. Measured on the live hub that costs ~11.7 KB per row
22548
- * (512 floats as TEXT, `JSON.parse`d on every search) and made semantic search
22549
- * load 5,000 rows before ranking anything.
22719
+ * This capability was introduced with the claim that SIX stores in
22720
+ * `addon-post-analysis` held vectors in a `JSON` settings-store columnobject
22721
+ * CLIP, face, plate, vehicle, identity, and the event store's derivatives. That
22722
+ * claim was never true, and leaving it here made five stores look like pending
22723
+ * work when three of them have no vector at all. Counted column by column on
22724
+ * 2026-08-30, exactly THREE ever held one:
22725
+ *
22726
+ * - `object-clip` — 512-dim CLIP image embedding, migrated 2026-08-06.
22727
+ * - `faces.embedding` — 512-dim ArcFace face embedding, migrated 2026-08-30.
22728
+ * - `identity-samples.embedding` — the same ArcFace vector for an ENROLLED
22729
+ * face, migrated 2026-08-30 into its OWN index (see below).
22730
+ *
22731
+ * `plates` and `vehicle-samples` store a plate STRING and a score; `vehicles`
22732
+ * and `identities` store a name; the event store stores no derivative vector.
22733
+ * They are not migration candidates and never were.
22734
+ *
22735
+ * Measured on the live hub the JSON encoding cost ~11.7 KB per row (512 floats
22736
+ * as TEXT, `JSON.parse`d on every search) and made semantic search load 5,000
22737
+ * rows before ranking anything.
22738
+ *
22739
+ * ## One index per COMPARISON, never per encoder
22740
+ *
22741
+ * `faces` and `identity-samples` hold the same 512 ArcFace dims from the same
22742
+ * model, and they still get two indexes. An index is a set of things that are
22743
+ * ranked against each other and that live and die together, and these two are
22744
+ * neither: a `faces` row is TRACK-OWNED and cascades away with its track under
22745
+ * a per-camera capacity cap, an `identity-samples` row is retention-EXEMPT
22746
+ * forever and is the gallery every recognition ranks against. One index would
22747
+ * mean every gallery load and every reconcile carried a filter whose failure
22748
+ * mode is either ranking a candidate against itself or reclaiming an enrolled
22749
+ * person's only sample. The dimension they share is not a reason to share an
22750
+ * index; the question they answer is, and it differs.
22550
22751
  *
22551
22752
  * The fix is not a faster loop, it is a different backend — and the backend
22552
22753
  * should be replaceable without touching six callers. So: a singleton
@@ -22651,7 +22852,20 @@ var VectorQueryResultSchema = object({
22651
22852
  */
22652
22853
  scanned: number(),
22653
22854
  /** True when the backend could not consider every row that passed the filter. */
22654
- truncated: boolean()
22855
+ truncated: boolean(),
22856
+ /**
22857
+ * The `topK` the backend actually ran with.
22858
+ *
22859
+ * Every backend has a ceiling — sqlite-vec's is 4,096 — and a caller asking
22860
+ * past it used to learn nothing but a boolean, from a WARN in the provider's
22861
+ * own log rather than in its answer. That is how an audit asking for 20,000
22862
+ * consumed 4,096 and reported `examined: 4096` as if it had walked the index,
22863
+ * for weeks. `truncated` says THAT the answer was short; this says BY HOW
22864
+ * MUCH, in the return value, where the caller cannot fail to see it.
22865
+ *
22866
+ * Equals the requested `topK` whenever nothing was lowered.
22867
+ */
22868
+ effectiveTopK: number().int().positive()
22655
22869
  });
22656
22870
  var VectorDeleteInputSchema = object({
22657
22871
  index: string(),
@@ -22680,6 +22894,68 @@ var VectorGetResultSchema = object({ items: array(object({
22680
22894
  id: string(),
22681
22895
  metadata: VectorMetadataSchema
22682
22896
  })) });
22897
+ /**
22898
+ * Ids to read back WITH their vectors.
22899
+ *
22900
+ * The sibling of {@link VectorGetResultSchema}, and deliberately a separate
22901
+ * method rather than a flag on it: `getByIds` promises no vectors and its one
22902
+ * caller depends on that promise. This one promises the opposite.
22903
+ *
22904
+ * It exists because a store cannot put its vectors here otherwise. An ArcFace
22905
+ * gallery is ranked IN PROCESS, per detection, against every enrolled sample —
22906
+ * a per-face cross-process KNN would be a network round trip inside the
22907
+ * recognition loop. So the gallery is loaded once and held in RAM, and loading
22908
+ * it requires the index to hand the floats back. Without this method the only
22909
+ * way to keep a readable vector is a JSON column, which is the thing this
22910
+ * capability exists to delete.
22911
+ *
22912
+ * BOUNDED BY THE CALLER: ids are named, never "everything". Enumerating an
22913
+ * index is {@link VectorScanInputSchema}'s job, and it returns no vectors.
22914
+ */
22915
+ var VectorFetchInputSchema = object({
22916
+ index: string(),
22917
+ ids: array(string())
22918
+ });
22919
+ var VectorFetchResultSchema = object({ items: array(object({
22920
+ id: string(),
22921
+ /** base64 Float32LE — the same wire form `upsert` accepts. */
22922
+ vector: string(),
22923
+ metadata: VectorMetadataSchema
22924
+ })) });
22925
+ /**
22926
+ * ENUMERATE an index: one page of rows in a stable order, no ranking.
22927
+ *
22928
+ * A reconcile does not want the nearest rows, it wants ALL of them, and asking
22929
+ * a KNN for "all" is the wrong question twice over. It hits the backend's `k`
22930
+ * ceiling — 4,096 on sqlite-vec against a 22,128-row index — and it needs a
22931
+ * probe vector it does not have, so the audit passed a ZERO vector whose cosine
22932
+ * distance to every row is degenerate. `examined: 4096` then read as "we
22933
+ * looked" for as long as anyone cared to read it.
22934
+ *
22935
+ * This is the primitive that question actually needs: a bounded page, ordered
22936
+ * by the backend's own row order, costing no distance computation at all.
22937
+ * Vectors are NOT returned — an enumeration that shipped 2 KB per row would be
22938
+ * the full-table read this capability was built to stop.
22939
+ */
22940
+ var VectorScanInputSchema = object({
22941
+ index: string(),
22942
+ /** Opaque resume point. `0` starts at the top; pass back `nextCursor`. */
22943
+ cursor: number().int().nonnegative().default(0),
22944
+ limit: number().int().positive()
22945
+ });
22946
+ var VectorScanResultSchema = object({
22947
+ items: array(object({
22948
+ id: string(),
22949
+ metadata: VectorMetadataSchema
22950
+ })),
22951
+ /**
22952
+ * Where the next page starts, or `null` when the walk reached the end.
22953
+ *
22954
+ * `null` is the ONLY end-of-index signal. A caller must not infer the end
22955
+ * from a short page: a backend is free to return fewer rows than asked.
22956
+ */
22957
+ nextCursor: number().int().nonnegative().nullable()
22958
+ });
22683
22959
  var VectorStatsInputSchema = object({ index: string() });
22684
22960
  var VectorStatsResultSchema = object({
22685
22961
  /** Provider id, so an operator can tell brute force from an ANN index. */
@@ -22698,7 +22974,7 @@ method(VectorDeclareIndexInputSchema, _void(), {
22698
22974
  }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
22699
22975
  kind: "mutation",
22700
22976
  auth: "admin"
22701
- }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
22977
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorFetchInputSchema, VectorFetchResultSchema, { auth: "admin" }), method(VectorScanInputSchema, VectorScanResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
22702
22978
  kind: "mutation",
22703
22979
  auth: "admin"
22704
22980
  }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
@@ -29325,6 +29601,9 @@ method(object({
29325
29601
  }), method(object({}), array(RelocateJobSchema).readonly(), {
29326
29602
  kind: "query",
29327
29603
  auth: "admin"
29604
+ }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
29605
+ kind: "query",
29606
+ auth: "admin"
29328
29607
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
29329
29608
  kind: "mutation",
29330
29609
  auth: "admin"
@@ -35959,6 +36238,12 @@ Object.freeze({
35959
36238
  addonId: null,
35960
36239
  access: "create"
35961
36240
  },
36241
+ "pipelineAnalytics.countRelocatableMedia": {
36242
+ capName: "pipeline-analytics",
36243
+ capScope: "device",
36244
+ addonId: null,
36245
+ access: "view"
36246
+ },
35962
36247
  "pipelineAnalytics.countUnstampedEventMedia": {
35963
36248
  capName: "pipeline-analytics",
35964
36249
  capScope: "device",
@@ -37123,6 +37408,12 @@ Object.freeze({
37123
37408
  addonId: null,
37124
37409
  access: "view"
37125
37410
  },
37411
+ "recording.getRelocateResidue": {
37412
+ capName: "recording",
37413
+ capScope: "system",
37414
+ addonId: null,
37415
+ access: "view"
37416
+ },
37126
37417
  "recording.getStorageMigrationMoveStatus": {
37127
37418
  capName: "recording",
37128
37419
  capScope: "system",
@@ -37669,12 +37960,30 @@ Object.freeze({
37669
37960
  addonId: null,
37670
37961
  access: "create"
37671
37962
  },
37963
+ "storageMigration.drain": {
37964
+ capName: "storage-migration",
37965
+ capScope: "system",
37966
+ addonId: null,
37967
+ access: "create"
37968
+ },
37969
+ "storageMigration.movers": {
37970
+ capName: "storage-migration",
37971
+ capScope: "system",
37972
+ addonId: null,
37973
+ access: "view"
37974
+ },
37672
37975
  "storageMigration.plan": {
37673
37976
  capName: "storage-migration",
37674
37977
  capScope: "system",
37675
37978
  addonId: null,
37676
37979
  access: "view"
37677
37980
  },
37981
+ "storageMigration.residue": {
37982
+ capName: "storage-migration",
37983
+ capScope: "system",
37984
+ addonId: null,
37985
+ access: "view"
37986
+ },
37678
37987
  "storageMigration.start": {
37679
37988
  capName: "storage-migration",
37680
37989
  capScope: "system",
@@ -38509,6 +38818,12 @@ Object.freeze({
38509
38818
  addonId: null,
38510
38819
  access: "delete"
38511
38820
  },
38821
+ "vectorStore.fetchByIds": {
38822
+ capName: "vector-store",
38823
+ capScope: "system",
38824
+ addonId: null,
38825
+ access: "view"
38826
+ },
38512
38827
  "vectorStore.getByIds": {
38513
38828
  capName: "vector-store",
38514
38829
  capScope: "system",
@@ -38521,6 +38836,12 @@ Object.freeze({
38521
38836
  addonId: null,
38522
38837
  access: "view"
38523
38838
  },
38839
+ "vectorStore.scan": {
38840
+ capName: "vector-store",
38841
+ capScope: "system",
38842
+ addonId: null,
38843
+ access: "view"
38844
+ },
38524
38845
  "vectorStore.stats": {
38525
38846
  capName: "vector-store",
38526
38847
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-petkit",
3
- "version": "0.2.43",
3
+ "version": "0.2.45",
4
4
  "description": "PetKit smart-feeder device-provider addon for CamStack — wraps the @apocaliss92/nodepetkit PetKit cloud client",
5
5
  "keywords": [
6
6
  "camstack",