@camstack/addon-decoder-nodeav 1.2.43 → 1.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/index.js +333 -12
  2. package/dist/index.mjs +333 -12
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8164,13 +8164,49 @@ var StorageMigrationParticipantSchema = _enum([
8164
8164
  "recorder",
8165
8165
  "analytics"
8166
8166
  ]);
8167
+ /**
8168
+ * The mover's own numbers, folded onto the coordinator's durable move record.
8169
+ *
8170
+ * The long half of a non-blocking migration is `draining`, and it is measured
8171
+ * in hours: 136 885 files at ~4 MB/s is about five of them. Before this shape
8172
+ * existed the only place those numbers appeared was a Loki line, so an operator
8173
+ * watching the Admin UI saw `phase: draining` and nothing else for a whole
8174
+ * afternoon.
8175
+ *
8176
+ * It is POLLED, never pushed. Events are telemetry and may be dropped
8177
+ * (D8/D11), and a dropped progress event is indistinguishable from a stalled
8178
+ * mover — which is the exact failure this is meant to end. The coordinator's
8179
+ * `waitForMoves` already fetches the whole {@link RelocateJob} on every tick to
8180
+ * read `state`; folding the counters costs no extra read and makes the durable
8181
+ * record say afterwards how far a move actually got.
8182
+ *
8183
+ * `filesTotal` is `null` for "no honest denominator" and is never zero-filled:
8184
+ * a windowed footage job (`sinceMs`) and a node with no ledger both genuinely
8185
+ * cannot say M, and a 0 there would render as "100 % done".
8186
+ */
8187
+ var StorageMigrationMoveProgressSchema = object({
8188
+ filesMoved: number().int().nonnegative(),
8189
+ /** The archive census — the **M** of "N of M" (D295). `null` = unknowable. */
8190
+ filesTotal: number().int().nonnegative().nullable(),
8191
+ bytesMoved: number().int().nonnegative(),
8192
+ /** The MOVER's start, not the migration's: a drain restarted after an addon
8193
+ * crash gets a new mover, and a rate computed from the migration's start
8194
+ * would silently average in the time nothing was running. */
8195
+ startedAt: number(),
8196
+ /** When the coordinator last read these numbers. Paired with `startedAt` it
8197
+ * is the only honest rate: both clocks are the hub's, so a UI never has to
8198
+ * subtract its own. */
8199
+ observedAt: number()
8200
+ });
8167
8201
  var StorageMigrationMoveSchema = object({
8168
8202
  storageClass: StorageMigrationClassSchema,
8169
8203
  fromLocationId: string(),
8170
8204
  toLocationId: string(),
8171
8205
  moverJobId: string().nullable(),
8172
8206
  state: RelocateJobStateSchema.nullable(),
8173
- error: string().nullable()
8207
+ error: string().nullable(),
8208
+ /** Last observed mover counters; `null` until the mover has been polled once. */
8209
+ progress: StorageMigrationMoveProgressSchema.nullable()
8174
8210
  });
8175
8211
  var StorageMigrationJobSchema = object({
8176
8212
  jobId: string(),
@@ -8216,6 +8252,98 @@ var StorageMigrationPlanSchema = object({
8216
8252
  findings: array(StorageMigrationFindingSchema)
8217
8253
  });
8218
8254
  /**
8255
+ * A mover as it exists RIGHT NOW, whether or not a migration job owns it.
8256
+ *
8257
+ * The coordinator's job record is the state of record for a migration, and its
8258
+ * moves carry {@link StorageMigrationMoveProgress}. But the movers are usable
8259
+ * standalone — `recording.relocateFootage` and `pipelineAnalytics.relocateMedia`
8260
+ * are both operator-callable, and on 2026-08-29 a five-hour drain was armed that
8261
+ * way because no supported UI path existed. A mover armed like that has no job
8262
+ * to fold progress into, so it has to be readable on its own or it is invisible.
8263
+ *
8264
+ * `migrationJobId` is what tells the two apart: `null` means nothing here
8265
+ * orchestrated it.
8266
+ */
8267
+ var StorageMigrationMoverSchema = object({
8268
+ lane: _enum(["footage", "media"]),
8269
+ job: RelocateJobSchema,
8270
+ /** The coordinator job that armed this mover, or `null` for a mover armed
8271
+ * directly against the owning addon. */
8272
+ migrationJobId: string().nullable(),
8273
+ /** When the hub read these counters. Stamped here so a rate is `bytesMoved`
8274
+ * over (`observedAt` − `job.startedAt`) with BOTH ends on the hub's clock —
8275
+ * a browser subtracting its own `Date.now()` from a server `startedAt` is a
8276
+ * rate made of two different clocks. */
8277
+ observedAt: number()
8278
+ });
8279
+ /**
8280
+ * What a SOURCE still holds for one storage class — the number that makes a
8281
+ * "drain remaining" action honest rather than hopeful.
8282
+ *
8283
+ * It comes from the archive (`SegmentHourLedger.census` for footage, the media
8284
+ * engine's own selection count for media), never from the resident index: a
8285
+ * drain sized off `RecordingIndex` is what reported `done` over 80.3 GB it had
8286
+ * never been told about (D295).
8287
+ *
8288
+ * `items`/`bytes` are `null` for "the archive could not be asked", which is
8289
+ * deliberately NOT zero: a drain is still offered for an unknown residue,
8290
+ * because refusing on an unanswerable read would hide exactly the case an
8291
+ * operator needs to act on.
8292
+ */
8293
+ var StorageMigrationResidueSchema = object({
8294
+ storageClass: StorageMigrationClassSchema,
8295
+ /** The location still holding the data. `'*'` for the media lane, whose rows
8296
+ * move from wherever they are rather than from one named source. */
8297
+ fromLocationId: string(),
8298
+ /** Where a drain would move it — the class's CURRENT default. */
8299
+ toLocationId: string(),
8300
+ /** Segments (footage lane) or rows (media lane) still on the source. */
8301
+ items: number().int().nonnegative().nullable(),
8302
+ /** Bytes on the source; `null` when the lane counts rows rather than bytes. */
8303
+ bytes: number().int().nonnegative().nullable()
8304
+ });
8305
+ /**
8306
+ * Run the DRAIN half and nothing else.
8307
+ *
8308
+ * A migration that reached `done` has already repointed, so `start` correctly
8309
+ * refuses its destination ("already the default") — there is nothing left to
8310
+ * repoint. But the drain can fail, be cancelled, be interrupted by a restart,
8311
+ * or finish against a work list that was a tenth of the archive (D295), and
8312
+ * before this there was no supported way to run only that half: the only way
8313
+ * through was calling `recording.relocateFootage` by hand over admin tRPC.
8314
+ *
8315
+ * `drain` NEVER calls `setDefaultLocations`. That is what keeps `start`'s
8316
+ * refusal meaningful: the two verbs are disjoint, so nothing here can silently
8317
+ * re-repoint a class that is already migrated.
8318
+ */
8319
+ var StorageMigrationDrainInputSchema = object({
8320
+ /** The classes to drain. Each must appear in `storageMigration.residue`, so
8321
+ * a class whose source is already empty is refused rather than started. */
8322
+ classes: array(StorageMigrationClassSchema).min(1),
8323
+ throttleMbps: number().min(1).max(1e3).optional()
8324
+ });
8325
+ /** What a footage source still holds, asked of the durable hour ledger. */
8326
+ var RelocateResidueInputSchema = object({
8327
+ fromLocationId: string().min(1),
8328
+ /** Narrow to one logical class; omit for every profile on the location. */
8329
+ footageClass: RelocateFootageClassSchema.optional()
8330
+ });
8331
+ /** `null` = the archive could not answer (no ledger on this node, or the
8332
+ * aggregate failed). Never conflated with an empty source. */
8333
+ var RelocateResidueSchema = object({
8334
+ segments: number().int().nonnegative(),
8335
+ bytes: number().int().nonnegative()
8336
+ }).nullable();
8337
+ /** How many rows a media pass would still act on against a given target — the
8338
+ * media lane's denominator AND its residue, from ONE derivation so the two can
8339
+ * never disagree. `null` = the count could not be taken. */
8340
+ var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8341
+ var RelocatableMediaCountInputSchema = object({
8342
+ toLocationId: string().min(1),
8343
+ /** Omitted = `move`. */
8344
+ mode: MediaRelocateModeSchema.optional()
8345
+ });
8346
+ /**
8219
8347
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8220
8348
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8221
8349
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8319,6 +8447,32 @@ var StorageLocationRefSchema = union([StorageLocationTypeSchema, string().regex(
8319
8447
  * two addons declaring the same `id` must agree on `cardinality` (validated
8320
8448
  * at kernel aggregation time, not here).
8321
8449
  */
8450
+ /**
8451
+ * `StorageAccess` — how the service that DECLARED a storage-location kind
8452
+ * actually reaches the bytes. It is the constraint that decides which
8453
+ * `storage-provider`s may back a location of that kind.
8454
+ *
8455
+ * - `'local-path'` — the service asks `storage.resolve` for a path string and
8456
+ * then does its own `node:fs` I/O on it (the recorder's segment writer, the
8457
+ * post-analysis media roots). Only a provider that serves a genuine local
8458
+ * filesystem (`getProviderInfo().nodeLocal === true`) can satisfy that: a
8459
+ * remote provider's `resolve` returns a path on the REMOTE host, and
8460
+ * `fs.readdir` of it on this node either fails or — far worse — succeeds
8461
+ * against a same-named local directory that is something else entirely.
8462
+ *
8463
+ * - `'cap-mediated'` — every byte travels through the `storage` cap
8464
+ * (`read`/`write`, or `beginUpload`/`writeChunk`/`finalizeUpload`). The
8465
+ * service never sees a path, so any provider can back it. `backups` is the
8466
+ * one kind that qualifies today.
8467
+ *
8468
+ * Before this existed, `recordings` was unreachable by SFTP/S3/WebDAV only as
8469
+ * an EMERGENT property of how the recorder happened to be written. Nothing
8470
+ * refused the configuration; the first write simply went somewhere wrong, and
8471
+ * a recording write that goes wrong surfaces as a silent black window rather
8472
+ * than an error (the read path does not `stat`). This turns that accident into
8473
+ * a declared, enforced, testable refusal.
8474
+ */
8475
+ var StorageAccessSchema = _enum(["local-path", "cap-mediated"]);
8322
8476
  var StorageLocationDeclarationSchema = object({
8323
8477
  /**
8324
8478
  * Global location identifier, e.g. `recordings` or `recordingsLow`.
@@ -8338,6 +8492,19 @@ var StorageLocationDeclarationSchema = object({
8338
8492
  */
8339
8493
  cardinality: _enum(["single", "multi"]),
8340
8494
  /**
8495
+ * HOW the declaring service reaches the bytes — and therefore WHICH
8496
+ * providers may back a location of this kind. See {@link StorageAccessSchema}
8497
+ * and {@link STORAGE_ACCESS_FALLBACK}.
8498
+ *
8499
+ * Absent means `'local-path'`. That default is FAIL-CLOSED on purpose: it
8500
+ * can only over-restrict (refuse a remote provider for a kind that might
8501
+ * have coped) and never under-restrict. Declaring `'cap-mediated'` is the
8502
+ * permissive direction and is therefore never inferred — a repo guard
8503
+ * (`scripts/check-storage-access-declarations.ts`) refuses to let it be
8504
+ * reached by omission.
8505
+ */
8506
+ access: StorageAccessSchema.optional(),
8507
+ /**
8341
8508
  * When set, the default instance for this location inherits its resolved
8342
8509
  * root from the named location's default instance. Useful for derivative
8343
8510
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
@@ -17993,8 +18160,10 @@ var TrackSchema = object({
17993
18160
  lastSeen: number(),
17994
18161
  /** Frame-rate position history (subject to maxPositionHistory cap). */
17995
18162
  positions: array(TrackPositionSchema).readonly(),
17996
- /** Periodic snapshots at snapshotIntervalMs cadence (subject to
17997
- * saveThumbnails policy). */
18163
+ /** Periodic snapshots at snapshotIntervalMs cadence DEBUG media, produced
18164
+ * only while `MediaSettings.debugMediaEnabled` is on for the camera (D299;
18165
+ * the retired `saveThumbnails` used to gate this and the rolling
18166
+ * `lastFrame` together). Empty is the healthy default, not a capture gap. */
17998
18167
  snapshots: array(TrackSnapshotSchema).readonly(),
17999
18168
  /** Deduplicated zones the track has entered at least once. Zone IDS. */
18000
18169
  zonesVisited: array(string()).readonly(),
@@ -18854,7 +19023,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18854
19023
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18855
19024
  kind: "mutation",
18856
19025
  auth: "admin"
18857
- }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19026
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
19027
+ kind: "query",
19028
+ auth: "admin"
19029
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
18858
19030
  kind: "query",
18859
19031
  auth: "admin"
18860
19032
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20813,6 +20985,9 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
20813
20985
  }), method(object({ jobId: string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
20814
20986
  kind: "mutation",
20815
20987
  auth: "admin"
20988
+ }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
20989
+ kind: "mutation",
20990
+ auth: "admin"
20816
20991
  });
20817
20992
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
20818
20993
  providerId: string().min(1),
@@ -21211,12 +21386,38 @@ response: record(string(), unknown()) }), object({
21211
21386
  *
21212
21387
  * ## Why this is a capability and not a helper
21213
21388
  *
21214
- * Six stores in `addon-post-analysis` already hold vectors object CLIP, face,
21215
- * plate, vehicle, identity, and the event store's derivativesand every one of
21216
- * them keeps its vectors in a `JSON` settings-store column and ranks them by
21217
- * brute-force cosine in JS. Measured on the live hub that costs ~11.7 KB per row
21218
- * (512 floats as TEXT, `JSON.parse`d on every search) and made semantic search
21219
- * load 5,000 rows before ranking anything.
21389
+ * This capability was introduced with the claim that SIX stores in
21390
+ * `addon-post-analysis` held vectors in a `JSON` settings-store columnobject
21391
+ * CLIP, face, plate, vehicle, identity, and the event store's derivatives. That
21392
+ * claim was never true, and leaving it here made five stores look like pending
21393
+ * work when three of them have no vector at all. Counted column by column on
21394
+ * 2026-08-30, exactly THREE ever held one:
21395
+ *
21396
+ * - `object-clip` — 512-dim CLIP image embedding, migrated 2026-08-06.
21397
+ * - `faces.embedding` — 512-dim ArcFace face embedding, migrated 2026-08-30.
21398
+ * - `identity-samples.embedding` — the same ArcFace vector for an ENROLLED
21399
+ * face, migrated 2026-08-30 into its OWN index (see below).
21400
+ *
21401
+ * `plates` and `vehicle-samples` store a plate STRING and a score; `vehicles`
21402
+ * and `identities` store a name; the event store stores no derivative vector.
21403
+ * They are not migration candidates and never were.
21404
+ *
21405
+ * Measured on the live hub the JSON encoding cost ~11.7 KB per row (512 floats
21406
+ * as TEXT, `JSON.parse`d on every search) and made semantic search load 5,000
21407
+ * rows before ranking anything.
21408
+ *
21409
+ * ## One index per COMPARISON, never per encoder
21410
+ *
21411
+ * `faces` and `identity-samples` hold the same 512 ArcFace dims from the same
21412
+ * model, and they still get two indexes. An index is a set of things that are
21413
+ * ranked against each other and that live and die together, and these two are
21414
+ * neither: a `faces` row is TRACK-OWNED and cascades away with its track under
21415
+ * a per-camera capacity cap, an `identity-samples` row is retention-EXEMPT
21416
+ * forever and is the gallery every recognition ranks against. One index would
21417
+ * mean every gallery load and every reconcile carried a filter whose failure
21418
+ * mode is either ranking a candidate against itself or reclaiming an enrolled
21419
+ * person's only sample. The dimension they share is not a reason to share an
21420
+ * index; the question they answer is, and it differs.
21220
21421
  *
21221
21422
  * The fix is not a faster loop, it is a different backend — and the backend
21222
21423
  * should be replaceable without touching six callers. So: a singleton
@@ -21321,7 +21522,20 @@ var VectorQueryResultSchema = object({
21321
21522
  */
21322
21523
  scanned: number(),
21323
21524
  /** True when the backend could not consider every row that passed the filter. */
21324
- truncated: boolean()
21525
+ truncated: boolean(),
21526
+ /**
21527
+ * The `topK` the backend actually ran with.
21528
+ *
21529
+ * Every backend has a ceiling — sqlite-vec's is 4,096 — and a caller asking
21530
+ * past it used to learn nothing but a boolean, from a WARN in the provider's
21531
+ * own log rather than in its answer. That is how an audit asking for 20,000
21532
+ * consumed 4,096 and reported `examined: 4096` as if it had walked the index,
21533
+ * for weeks. `truncated` says THAT the answer was short; this says BY HOW
21534
+ * MUCH, in the return value, where the caller cannot fail to see it.
21535
+ *
21536
+ * Equals the requested `topK` whenever nothing was lowered.
21537
+ */
21538
+ effectiveTopK: number().int().positive()
21325
21539
  });
21326
21540
  var VectorDeleteInputSchema = object({
21327
21541
  index: string(),
@@ -21350,6 +21564,68 @@ var VectorGetResultSchema = object({ items: array(object({
21350
21564
  id: string(),
21351
21565
  metadata: VectorMetadataSchema
21352
21566
  })) });
21567
+ /**
21568
+ * Ids to read back WITH their vectors.
21569
+ *
21570
+ * The sibling of {@link VectorGetResultSchema}, and deliberately a separate
21571
+ * method rather than a flag on it: `getByIds` promises no vectors and its one
21572
+ * caller depends on that promise. This one promises the opposite.
21573
+ *
21574
+ * It exists because a store cannot put its vectors here otherwise. An ArcFace
21575
+ * gallery is ranked IN PROCESS, per detection, against every enrolled sample —
21576
+ * a per-face cross-process KNN would be a network round trip inside the
21577
+ * recognition loop. So the gallery is loaded once and held in RAM, and loading
21578
+ * it requires the index to hand the floats back. Without this method the only
21579
+ * way to keep a readable vector is a JSON column, which is the thing this
21580
+ * capability exists to delete.
21581
+ *
21582
+ * BOUNDED BY THE CALLER: ids are named, never "everything". Enumerating an
21583
+ * index is {@link VectorScanInputSchema}'s job, and it returns no vectors.
21584
+ */
21585
+ var VectorFetchInputSchema = object({
21586
+ index: string(),
21587
+ ids: array(string())
21588
+ });
21589
+ var VectorFetchResultSchema = object({ items: array(object({
21590
+ id: string(),
21591
+ /** base64 Float32LE — the same wire form `upsert` accepts. */
21592
+ vector: string(),
21593
+ metadata: VectorMetadataSchema
21594
+ })) });
21595
+ /**
21596
+ * ENUMERATE an index: one page of rows in a stable order, no ranking.
21597
+ *
21598
+ * A reconcile does not want the nearest rows, it wants ALL of them, and asking
21599
+ * a KNN for "all" is the wrong question twice over. It hits the backend's `k`
21600
+ * ceiling — 4,096 on sqlite-vec against a 22,128-row index — and it needs a
21601
+ * probe vector it does not have, so the audit passed a ZERO vector whose cosine
21602
+ * distance to every row is degenerate. `examined: 4096` then read as "we
21603
+ * looked" for as long as anyone cared to read it.
21604
+ *
21605
+ * This is the primitive that question actually needs: a bounded page, ordered
21606
+ * by the backend's own row order, costing no distance computation at all.
21607
+ * Vectors are NOT returned — an enumeration that shipped 2 KB per row would be
21608
+ * the full-table read this capability was built to stop.
21609
+ */
21610
+ var VectorScanInputSchema = object({
21611
+ index: string(),
21612
+ /** Opaque resume point. `0` starts at the top; pass back `nextCursor`. */
21613
+ cursor: number().int().nonnegative().default(0),
21614
+ limit: number().int().positive()
21615
+ });
21616
+ var VectorScanResultSchema = object({
21617
+ items: array(object({
21618
+ id: string(),
21619
+ metadata: VectorMetadataSchema
21620
+ })),
21621
+ /**
21622
+ * Where the next page starts, or `null` when the walk reached the end.
21623
+ *
21624
+ * `null` is the ONLY end-of-index signal. A caller must not infer the end
21625
+ * from a short page: a backend is free to return fewer rows than asked.
21626
+ */
21627
+ nextCursor: number().int().nonnegative().nullable()
21628
+ });
21353
21629
  var VectorStatsInputSchema = object({ index: string() });
21354
21630
  var VectorStatsResultSchema = object({
21355
21631
  /** Provider id, so an operator can tell brute force from an ANN index. */
@@ -21368,7 +21644,7 @@ method(VectorDeclareIndexInputSchema, _void(), {
21368
21644
  }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
21369
21645
  kind: "mutation",
21370
21646
  auth: "admin"
21371
- }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21647
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorFetchInputSchema, VectorFetchResultSchema, { auth: "admin" }), method(VectorScanInputSchema, VectorScanResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21372
21648
  kind: "mutation",
21373
21649
  auth: "admin"
21374
21650
  }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
@@ -26272,6 +26548,9 @@ method(object({
26272
26548
  }), method(object({}), array(RelocateJobSchema).readonly(), {
26273
26549
  kind: "query",
26274
26550
  auth: "admin"
26551
+ }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26552
+ kind: "query",
26553
+ auth: "admin"
26275
26554
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26276
26555
  kind: "mutation",
26277
26556
  auth: "admin"
@@ -31237,6 +31516,12 @@ Object.freeze({
31237
31516
  addonId: null,
31238
31517
  access: "create"
31239
31518
  },
31519
+ "pipelineAnalytics.countRelocatableMedia": {
31520
+ capName: "pipeline-analytics",
31521
+ capScope: "device",
31522
+ addonId: null,
31523
+ access: "view"
31524
+ },
31240
31525
  "pipelineAnalytics.countUnstampedEventMedia": {
31241
31526
  capName: "pipeline-analytics",
31242
31527
  capScope: "device",
@@ -32401,6 +32686,12 @@ Object.freeze({
32401
32686
  addonId: null,
32402
32687
  access: "view"
32403
32688
  },
32689
+ "recording.getRelocateResidue": {
32690
+ capName: "recording",
32691
+ capScope: "system",
32692
+ addonId: null,
32693
+ access: "view"
32694
+ },
32404
32695
  "recording.getStorageMigrationMoveStatus": {
32405
32696
  capName: "recording",
32406
32697
  capScope: "system",
@@ -32947,12 +33238,30 @@ Object.freeze({
32947
33238
  addonId: null,
32948
33239
  access: "create"
32949
33240
  },
33241
+ "storageMigration.drain": {
33242
+ capName: "storage-migration",
33243
+ capScope: "system",
33244
+ addonId: null,
33245
+ access: "create"
33246
+ },
33247
+ "storageMigration.movers": {
33248
+ capName: "storage-migration",
33249
+ capScope: "system",
33250
+ addonId: null,
33251
+ access: "view"
33252
+ },
32950
33253
  "storageMigration.plan": {
32951
33254
  capName: "storage-migration",
32952
33255
  capScope: "system",
32953
33256
  addonId: null,
32954
33257
  access: "view"
32955
33258
  },
33259
+ "storageMigration.residue": {
33260
+ capName: "storage-migration",
33261
+ capScope: "system",
33262
+ addonId: null,
33263
+ access: "view"
33264
+ },
32956
33265
  "storageMigration.start": {
32957
33266
  capName: "storage-migration",
32958
33267
  capScope: "system",
@@ -33787,6 +34096,12 @@ Object.freeze({
33787
34096
  addonId: null,
33788
34097
  access: "delete"
33789
34098
  },
34099
+ "vectorStore.fetchByIds": {
34100
+ capName: "vector-store",
34101
+ capScope: "system",
34102
+ addonId: null,
34103
+ access: "view"
34104
+ },
33790
34105
  "vectorStore.getByIds": {
33791
34106
  capName: "vector-store",
33792
34107
  capScope: "system",
@@ -33799,6 +34114,12 @@ Object.freeze({
33799
34114
  addonId: null,
33800
34115
  access: "view"
33801
34116
  },
34117
+ "vectorStore.scan": {
34118
+ capName: "vector-store",
34119
+ capScope: "system",
34120
+ addonId: null,
34121
+ access: "view"
34122
+ },
33802
34123
  "vectorStore.stats": {
33803
34124
  capName: "vector-store",
33804
34125
  capScope: "system",
package/dist/index.mjs CHANGED
@@ -8160,13 +8160,49 @@ var StorageMigrationParticipantSchema = _enum([
8160
8160
  "recorder",
8161
8161
  "analytics"
8162
8162
  ]);
8163
+ /**
8164
+ * The mover's own numbers, folded onto the coordinator's durable move record.
8165
+ *
8166
+ * The long half of a non-blocking migration is `draining`, and it is measured
8167
+ * in hours: 136 885 files at ~4 MB/s is about five of them. Before this shape
8168
+ * existed the only place those numbers appeared was a Loki line, so an operator
8169
+ * watching the Admin UI saw `phase: draining` and nothing else for a whole
8170
+ * afternoon.
8171
+ *
8172
+ * It is POLLED, never pushed. Events are telemetry and may be dropped
8173
+ * (D8/D11), and a dropped progress event is indistinguishable from a stalled
8174
+ * mover — which is the exact failure this is meant to end. The coordinator's
8175
+ * `waitForMoves` already fetches the whole {@link RelocateJob} on every tick to
8176
+ * read `state`; folding the counters costs no extra read and makes the durable
8177
+ * record say afterwards how far a move actually got.
8178
+ *
8179
+ * `filesTotal` is `null` for "no honest denominator" and is never zero-filled:
8180
+ * a windowed footage job (`sinceMs`) and a node with no ledger both genuinely
8181
+ * cannot say M, and a 0 there would render as "100 % done".
8182
+ */
8183
+ var StorageMigrationMoveProgressSchema = object({
8184
+ filesMoved: number().int().nonnegative(),
8185
+ /** The archive census — the **M** of "N of M" (D295). `null` = unknowable. */
8186
+ filesTotal: number().int().nonnegative().nullable(),
8187
+ bytesMoved: number().int().nonnegative(),
8188
+ /** The MOVER's start, not the migration's: a drain restarted after an addon
8189
+ * crash gets a new mover, and a rate computed from the migration's start
8190
+ * would silently average in the time nothing was running. */
8191
+ startedAt: number(),
8192
+ /** When the coordinator last read these numbers. Paired with `startedAt` it
8193
+ * is the only honest rate: both clocks are the hub's, so a UI never has to
8194
+ * subtract its own. */
8195
+ observedAt: number()
8196
+ });
8163
8197
  var StorageMigrationMoveSchema = object({
8164
8198
  storageClass: StorageMigrationClassSchema,
8165
8199
  fromLocationId: string(),
8166
8200
  toLocationId: string(),
8167
8201
  moverJobId: string().nullable(),
8168
8202
  state: RelocateJobStateSchema.nullable(),
8169
- error: string().nullable()
8203
+ error: string().nullable(),
8204
+ /** Last observed mover counters; `null` until the mover has been polled once. */
8205
+ progress: StorageMigrationMoveProgressSchema.nullable()
8170
8206
  });
8171
8207
  var StorageMigrationJobSchema = object({
8172
8208
  jobId: string(),
@@ -8212,6 +8248,98 @@ var StorageMigrationPlanSchema = object({
8212
8248
  findings: array(StorageMigrationFindingSchema)
8213
8249
  });
8214
8250
  /**
8251
+ * A mover as it exists RIGHT NOW, whether or not a migration job owns it.
8252
+ *
8253
+ * The coordinator's job record is the state of record for a migration, and its
8254
+ * moves carry {@link StorageMigrationMoveProgress}. But the movers are usable
8255
+ * standalone — `recording.relocateFootage` and `pipelineAnalytics.relocateMedia`
8256
+ * are both operator-callable, and on 2026-08-29 a five-hour drain was armed that
8257
+ * way because no supported UI path existed. A mover armed like that has no job
8258
+ * to fold progress into, so it has to be readable on its own or it is invisible.
8259
+ *
8260
+ * `migrationJobId` is what tells the two apart: `null` means nothing here
8261
+ * orchestrated it.
8262
+ */
8263
+ var StorageMigrationMoverSchema = object({
8264
+ lane: _enum(["footage", "media"]),
8265
+ job: RelocateJobSchema,
8266
+ /** The coordinator job that armed this mover, or `null` for a mover armed
8267
+ * directly against the owning addon. */
8268
+ migrationJobId: string().nullable(),
8269
+ /** When the hub read these counters. Stamped here so a rate is `bytesMoved`
8270
+ * over (`observedAt` − `job.startedAt`) with BOTH ends on the hub's clock —
8271
+ * a browser subtracting its own `Date.now()` from a server `startedAt` is a
8272
+ * rate made of two different clocks. */
8273
+ observedAt: number()
8274
+ });
8275
+ /**
8276
+ * What a SOURCE still holds for one storage class — the number that makes a
8277
+ * "drain remaining" action honest rather than hopeful.
8278
+ *
8279
+ * It comes from the archive (`SegmentHourLedger.census` for footage, the media
8280
+ * engine's own selection count for media), never from the resident index: a
8281
+ * drain sized off `RecordingIndex` is what reported `done` over 80.3 GB it had
8282
+ * never been told about (D295).
8283
+ *
8284
+ * `items`/`bytes` are `null` for "the archive could not be asked", which is
8285
+ * deliberately NOT zero: a drain is still offered for an unknown residue,
8286
+ * because refusing on an unanswerable read would hide exactly the case an
8287
+ * operator needs to act on.
8288
+ */
8289
+ var StorageMigrationResidueSchema = object({
8290
+ storageClass: StorageMigrationClassSchema,
8291
+ /** The location still holding the data. `'*'` for the media lane, whose rows
8292
+ * move from wherever they are rather than from one named source. */
8293
+ fromLocationId: string(),
8294
+ /** Where a drain would move it — the class's CURRENT default. */
8295
+ toLocationId: string(),
8296
+ /** Segments (footage lane) or rows (media lane) still on the source. */
8297
+ items: number().int().nonnegative().nullable(),
8298
+ /** Bytes on the source; `null` when the lane counts rows rather than bytes. */
8299
+ bytes: number().int().nonnegative().nullable()
8300
+ });
8301
+ /**
8302
+ * Run the DRAIN half and nothing else.
8303
+ *
8304
+ * A migration that reached `done` has already repointed, so `start` correctly
8305
+ * refuses its destination ("already the default") — there is nothing left to
8306
+ * repoint. But the drain can fail, be cancelled, be interrupted by a restart,
8307
+ * or finish against a work list that was a tenth of the archive (D295), and
8308
+ * before this there was no supported way to run only that half: the only way
8309
+ * through was calling `recording.relocateFootage` by hand over admin tRPC.
8310
+ *
8311
+ * `drain` NEVER calls `setDefaultLocations`. That is what keeps `start`'s
8312
+ * refusal meaningful: the two verbs are disjoint, so nothing here can silently
8313
+ * re-repoint a class that is already migrated.
8314
+ */
8315
+ var StorageMigrationDrainInputSchema = object({
8316
+ /** The classes to drain. Each must appear in `storageMigration.residue`, so
8317
+ * a class whose source is already empty is refused rather than started. */
8318
+ classes: array(StorageMigrationClassSchema).min(1),
8319
+ throttleMbps: number().min(1).max(1e3).optional()
8320
+ });
8321
+ /** What a footage source still holds, asked of the durable hour ledger. */
8322
+ var RelocateResidueInputSchema = object({
8323
+ fromLocationId: string().min(1),
8324
+ /** Narrow to one logical class; omit for every profile on the location. */
8325
+ footageClass: RelocateFootageClassSchema.optional()
8326
+ });
8327
+ /** `null` = the archive could not answer (no ledger on this node, or the
8328
+ * aggregate failed). Never conflated with an empty source. */
8329
+ var RelocateResidueSchema = object({
8330
+ segments: number().int().nonnegative(),
8331
+ bytes: number().int().nonnegative()
8332
+ }).nullable();
8333
+ /** How many rows a media pass would still act on against a given target — the
8334
+ * media lane's denominator AND its residue, from ONE derivation so the two can
8335
+ * never disagree. `null` = the count could not be taken. */
8336
+ var RelocatableMediaCountSchema = object({ rows: number().int().nonnegative() }).nullable();
8337
+ var RelocatableMediaCountInputSchema = object({
8338
+ toLocationId: string().min(1),
8339
+ /** Omitted = `move`. */
8340
+ mode: MediaRelocateModeSchema.optional()
8341
+ });
8342
+ /**
8215
8343
  * `StorageLocationType` — an addon-declared id that identifies the *kind* of
8216
8344
  * storage a location serves. Defined here (not in `capabilities/storage.cap.ts`)
8217
8345
  * so the persisted record schema and the consumer-facing cap can both consume it
@@ -8315,6 +8443,32 @@ var StorageLocationRefSchema = union([StorageLocationTypeSchema, string().regex(
8315
8443
  * two addons declaring the same `id` must agree on `cardinality` (validated
8316
8444
  * at kernel aggregation time, not here).
8317
8445
  */
8446
+ /**
8447
+ * `StorageAccess` — how the service that DECLARED a storage-location kind
8448
+ * actually reaches the bytes. It is the constraint that decides which
8449
+ * `storage-provider`s may back a location of that kind.
8450
+ *
8451
+ * - `'local-path'` — the service asks `storage.resolve` for a path string and
8452
+ * then does its own `node:fs` I/O on it (the recorder's segment writer, the
8453
+ * post-analysis media roots). Only a provider that serves a genuine local
8454
+ * filesystem (`getProviderInfo().nodeLocal === true`) can satisfy that: a
8455
+ * remote provider's `resolve` returns a path on the REMOTE host, and
8456
+ * `fs.readdir` of it on this node either fails or — far worse — succeeds
8457
+ * against a same-named local directory that is something else entirely.
8458
+ *
8459
+ * - `'cap-mediated'` — every byte travels through the `storage` cap
8460
+ * (`read`/`write`, or `beginUpload`/`writeChunk`/`finalizeUpload`). The
8461
+ * service never sees a path, so any provider can back it. `backups` is the
8462
+ * one kind that qualifies today.
8463
+ *
8464
+ * Before this existed, `recordings` was unreachable by SFTP/S3/WebDAV only as
8465
+ * an EMERGENT property of how the recorder happened to be written. Nothing
8466
+ * refused the configuration; the first write simply went somewhere wrong, and
8467
+ * a recording write that goes wrong surfaces as a silent black window rather
8468
+ * than an error (the read path does not `stat`). This turns that accident into
8469
+ * a declared, enforced, testable refusal.
8470
+ */
8471
+ var StorageAccessSchema = _enum(["local-path", "cap-mediated"]);
8318
8472
  var StorageLocationDeclarationSchema = object({
8319
8473
  /**
8320
8474
  * Global location identifier, e.g. `recordings` or `recordingsLow`.
@@ -8334,6 +8488,19 @@ var StorageLocationDeclarationSchema = object({
8334
8488
  */
8335
8489
  cardinality: _enum(["single", "multi"]),
8336
8490
  /**
8491
+ * HOW the declaring service reaches the bytes — and therefore WHICH
8492
+ * providers may back a location of this kind. See {@link StorageAccessSchema}
8493
+ * and {@link STORAGE_ACCESS_FALLBACK}.
8494
+ *
8495
+ * Absent means `'local-path'`. That default is FAIL-CLOSED on purpose: it
8496
+ * can only over-restrict (refuse a remote provider for a kind that might
8497
+ * have coped) and never under-restrict. Declaring `'cap-mediated'` is the
8498
+ * permissive direction and is therefore never inferred — a repo guard
8499
+ * (`scripts/check-storage-access-declarations.ts`) refuses to let it be
8500
+ * reached by omission.
8501
+ */
8502
+ access: StorageAccessSchema.optional(),
8503
+ /**
8337
8504
  * When set, the default instance for this location inherits its resolved
8338
8505
  * root from the named location's default instance. Useful for derivative
8339
8506
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
@@ -17989,8 +18156,10 @@ var TrackSchema = object({
17989
18156
  lastSeen: number(),
17990
18157
  /** Frame-rate position history (subject to maxPositionHistory cap). */
17991
18158
  positions: array(TrackPositionSchema).readonly(),
17992
- /** Periodic snapshots at snapshotIntervalMs cadence (subject to
17993
- * saveThumbnails policy). */
18159
+ /** Periodic snapshots at snapshotIntervalMs cadence DEBUG media, produced
18160
+ * only while `MediaSettings.debugMediaEnabled` is on for the camera (D299;
18161
+ * the retired `saveThumbnails` used to gate this and the rolling
18162
+ * `lastFrame` together). Empty is the healthy default, not a capture gap. */
17994
18163
  snapshots: array(TrackSnapshotSchema).readonly(),
17995
18164
  /** Deduplicated zones the track has entered at least once. Zone IDS. */
17996
18165
  zonesVisited: array(string()).readonly(),
@@ -18850,7 +19019,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
18850
19019
  }), method(RelocateMediaInputSchema, object({ jobId: string() }), {
18851
19020
  kind: "mutation",
18852
19021
  auth: "admin"
18853
- }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
19022
+ }), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
19023
+ kind: "query",
19024
+ auth: "admin"
19025
+ }), method(object({}), array(RelocateJobSchema).readonly(), {
18854
19026
  kind: "query",
18855
19027
  auth: "admin"
18856
19028
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
@@ -20809,6 +20981,9 @@ method(StorageMigrationInputSchema, StorageMigrationPlanSchema, { auth: "admin"
20809
20981
  }), method(object({ jobId: string().optional() }), StorageMigrationJobSchema.nullable(), { auth: "admin" }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
20810
20982
  kind: "mutation",
20811
20983
  auth: "admin"
20984
+ }), method(object({}), array(StorageMigrationMoverSchema).readonly(), { auth: "admin" }), method(object({}), array(StorageMigrationResidueSchema).readonly(), { auth: "admin" }), method(StorageMigrationDrainInputSchema, object({ jobId: string() }), {
20985
+ kind: "mutation",
20986
+ auth: "admin"
20812
20987
  });
20813
20988
  var ProviderInfoSchema = discriminatedUnion("shouldSaveDiskSpace", [object({
20814
20989
  providerId: string().min(1),
@@ -21207,12 +21382,38 @@ response: record(string(), unknown()) }), object({
21207
21382
  *
21208
21383
  * ## Why this is a capability and not a helper
21209
21384
  *
21210
- * Six stores in `addon-post-analysis` already hold vectors object CLIP, face,
21211
- * plate, vehicle, identity, and the event store's derivativesand every one of
21212
- * them keeps its vectors in a `JSON` settings-store column and ranks them by
21213
- * brute-force cosine in JS. Measured on the live hub that costs ~11.7 KB per row
21214
- * (512 floats as TEXT, `JSON.parse`d on every search) and made semantic search
21215
- * load 5,000 rows before ranking anything.
21385
+ * This capability was introduced with the claim that SIX stores in
21386
+ * `addon-post-analysis` held vectors in a `JSON` settings-store columnobject
21387
+ * CLIP, face, plate, vehicle, identity, and the event store's derivatives. That
21388
+ * claim was never true, and leaving it here made five stores look like pending
21389
+ * work when three of them have no vector at all. Counted column by column on
21390
+ * 2026-08-30, exactly THREE ever held one:
21391
+ *
21392
+ * - `object-clip` — 512-dim CLIP image embedding, migrated 2026-08-06.
21393
+ * - `faces.embedding` — 512-dim ArcFace face embedding, migrated 2026-08-30.
21394
+ * - `identity-samples.embedding` — the same ArcFace vector for an ENROLLED
21395
+ * face, migrated 2026-08-30 into its OWN index (see below).
21396
+ *
21397
+ * `plates` and `vehicle-samples` store a plate STRING and a score; `vehicles`
21398
+ * and `identities` store a name; the event store stores no derivative vector.
21399
+ * They are not migration candidates and never were.
21400
+ *
21401
+ * Measured on the live hub the JSON encoding cost ~11.7 KB per row (512 floats
21402
+ * as TEXT, `JSON.parse`d on every search) and made semantic search load 5,000
21403
+ * rows before ranking anything.
21404
+ *
21405
+ * ## One index per COMPARISON, never per encoder
21406
+ *
21407
+ * `faces` and `identity-samples` hold the same 512 ArcFace dims from the same
21408
+ * model, and they still get two indexes. An index is a set of things that are
21409
+ * ranked against each other and that live and die together, and these two are
21410
+ * neither: a `faces` row is TRACK-OWNED and cascades away with its track under
21411
+ * a per-camera capacity cap, an `identity-samples` row is retention-EXEMPT
21412
+ * forever and is the gallery every recognition ranks against. One index would
21413
+ * mean every gallery load and every reconcile carried a filter whose failure
21414
+ * mode is either ranking a candidate against itself or reclaiming an enrolled
21415
+ * person's only sample. The dimension they share is not a reason to share an
21416
+ * index; the question they answer is, and it differs.
21216
21417
  *
21217
21418
  * The fix is not a faster loop, it is a different backend — and the backend
21218
21419
  * should be replaceable without touching six callers. So: a singleton
@@ -21317,7 +21518,20 @@ var VectorQueryResultSchema = object({
21317
21518
  */
21318
21519
  scanned: number(),
21319
21520
  /** True when the backend could not consider every row that passed the filter. */
21320
- truncated: boolean()
21521
+ truncated: boolean(),
21522
+ /**
21523
+ * The `topK` the backend actually ran with.
21524
+ *
21525
+ * Every backend has a ceiling — sqlite-vec's is 4,096 — and a caller asking
21526
+ * past it used to learn nothing but a boolean, from a WARN in the provider's
21527
+ * own log rather than in its answer. That is how an audit asking for 20,000
21528
+ * consumed 4,096 and reported `examined: 4096` as if it had walked the index,
21529
+ * for weeks. `truncated` says THAT the answer was short; this says BY HOW
21530
+ * MUCH, in the return value, where the caller cannot fail to see it.
21531
+ *
21532
+ * Equals the requested `topK` whenever nothing was lowered.
21533
+ */
21534
+ effectiveTopK: number().int().positive()
21321
21535
  });
21322
21536
  var VectorDeleteInputSchema = object({
21323
21537
  index: string(),
@@ -21346,6 +21560,68 @@ var VectorGetResultSchema = object({ items: array(object({
21346
21560
  id: string(),
21347
21561
  metadata: VectorMetadataSchema
21348
21562
  })) });
21563
+ /**
21564
+ * Ids to read back WITH their vectors.
21565
+ *
21566
+ * The sibling of {@link VectorGetResultSchema}, and deliberately a separate
21567
+ * method rather than a flag on it: `getByIds` promises no vectors and its one
21568
+ * caller depends on that promise. This one promises the opposite.
21569
+ *
21570
+ * It exists because a store cannot put its vectors here otherwise. An ArcFace
21571
+ * gallery is ranked IN PROCESS, per detection, against every enrolled sample —
21572
+ * a per-face cross-process KNN would be a network round trip inside the
21573
+ * recognition loop. So the gallery is loaded once and held in RAM, and loading
21574
+ * it requires the index to hand the floats back. Without this method the only
21575
+ * way to keep a readable vector is a JSON column, which is the thing this
21576
+ * capability exists to delete.
21577
+ *
21578
+ * BOUNDED BY THE CALLER: ids are named, never "everything". Enumerating an
21579
+ * index is {@link VectorScanInputSchema}'s job, and it returns no vectors.
21580
+ */
21581
+ var VectorFetchInputSchema = object({
21582
+ index: string(),
21583
+ ids: array(string())
21584
+ });
21585
+ var VectorFetchResultSchema = object({ items: array(object({
21586
+ id: string(),
21587
+ /** base64 Float32LE — the same wire form `upsert` accepts. */
21588
+ vector: string(),
21589
+ metadata: VectorMetadataSchema
21590
+ })) });
21591
+ /**
21592
+ * ENUMERATE an index: one page of rows in a stable order, no ranking.
21593
+ *
21594
+ * A reconcile does not want the nearest rows, it wants ALL of them, and asking
21595
+ * a KNN for "all" is the wrong question twice over. It hits the backend's `k`
21596
+ * ceiling — 4,096 on sqlite-vec against a 22,128-row index — and it needs a
21597
+ * probe vector it does not have, so the audit passed a ZERO vector whose cosine
21598
+ * distance to every row is degenerate. `examined: 4096` then read as "we
21599
+ * looked" for as long as anyone cared to read it.
21600
+ *
21601
+ * This is the primitive that question actually needs: a bounded page, ordered
21602
+ * by the backend's own row order, costing no distance computation at all.
21603
+ * Vectors are NOT returned — an enumeration that shipped 2 KB per row would be
21604
+ * the full-table read this capability was built to stop.
21605
+ */
21606
+ var VectorScanInputSchema = object({
21607
+ index: string(),
21608
+ /** Opaque resume point. `0` starts at the top; pass back `nextCursor`. */
21609
+ cursor: number().int().nonnegative().default(0),
21610
+ limit: number().int().positive()
21611
+ });
21612
+ var VectorScanResultSchema = object({
21613
+ items: array(object({
21614
+ id: string(),
21615
+ metadata: VectorMetadataSchema
21616
+ })),
21617
+ /**
21618
+ * Where the next page starts, or `null` when the walk reached the end.
21619
+ *
21620
+ * `null` is the ONLY end-of-index signal. A caller must not infer the end
21621
+ * from a short page: a backend is free to return fewer rows than asked.
21622
+ */
21623
+ nextCursor: number().int().nonnegative().nullable()
21624
+ });
21349
21625
  var VectorStatsInputSchema = object({ index: string() });
21350
21626
  var VectorStatsResultSchema = object({
21351
21627
  /** Provider id, so an operator can tell brute force from an ANN index. */
@@ -21364,7 +21640,7 @@ method(VectorDeclareIndexInputSchema, _void(), {
21364
21640
  }), method(VectorUpsertInputSchema, VectorUpsertResultSchema, {
21365
21641
  kind: "mutation",
21366
21642
  auth: "admin"
21367
- }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21643
+ }), method(VectorQueryInputSchema, VectorQueryResultSchema, { auth: "admin" }), method(VectorGetInputSchema, VectorGetResultSchema, { auth: "admin" }), method(VectorFetchInputSchema, VectorFetchResultSchema, { auth: "admin" }), method(VectorScanInputSchema, VectorScanResultSchema, { auth: "admin" }), method(VectorDeleteInputSchema, VectorDeleteResultSchema, {
21368
21644
  kind: "mutation",
21369
21645
  auth: "admin"
21370
21646
  }), method(VectorDeleteByFilterInputSchema, VectorDeleteResultSchema, {
@@ -26268,6 +26544,9 @@ method(object({
26268
26544
  }), method(object({}), array(RelocateJobSchema).readonly(), {
26269
26545
  kind: "query",
26270
26546
  auth: "admin"
26547
+ }), method(RelocateResidueInputSchema, RelocateResidueSchema, {
26548
+ kind: "query",
26549
+ auth: "admin"
26271
26550
  }), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
26272
26551
  kind: "mutation",
26273
26552
  auth: "admin"
@@ -31233,6 +31512,12 @@ Object.freeze({
31233
31512
  addonId: null,
31234
31513
  access: "create"
31235
31514
  },
31515
+ "pipelineAnalytics.countRelocatableMedia": {
31516
+ capName: "pipeline-analytics",
31517
+ capScope: "device",
31518
+ addonId: null,
31519
+ access: "view"
31520
+ },
31236
31521
  "pipelineAnalytics.countUnstampedEventMedia": {
31237
31522
  capName: "pipeline-analytics",
31238
31523
  capScope: "device",
@@ -32397,6 +32682,12 @@ Object.freeze({
32397
32682
  addonId: null,
32398
32683
  access: "view"
32399
32684
  },
32685
+ "recording.getRelocateResidue": {
32686
+ capName: "recording",
32687
+ capScope: "system",
32688
+ addonId: null,
32689
+ access: "view"
32690
+ },
32400
32691
  "recording.getStorageMigrationMoveStatus": {
32401
32692
  capName: "recording",
32402
32693
  capScope: "system",
@@ -32943,12 +33234,30 @@ Object.freeze({
32943
33234
  addonId: null,
32944
33235
  access: "create"
32945
33236
  },
33237
+ "storageMigration.drain": {
33238
+ capName: "storage-migration",
33239
+ capScope: "system",
33240
+ addonId: null,
33241
+ access: "create"
33242
+ },
33243
+ "storageMigration.movers": {
33244
+ capName: "storage-migration",
33245
+ capScope: "system",
33246
+ addonId: null,
33247
+ access: "view"
33248
+ },
32946
33249
  "storageMigration.plan": {
32947
33250
  capName: "storage-migration",
32948
33251
  capScope: "system",
32949
33252
  addonId: null,
32950
33253
  access: "view"
32951
33254
  },
33255
+ "storageMigration.residue": {
33256
+ capName: "storage-migration",
33257
+ capScope: "system",
33258
+ addonId: null,
33259
+ access: "view"
33260
+ },
32952
33261
  "storageMigration.start": {
32953
33262
  capName: "storage-migration",
32954
33263
  capScope: "system",
@@ -33783,6 +34092,12 @@ Object.freeze({
33783
34092
  addonId: null,
33784
34093
  access: "delete"
33785
34094
  },
34095
+ "vectorStore.fetchByIds": {
34096
+ capName: "vector-store",
34097
+ capScope: "system",
34098
+ addonId: null,
34099
+ access: "view"
34100
+ },
33786
34101
  "vectorStore.getByIds": {
33787
34102
  capName: "vector-store",
33788
34103
  capScope: "system",
@@ -33795,6 +34110,12 @@ Object.freeze({
33795
34110
  addonId: null,
33796
34111
  access: "view"
33797
34112
  },
34113
+ "vectorStore.scan": {
34114
+ capName: "vector-store",
34115
+ capScope: "system",
34116
+ addonId: null,
34117
+ access: "view"
34118
+ },
33798
34119
  "vectorStore.stats": {
33799
34120
  capName: "vector-store",
33800
34121
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-decoder-nodeav",
3
- "version": "1.2.43",
3
+ "version": "1.2.45",
4
4
  "description": "Standalone in-process node-av decoder addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",