@camstack/addon-post-analysis 1.2.316 → 1.2.319

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.
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_clip_model_registry = require("../clip-model-registry-D-zzCBKK.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-BjYpZuuI.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -51362,6 +51362,11 @@ var MEDIA_ID_SEGMENT = "m";
51362
51362
  /** Path segment that introduces an OWNER question ("best media of"). */
51363
51363
  var MEDIA_OWNER_SEGMENT = "o";
51364
51364
  /**
51365
+ * Path segment that introduces an event-owner GENERATION (D694) —
51366
+ * `/g/<deviceId>/<recordAt>` after the owner id.
51367
+ */
51368
+ var MEDIA_GENERATION_SEGMENT = "g";
51369
+ /**
51365
51370
  * The plane path for one media row.
51366
51371
  *
51367
51372
  * `prefix` is `/addon/<addonId>/event-media` — a pure function of the addon
@@ -51378,6 +51383,23 @@ function mediaFileUrl(prefix, mediaId) {
51378
51383
  return `${prefix}/${MEDIA_ID_SEGMENT}/${mediaId}`;
51379
51384
  }
51380
51385
  /**
51386
+ * The plane path for "whatever media best represents this owner".
51387
+ *
51388
+ * The owner id IS encoded: a trackId is a UUID today but an owner id is an
51389
+ * arbitrary row key (the summary mosaic's is `summary-<ruleId>-<windowEndMs>`),
51390
+ * and one slash in it would silently re-cut the path.
51391
+ *
51392
+ * When `generation` is set and `ownerType` is an event table, the path carries
51393
+ * `/g/<deviceId>/<recordAt>` so a reused rowid cannot resolve another record's
51394
+ * crop. Generation is ignored (and not minted) for non-event owners — a track
51395
+ * id is random and needs none.
51396
+ */
51397
+ function mediaOwnerUrl(prefix, ownerType, ownerId, generation) {
51398
+ const base = `${prefix}/${MEDIA_OWNER_SEGMENT}/${ownerType}/${encodeURIComponent(ownerId)}`;
51399
+ if (generation === void 0 || !require_clip_model_registry.isEventOwnerType(ownerType)) return base;
51400
+ return `${base}/${MEDIA_GENERATION_SEGMENT}/${generation.deviceId}/${generation.recordAt}`;
51401
+ }
51402
+ /**
51381
51403
  * A stored row as the wire sees it: the ref, the address, and — transitionally
51382
51404
  * — the bytes.
51383
51405
  *
@@ -59230,6 +59252,12 @@ function openGzipStream(out) {
59230
59252
  onceDrain: (listener) => {
59231
59253
  gzip.once("drain", listener);
59232
59254
  },
59255
+ flush: () => new Promise((resolve, reject) => {
59256
+ gzip.flush((err) => {
59257
+ if (err !== null && err !== void 0) reject(err);
59258
+ else resolve();
59259
+ });
59260
+ }),
59233
59261
  finish: async () => {
59234
59262
  gzip.end();
59235
59263
  await closed;
@@ -59325,6 +59353,25 @@ var TapeSegmentWriter = class {
59325
59353
  });
59326
59354
  }
59327
59355
  /**
59356
+ * Push the sink's pending gzip output to disk without ending the segment.
59357
+ * A quiet / silent camera otherwise keeps its tail only in the zlib
59358
+ * buffer until the next rotation (D681 follow-up). Never throws into the
59359
+ * caller's frame path: a flush fault is logged and swallowed here.
59360
+ */
59361
+ async flushToDisk() {
59362
+ try {
59363
+ await this.deps.sink.flush();
59364
+ } catch (err) {
59365
+ this.deps.logger.warn("tracking tape segment flush failed", {
59366
+ tags: { deviceId: this.deps.deviceId },
59367
+ meta: {
59368
+ segmentOriginMs: this.deps.segmentOriginMs,
59369
+ error: require_clip_model_registry.errMsg(err)
59370
+ }
59371
+ });
59372
+ }
59373
+ }
59374
+ /**
59328
59375
  * Always ends the segment's records (queue flush, lost-span gap, `end`
59329
59376
  * record) before touching the sink, so a sink that cannot finish still
59330
59377
  * leaves a decodable body behind it. `sink.finish()`'s rejection is caught
@@ -59651,6 +59698,21 @@ var TapeRecorder = class {
59651
59698
  if (cam !== void 0) await this.closeSegment(deviceId, cam, "segment-end");
59652
59699
  }
59653
59700
  /**
59701
+ * Push every open segment's pending gzip output to disk without rotating
59702
+ * (D681 follow-up). A silent / quiet camera otherwise keeps its tail only
59703
+ * in the zlib buffer until the next frame or the 10-minute cut — a crash
59704
+ * loses it, and `bytesLastHour` lags behind what was already recorded.
59705
+ * Never throws: a per-camera flush fault is logged inside the writer.
59706
+ */
59707
+ async flushOpenSegments() {
59708
+ const pending = [];
59709
+ for (const cam of this.cameras.values()) {
59710
+ const writer = cam.segment?.writer;
59711
+ if (writer !== void 0 && writer !== null) pending.push(writer.flushToDisk());
59712
+ }
59713
+ await Promise.all(pending);
59714
+ }
59715
+ /**
59654
59716
  * Close the camera's open segment only if it holds time at or before
59655
59717
  * `atMs` — what a promotion of a window ending at `atMs` needs on disk
59656
59718
  * (Task 12 fix round, H1). A window wholly before the open segment leaves
@@ -60150,6 +60212,29 @@ var TapeShelf = class {
60150
60212
  };
60151
60213
  }
60152
60214
  };
60215
+ /**
60216
+ * Log every refusal the sweep already measured (D391 / D681 follow-up).
60217
+ * `sweep()` itself never throws for `rootUnreadable` / per-camera `failed`:
60218
+ * those land on the report, and a caller that only `.catch`es would drop them
60219
+ * silently even when the shelf already warned at the `readdir` site. One
60220
+ * tagged line per refusal at the sweep boundary closes that gap.
60221
+ */
60222
+ function logTapeSweepReport(report, logger) {
60223
+ if (report.rootUnreadable !== null) logger.warn("tracking tape sweep refused", { meta: {
60224
+ reason: report.rootUnreadable,
60225
+ scope: "root"
60226
+ } });
60227
+ for (const [deviceId, outcome] of report.perDevice) {
60228
+ if (!("failed" in outcome) || outcome.failed !== true) continue;
60229
+ logger.warn("tracking tape sweep refused", {
60230
+ tags: { deviceId },
60231
+ meta: {
60232
+ reason: outcome.reason,
60233
+ scope: "camera"
60234
+ }
60235
+ });
60236
+ }
60237
+ }
60153
60238
  //#endregion
60154
60239
  //#region src/pipeline-analytics/tracking-bench/recorder/tape-operation-queue.ts
60155
60240
  /** Generous: a promotion reads its window's segments and walks back through
@@ -61203,6 +61288,17 @@ function shelfFigures(listing, now) {
61203
61288
  ...last !== void 0 ? { heldToMs: last.originMs } : {}
61204
61289
  };
61205
61290
  }
61291
+ /**
61292
+ * Default `debug.tape.status` (no `deviceIds`): drop the noise. An Off camera
61293
+ * with a measured empty shelf is not a cost row and is not promotable; keep
61294
+ * everything else — Recording / Refused / Failing / Unknown, Off with held
61295
+ * segments (still promotable after the switch went off), and Off whose shelf
61296
+ * could not be read (D393: never silently hide an unreadable camera). Explicit
61297
+ * `deviceIds` still return every asked-about camera, Off included (D62).
61298
+ */
61299
+ function defaultTapeStatusCameras(rows) {
61300
+ return rows.filter((row) => row.state !== "off" || row.segmentsHeld === null || row.segmentsHeld > 0);
61301
+ }
61206
61302
  function describeTapeStatus(input) {
61207
61303
  return input.deviceIds.map((deviceId) => {
61208
61304
  const shelf = {
@@ -73112,7 +73208,7 @@ var AnalyticsQueryFacade = class {
73112
73208
  async getMotionEvents(input) {
73113
73209
  const events = await (this.deps.eventStore()?.queryMotion(input) ?? Promise.resolve([]));
73114
73210
  if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
73115
- return this.withMediaUrl(events);
73211
+ return this.withMediaUrl(events, "motion");
73116
73212
  }
73117
73213
  async getEventDensity(input) {
73118
73214
  return this.deps.eventStore()?.densityByKind(input) ?? [];
@@ -73169,12 +73265,12 @@ var AnalyticsQueryFacade = class {
73169
73265
  async getObjectEvents(input) {
73170
73266
  const events = await (this.deps.eventStore()?.queryObject(input) ?? Promise.resolve([]));
73171
73267
  if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
73172
- return this.withMediaUrl(events);
73268
+ return this.withMediaUrl(events, "object");
73173
73269
  }
73174
73270
  async getAudioEvents(input) {
73175
73271
  const events = await (this.deps.eventStore()?.queryAudio(input) ?? Promise.resolve([]));
73176
73272
  if (input.projection !== "slim" || this.deps.eventMediaBaseUrl() === null) return events;
73177
- return this.withMediaUrl(events);
73273
+ return this.withMediaUrl(events, "audio");
73178
73274
  }
73179
73275
  async getEventMedia(input) {
73180
73276
  const store = this.deps.mediaStore();
@@ -73441,13 +73537,20 @@ var AnalyticsQueryFacade = class {
73441
73537
  *
73442
73538
  * Only called when `eventMediaBaseUrl()` is non-null (guard sits in
73443
73539
  * the callers), so the template literal is safe.
73540
+ *
73541
+ * The URL names the owner TABLE and the event's GENERATION (D482 + D694):
73542
+ * a bare `${base}/${id}` was both ambiguous across tables and unable to
73543
+ * survive a reused rowid. New stamps always carry `/o/<type>/<id>/g/…`.
73444
73544
  */
73445
- withMediaUrl(events) {
73545
+ withMediaUrl(events, ownerType) {
73446
73546
  const base = this.deps.eventMediaBaseUrl();
73447
73547
  if (base === null) return events;
73448
73548
  return events.map((e) => ({
73449
73549
  ...e,
73450
- mediaUrl: `${base}/${e.id}`
73550
+ mediaUrl: mediaOwnerUrl(base, ownerType, String(e.id), {
73551
+ deviceId: e.deviceId,
73552
+ recordAt: e.timestamp
73553
+ })
73451
73554
  }));
73452
73555
  }
73453
73556
  };
@@ -76279,18 +76382,26 @@ function eventMediaEtag(media) {
76279
76382
  }
76280
76383
  /** Digits only — `Number('')` is 0 and `Number(' ')` is 0, so neither is used. */
76281
76384
  var DIGITS = /^\d+$/;
76385
+ /** One run of digits, for composing the event-owner remainder pattern. */
76386
+ var DIGIT_RUN = "\\d+";
76387
+ /**
76388
+ * Event-owner remainder with an optional generation suffix (D694):
76389
+ * `<ownerId>` or `<ownerId>/g/<deviceId>/<recordAt>`. Owner ids for event
76390
+ * tables are decimal rowids; the generation segment is the literal `g`.
76391
+ */
76392
+ var EVENT_OWNER_REMAINDER = new RegExp(`^(${DIGIT_RUN})(?:/${MEDIA_GENERATION_SEGMENT}/(${DIGIT_RUN})/(${DIGIT_RUN}))?$`);
76282
76393
  /**
76283
76394
  * Read a plane path. `null` for anything that is not one of the two forms —
76284
76395
  * including the BARE id the plane used to accept, which is gone: a bare number
76285
76396
  * could be a media id or an event id, and after D474 an event id could be one
76286
76397
  * of three rows. Ambiguity at the door is what this record removes.
76287
76398
  *
76288
- * The owner id is taken as the whole remainder rather than one segment: the
76289
- * hub's wildcard param is percent-decoded by find-my-way before the sub-path
76290
- * is proxied, so a `%2F` an owner id carried would arrive here as a real
76291
- * slash, and re-cutting on it would silently address a different owner. It is
76292
- * only ever a DB query VALUE (`where: { ownerId }`) — never a filesystem path,
76293
- * which the row itself supplies.
76399
+ * For non-event owners the id is the whole remainder rather than one segment:
76400
+ * the hub's wildcard param is percent-decoded by find-my-way before the
76401
+ * sub-path is proxied, so a `%2F` an owner id carried would arrive here as a
76402
+ * real slash, and re-cutting on it would silently address a different owner.
76403
+ * Event owners (decimal rowids) may end with `/g/<deviceId>/<recordAt>` —
76404
+ * that suffix is the generation, not part of the id (D694).
76294
76405
  */
76295
76406
  function parseMediaAddress(rawPath) {
76296
76407
  const path = rawPath.replace(/^\/+/, "");
@@ -76310,13 +76421,35 @@ function parseMediaAddress(rawPath) {
76310
76421
  const typeSlash = rest.indexOf("/");
76311
76422
  if (typeSlash <= 0) return null;
76312
76423
  const ownerType = rest.slice(0, typeSlash);
76313
- const ownerId = rest.slice(typeSlash + 1);
76314
- if (ownerId.length === 0) return null;
76424
+ const remainder = rest.slice(typeSlash + 1);
76425
+ if (remainder.length === 0) return null;
76315
76426
  if (!require_clip_model_registry.isMediaOwnerType(ownerType)) return null;
76427
+ if (require_clip_model_registry.isEventOwnerType(ownerType)) {
76428
+ const match = EVENT_OWNER_REMAINDER.exec(remainder);
76429
+ if (match === null) return null;
76430
+ const ownerId = match[1];
76431
+ const deviceIdRaw = match[2];
76432
+ const recordAtRaw = match[3];
76433
+ if (ownerId === void 0) return null;
76434
+ if (deviceIdRaw !== void 0 && recordAtRaw !== void 0) return {
76435
+ kind: "owner",
76436
+ ownerType,
76437
+ ownerId,
76438
+ generation: {
76439
+ deviceId: Number(deviceIdRaw),
76440
+ recordAt: Number(recordAtRaw)
76441
+ }
76442
+ };
76443
+ return {
76444
+ kind: "owner",
76445
+ ownerType,
76446
+ ownerId
76447
+ };
76448
+ }
76316
76449
  return {
76317
76450
  kind: "owner",
76318
76451
  ownerType,
76319
- ownerId
76452
+ ownerId: remainder
76320
76453
  };
76321
76454
  }
76322
76455
  /** Set by the HUB for a `csv_*` share token admitted on this plane; never by a client. */
@@ -78993,6 +79126,218 @@ function createRetrainExportHandler(deps) {
78993
79126
  };
78994
79127
  }
78995
79128
  //#endregion
79129
+ //#region src/pipeline-analytics/retrain/retrain-collection-store.ts
79130
+ /**
79131
+ * @durable class=ledger owner=pipeline-analytics
79132
+ * write="one row per named training collection the operator creates. Names are
79133
+ * unique on nameKey (lower(trim)); empty names never become rows."
79134
+ * retention="none — operator-owned corpus metadata. Rows leave only when an
79135
+ * explicit delete API lands (not V1)."
79136
+ */
79137
+ var RETRAIN_COLLECTIONS_COLLECTION = "pipeline-analytics:retrain-collections";
79138
+ var RETRAIN_COLLECTION_COLUMNS = [
79139
+ {
79140
+ name: "id",
79141
+ type: "TEXT",
79142
+ primaryKey: true,
79143
+ notNull: true
79144
+ },
79145
+ {
79146
+ name: "name",
79147
+ type: "TEXT",
79148
+ notNull: true
79149
+ },
79150
+ {
79151
+ name: "nameKey",
79152
+ type: "TEXT",
79153
+ notNull: true
79154
+ },
79155
+ {
79156
+ name: "createdAt",
79157
+ type: "INTEGER",
79158
+ notNull: true
79159
+ },
79160
+ {
79161
+ name: "updatedAt",
79162
+ type: "INTEGER",
79163
+ notNull: true
79164
+ }
79165
+ ];
79166
+ var RETRAIN_COLLECTION_INDEXES = [{
79167
+ name: "idx_retrain_collections_name_key",
79168
+ columns: ["nameKey"],
79169
+ unique: true
79170
+ }, {
79171
+ name: "idx_retrain_collections_created",
79172
+ columns: ["createdAt"]
79173
+ }];
79174
+ function normalizeCollectionName(raw) {
79175
+ const name = raw.trim();
79176
+ if (name.length === 0) return null;
79177
+ return {
79178
+ name,
79179
+ nameKey: name.toLowerCase()
79180
+ };
79181
+ }
79182
+ var RetrainCollectionStore = class {
79183
+ store;
79184
+ logger;
79185
+ now;
79186
+ newId;
79187
+ countFrames;
79188
+ constructor(deps) {
79189
+ this.store = deps.store;
79190
+ this.logger = deps.logger;
79191
+ this.now = deps.now;
79192
+ this.newId = deps.newId;
79193
+ this.countFrames = deps.countFrames;
79194
+ }
79195
+ static async declare(store) {
79196
+ await store.declareCollection.mutate({
79197
+ collection: RETRAIN_COLLECTIONS_COLLECTION,
79198
+ columns: [...RETRAIN_COLLECTION_COLUMNS],
79199
+ indexes: [...RETRAIN_COLLECTION_INDEXES]
79200
+ });
79201
+ }
79202
+ async create(input) {
79203
+ const normalized = normalizeCollectionName(input.name);
79204
+ if (normalized === null) return {
79205
+ kind: "refused",
79206
+ reason: "empty-name"
79207
+ };
79208
+ if (await this.findByNameKey(normalized.nameKey) !== null) return {
79209
+ kind: "refused",
79210
+ reason: "duplicate-name"
79211
+ };
79212
+ const createdAt = this.now();
79213
+ const id = this.newId();
79214
+ await this.store.set.mutate({
79215
+ collection: RETRAIN_COLLECTIONS_COLLECTION,
79216
+ key: id,
79217
+ value: {
79218
+ name: normalized.name,
79219
+ nameKey: normalized.nameKey,
79220
+ createdAt,
79221
+ updatedAt: createdAt
79222
+ }
79223
+ });
79224
+ return {
79225
+ kind: "created",
79226
+ collection: {
79227
+ id,
79228
+ name: normalized.name,
79229
+ createdAt,
79230
+ frameCount: 0
79231
+ }
79232
+ };
79233
+ }
79234
+ async list() {
79235
+ let rows;
79236
+ try {
79237
+ rows = await this.store.query.query({
79238
+ collection: RETRAIN_COLLECTIONS_COLLECTION,
79239
+ filter: { orderBy: {
79240
+ field: "createdAt",
79241
+ direction: "desc"
79242
+ } }
79243
+ });
79244
+ } catch (err) {
79245
+ this.logger.warn("retrain collections list failed", { meta: { error: String(err) } });
79246
+ return [];
79247
+ }
79248
+ const out = [];
79249
+ for (const row of rows) {
79250
+ const name = typeof row.data["name"] === "string" ? row.data["name"] : "";
79251
+ const createdAt = Number(row.data["createdAt"] ?? 0);
79252
+ const frameCount = this.countFrames !== void 0 ? await this.countFrames(row.id) : 0;
79253
+ out.push({
79254
+ id: row.id,
79255
+ name,
79256
+ createdAt,
79257
+ frameCount
79258
+ });
79259
+ }
79260
+ return out;
79261
+ }
79262
+ async getById(id) {
79263
+ try {
79264
+ const row = await this.store.get.query({
79265
+ collection: RETRAIN_COLLECTIONS_COLLECTION,
79266
+ key: id
79267
+ });
79268
+ if (row === null || row === void 0) return null;
79269
+ const data = row;
79270
+ return {
79271
+ id,
79272
+ name: typeof data["name"] === "string" ? data["name"] : "",
79273
+ createdAt: Number(data["createdAt"] ?? 0),
79274
+ frameCount: this.countFrames !== void 0 ? await this.countFrames(id) : 0
79275
+ };
79276
+ } catch (err) {
79277
+ this.logger.warn("retrain collection get failed", { meta: {
79278
+ collectionId: id,
79279
+ error: String(err)
79280
+ } });
79281
+ return null;
79282
+ }
79283
+ }
79284
+ /**
79285
+ * Stub until still ingest lands. Validates the collection exists, then
79286
+ * refuses with a named reason so callers can wire UI against a stable shape.
79287
+ */
79288
+ async addRetrainFrameFromRecording(input) {
79289
+ if (await this.getById(input.collectionId) === null) {
79290
+ this.logger.info("retrain collection addFrame refused: unknown-collection", {
79291
+ tags: { deviceId: input.deviceId },
79292
+ meta: {
79293
+ collectionId: input.collectionId,
79294
+ epochMs: input.epochMs
79295
+ }
79296
+ });
79297
+ return {
79298
+ kind: "refused",
79299
+ reason: "unknown-collection"
79300
+ };
79301
+ }
79302
+ this.logger.info("retrain collection addFrame stub: still-capture-unimplemented", {
79303
+ tags: { deviceId: input.deviceId },
79304
+ meta: {
79305
+ collectionId: input.collectionId,
79306
+ epochMs: input.epochMs
79307
+ }
79308
+ });
79309
+ return {
79310
+ kind: "refused",
79311
+ reason: "still-capture-unimplemented",
79312
+ detail: "Still ingest lands in plan Task 3"
79313
+ };
79314
+ }
79315
+ async findByNameKey(nameKey) {
79316
+ try {
79317
+ const row = (await this.store.query.query({
79318
+ collection: RETRAIN_COLLECTIONS_COLLECTION,
79319
+ filter: {
79320
+ where: { nameKey },
79321
+ limit: 1
79322
+ }
79323
+ }))[0];
79324
+ if (row === void 0) return null;
79325
+ return {
79326
+ id: row.id,
79327
+ name: typeof row.data["name"] === "string" ? row.data["name"] : "",
79328
+ createdAt: Number(row.data["createdAt"] ?? 0),
79329
+ frameCount: 0
79330
+ };
79331
+ } catch (err) {
79332
+ this.logger.warn("retrain collection findByNameKey failed", { meta: {
79333
+ nameKey,
79334
+ error: String(err)
79335
+ } });
79336
+ return null;
79337
+ }
79338
+ }
79339
+ };
79340
+ //#endregion
78996
79341
  //#region src/pipeline-analytics/retrain/retrain-assist.ts
78997
79342
  /** The whole frame, as a normalised rectangle. */
78998
79343
  var WHOLE_FRAME = {
@@ -82402,7 +82747,8 @@ async function rankKeyEvents(candidates, options, peakLookup) {
82402
82747
  ...t.markForTrain !== void 0 ? { markForTrain: t.markForTrain } : {},
82403
82748
  ...t.retrainStatus !== void 0 ? { retrainStatus: t.retrainStatus } : {},
82404
82749
  ...t.debug !== void 0 ? { debug: t.debug } : {},
82405
- ...t.favourited !== void 0 ? { favourited: t.favourited } : {}
82750
+ ...t.favourited !== void 0 ? { favourited: t.favourited } : {},
82751
+ ...t.audioLabels !== void 0 && t.audioLabels.length > 0 ? { audioLabels: t.audioLabels } : {}
82406
82752
  });
82407
82753
  }
82408
82754
  scored.sort((a, b) => b.importance - a.importance);
@@ -82829,16 +83175,19 @@ var MotionOnsetRegistry = class {
82829
83175
  * - `resolveSearchThumbnailUrl` — the search-hit thumbnail resolver. It points
82830
83176
  * a hit at the CLIP embedding row's OWN crop (the crop captured on the
82831
83177
  * embedding's peak frame → the hit thumbnail IS the embedded crop), and only
82832
- * falls back to the representative event's crop when the embedding row has no
82833
- * crop yet, so a hit never renders blank.
83178
+ * falls back to the representative event's owner ladder when the embedding
83179
+ * row has no crop yet, so a hit never renders blank.
82834
83180
  */
82835
83181
  function isClipObjectEmbedding(t, registry = require_clip_model_registry.BUILTIN_CLIP_MODELS) {
82836
83182
  return Array.isArray(t.embedding) && t.embedding.length > 0 && typeof t.embeddingModelId === "string" && registry.resolve(t.embeddingModelId) !== null;
82837
83183
  }
82838
83184
  function resolveSearchThumbnailUrl(input) {
82839
83185
  if (input.baseUrl === null) return void 0;
82840
- const id = input.embeddingMediaKey ?? input.eventId;
82841
- return `${input.baseUrl}/${encodeURIComponent(id)}`;
83186
+ if (input.embeddingMediaId !== void 0) return mediaFileUrl(input.baseUrl, input.embeddingMediaId);
83187
+ return mediaOwnerUrl(input.baseUrl, "object", input.eventId, {
83188
+ deviceId: input.deviceId,
83189
+ recordAt: input.recordAt
83190
+ });
82842
83191
  }
82843
83192
  //#endregion
82844
83193
  //#region src/pipeline-analytics/pipeline/onboard-birth-authority.ts
@@ -100998,7 +101347,10 @@ function ofGeneration(files, scope) {
100998
101347
  function makeNcOwnerMediaReader(deps) {
100999
101348
  return async (ownerKind, ownerId, eventScope) => {
101000
101349
  if (ownerKind === "track") return deps.media.listByOwner("track", await deps.resolveTrackOwnerId(ownerId));
101001
- if (eventScope === void 0) return deps.media.listByOwner("object", ownerId);
101350
+ if (eventScope === void 0) {
101351
+ deps.logger.error("object media read refused — NcEventMediaScope required", { meta: { ownerId } });
101352
+ return [];
101353
+ }
101002
101354
  const onCamera = await deps.media.listByOwner("object", ownerId, eventScope.deviceId);
101003
101355
  const kept = ofGeneration(onCamera, eventScope);
101004
101356
  if (kept.length < onCamera.length) deps.logger.warn("event media of another record on a reused event id — not used", {
@@ -102091,6 +102443,10 @@ var NC_ARTIFACT_PRUNE_INTERVAL_MS = 36e5;
102091
102443
  * (every camera switched off): the bound is 24 h, not "24 h after the next
102092
102444
  * close" (D681). */
102093
102445
  var TAPE_SWEEP_INTERVAL_MS = 36e5;
102446
+ /** How often open tape segments push their pending gzip output to disk
102447
+ * without rotating (D681 follow-up): a silent camera otherwise keeps its
102448
+ * tail only in the zlib buffer until the next frame or the 10-minute cut. */
102449
+ var TAPE_FLUSH_INTERVAL_MS = 6e4;
102094
102450
  /** What a publish answers on a node whose artifact plane never came up: no
102095
102451
  * link, no index row — and, exactly as before, bytes-only delivery. */
102096
102452
  var NO_PUBLICATION = {
@@ -102624,6 +102980,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
102624
102980
  ncArtifactReadDataPlane = null;
102625
102981
  ncArtifactPruneTimer = null;
102626
102982
  tapeSweepTimer = null;
102983
+ tapeFlushTimer = null;
102627
102984
  /** Why `startTrackingTapes` threw on a node that was allowed to record —
102628
102985
  * reported `failing` per switched-on camera, never as `recording`. */
102629
102986
  tapeStartFailure = null;
@@ -104428,6 +104785,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
104428
104785
  StationaryObjectRegistry.declare(api.settingsStore),
104429
104786
  RetrainFrameStore.declare(api.settingsStore),
104430
104787
  RetrainAnnotationStore.declare(api.settingsStore),
104788
+ RetrainCollectionStore.declare(api.settingsStore),
104431
104789
  OpsLogStore.declare(api.settingsStore),
104432
104790
  DebugNoteArchiveStore.declare(api.settingsStore),
104433
104791
  TrackingTapeIndexStore.declare(api.settingsStore),
@@ -106876,6 +107234,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
106876
107234
  clearInterval(this.tapeSweepTimer);
106877
107235
  this.tapeSweepTimer = null;
106878
107236
  }
107237
+ if (this.tapeFlushTimer !== null) {
107238
+ clearInterval(this.tapeFlushTimer);
107239
+ this.tapeFlushTimer = null;
107240
+ }
106879
107241
  if (this.ncArtifactPruneTimer !== null) {
106880
107242
  clearInterval(this.ncArtifactPruneTimer);
106881
107243
  this.ncArtifactPruneTimer = null;
@@ -113649,10 +114011,13 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
113649
114011
  });
113650
114012
  const scored = [];
113651
114013
  for (const { event, score } of bestEventByTrackId.values()) {
114014
+ const embeddingMediaId = event.trackId !== void 0 ? embeddingMediaIdByTrackId.get(event.trackId) : void 0;
113652
114015
  const mediaUrl = resolveSearchThumbnailUrl({
113653
114016
  baseUrl: this.eventMediaBaseUrl,
113654
114017
  eventId: String(event.id),
113655
- ...event.trackId !== void 0 && embeddingMediaIdByTrackId.has(event.trackId) ? { embeddingMediaId: embeddingMediaIdByTrackId.get(event.trackId) } : {}
114018
+ deviceId: event.deviceId,
114019
+ recordAt: event.timestamp,
114020
+ ...embeddingMediaId !== void 0 ? { embeddingMediaId } : {}
113656
114021
  });
113657
114022
  const keyFrameMediaId = event.trackId !== void 0 ? keyFrameMediaIdByTrackId.get(event.trackId) : void 0;
113658
114023
  scored.push({
@@ -114039,7 +114404,9 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114039
114404
  logger
114040
114405
  });
114041
114406
  const sweep = (deviceId) => {
114042
- shelf.sweep().catch((err) => {
114407
+ shelf.sweep().then((report) => {
114408
+ logTapeSweepReport(report, logger);
114409
+ }).catch((err) => {
114043
114410
  logger.warn("tracking tape sweep failed", {
114044
114411
  ...deviceId !== null ? { tags: { deviceId } } : {},
114045
114412
  meta: { error: require_clip_model_registry.errMsg(err) }
@@ -114071,6 +114438,10 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114071
114438
  sweep(null);
114072
114439
  this.tapeSweepTimer = setInterval(() => sweep(null), TAPE_SWEEP_INTERVAL_MS);
114073
114440
  this.tapeSweepTimer.unref?.();
114441
+ this.tapeFlushTimer = setInterval(() => {
114442
+ this.tapeRecorder?.flushOpenSegments();
114443
+ }, TAPE_FLUSH_INTERVAL_MS);
114444
+ this.tapeFlushTimer.unref?.();
114074
114445
  this.ctx.logger.info("tracking tapes ready", { meta: { seededProcessors: this.processorInputs.size } });
114075
114446
  } catch (err) {
114076
114447
  this.tapeStartFailure = require_clip_model_registry.errMsg(err);
@@ -114263,7 +114634,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114263
114634
  segments.set(deviceId, await this.tapeListingOf(shelf, deviceId));
114264
114635
  }
114265
114636
  const recorder = this.tapeRecorder;
114266
- return { cameras: describeTapeStatus({
114637
+ const cameras = describeTapeStatus({
114267
114638
  deviceIds,
114268
114639
  switchOn,
114269
114640
  switchUnreadable,
@@ -114272,7 +114643,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114272
114643
  startFailure: this.tapeStartFailure,
114273
114644
  segments,
114274
114645
  now: Date.now()
114275
- }) };
114646
+ });
114647
+ return { cameras: input.deviceIds === void 0 ? defaultTapeStatusCameras(cameras) : cameras };
114276
114648
  },
114277
114649
  "debug.tape.promote": async (input) => {
114278
114650
  const deps = this.tapePromotionDeps();
@@ -114721,7 +115093,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114721
115093
  * owner table, so the motion/object coin-flip is gone from here too.
114722
115094
  */
114723
115095
  async readMediaByAddress(address, variant, preferKind) {
114724
- const base = address.kind === "row" ? await this.readMediaById(address.mediaId) : await this.readOwnerThumbnail(address.ownerType, address.ownerId, preferKind);
115096
+ const base = address.kind === "row" ? await this.readMediaById(address.mediaId) : await this.readOwnerThumbnail(address.ownerType, address.ownerId, preferKind, address.generation);
114725
115097
  if (base === null || variant === void 0) return base;
114726
115098
  return this.applyThumbVariant(base, variant);
114727
115099
  }
@@ -114779,7 +115151,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114779
115151
  * ~144 MB and allocated ~190 MB of base64 to deliver ~1.2 MB, all of it on
114780
115152
  * the single-threaded post-analysis runner.
114781
115153
  */
114782
- async readOwnerThumbnail(ownerType, rawOwnerId, preferKind) {
115154
+ async readOwnerThumbnail(ownerType, rawOwnerId, preferKind, addressGeneration) {
114783
115155
  const ownerId = ownerType === "track" ? await resolveMediaOwnerId(ownerType, rawOwnerId, (id) => this.trackAliasStore?.resolve(id) ?? Promise.resolve(null), (failure) => this.aliasResolveFailureLogger?.(failure), "readOwnerThumbnail") : rawOwnerId;
114784
115156
  const listInfo = (ownerKind, id, deviceId) => this.mediaStore?.listInfoByOwner(ownerKind, id, deviceId) ?? Promise.resolve([]);
114785
115157
  const getTrackIdForEvent = (eventId) => require_clip_model_registry.isEventOwnerType(ownerType) ? this.eventStore?.getTrackIdForEvent(eventId, ownerType) ?? Promise.resolve(null) : Promise.resolve(null);
@@ -114793,8 +115165,11 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114793
115165
  };
114794
115166
  };
114795
115167
  let ownFiles = await listInfo(ownerType, ownerId);
114796
- if (require_clip_model_registry.isEventOwnerType(ownerType) && this.eventStore !== null) {
114797
- const gen = await this.eventStore.eventGenerationOf(ownerType, ownerId);
115168
+ if (require_clip_model_registry.isEventOwnerType(ownerType)) {
115169
+ const gen = addressGeneration !== void 0 ? {
115170
+ deviceId: addressGeneration.deviceId,
115171
+ timestamp: addressGeneration.recordAt
115172
+ } : this.eventStore !== null ? await this.eventStore.eventGenerationOf(ownerType, ownerId) : null;
114798
115173
  if (gen !== null) {
114799
115174
  const onCamera = await listInfo(ownerType, ownerId, gen.deviceId);
114800
115175
  const kept = onCamera.filter((f) => f.timestamp === gen.timestamp);
@@ -114806,7 +115181,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
114806
115181
  recordAt: gen.timestamp,
114807
115182
  refused: onCamera.length - kept.length,
114808
115183
  kept: kept.length,
114809
- via: "readOwnerThumbnail"
115184
+ via: "readOwnerThumbnail",
115185
+ scopedBy: addressGeneration !== void 0 ? "url" : "live-row"
114810
115186
  }
114811
115187
  });
114812
115188
  ownFiles = kept;