camstack 1.2.44 → 1.2.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14522,7 +14522,7 @@ function date4(params) {
14522
14522
  // ../../node_modules/zod/v4/classic/external.js
14523
14523
  config(en_default());
14524
14524
 
14525
- // ../types/dist/sleep-swxwnJmL.mjs
14525
+ // ../types/dist/sleep-CdbM8ge4.mjs
14526
14526
  var WELL_KNOWN_TABS = [
14527
14527
  {
14528
14528
  id: "overview",
@@ -23694,7 +23694,23 @@ var NcHistoryEntrySchema = external_exports.object({
23694
23694
  updatedAt: external_exports.number(),
23695
23695
  /** Failure detail — present on a `dead` row. */
23696
23696
  error: external_exports.string().optional(),
23697
- subject: NcHistorySubjectSchema
23697
+ subject: NcHistorySubjectSchema,
23698
+ /**
23699
+ * Ids of the artefacts (still, then gif, then clip) this row's successful
23700
+ * delivery indexed in the artefact library — a REFERENCE, never the bytes
23701
+ * (an artefact is often megabytes; this row is durable JSON rewritten on
23702
+ * every delivery attempt). Absent on a row still pending/dead, a row
23703
+ * delivered before this field shipped, or a wiring with no artefact index.
23704
+ *
23705
+ * Resolve one to a fetchable URL with `resolveArtifactUrl` — an id
23706
+ * outlives any one URL's TTL, so a caller mints a fresh link on demand
23707
+ * rather than trusting one frozen at delivery time. `resolveArtifactUrl`
23708
+ * also answers `null` for an id whose artefact has since expired past the
23709
+ * retained shelf's own age bound — the degrade a caller (the Home
23710
+ * Assistant export) must render as "no image right now", never as a
23711
+ * broken link.
23712
+ */
23713
+ artifactIds: external_exports.array(external_exports.string().min(1)).optional()
23698
23714
  });
23699
23715
  var NcHistoryFilterSchema = external_exports.object({
23700
23716
  ruleId: external_exports.string().optional(),
@@ -23940,6 +23956,20 @@ var notificationRulesCapability = {
23940
23956
  */
23941
23957
  getHistory: method(external_exports.object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), external_exports.object({ entries: external_exports.array(NcHistoryEntrySchema) }), { auth: "admin" }),
23942
23958
  /**
23959
+ * Mint a fresh, externally-reachable URL for one artefact a history row
23960
+ * named in {@link NcHistoryEntrySchema.shape.artifactIds} — the SAME
23961
+ * signed-link mechanism the dispatcher uses to attach media to an
23962
+ * outgoing notification (one derivation; this never re-implements it).
23963
+ *
23964
+ * `url: null` on THREE causes a caller must treat identically ("no image
23965
+ * right now", never a broken link): the id is unknown, its artefact has
23966
+ * expired past the retained shelf's own age bound, or this install has no
23967
+ * externally-reachable base URL. A caller that received a URL on a
23968
+ * previous call and now gets `null` must stop showing it — a link that
23969
+ * worked five minutes ago is not proof it works now.
23970
+ */
23971
+ resolveArtifactUrl: method(external_exports.object({ artifactId: external_exports.string().min(1) }), external_exports.object({ url: external_exports.string().nullable() }), { auth: "admin" }),
23972
+ /**
23943
23973
  * Snooze windows currently in effect, plus any whose digest has not yet
23944
23974
  * gone out. `caller: 'required'`: a user sees their OWN windows and the
23945
23975
  * global ones that silence them, never another person's private silence.
@@ -24179,2033 +24209,2245 @@ var oauthIntegrationCapability = {
24179
24209
  getDescriptor: method(external_exports.void(), OauthIntegrationDescriptorSchema, { auth: "admin" })
24180
24210
  }
24181
24211
  };
24182
- var TrackStateSchema = external_exports.enum([
24183
- "new",
24184
- "entered",
24185
- "left",
24186
- "moving",
24187
- "idle"
24188
- ]);
24189
- var EventKindSchema = external_exports.enum([
24190
- "motion",
24191
- "object",
24192
- "audio"
24193
- ]);
24194
- var TrackZoneFilterSchema = external_exports.discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
24195
- var EventKindIconSchema = external_exports.enum([
24196
- "motion",
24197
- "audio",
24198
- "person",
24199
- "vehicle",
24200
- "animal",
24201
- "door",
24202
- "pir",
24203
- "smoke",
24204
- "water",
24205
- "button",
24206
- "package",
24207
- "generic"
24212
+ var NativeCropRefSchema = external_exports.object({
24213
+ /** Handle keying the retained native surface (node-pinned to its owner). */
24214
+ handle: FrameHandleSchema,
24215
+ /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
24216
+ cropFrameSpace: external_exports.object({
24217
+ x: external_exports.number(),
24218
+ y: external_exports.number(),
24219
+ w: external_exports.number(),
24220
+ h: external_exports.number()
24221
+ })
24222
+ });
24223
+ external_exports.object({
24224
+ crop: external_exports.object({
24225
+ left: external_exports.number(),
24226
+ top: external_exports.number(),
24227
+ width: external_exports.number().positive(),
24228
+ height: external_exports.number().positive()
24229
+ }).optional(),
24230
+ content: external_exports.object({
24231
+ width: external_exports.number().int().positive(),
24232
+ height: external_exports.number().int().positive()
24233
+ }),
24234
+ fit: external_exports.enum(["stretch", "contain"]),
24235
+ format: external_exports.enum([
24236
+ "rgb",
24237
+ "gray",
24238
+ "jpeg"
24239
+ ])
24240
+ });
24241
+ var FrameRefSchema = external_exports.object({
24242
+ registryId: external_exports.string().min(1),
24243
+ id: external_exports.string().min(1),
24244
+ width: external_exports.number().int().positive(),
24245
+ height: external_exports.number().int().positive(),
24246
+ format: external_exports.enum(["rgb", "gray"]),
24247
+ timestamp: external_exports.number(),
24248
+ capturedAt: external_exports.number().optional()
24249
+ });
24250
+ var ModelFormatSchema$1 = external_exports.enum([
24251
+ "onnx",
24252
+ "coreml",
24253
+ "openvino",
24254
+ "tflite",
24255
+ "pt",
24256
+ "gguf"
24208
24257
  ]);
24209
- var EventKindCategorySchema = external_exports.enum([
24210
- "motion",
24211
- "audio",
24212
- "detection",
24213
- "sensor",
24214
- "control",
24215
- "custom",
24216
- "package"
24258
+ var PipelineSlotSchema = external_exports.enum([
24259
+ "detector",
24260
+ "cropper",
24261
+ "classifier",
24262
+ "refiner",
24263
+ "audio-classifier"
24217
24264
  ]);
24218
- var EventKindLevelSchema = external_exports.enum(["macro", "sub"]);
24219
- var EventKindDescriptorSchema = external_exports.object({
24220
- /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
24221
- kind: external_exports.string(),
24222
- /** i18n key resolved on the UI side; `label` is the English fallback. */
24223
- labelKey: external_exports.string(),
24224
- /** English fallback label (kept for clients that don't translate). */
24265
+ var PipelineEngineChoiceSchema = external_exports.object({
24266
+ runtime: external_exports.enum(["node", "python"]),
24267
+ backend: external_exports.string(),
24268
+ format: ModelFormatSchema$1,
24269
+ device: external_exports.string().optional()
24270
+ });
24271
+ var EngineDeviceInfoSchema = external_exports.object({
24272
+ id: external_exports.string(),
24225
24273
  label: external_exports.string(),
24226
- /** Hex color for timeline/legend rendering. */
24227
- color: external_exports.string(),
24228
- /** Dictionary id → lucide component on the UI side. */
24229
- iconId: external_exports.string(),
24230
- /** Legacy closed-vocab glyph — fallback for `iconId`. */
24231
- icon: EventKindIconSchema,
24232
- category: EventKindCategorySchema,
24233
- /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
24234
- parentKind: external_exports.string().nullable(),
24235
- /** Derived from `parentKind`, explicit for the client tree. */
24236
- level: EventKindLevelSchema,
24237
- /** Which cap + device contributes this kind. For built-ins the camera
24238
- * itself; for sensor kinds the LINKED source device. */
24239
- source: external_exports.object({
24240
- capName: external_exports.string(),
24241
- deviceId: external_exports.number()
24242
- })
24274
+ description: external_exports.string().optional()
24243
24275
  });
24244
- var EventKindsForDeviceSchema = external_exports.object({
24245
- deviceId: external_exports.number(),
24246
- kinds: external_exports.array(EventKindDescriptorSchema).readonly()
24276
+ var AvailableEngineSchema = external_exports.object({
24277
+ engine: PipelineEngineChoiceSchema,
24278
+ devices: external_exports.array(EngineDeviceInfoSchema).readonly(),
24279
+ defaultDevice: external_exports.string()
24247
24280
  });
24248
- var SensorEventSchema = external_exports.object({
24281
+ var PipelineDefaultStepSchema = external_exports.lazy(() => external_exports.object({
24282
+ addonId: external_exports.string(),
24283
+ addonName: external_exports.string(),
24284
+ slot: PipelineSlotSchema,
24285
+ inputClasses: external_exports.array(external_exports.string()).readonly(),
24286
+ outputClasses: external_exports.array(external_exports.string()).readonly(),
24287
+ enabled: external_exports.boolean(),
24288
+ modelId: external_exports.string(),
24289
+ children: external_exports.array(PipelineDefaultStepSchema).readonly(),
24290
+ group: external_exports.string().optional(),
24291
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
24292
+ }));
24293
+ var PipelineTemplateStepSchema = external_exports.lazy(() => external_exports.object({
24294
+ addonId: external_exports.string(),
24295
+ enabled: external_exports.boolean(),
24296
+ modelId: external_exports.string(),
24297
+ children: external_exports.array(PipelineTemplateStepSchema).readonly(),
24298
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
24299
+ }));
24300
+ var PipelineTemplateSchema$1 = external_exports.object({
24249
24301
  id: external_exports.string(),
24250
- /** The CAMERA the event is attributed to (a sensor linked to N cameras
24251
- * yields N rows, one per camera). */
24252
- deviceId: external_exports.number(),
24253
- /** The linked sensor device whose state changed. */
24254
- sourceDeviceId: external_exports.number(),
24255
- /** Event kind id — matches an `EventKindDescriptor.kind`. */
24256
- kind: external_exports.string(),
24257
- /** Snapshot of the sensor cap's runtime-state slice at the change. */
24258
- value: external_exports.record(external_exports.string(), external_exports.unknown()).nullable(),
24259
- timestamp: external_exports.number()
24260
- });
24261
- var TrackPositionSchema = external_exports.object({
24262
- x: external_exports.number(),
24263
- y: external_exports.number(),
24264
- timestamp: external_exports.number(),
24265
- bbox: BoundingBoxSchema
24302
+ name: external_exports.string(),
24303
+ createdAt: external_exports.string(),
24304
+ updatedAt: external_exports.string(),
24305
+ engine: PipelineEngineChoiceSchema,
24306
+ steps: external_exports.array(PipelineTemplateStepSchema).readonly()
24266
24307
  });
24267
- var TrackSnapshotSchema = external_exports.object({
24268
- timestamp: external_exports.number(),
24269
- position: TrackPositionSchema,
24270
- /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
24271
- mediaKey: external_exports.string()
24308
+ var PipelineModelOptionSchema = external_exports.object({
24309
+ id: external_exports.string(),
24310
+ name: external_exports.string(),
24311
+ formats: external_exports.record(external_exports.string(), external_exports.object({
24312
+ downloaded: external_exports.boolean(),
24313
+ sizeMB: external_exports.number()
24314
+ })),
24315
+ group: ModelVariantGroupSchema.optional(),
24316
+ legacy: external_exports.boolean().optional(),
24317
+ provider: ModelProviderIdSchema.optional()
24272
24318
  });
24273
- var TrackEnvelopeSchema = external_exports.object({
24274
- minX: external_exports.number(),
24275
- minY: external_exports.number(),
24276
- maxX: external_exports.number(),
24277
- maxY: external_exports.number()
24319
+ var ConfigFieldBridge = external_exports.custom();
24320
+ var PipelineAddonSchemaSchema = external_exports.object({
24321
+ id: external_exports.string(),
24322
+ name: external_exports.string(),
24323
+ slot: PipelineSlotSchema,
24324
+ inputClasses: external_exports.array(external_exports.string()).readonly(),
24325
+ outputClasses: external_exports.array(external_exports.string()).readonly(),
24326
+ childSlots: external_exports.array(PipelineSlotSchema).readonly(),
24327
+ models: external_exports.array(PipelineModelOptionSchema).readonly(),
24328
+ defaultModelId: external_exports.string(),
24329
+ defaultModelIdByFormat: external_exports.record(external_exports.string(), external_exports.string()).optional(),
24330
+ enabledByDefault: external_exports.boolean().optional(),
24331
+ backfillIntoExistingOverrides: external_exports.boolean().optional(),
24332
+ defaultConfidence: external_exports.number(),
24333
+ group: external_exports.string().optional(),
24334
+ configSchema: external_exports.array(ConfigFieldBridge).readonly().optional()
24278
24335
  });
24279
- var TrackProjectionSchema = external_exports.enum(["full", "slim"]);
24280
- var TrackAudioLabelSchema = external_exports.object({
24336
+ var PipelineSlotSchemaSchema = external_exports.object({
24337
+ id: PipelineSlotSchema,
24281
24338
  label: external_exports.string(),
24282
- /** Highest classification score observed across the label's episodes. */
24283
- peakScore: external_exports.number(),
24284
- /** Number of coalesced audio-event episodes carrying this label. */
24285
- count: external_exports.number(),
24286
- firstAt: external_exports.number(),
24287
- lastAt: external_exports.number()
24288
- });
24289
- var TrackSourceSchema = external_exports.enum([
24290
- "pipeline",
24291
- "sensor",
24292
- "audio"
24293
- ]);
24294
- var RetrainStatusSchema = external_exports.enum([
24295
- "none",
24296
- "staging",
24297
- "trained"
24298
- ]);
24299
- var TrackFlagFields = {
24300
- /** Operator marked this track as training material — i.e. `retrainStatus` is
24301
- * `'staging'`. */
24302
- markForTrain: external_exports.boolean().optional(),
24303
- /** Operator marked this track for diagnostic attention. */
24304
- debug: external_exports.boolean().optional(),
24305
- /** Operator favourited this track. Pins it against pruning. */
24306
- favourited: external_exports.boolean().optional()
24307
- };
24308
- var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
24309
- var TrackFlagsPatchSchema = external_exports.object(TrackFlagFields);
24310
- var TrackFlagsSchema = external_exports.object({
24311
- trackId: external_exports.string(),
24312
- markForTrain: external_exports.boolean(),
24313
- debug: external_exports.boolean(),
24314
- favourited: external_exports.boolean(),
24315
- /** The lifecycle state the boolean was derived from. Required here (unlike on
24316
- * a track row) because this shape is only ever produced by the write body,
24317
- * which always knows it — and a surface that has just written needs to render
24318
- * `trained` without a re-fetch. */
24319
- retrainStatus: RetrainStatusSchema
24339
+ priority: external_exports.number(),
24340
+ parentSlot: PipelineSlotSchema.nullable(),
24341
+ addons: external_exports.array(PipelineAddonSchemaSchema).readonly()
24320
24342
  });
24321
- var LabelTierSchema = external_exports.union([external_exports.literal(1), external_exports.literal(2)]);
24322
- var LabelAttributionSchema = external_exports.object({
24323
- stepId: external_exports.string(),
24324
- modelId: external_exports.string().optional(),
24325
- decidedAt: external_exports.number(),
24343
+ var PipelineSchemaSchema = external_exports.object({
24344
+ availableEngines: external_exports.array(AvailableEngineSchema).readonly(),
24345
+ selectedEngine: PipelineEngineChoiceSchema,
24346
+ slots: external_exports.array(PipelineSlotSchemaSchema).readonly()
24347
+ });
24348
+ var EngineProvisioningSchema = external_exports.object({
24349
+ runtimeId: external_exports.enum([
24350
+ "onnx",
24351
+ "openvino",
24352
+ "coreml",
24353
+ "edgetpu"
24354
+ ]).nullable(),
24355
+ device: external_exports.string().nullable(),
24356
+ state: external_exports.enum([
24357
+ "idle",
24358
+ "installing",
24359
+ "verifying",
24360
+ "ready",
24361
+ "failed"
24362
+ ]),
24363
+ progress: external_exports.number().optional(),
24364
+ error: external_exports.string().optional(),
24365
+ nextRetryAt: external_exports.number().optional(),
24326
24366
  /**
24327
- * The GALLERY id behind a recognised tier-2 label — a face-gallery
24328
- * `Identity.id` or a plate-gallery `Vehicle.id` (both `randomUUID`).
24329
- *
24330
- * The text alone is a DISPLAY NAME, and a display name is renameable: a
24331
- * notification rule authored on "Gianluca" stopped matching the moment the
24332
- * operator fixed the spelling in the gallery, and nothing said so. The id is
24333
- * the thing that does not move, so it is what a rule matches on
24334
- * (`NcConditions.identities`) and the text is what a human is shown.
24335
- *
24336
- * Absent when the label names no gallery row — a plate the OCR read but no
24337
- * vehicle claims, a sub-class, a species, any tier-1 value.
24367
+ * Gate A (config-correctness gate at engine change): human-readable
24368
+ * config issues surfaced EAGERLY when the node's engine changes — model
24369
+ * substitutions ("chose X, running Y") and zero-build steps ("no model
24370
+ * has a <format> build"). Additive/optional: informational only, never
24371
+ * enforced here `assertEngineReady` (readiness) still gates inference.
24372
+ * Absent/empty when the node-default tree resolves cleanly.
24338
24373
  */
24339
- identityId: external_exports.string().optional()
24374
+ configIssues: external_exports.array(external_exports.string()).optional()
24340
24375
  });
24341
- var TieredLabelFields = {
24342
- /** Tier 1 — the sub-class. See {@link LabelTierSchema}. */
24343
- label: external_exports.string().optional(),
24344
- /** Confidence of the tier-1 value, as reported by the deciding step. */
24345
- labelScore: external_exports.number().optional(),
24346
- /** Provenance of the tier-1 value. See {@link LabelAttributionSchema}. */
24347
- labelMeta: LabelAttributionSchema.optional(),
24348
- /** Tier 2 — the instance. See {@link LabelTierSchema}. */
24349
- subLabel: external_exports.string().optional(),
24350
- /** Confidence of the tier-2 value, as reported by the deciding step. */
24351
- subLabelScore: external_exports.number().optional(),
24352
- /** Provenance of the tier-2 value. See {@link LabelAttributionSchema}. */
24353
- subLabelMeta: LabelAttributionSchema.optional()
24354
- };
24355
- var TrainingExportDeviceTotalsSchema = external_exports.object({
24356
- deviceId: external_exports.number(),
24357
- tracks: external_exports.number().int(),
24358
- files: external_exports.number().int(),
24359
- bytes: external_exports.number().int()
24376
+ var PipelineStepInputSchema = external_exports.lazy(() => external_exports.object({
24377
+ addonId: external_exports.string(),
24378
+ modelId: external_exports.string().optional(),
24379
+ enabled: external_exports.boolean().default(true),
24380
+ children: external_exports.array(PipelineStepInputSchema).optional(),
24381
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
24382
+ jumpDeviceKey: external_exports.string().optional()
24383
+ }));
24384
+ var ModelSubstitutionSchema = external_exports.object({
24385
+ addonId: external_exports.string(),
24386
+ chosen: external_exports.string(),
24387
+ running: external_exports.string(),
24388
+ format: external_exports.string()
24360
24389
  });
24361
- var TrainingExportSummarySchema = external_exports.object({
24362
- generatedAt: external_exports.number(),
24363
- trackCount: external_exports.number().int(),
24364
- fileCount: external_exports.number().int(),
24365
- byteCount: external_exports.number().int(),
24366
- /** More marked tracks exist than a single pass carries. */
24367
- truncated: external_exports.boolean(),
24368
- devices: external_exports.array(TrainingExportDeviceTotalsSchema).readonly()
24390
+ var PipelineValidationIssueSchema = external_exports.object({
24391
+ addonId: external_exports.string(),
24392
+ kind: external_exports.enum(["unknown-addon", "no-format-build"]),
24393
+ detail: external_exports.string()
24369
24394
  });
24370
- var TrackSchema = external_exports.object({
24371
- trackId: external_exports.string(),
24372
- deviceId: external_exports.number(),
24373
- className: external_exports.string(),
24374
- ...TieredLabelFields,
24375
- producingDeviceName: external_exports.string().optional(),
24376
- /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
24377
- source: TrackSourceSchema.optional(),
24378
- firstSeen: external_exports.number(),
24379
- lastSeen: external_exports.number(),
24380
- /** Frame-rate position history (subject to maxPositionHistory cap). */
24381
- positions: external_exports.array(TrackPositionSchema).readonly(),
24382
- /** Periodic snapshots at snapshotIntervalMs cadence (subject to
24383
- * saveThumbnails policy). */
24384
- snapshots: external_exports.array(TrackSnapshotSchema).readonly(),
24385
- /** Deduplicated zones the track has entered at least once. Zone IDS. */
24386
- zonesVisited: external_exports.array(external_exports.string()).readonly(),
24387
- /**
24388
- * Human NAMES for {@link zonesVisited}, resolved at READ time against the
24389
- * `zones` capability.
24390
- *
24391
- * `zonesVisited` persists ids (`cfeec78c-8d69-…`), which no operator can type
24392
- * and no card can render — so every free-text search surface was structurally
24393
- * unable to answer "show me the tracks in Uscio", and did not fail loudly, it
24394
- * just returned nothing. Resolving here rather than in each client keeps ONE
24395
- * derivation and costs the clients no extra call (the `zones` cap is
24396
- * per-device, so a client-side resolve would be a per-camera fan-out on a
24397
- * surface built to avoid exactly that).
24398
- *
24399
- * Resolved, never invented: a zone deleted since the track was written has no
24400
- * name and is DROPPED, so this array can be shorter than `zonesVisited` — the
24401
- * two are not positionally aligned. Absent when the track visited no zone, or
24402
- * when the zone catalogue could not be read.
24403
- */
24404
- zoneNames: external_exports.array(external_exports.string()).readonly().optional(),
24405
- /** Deduplicated set of detector classes observed for this track over its
24406
- * life (a track may be reclassified, e.g. person→vehicle). Absent on
24407
- * legacy rows written before class accumulation shipped. */
24408
- classes: external_exports.array(external_exports.string()).readonly().optional(),
24409
- /** Cumulative normalized distance travelled (0..1 units = full frame width). */
24410
- totalDistance: external_exports.number(),
24411
- state: TrackStateSchema,
24412
- active: external_exports.boolean(),
24413
- /** Deterministic key-event importance score in [0,1] (server-computed at
24414
- * track expiry, recomputed on late label). Absent on legacy rows written
24415
- * before scoring shipped — consumers degrade to absence / compute-on-read. */
24416
- importance: external_exports.number().optional(),
24417
- /** Id of the track's highest-confidence ObjectEvent (its representative
24418
- * "best" frame). Absent when the track produced no object events. */
24419
- bestEventId: external_exports.string().optional(),
24420
- /** Tag of the importance sub-signal that dominated the score
24421
- * (identity|dwell|proximity|class|confidence|travel|zone). */
24422
- importanceReason: external_exports.string().optional(),
24423
- /** Audio-classification labels heard on the camera during the track's
24424
- * life (score ≥ device `classificationMinScore`), aggregated per label.
24425
- * Absent on legacy rows / tracks with no confident audio. */
24426
- audioLabels: external_exports.array(TrackAudioLabelSchema).readonly().optional(),
24427
- /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
24428
- * Populated from the persisted envelope columns on historical reads;
24429
- * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
24430
- envelope: TrackEnvelopeSchema.optional(),
24431
- /**
24432
- * A face DETECTOR found a face on this track — nothing more. It says the
24433
- * detail plane produced a `face` detail; it does NOT say the face was
24434
- * embedded, matched, above `minFacePx`, or that the recognizer was even
24435
- * enabled. Set once and never cleared.
24436
- *
24437
- * **This exists so "face present but not recognised" is expressible.** A
24438
- * recognised identity lands in `subLabel` (attributed to the face chain via
24439
- * `subLabelMeta.stepId`), so before this field a track with an unmatched face
24440
- * and a track with no face at all were byte-identical on the wire and no
24441
- * surface could tell them apart. The read is `hasFace === true && subLabel
24442
- * === undefined`.
24443
- *
24444
- * **Absent ≠ false.** Every row written before the column existed omits it,
24445
- * and so does every server that predates the field — a consumer must test
24446
- * `=== true` and render nothing otherwise, never infer "no face".
24447
- */
24448
- hasFace: external_exports.boolean().optional(),
24449
- /**
24450
- * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
24451
- * face an operator could ASSIGN to an identity.
24452
- *
24453
- * The STRICT twin of {@link hasFace}, and the pair only earns its keep
24454
- * because the two disagree. `hasFace` is stamped at the TOP of the face
24455
- * branch, before every gate, and means no more than "a face detector produced
24456
- * a face detail". This one is stamped at the single moment the gallery row
24457
- * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
24458
- * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
24459
- * candidate gate, the imageless-track drop (no crop was ever captured) and
24460
- * the crop-store drop. Everything between the detector and that insert can
24461
- * legitimately refuse the face, so a flag written any earlier promises the
24462
- * operator something to assign and delivers nothing.
24463
- *
24464
- * **Independent of recognition.** A face collected but never auto-matched is
24465
- * still assignable — it is in fact the face an operator most wants to reach —
24466
- * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
24467
- * `subLabel`; this says only that the raw material exists.
24468
- *
24469
- * **Set once, never cleared.** A track that produced a gallery row produced
24470
- * one; deleting the row later is the gallery's business, not this flag's.
24471
- *
24472
- * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
24473
- * before the column omits it, and so does every server that predates the
24474
- * field. A consumer must test `=== true` and render nothing otherwise —
24475
- * never infer "no assignable face".
24476
- */
24477
- hasEmbeddedFace: external_exports.boolean().optional(),
24395
+ var PipelineValidationResultSchema = external_exports.object({
24396
+ ok: external_exports.boolean(),
24397
+ issues: external_exports.array(PipelineValidationIssueSchema).readonly(),
24398
+ substitutions: external_exports.array(ModelSubstitutionSchema).readonly(),
24399
+ /** The node's `currentEngine.format` this validation ran against. */
24400
+ format: external_exports.string()
24401
+ });
24402
+ var ReferenceImageEntrySchema = external_exports.object({
24403
+ filename: external_exports.string(),
24404
+ stepIds: external_exports.array(external_exports.string()).readonly().optional()
24405
+ });
24406
+ var ReferenceImageBodySchema = external_exports.object({
24407
+ base64: external_exports.string(),
24408
+ filename: external_exports.string()
24409
+ });
24410
+ var ReferenceAudioEntrySchema = external_exports.object({
24411
+ filename: external_exports.string(),
24412
+ sizeKb: external_exports.number()
24413
+ });
24414
+ var ReferenceAudioBodySchema = external_exports.object({ base64: external_exports.string() });
24415
+ var AudioBackendSchema = external_exports.object({
24416
+ id: external_exports.string(),
24417
+ name: external_exports.string(),
24418
+ description: external_exports.string(),
24419
+ available: external_exports.boolean(),
24478
24420
  /**
24479
- * This subject CONTAINS a folded rider a person the rider-pairing step
24480
- * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
24481
- * so the passage is tracked once and as a VEHICLE.
24482
- *
24483
- * It exists because the fold's record was dishonest. D34 and the code both
24484
- * said "the person is not lost — it is reported so both entities stay on the
24485
- * record"; in fact the pair went into a per-processor RAM field behind an
24486
- * accessor nobody called, and every durable surface said `vehicle`, full
24487
- * stop. This is the composition note that makes the row true.
24488
- *
24489
- * A COMPOSITION, never a class and never a label. "This vehicle contains a
24490
- * person" is not an answer to "what is this" — both label tiers would refuse
24491
- * a macro token anyway (D89), and correctly. Nothing here changes what the
24492
- * subject IS: a cyclist stays one vehicle track, occupancy still counts one,
24493
- * and a `person` rule still does not fire for someone cycling past.
24494
- *
24495
- * **Absent ≠ false**, exactly like {@link hasFace}: every row written before
24496
- * the column, and every hub that predates the field, omits it. Test
24497
- * `=== true` and render nothing otherwise — never infer "no rider".
24421
+ * Raw classifier labels this backend can emit (e.g. YAMNet's
24422
+ * 521-class set or Apple SoundAnalysis's 303-class set). Used by
24423
+ * the benchmark UI to populate the `enabledMicroClasses` filter
24424
+ * specific to the selected backend without a separate fetch.
24498
24425
  */
24499
- hasRider: external_exports.boolean().optional(),
24500
- ...TrackFlagFields,
24501
- ...TrackRetrainFields
24426
+ rawLabels: external_exports.array(external_exports.string()).readonly().optional()
24502
24427
  });
24503
- var BaseEventFields = {
24504
- id: external_exports.string(),
24505
- deviceId: external_exports.number(),
24506
- timestamp: external_exports.number()
24507
- };
24508
- var MotionEventSchema = external_exports.object({
24509
- ...BaseEventFields,
24510
- kind: external_exports.literal("motion"),
24511
- regionCount: external_exports.number(),
24512
- /** Heavy JSON array — omitted in slim projection. */
24513
- regions: external_exports.array(external_exports.object({
24514
- bbox: BoundingBoxSchema,
24515
- pixelCount: external_exports.number(),
24516
- intensity: external_exports.number()
24517
- })).readonly().optional(),
24518
- /** Omitted in slim projection. */
24519
- frameWidth: external_exports.number().optional(),
24520
- /** Omitted in slim projection. */
24521
- frameHeight: external_exports.number().optional(),
24522
- /** Populated by B5 (recording playback URL for this event). */
24523
- mediaUrl: external_exports.string().optional()
24524
- });
24525
- var DetectionSourceSchema = external_exports.enum(["pipeline", "onboard"]);
24526
- var ZoneCrossingDirectionSchema = external_exports.enum(["enter", "exit"]);
24527
- var ZoneCrossingSchema = external_exports.object({
24528
- direction: ZoneCrossingDirectionSchema,
24529
- /** Admin zone id crossed. */
24530
- zoneId: external_exports.string(),
24531
- /** Zone display name at crossing time (falls back to the id). */
24532
- zoneName: external_exports.string().optional()
24533
- });
24534
- var ObjectEventSchema = external_exports.object({
24535
- ...BaseEventFields,
24536
- kind: external_exports.literal("object"),
24537
- /** Detection source. Optional for backward-compat; absent ⇒ `pipeline`. */
24538
- source: DetectionSourceSchema.optional(),
24539
- /**
24540
- * Inference-frame id shared by every object event emitted from the SAME frame
24541
- * — the "scene/parent" key. Lets a consumer group co-occurring detections (e.g.
24542
- * 2 people + 1 dog) under one full frame, and link a track's frames over time
24543
- * (group by `trackId`, pick a representative `frameId` as the parent frame).
24544
- * Optional for backward-compat with pre-existing rows / the slim projection
24545
- * includes it (it is light). Absent on rows written before this field.
24546
- */
24547
- frameId: external_exports.string().optional(),
24548
- /** Omitted in slim projection. */
24549
- trackId: external_exports.string().optional(),
24550
- className: external_exports.string(),
24551
- ...TieredLabelFields,
24552
- /** Omitted in slim projection. */
24553
- confidence: external_exports.number().optional(),
24554
- /** Heavy JSON — omitted in slim projection. */
24555
- bbox: BoundingBoxSchema.optional(),
24556
- /** Heavy JSON — omitted in slim projection. */
24557
- zones: external_exports.array(external_exports.string()).readonly().optional(),
24558
- /** Omitted in slim projection. */
24559
- state: TrackStateSchema.optional(),
24560
- /**
24561
- * The zone crossing this event IS, when it is one. Absent on every other
24562
- * event kind (movement state, appearance, package) — see
24563
- * {@link ZoneCrossingSchema}. Omitted in slim projection.
24564
- */
24565
- zoneCrossing: ZoneCrossingSchema.optional(),
24566
- /** Detection-frame dimensions in pixels — let consumers normalize the
24567
- * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
24568
- frameWidth: external_exports.number().optional(),
24569
- frameHeight: external_exports.number().optional(),
24570
- /** MediaStore key for the crop attached to this event (if any). */
24571
- mediaKey: external_exports.string().optional(),
24572
- /** Design B: MediaStore key of the track's native-resolution key frame (the
24573
- * best-detection full frame). Resolve via the event-media data-plane
24574
- * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
24575
- * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
24576
- * sources — consumers fall back to `mediaKey` (the tight crop). */
24577
- keyFrameMediaKey: external_exports.string().optional(),
24578
- /** Populated by B5 (recording playback URL for this event). */
24579
- mediaUrl: external_exports.string().optional(),
24580
- /** The parent track's key-event importance [0,1], propagated to every object
24581
- * event of the track (so an event row can be sorted by importance without a
24582
- * track join). Absent on legacy rows / before the track was scored. */
24583
- importance: external_exports.number().optional()
24584
- });
24585
- var AudioEventSchema = external_exports.object({
24586
- ...BaseEventFields,
24587
- kind: external_exports.literal("audio"),
24588
- rms: external_exports.number(),
24589
- dbfs: external_exports.number(),
24590
- classification: external_exports.object({
24591
- className: external_exports.string(),
24592
- originalClass: external_exports.string().optional(),
24593
- score: external_exports.number()
24594
- }).optional(),
24595
- /** Populated by B5 (recording playback URL for this event). */
24596
- mediaUrl: external_exports.string().optional()
24597
- });
24598
- var MediaFileKindEnum = external_exports.enum([
24599
- "crop",
24600
- "thumbnail",
24601
- "snapshot",
24602
- "firstFrame",
24603
- "lastFrame",
24604
- "fullFrame",
24605
- "fullFrameBoxed",
24606
- "faceCrop",
24607
- "plateCrop",
24608
- "keyFrame",
24609
- "keyFrameSmall",
24610
- "thumbnailSmall"
24611
- ]);
24612
- var MediaFileSchema = external_exports.object({
24613
- key: external_exports.string(),
24614
- kind: MediaFileKindEnum,
24615
- base64: external_exports.string(),
24616
- sizeBytes: external_exports.number(),
24617
- timestamp: external_exports.number()
24618
- });
24619
- var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
24620
- var RetrainMacroClassSchema = external_exports.enum([
24621
- "person",
24622
- "vehicle",
24623
- "animal",
24624
- "package",
24625
- "face",
24626
- "plate"
24627
- ]);
24628
- var RetrainAnnotationKindSchema = external_exports.enum(["subject", "model_error"]);
24629
- var RetrainAnnotationSourceSchema = external_exports.enum(["operator", "assist"]);
24630
- var RetrainBboxSchema = external_exports.object({
24631
- x: external_exports.number(),
24632
- y: external_exports.number(),
24633
- w: external_exports.number(),
24634
- h: external_exports.number()
24635
- });
24636
- var RetrainAnnotationSchema = external_exports.object({
24637
- id: external_exports.string(),
24638
- trackId: external_exports.string(),
24639
- deviceId: external_exports.number(),
24640
- /** The COPY in retrain storage — never the source track's media key. */
24641
- mediaKey: external_exports.string(),
24642
- bbox: RetrainBboxSchema,
24643
- macroClass: RetrainMacroClassSchema,
24644
- label: external_exports.string().optional(),
24645
- subLabel: external_exports.string().optional(),
24646
- kind: RetrainAnnotationKindSchema,
24647
- source: RetrainAnnotationSourceSchema,
24648
- /** Which model proposed this box — or, on a `model_error`, drew the phantom. */
24649
- assistModelId: external_exports.string().optional(),
24650
- assistScore: external_exports.number().optional(),
24651
- exportedInBatch: external_exports.string().optional(),
24652
- createdAt: external_exports.number()
24653
- });
24654
- var RetrainAnnotationDraftSchema = RetrainAnnotationSchema.omit({
24655
- id: true,
24656
- trackId: true,
24657
- deviceId: true,
24658
- mediaKey: true,
24659
- createdAt: true,
24660
- exportedInBatch: true
24661
- });
24662
- var RetrainTrackSchema = external_exports.object({
24663
- trackId: external_exports.string(),
24664
- deviceId: external_exports.number(),
24665
- className: external_exports.string(),
24666
- label: external_exports.string().optional(),
24667
- firstSeen: external_exports.number(),
24668
- lastSeen: external_exports.number(),
24669
- /** How many frames the dataset already holds from this track. */
24670
- frameCount: external_exports.number().int(),
24671
- /** How many subjects have been annotated on those frames. `0` with
24672
- * `frameCount: 0` is exactly "staging, still to work". */
24673
- annotationCount: external_exports.number().int()
24674
- });
24675
- var RetrainFrameCandidateSchema = external_exports.object({
24676
- mediaKey: external_exports.string(),
24677
- kind: MediaFileKindEnum,
24678
- timestamp: external_exports.number(),
24679
- sizeBytes: external_exports.number().int(),
24680
- /** A copy of this original already exists — selecting it is free and cannot
24681
- * fail, whatever became of the original. */
24682
- copied: external_exports.boolean()
24683
- });
24684
- var RetrainFrameSchema = external_exports.object({
24685
- frameId: external_exports.string(),
24686
- deviceId: external_exports.number(),
24687
- trackId: external_exports.string(),
24688
- /** Provenance only. It may already point at nothing — that is expected. */
24689
- sourceMediaKey: external_exports.string(),
24690
- sourceKind: MediaFileKindEnum,
24691
- sizeBytes: external_exports.number().int(),
24692
- width: external_exports.number().int(),
24693
- height: external_exports.number().int(),
24694
- copiedAt: external_exports.number()
24695
- });
24696
- var RetrainCopyRefusalSchema = external_exports.enum([
24697
- "source-missing",
24698
- "unreadable-image",
24699
- "write-failed"
24700
- ]);
24701
- var RetrainFrameSelectionSchema = external_exports.object({
24702
- copied: external_exports.array(RetrainFrameSchema).readonly(),
24703
- refused: external_exports.array(external_exports.object({
24704
- sourceMediaKey: external_exports.string(),
24705
- reason: RetrainCopyRefusalSchema
24706
- })).readonly()
24428
+ var AudioCapabilitiesSchema = external_exports.object({
24429
+ activeBackend: external_exports.string(),
24430
+ availableBackends: external_exports.array(AudioBackendSchema).readonly(),
24431
+ sampleRate: external_exports.number(),
24432
+ chunkDurationMs: external_exports.number()
24707
24433
  });
24708
- var RetrainFrameListSchema = external_exports.object({
24709
- candidates: external_exports.array(RetrainFrameCandidateSchema).readonly(),
24710
- copies: external_exports.array(RetrainFrameSchema).readonly(),
24711
- /** What the page pre-selects — the native key frame when one survives. */
24712
- autoPickMediaKey: external_exports.string().optional()
24434
+ var DownloadModelResultSchema = external_exports.object({
24435
+ filePath: external_exports.string(),
24436
+ sizeMB: external_exports.number(),
24437
+ durationMs: external_exports.number()
24713
24438
  });
24714
- var RetrainAssistSubjectSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
24715
- kind: external_exports.literal("package"),
24716
- zone: RetrainBboxSchema.optional()
24717
- }), external_exports.object({
24718
- kind: external_exports.literal("objects"),
24719
- modelId: external_exports.string(),
24720
- minScore: external_exports.number().optional()
24721
- })]);
24722
- var RetrainAssistResultSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
24723
- kind: external_exports.literal("proposed"),
24724
- modelId: external_exports.string(),
24725
- stepId: external_exports.string(),
24726
- minScore: external_exports.number(),
24727
- /** Drafts, ready to edit. `source: 'assist'` until the operator touches one. */
24728
- proposals: external_exports.array(RetrainAnnotationDraftSchema).readonly(),
24729
- /** Returned by the runner but removed by the threshold. */
24730
- belowThreshold: external_exports.number().int()
24731
- }), external_exports.object({
24732
- kind: external_exports.literal("refused"),
24733
- /** `no-zone` is ours; the rest are the runner's own refusal vocabulary. */
24734
- reason: external_exports.string(),
24735
- detail: external_exports.string().optional()
24736
- })]);
24737
- var RetrainTransitionResultSchema = external_exports.object({
24738
- trackId: external_exports.string(),
24739
- /** Where the track ended up, whatever happened. */
24740
- retrainStatus: RetrainStatusSchema,
24741
- /** `false` ⇒ the move was refused or was a no-op; `reason` says which. */
24742
- changed: external_exports.boolean(),
24743
- reason: external_exports.enum([
24744
- "unknown-track",
24745
- "no-frames-copied",
24746
- "not-staging",
24747
- "not-trained",
24748
- "unchanged"
24749
- ]).optional()
24439
+ var AudioTestResultSchema = external_exports.object({
24440
+ success: external_exports.boolean(),
24441
+ error: external_exports.string().optional(),
24442
+ frame: external_exports.custom().optional()
24750
24443
  });
24751
- var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
24752
- var MAX_EVENT_QUERY_LIMIT = 5e3;
24753
- var DeviceEventQueryInput = external_exports.object({
24754
- deviceId: external_exports.number(),
24755
- since: external_exports.number().optional(),
24756
- until: external_exports.number().optional(),
24757
- limit: external_exports.number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
24758
- /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
24759
- * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
24760
- * exact behaviour. Callers may omit this field — the store defaults to
24761
- * `full` when not provided. */
24762
- projection: external_exports.enum(["full", "slim"]).optional()
24763
- });
24764
- var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: external_exports.string().optional() });
24765
- var RecentTracksQueryInput = external_exports.object({
24766
- /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
24767
- deviceIds: external_exports.array(external_exports.number()),
24768
- /** Window lower bound on `lastSeen` (inclusive). */
24769
- since: external_exports.number().optional(),
24770
- /** Window upper bound on `lastSeen` (inclusive). */
24771
- until: external_exports.number().optional(),
24772
- /** Page size. Default 200, max 1000. */
24773
- limit: external_exports.number().int().min(1).max(1e3).default(200),
24774
- /** Opaque continuation cursor from a previous page's `nextCursor`.
24775
- * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
24776
- cursor: external_exports.string().optional(),
24777
- /** See {@link TrackProjectionSchema}. Default `full`. */
24778
- projection: TrackProjectionSchema.optional(),
24779
- /** Include stationary-promoted rows (parked objects). Default false: the
24780
- * feed lists passages; parking records live on the stationary registry. */
24781
- includeStationary: external_exports.boolean().optional()
24782
- });
24783
- var RecentTracksPageSchema = external_exports.object({
24784
- /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
24785
- tracks: external_exports.array(TrackSchema).readonly(),
24786
- /** Cursor for the next page, or null when this page is the last. */
24787
- nextCursor: external_exports.string().nullable()
24788
- });
24789
- var LIST_GROUPS_DEFAULT_LIMIT = 40;
24790
- var LIST_GROUPS_MAX_LIMIT = 100;
24791
- var AnalyticsGroupRecordSchema = external_exports.object({
24792
- id: external_exports.string(),
24793
- deviceId: external_exports.number().int(),
24794
- openedAt: external_exports.number().int(),
24795
- closedAt: external_exports.number().int(),
24796
- timestamp: external_exports.number().int(),
24797
- memberCount: external_exports.number().int(),
24798
- memberTrackIds: external_exports.array(external_exports.string()).readonly(),
24799
- className: external_exports.string(),
24800
- classes: external_exports.array(external_exports.string()).readonly(),
24801
- /** Relative event-media path, or null when the group has no picture yet. */
24802
- mediaUrl: external_exports.string().nullable(),
24803
- singleton: external_exports.boolean()
24804
- });
24805
- var AnalyticsGroupMemberSchema = external_exports.object({
24806
- trackId: external_exports.string(),
24807
- deviceId: external_exports.number().int(),
24808
- className: external_exports.string(),
24809
- firstSeen: external_exports.number().int(),
24810
- lastSeen: external_exports.number().int(),
24811
- mediaUrl: external_exports.string().nullable()
24812
- });
24813
- var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: external_exports.array(AnalyticsGroupMemberSchema).readonly() });
24814
- var ListGroupsQueryInput = external_exports.object({
24815
- /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
24816
- deviceIds: external_exports.array(external_exports.number()),
24817
- /** Window lower bound on `closedAt` (inclusive). */
24818
- since: external_exports.number().optional(),
24819
- /** Window upper bound on `openedAt` (inclusive). */
24820
- until: external_exports.number().optional(),
24821
- limit: external_exports.number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
24822
- /** Opaque continuation cursor from a previous page's `nextCursor`. */
24823
- cursor: external_exports.string().optional()
24824
- });
24825
- var ListGroupsPageSchema = external_exports.object({
24826
- groups: external_exports.array(AnalyticsGroupRecordSchema).readonly(),
24827
- nextCursor: external_exports.string().nullable()
24828
- });
24829
- var KeyEventQueryInput = external_exports.object({
24830
- deviceId: external_exports.number(),
24831
- /** Window lower bound (track firstSeen ≥ since). */
24832
- since: external_exports.number(),
24833
- /** Window upper bound (track firstSeen ≤ until). */
24834
- until: external_exports.number(),
24835
- limit: external_exports.number().int().min(1).max(200).default(50),
24836
- /** Drop tracks scoring below this importance. */
24837
- minImportance: external_exports.number().min(0).max(1).optional(),
24838
- /** Restrict to a single class (e.g. 'person'). */
24839
- classFilter: external_exports.string().optional()
24840
- });
24841
- var KeyEventSchema = external_exports.object({
24842
- /** The representative event id (the track's best ObjectEvent, else its trackId). */
24843
- id: external_exports.string(),
24844
- trackId: external_exports.string(),
24845
- /** Track start time (firstSeen). */
24846
- timestamp: external_exports.number(),
24847
- className: external_exports.string(),
24848
- ...TieredLabelFields,
24849
- importance: external_exports.number(),
24850
- /** Highest-confidence ObjectEvent id for the track (empty when none). */
24851
- bestEventId: external_exports.string(),
24852
- /** Track lifetime in ms (lastSeen - firstSeen). */
24853
- windowMs: external_exports.number().optional(),
24854
- ...TrackFlagFields,
24855
- ...TrackRetrainFields
24856
- });
24857
- var TrackedDetectionSchema = external_exports.object({
24858
- trackId: external_exports.string(),
24859
- className: external_exports.string(),
24860
- confidence: external_exports.number(),
24861
- bbox: BoundingBoxSchema,
24862
- zones: external_exports.array(external_exports.string()).readonly(),
24863
- state: TrackStateSchema
24864
- });
24865
- var OverlayDetectionSchema = external_exports.looseObject({
24866
- id: external_exports.string(),
24867
- kind: external_exports.enum(["first-level", "detail"]),
24868
- macroClass: external_exports.string(),
24869
- score: external_exports.number(),
24870
- bbox: external_exports.object({
24871
- x: external_exports.number(),
24872
- y: external_exports.number(),
24873
- width: external_exports.number(),
24874
- height: external_exports.number()
24875
- }),
24876
- labels: external_exports.array(external_exports.looseObject({
24877
- label: external_exports.string(),
24878
- score: external_exports.number()
24879
- })).readonly(),
24880
- parentId: external_exports.string().optional()
24881
- });
24882
- var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: external_exports.number() });
24883
- var SearchObjectEventsInput = external_exports.object({
24884
- text: external_exports.string(),
24885
- deviceId: external_exports.number().optional(),
24886
- since: external_exports.number().optional(),
24887
- until: external_exports.number().optional(),
24888
- classFilter: external_exports.string().optional(),
24889
- limit: external_exports.number().default(50),
24890
- minScore: external_exports.number().min(0).max(1).default(0.2)
24891
- });
24892
- var TrackCascadeCountsSchema = external_exports.object({
24893
- /** Persisted track roots deleted (authoritative). */
24894
- tracks: external_exports.number().int(),
24895
- /** Object events removed with their tracks (best-effort; see note above). */
24896
- events: external_exports.number().int(),
24897
- /** Track/face/plate-owned media removed (best-effort). Never identity media. */
24898
- media: external_exports.number().int(),
24899
- /** Unassigned (non-enrolled) face reads removed (best-effort). */
24900
- faces: external_exports.number().int(),
24901
- /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
24902
- plates: external_exports.number().int(),
24903
- /** Per-track CLIP search vectors removed (best-effort). */
24904
- embeddings: external_exports.number().int(),
24905
- /** Group membership + group rows removed with their last member (best-effort). */
24906
- groups: external_exports.number().int()
24907
- });
24908
- var DiskReconcileCountsSchema = external_exports.object({
24909
- mediaDropped: external_exports.number().int(),
24910
- tracks: external_exports.number().int(),
24911
- events: external_exports.number().int()
24912
- });
24913
- var EventStoreDeviceFootprintSchema = external_exports.object({
24914
- deviceId: external_exports.number(),
24915
- /** Persisted event rows (motion + object + audio) for the camera. */
24916
- rows: external_exports.number().int(),
24917
- /** Event-owned media bytes on disk for the camera. */
24918
- bytes: external_exports.number().int()
24919
- });
24920
- var EventStoreFootprintSchema = external_exports.object({
24921
- totalRows: external_exports.number().int(),
24922
- totalBytes: external_exports.number().int(),
24923
- devices: external_exports.array(EventStoreDeviceFootprintSchema).readonly()
24924
- });
24925
- var EventPruneCountsSchema = external_exports.object({
24926
- motion: external_exports.number().int(),
24927
- object: external_exports.number().int(),
24928
- audio: external_exports.number().int()
24929
- });
24930
- var RebuildObjectEmbeddingsInput = external_exports.object({
24931
- /** Restrict to one camera. Omit for the whole fleet. */
24932
- deviceId: external_exports.number().optional(),
24933
- since: external_exports.number().optional(),
24934
- until: external_exports.number().optional(),
24935
- /** Stop after this many tracks; the result reports whether more remain. */
24936
- maxTracks: external_exports.number().int().positive().optional(),
24937
- /**
24938
- * Run every embedding on THIS node instead of round-robining the fleet.
24939
- *
24940
- * Named `executeOnNodeId` and not `nodeId` on purpose: an inline `nodeId`
24941
- * field in cap args is read by `parent-unowned-call.ts` as a ROUTING PIN, so
24942
- * calling it that would pin the rebuild REQUEST itself to that node — the
24943
- * rebuild orchestration lives on the hub, and only the per-track step runs
24944
- * remotely. This field is data; the per-track pin is applied inside.
24945
- *
24946
- * Absent ⇒ round-robin over every online node whose runner can serve the
24947
- * pinned model.
24948
- */
24949
- executeOnNodeId: external_exports.string().optional(),
24950
- /**
24951
- * Milliseconds to wait between tracks; omit for the built-in default, `0` to
24952
- * run flat out.
24953
- *
24954
- * A rebuild is bulk maintenance on hub-main's single thread. Measured
24955
- * 2026-08-06, an unpaced pass held that thread busy 82.2 s out of 120 and
24956
- * pushed `nodes.topology` from 0.25 s to 26 s for 43 minutes. The value in
24957
- * force is logged at start and finish so a deliberately slow pass reads
24958
- * differently from a stalled one.
24959
- */
24960
- pacingMs: external_exports.number().int().nonnegative().optional()
24961
- });
24962
- var WipeObjectEmbeddingsResultSchema = external_exports.object({ deleted: external_exports.number() });
24963
- var RebuildObjectEmbeddingsResultSchema = external_exports.object({
24964
- started: external_exports.boolean(),
24965
- /** True when a pass was already running; the new request is ignored. */
24966
- alreadyRunning: external_exports.boolean()
24967
- });
24968
- var RebuildStatusSchema = external_exports.object({
24969
- running: external_exports.boolean(),
24970
- scanned: external_exports.number(),
24971
- rebuilt: external_exports.number(),
24972
- /** Tracks whose key frame is gone — nothing to re-embed from. */
24973
- missingKeyFrame: external_exports.number(),
24974
- /** Tracks with no usable detection box. */
24975
- missingBbox: external_exports.number(),
24976
- /**
24977
- * Tracks an executing node REFUSED rather than broke on — an unreadable key
24978
- * frame, a step that threw. Separate from `failed` because the remedy is
24979
- * different, and because a whole camera silently contributing zero vectors
24980
- * is the shape of failure a rebuild must never hide.
24981
- */
24982
- notRunnable: external_exports.number(),
24983
- /**
24984
- * The pass stopped because NO node could serve the pinned model.
24985
- *
24986
- * Distinct from `notRunnable` on purpose: that one says "this track was
24987
- * refused", this one says "the cluster cannot do this work at all" — every
24988
- * candidate node either lacks the `clip-embedding` step, lacks a build of the
24989
- * pinned model for its engine format, or dropped out. The remedy is a model /
24990
- * engine change, not a per-camera one. Non-zero here always comes with
24991
- * `complete: false`.
24992
- */
24993
- noCapableNode: external_exports.number(),
24994
- failed: external_exports.number(),
24995
- /** Set once a pass ends: true only when EVERYTHING was covered. */
24996
- complete: external_exports.boolean().nullable(),
24997
- startedAtMs: external_exports.number().nullable(),
24998
- finishedAtMs: external_exports.number().nullable(),
24999
- /** Present when the pass ended by throwing. */
25000
- error: external_exports.string().nullable()
25001
- });
25002
- var pipelineAnalyticsCapability = {
25003
- name: "pipeline-analytics",
25004
- scope: "device",
25005
- mode: "singleton",
25006
- kind: "wrapper",
25007
- defaultActive: true,
25008
- deviceTypes: [DeviceType.Camera],
25009
- exposesDeviceSettings: true,
25010
- methods: {
25011
- getActiveTracks: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(TrackSchema).readonly()),
25012
- getTrack: method(external_exports.object({
25013
- deviceId: external_exports.number(),
25014
- trackId: external_exports.string()
25015
- }), TrackSchema.nullable()),
25016
- /** Historical completed tracks for a device. Queried from the
25017
- * persisted `pipeline-analytics:tracks` collection; active tracks
25018
- * still in RAM are not included. */
25019
- listTracks: method(external_exports.object({
25020
- deviceId: external_exports.number(),
25021
- since: external_exports.number().optional(),
25022
- until: external_exports.number().optional(),
25023
- limit: external_exports.number().optional(),
25024
- /** Spatial filter — only tracks whose trajectory intersects the zone
25025
- * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
25026
- * envelope columns, then precisely tested per position. Tracks with
25027
- * an unknown envelope (no frame dims at persist time) always match. */
25028
- zone: TrackZoneFilterSchema.optional(),
25029
- /** See {@link TrackProjectionSchema}. Default `full` (backward
25030
- * compatible — omitting the field keeps today's exact behaviour). */
25031
- projection: TrackProjectionSchema.optional(),
25032
- /** Include stationary-promoted rows (parked objects handed to the
25033
- * stationary registry). Default false: the timeline lists passages,
25034
- * not parking records (operator decision, 2026-08-15). */
25035
- includeStationary: external_exports.boolean().optional()
25036
- }), external_exports.array(TrackSchema).readonly()),
25037
- /**
25038
- * Batched cluster-wide track listing — ONE call for the events page /
25039
- * reel first paint instead of a per-camera `listTracks` fan-out. Merges
25040
- * the persisted completed tracks of every requested device, sorted by
25041
- * `lastSeen` DESC with a stable (lastSeen, trackId) cursor. Per-device
25042
- * indexed pages (`idx_tracks_device_lastSeen`) are k-way merged
25043
- * provider-side; `projection: 'slim'` drops the heavy `positions[]` /
25044
- * `snapshots[]` JSON (returned as empty arrays). Active in-RAM tracks
25045
- * are not included (same contract as `listTracks`).
25046
- */
25047
- listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
25048
- /**
25049
- * Batched co-moving group listing — the Groups feed. Same merge/cursor
25050
- * contract as {@link listRecentTracks}. A group is a sealed partition of
25051
- * one session; `getGroup` is the detail with members.
25052
- */
25053
- listGroups: method(ListGroupsQueryInput, ListGroupsPageSchema),
25054
- getGroup: method(external_exports.object({
25055
- deviceId: external_exports.number(),
25056
- groupId: external_exports.string().min(1)
25057
- }), AnalyticsGroupDetailSchema.nullable()),
25058
- clearTracks: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), {
25059
- kind: "mutation",
25060
- auth: "admin"
25061
- }),
25062
- getMotionEvents: method(DeviceEventQueryInput, external_exports.array(MotionEventSchema).readonly()),
25063
- getObjectEvents: method(ObjectEventQueryInput, external_exports.array(ObjectEventSchema).readonly()),
25064
- getAudioEvents: method(DeviceEventQueryInput, external_exports.array(AudioEventSchema).readonly()),
25065
- /**
25066
- * Every event kind the device can produce: built-ins (motion + audio),
25067
- * detection classes actually observed for the device (from the track
25068
- * history), and sensor kinds contributed by LINKED devices (resolved via
25069
- * `device-manager.getLinkedDevices`, mapped through `EVENT_KIND_BY_CAP`).
25070
- */
25071
- listEventKinds: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(EventKindDescriptorSchema).readonly()),
25072
- /**
25073
- * The same answer for many cameras in ONE call.
25074
- *
25075
- * `listEventKinds` is the slowest per-device query on the hub — measured
25076
- * at ~100 ms against 6-11 ms for its neighbours — because composing it
25077
- * costs a `getCameraStatus`, a `getLinkedDevices` and one `getBindings`
25078
- * per linked device. The viewer asks for it once per camera at first
25079
- * paint, so twelve cameras paid twelve of those round-trips on top of the
25080
- * per-camera cost.
25081
- *
25082
- * Batched, the camera statuses come from ONE `getCameraStatuses` instead
25083
- * of N, and the per-device composition runs concurrently.
25084
- *
25085
- * Per-device rather than pre-merged on purpose: a caller that renders one
25086
- * camera's lanes needs to know WHICH camera a kind came from, and merging
25087
- * is three lines the caller can do. `listEventKinds` stays for
25088
- * single-device callers.
25089
- */
25090
- listEventKindsBatch: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).min(1).max(200) }), external_exports.array(EventKindsForDeviceSchema).readonly()),
25091
- /**
25092
- * Sensor-event history for a camera: state changes of LINKED sensor
25093
- * devices, attributed to the camera at ingest time (one row per linked
25094
- * camera). Mirrors `getAudioEvents` query semantics; `kinds` narrows to
25095
- * a kind subset.
25096
- */
25097
- getSensorEvents: method(external_exports.object({
25098
- deviceId: external_exports.number(),
25099
- since: external_exports.number().optional(),
25100
- until: external_exports.number().optional(),
25101
- kinds: external_exports.array(external_exports.string()).optional(),
25102
- limit: external_exports.number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
25103
- }), external_exports.array(SensorEventSchema).readonly()),
25104
- /**
25105
- * Importance-ranked highlights for a device+window. Queries completed
25106
- * tracks by (deviceId, firstSeen ∈ [since,until]), scores each (or reuses
25107
- * the persisted score), filters by minImportance/classFilter, orders by
25108
- * importance desc, and returns up to `limit` compact key events mapped to
25109
- * each track's best event. Legacy tracks lacking a persisted score are
25110
- * scored on-read (no write). Degrades to `[]` on error.
25111
- */
25112
- getKeyEvents: method(KeyEventQueryInput, external_exports.array(KeyEventSchema).readonly()),
25113
- /** Server-side bucketed event counts for the 24-hour timeline.
25114
- * Returns one entry per non-empty bucket; empty buckets are omitted. */
25115
- getEventDensity: method(external_exports.object({
25116
- deviceId: external_exports.number(),
25117
- since: external_exports.number(),
25118
- until: external_exports.number(),
25119
- bucketMs: external_exports.number().int().positive()
25120
- }), external_exports.array(external_exports.object({
25121
- bucketStart: external_exports.number(),
25122
- motion: external_exports.number().int(),
25123
- object: external_exports.number().int(),
25124
- audio: external_exports.number().int()
25125
- })).readonly()),
25126
- /**
25127
- * @deprecated Prefer `pruneTracksBefore` — the track is the ROOT of the
25128
- * analytics model and retention must cascade from it (design §5.1). This
25129
- * event-only prune leaves orphaned tracks/faces/plates/embeddings behind.
25130
- * Retained as a compat shim for the Phase-B3 recorder orchestration caller,
25131
- * which prunes events to keep history aligned with available footage and
25132
- * reads the per-kind counts. Delete all events (motion + object + audio)
25133
- * for the given device older than `cutoffMs` (exclusive) + their thumbnails
25134
- * in lockstep; returns per-kind deleted counts.
25135
- */
25136
- pruneEventsBefore: method(external_exports.object({
25137
- deviceId: external_exports.number(),
25138
- cutoffMs: external_exports.number()
25139
- }), external_exports.object({
25140
- motion: external_exports.number().int(),
25141
- object: external_exports.number().int(),
25142
- audio: external_exports.number().int()
25143
- }), {
25144
- kind: "mutation",
25145
- auth: "admin"
25146
- }),
25147
- /**
25148
- * Track-centric retention entry point (design §5.1). Selects every
25149
- * persisted track for the device whose `lastSeen < cutoffMs` (paged) and
25150
- * runs the extensible whole-track deletion cascade (design §3): object
25151
- * events + their media, track/face/plate-owned media, unassigned face
25152
- * reads, per-track CLIP embeddings, then the track root LAST. ENROLLED
25153
- * (assigned) faces/plates and `ownerKind:'identity'` media are exempt —
25154
- * the durable matching gallery always persists. Fires the per-track
25155
- * live-state cleanup so a pruned track can't linger in dispatch/overlay
25156
- * state. Returns per-family deleted counts (`tracks` authoritative).
25157
- *
25158
- * Replaces `pruneEventsBefore` as the correct time-based retention prune.
25159
- */
25160
- pruneTracksBefore: method(external_exports.object({
25161
- deviceId: external_exports.number(),
25162
- cutoffMs: external_exports.number()
25163
- }), TrackCascadeCountsSchema, {
25164
- kind: "mutation",
25165
- auth: "admin"
25166
- }),
25167
- /**
25168
- * Operator "clean slate" for a device (design §5.3): prune EVERY track for
25169
- * the device via the same cascade as `pruneTracksBefore` with `cutoffMs =
25170
- * now`. One call per device — no client-side track enumeration. Enrolled
25171
- * gallery + identity media are exempt (design §4). Returns per-family
25172
- * deleted counts.
25173
- */
25174
- wipeAllAnalytics: method(external_exports.object({ deviceId: external_exports.number() }), TrackCascadeCountsSchema, {
25175
- kind: "mutation",
25176
- auth: "admin"
25177
- }),
25178
- /**
25179
- * Disk-wins reconcile for one camera. Drops media index rows whose blobs
25180
- * are gone, then cascades tracks (including favourited and staging) that
25181
- * have no remaining files. Enrolled identity/vehicle/scene media is never
25182
- * probed. Trackless motion/audio events with no remaining file are dropped,
25183
- * including snapshot-less rows.
25184
- */
25185
- reconcileFromDisk: method(external_exports.object({ deviceId: external_exports.number() }), DiskReconcileCountsSchema, {
25186
- kind: "mutation",
25187
- auth: "admin"
25188
- }),
25189
- /**
25190
- * Delete whole tracks (object events) by id for the given device,
25191
- * cascading their media in lockstep. Returns the number of tracks
25192
- * actually deleted plus the ids that could not be removed.
25193
- *
25194
- * Operator-driven from the DI Events page (batch delete). Routes through
25195
- * the same widened track-deletion cascade (design §5.2).
25196
- */
25197
- deleteTracks: method(external_exports.object({
25198
- deviceId: external_exports.number(),
25199
- trackIds: external_exports.array(external_exports.string()).min(1)
25200
- }), external_exports.object({
25201
- deleted: external_exports.number().int(),
25202
- failed: external_exports.array(external_exports.string()).readonly()
25203
- }), {
25204
- kind: "mutation",
25205
- auth: "admin"
25206
- }),
25207
- /**
25208
- * Set the per-track operator flags (`markForTrain`, `debug`, `favourited`) on ONE track.
25209
- * The patch is PARTIAL — an omitted key is left untouched — because the
25210
- * three surfaces that write it (admin Events grid, viewer track detail,
25211
- * viewer cluster detail) each own one toggle and must not clobber the other.
25212
- *
25213
- * Writes the track ROW: `markForTrain`/`debug` are per-TRACK state, so they
25214
- * live where `label` and `importance` live, not in any per-device settings
25215
- * store. Updates the in-RAM active track too, so a flag set on a live track
25216
- * survives its expiry-time persist.
25217
- *
25218
- * `auth: 'protected'` (the default), NOT `admin`: the viewer is an
25219
- * authenticated non-admin surface and two of the three call sites are
25220
- * there. The note that used to sit here said to revisit this the day a flag
25221
- * gained an effect that costs storage, and D81 is that day — `markForTrain`
25222
- * now pins. It STAYS protected, and the reason is that the alternative
25223
- * makes the feature pointless: marking a track is something you do while
25224
- * looking at it, on the surface you were already looking at it on, and that
25225
- * surface is the viewer. What the storage cost gets instead is a BOUND — a
25226
- * per-device pin budget enforced in the body, refusing a new pin past the
25227
- * limit while always allowing un-marking. `deleteTracks` next door is still
25228
- * admin, because destroying evidence and preserving it are not symmetric.
25229
- *
25230
- * `markForTrain` writes the retrain LIFECYCLE, not a boolean column: `true`
25231
- * is `none → staging`, `false` is `staging → none`. A track already
25232
- * `trained` refuses BOTH — its frames are copies inside the retrain dataset
25233
- * and re-staging it from a generic toggle is how the same material gets
25234
- * annotated twice under two ground truths. Returning a trained track to
25235
- * staging is a deliberate action of the retrain page, which is also the only
25236
- * thing that produces `trained` in the first place.
25237
- *
25238
- * Returns the RESOLVED state of both flags (absent → `false`) plus the
25239
- * `retrainStatus` they were derived from, so a caller can drive its toggle
25240
- * — and render a `trained` badge — without a re-fetch. Rejects an unknown
25241
- * track, a new staging mark on a device already holding its full budget, and
25242
- * any `markForTrain` write against a trained track.
25243
- */
25244
- setTrackFlags: method(external_exports.object({
25245
- /** Log/audit scope only — the trackId is globally unique on its own. */
25246
- deviceId: external_exports.number(),
25247
- trackId: external_exports.string(),
25248
- flags: TrackFlagsPatchSchema
25249
- }), TrackFlagsSchema, { kind: "mutation" }),
25250
- /**
25251
- * Durable event-store footprint for the management UI: event rows
25252
- * (motion + object + audio) counted per camera + total, plus the
25253
- * event-owned media bytes on disk per camera + total. Stat/count-based,
25254
- * computed on demand.
25255
- */
25256
- getEventStoreFootprint: method(external_exports.object({}), EventStoreFootprintSchema, {
25257
- kind: "query",
25258
- auth: "admin"
25259
- }),
25260
- /**
25261
- * Cluster-wide prune of events older than `olderThanMs` (exclusive) across
25262
- * every camera, deleting each event's media in lockstep. Logged to the
25263
- * events ops-log with `reason` (default `'retention'`). Returns the summed
25264
- * per-kind deleted counts.
25265
- */
25266
- pruneEvents: method(external_exports.object({
25267
- olderThanMs: external_exports.number(),
25268
- reason: OpsLogReasonSchema.optional()
25269
- }), EventPruneCountsSchema, {
25270
- kind: "mutation",
25271
- auth: "admin"
25272
- }),
24444
+ var PipelineConfigBridge = external_exports.custom();
24445
+ var ConfigUISchemaBridge = external_exports.custom();
24446
+ var ConfigUISchemaNullableBridge = external_exports.custom();
24447
+ var InferenceCapabilitiesBridge = external_exports.custom();
24448
+ var ModelAvailabilityListBridge = external_exports.custom();
24449
+ var PipelineRunResultBridge = external_exports.custom();
24450
+ var pipelineExecutorCapability = {
24451
+ name: "pipeline-executor",
24452
+ scope: "system",
24453
+ mode: "singleton",
24454
+ methods: {
24455
+ getAvailableEngines: method(external_exports.void(), external_exports.array(PipelineEngineChoiceSchema)),
24456
+ getSelectedEngine: method(external_exports.void(), PipelineEngineChoiceSchema),
24457
+ getDefaultSteps: method(PipelineEngineChoiceSchema, external_exports.array(PipelineDefaultStepSchema)),
25273
24458
  /**
25274
- * Manually delete EVERY event (motion + object + audio) for one camera and
25275
- * its event-owned media in lockstep. Logged to the events ops-log as
25276
- * `op:'manual-delete', reason:'manual'`. Destructive the admin UI guards
25277
- * it behind a confirm.
24459
+ * Per-node detection-engine provisioning snapshot. Returns the live
24460
+ * state of the lazy runtime-provisioning machine on `nodeId`
24461
+ * (idle / installing / verifying / ready / failed). The UI pairs this
24462
+ * one-shot query with the `pipeline.engine-provisioning` live event
24463
+ * (emitted on every transition) to drive a per-node "engine ready?"
24464
+ * indicator without polling. Phase 2.
25278
24465
  */
25279
- deleteDeviceEvents: method(external_exports.object({ deviceId: external_exports.number() }), EventPruneCountsSchema, {
25280
- kind: "mutation",
25281
- auth: "admin"
25282
- }),
25283
- /** The events ops-log rows (newest-first), optionally scoped to one camera.
25284
- * Backed by a declared pipeline-analytics SQLite collection. */
25285
- /** Internal migration-participant lease. It drains active MediaStore
25286
- * writes and refuses new ones without changing analytics bindings. */
25287
- pauseForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ paused: external_exports.literal(true) }), {
25288
- kind: "mutation",
25289
- auth: "admin"
25290
- }),
25291
- resumeForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ resumed: external_exports.literal(true) }), {
25292
- kind: "mutation",
25293
- auth: "admin"
25294
- }),
25295
- /** Drops cached location roots after the storage coordinator repoints a
25296
- * default so future event-media writes use the new root. */
25297
- refreshStorageLocationsForMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ refreshed: external_exports.literal(true) }), {
25298
- kind: "mutation",
25299
- auth: "admin"
25300
- }),
25301
- startStorageMigrationMove: method(StorageMigrationMediaMoveInputSchema, external_exports.object({ jobId: external_exports.string() }), {
25302
- kind: "mutation",
25303
- auth: "admin"
25304
- }),
25305
- getStorageMigrationMoveStatus: method(external_exports.object({ jobId: external_exports.string() }), RelocateJobSchema.nullable(), { auth: "admin" }),
25306
- cancelStorageMigrationMove: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
24466
+ getEngineProvisioning: method(external_exports.object({ nodeId: external_exports.string() }), EngineProvisioningSchema),
24467
+ getVideoPipelineSteps: method(external_exports.void(), external_exports.record(external_exports.string(), external_exports.object({
24468
+ modelId: external_exports.string(),
24469
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).readonly()
24470
+ }))),
24471
+ setVideoPipelineSteps: method(external_exports.object({ steps: external_exports.record(external_exports.string(), external_exports.object({
24472
+ modelId: external_exports.string(),
24473
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).readonly()
24474
+ })) }), external_exports.object({ success: external_exports.literal(true) }), {
25307
24475
  kind: "mutation",
25308
24476
  auth: "admin"
25309
24477
  }),
25310
24478
  /**
25311
- * Moves event media between locations the OPERATOR's mover, without the
25312
- * coordinator's lease. Twin of `recording.relocateFootage`: the same
25313
- * engine the lease-gated coordinated migration uses
25314
- * (`startStorageMigrationMove`), armable in the background and WITHOUT
25315
- * pausing anything. Exists because `eventMedia` was the only storage
25316
- * class whose only move path went through the recorder's global pause.
24479
+ * Clear THIS node's executor-side PER-DEVICE settings stores (the
24480
+ * per-camera step overrides the object-detection root reads via
24481
+ * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key the
24482
+ * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
24483
+ * dispatches to that node, so the provider method runs ON the target
24484
+ * node and receives no `nodeId`.
24485
+ *
24486
+ * This is the slimmed executor leg of the orchestrator's
24487
+ * `resetNodePipelineDefaults` flow (which owns the real reset: node
24488
+ * addonDefaults pins + per-camera orchestrator overrides). The legacy
24489
+ * `resetToDefault` — which reset a persisted global step-tree seed
24490
+ * nothing in the live per-camera path read — was removed together with
24491
+ * that seed.
25317
24492
  */
25318
- relocateMedia: method(RelocateMediaInputSchema, external_exports.object({ jobId: external_exports.string() }), {
25319
- kind: "mutation",
25320
- auth: "admin"
25321
- }),
25322
- /** Every relocate job this addon knows about, newest first (in RAM: the
25323
- * move is resumable, so a lost list costs nothing but the display). */
25324
- listRelocateMediaJobs: method(external_exports.object({}), external_exports.array(RelocateJobSchema).readonly(), {
25325
- kind: "query",
25326
- auth: "admin"
25327
- }),
25328
- /** Cancel a running or queued relocate job. */
25329
- cancelRelocateMedia: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
24493
+ clearDeviceOverrides: method(external_exports.object({ nodeId: external_exports.string() }), external_exports.object({
24494
+ success: external_exports.literal(true),
24495
+ clearedDevices: external_exports.number()
24496
+ }), {
25330
24497
  kind: "mutation",
25331
24498
  auth: "admin"
25332
24499
  }),
25333
- listOpsLog: method(OpsLogQueryInputSchema, external_exports.array(OpsLogEntrySchema).readonly(), {
25334
- kind: "query",
25335
- auth: "admin"
25336
- }),
25337
24500
  /**
25338
- * The CHEAP QUESTION, asked before any media moves: how big is the dataset
25339
- * the marked (`markForTrain`) tracks would produce?
24501
+ * Which of THIS node's inference devices the executor currently refuses,
24502
+ * and why. `nodeId` is the ROUTING key (stripped by the generated router).
25340
24503
  *
25341
- * Answered from media INDEX rows only key, kind, size, timestamp so it
25342
- * costs ~2 KB of reads per track and no blob reads at all. The measured harm
25343
- * behind D56 was a bulk pass that read and base64'd every blob a track owned
25344
- * before deciding anything, taking hub-main to 82 s busy out of 120; an
25345
- * export is that same I/O shape, so it inherits the same discipline: know
25346
- * the size, then decide.
24504
+ * The channel that did not exist. Pool health was known only inside the
24505
+ * detection addon and was an input to no routing decision anywhere: the
24506
+ * per-dispatch capability gate is keyed on model FORMAT and so can never
24507
+ * separate `openvino:gpu` from `openvino:npu`, and the orchestrator's live
24508
+ * eligibility probe (`platformProbe.getCapabilities`) answers about
24509
+ * HARDWARE which was present throughout. So when the hub's `openvino:gpu`
24510
+ * Python worker was SIGABRT'd by the Intel GPU plugin on 2026-08-25, the
24511
+ * balancer went on handing that dead pool cameras by rotation for 31 hours:
24512
+ * ~370 000 `PoolWorker[w0]: not initialized` lines, every frame lost.
25347
24513
  *
25348
- * `truncated` reports that more marked tracks exist than one pass carries.
25349
- * Empty `deviceIds` every device that has marked tracks.
24514
+ * Read semantics the caller depends on, and which the provider guarantees:
24515
+ * this is a synchronous read of in-memory state. It never probes hardware,
24516
+ * never spawns a pool and never throws — an EMPTY `unhealthy` means "asked,
24517
+ * nothing is refused", which is what re-admits a device. A read that FAILS
24518
+ * (node offline, version skew) must therefore be distinguishable from an
24519
+ * empty answer, and it is: it rejects.
25350
24520
  */
25351
- getTrainingExportSummary: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), TrainingExportSummarySchema, {
25352
- kind: "query",
25353
- auth: "admin"
25354
- }),
24521
+ getInferenceDeviceHealth: method(external_exports.object({ nodeId: external_exports.string() }), external_exports.object({ unhealthy: external_exports.array(external_exports.object({
24522
+ /** `<backend>:<device>`, e.g. `openvino:gpu`. */
24523
+ deviceKey: external_exports.string(),
24524
+ /**
24525
+ * `failed` — the per-device restart budget is exhausted; no pool
24526
+ * will be spawned until an operator re-arms it or the runner
24527
+ * respawns. `backoff` — under budget, waiting out the backoff (or
24528
+ * a cached pool observed dead and not yet condemned).
24529
+ */
24530
+ state: external_exports.enum(["failed", "backoff"]),
24531
+ /** Epoch ms of the death that produced this state. */
24532
+ since: external_exports.number(),
24533
+ /** Pool deaths inside the current window. */
24534
+ deaths: external_exports.number(),
24535
+ /** The last death's message. */
24536
+ lastError: external_exports.string()
24537
+ })).readonly() })),
25355
24538
  /**
25356
- * Where to download the dataset archive.
25357
- *
25358
- * The BYTES do not come back through this cap — they come from the returned
25359
- * data-plane URL, which streams a tar built entry by entry. A multi-gigabyte
25360
- * archive base64'd through a unary RPC envelope would be held whole in
25361
- * memory twice on a hub this repo has already OOM'd once (D9/D18 are the
25362
- * same lesson about frames). `getDownloadUrl` on `recordingExport` is the
25363
- * precedent, and this follows it deliberately.
24539
+ * Re-arm a terminally `failed` inference device on `nodeId`: forget its
24540
+ * restart budget so the next dispatch builds a fresh pool.
25364
24541
  *
25365
- * The archive contains a `manifest.json` FIRST, then the stored media
25366
- * VERBATIM under `tracks/<deviceId>/<trackId>/…`. No crop is derived and no
25367
- * model is run: a training set's pixels must be the pixels the pipeline saw.
24542
+ * The terminal state is deliberate (the abort it bounds is deterministic —
24543
+ * an automatic probation would just respawn Python forever, more slowly),
24544
+ * and a terminal state an operator cannot leave is a silent fault. This is
24545
+ * the way out. `rearmed:false` means there was nothing to forget.
25368
24546
  */
25369
- getTrainingExportUrl: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), external_exports.object({ url: external_exports.string() }), {
25370
- kind: "query",
24547
+ rearmInferenceDevice: method(external_exports.object({
24548
+ nodeId: external_exports.string(),
24549
+ deviceKey: external_exports.string()
24550
+ }), external_exports.object({ rearmed: external_exports.boolean() }), {
24551
+ kind: "mutation",
25371
24552
  auth: "admin"
25372
24553
  }),
24554
+ getSchema: method(external_exports.void(), PipelineSchemaSchema),
24555
+ getGlobalSteps: method(external_exports.void(), external_exports.array(PipelineDefaultStepSchema).readonly().nullable()),
24556
+ getGlobalPipelineConfig: method(external_exports.void(), PipelineConfigBridge),
24557
+ getOrchestratorConfigSchema: method(external_exports.void(), ConfigUISchemaBridge),
25373
24558
  /**
25374
- * The staging worklist for one camera, or for every camera that has one.
24559
+ * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
24560
+ * node's `currentEngine.format` — resolves `steps` the same way the
24561
+ * runtime dispatch path would, and reports what WOULD happen without
24562
+ * touching any node-global state. Called by the orchestrator at attach
24563
+ * time (`attachOn`), node-pinned to the TARGET node, so config problems
24564
+ * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
24565
+ * per-frame resolve. `ok` is false iff `issues` is non-empty (both
24566
+ * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
24567
+ * is informational (a degraded-but-loadable model swap) and never
24568
+ * affects `ok`. Never throws.
24569
+ */
24570
+ validatePipeline: method(external_exports.object({ steps: external_exports.array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
24571
+ listTemplates: method(external_exports.void(), external_exports.array(PipelineTemplateSchema$1).readonly()),
24572
+ saveTemplate: method(external_exports.object({
24573
+ name: external_exports.string(),
24574
+ steps: external_exports.array(PipelineTemplateStepSchema).readonly(),
24575
+ engine: PipelineEngineChoiceSchema
24576
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
24577
+ updateTemplate: method(external_exports.object({
24578
+ id: external_exports.string(),
24579
+ name: external_exports.string().optional(),
24580
+ steps: external_exports.array(PipelineTemplateStepSchema).readonly().optional()
24581
+ }), PipelineTemplateSchema$1, { kind: "mutation" }),
24582
+ deleteTemplate: method(external_exports.object({ id: external_exports.string() }), external_exports.void(), { kind: "mutation" }),
24583
+ getCapabilities: method(external_exports.void(), InferenceCapabilitiesBridge),
24584
+ getAddonModels: method(external_exports.object({ addonId: external_exports.string() }), ModelAvailabilityListBridge),
24585
+ downloadModel: method(external_exports.object({
24586
+ addonId: external_exports.string(),
24587
+ modelId: external_exports.string(),
24588
+ format: ModelFormatSchema$1
24589
+ }), DownloadModelResultSchema, { kind: "mutation" }),
24590
+ deleteModel: method(external_exports.object({
24591
+ addonId: external_exports.string(),
24592
+ modelId: external_exports.string(),
24593
+ format: ModelFormatSchema$1
24594
+ }), external_exports.object({ success: external_exports.literal(true) }), { kind: "mutation" }),
24595
+ /**
24596
+ * Stateless single-frame execution. Callers (runner, benchmark) pass
24597
+ * the complete `engine` + `steps` tree; the executor holds no state
24598
+ * about cameras or saved pipelines.
25375
24599
  *
25376
- * Fetched ON DEMAND, over the staging set only the page never scans
25377
- * history, because making the working set small is the entire purpose of
25378
- * the mark. Each row carries how many frames the dataset already holds from
25379
- * the track and how many subjects were annotated on them, so
25380
- * `frameCount: 0` reads as "still to work" without a second call per track.
24600
+ * `engine` is optional during the migration window to preserve the
24601
+ * legacy call shape used by existing benchmark code; once all
24602
+ * callers pass it explicitly we make it required.
25381
24603
  *
25382
- * `auth: 'admin'`, unlike the viewer-level mark itself: marking a track is
25383
- * curation you do while looking at it, but building the training set the
25384
- * fleet's models are fine-tuned on is not.
24604
+ * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
24605
+ * `referenceImage` must be provided:
24606
+ * - `frame`: runtime dispatch path (runner decoded broker frame).
24607
+ * Carries the raw buffer, dimensions, and format; the executor
24608
+ * uses it directly without base64 round-tripping.
24609
+ * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
24610
+ * decoded frame. Both runner and executor are hub-local processes
24611
+ * sharing `/dev/shm`, so the executor maps the named segment and
24612
+ * reads the pixels back zero-copy — eliminating the ~1.2MB
24613
+ * re-serialisation over UDS/MsgPack the `frame` path pays per call.
24614
+ * High-risk: the FrameRing is a latest-wins seqlock with no
24615
+ * refcount, so a recycled slot yields a null read; the executor
24616
+ * then degrades to an empty result and the runner ships pixels via
24617
+ * `frame` as the fallback (queue-depth gated on the runner side).
24618
+ * - `imageBase64`: one-shot test path (benchmark ImageTab).
24619
+ * - `referenceImage`: named file from the reference-image store.
25385
24620
  */
25386
- listRetrainStaging: method(external_exports.object({
25387
- /** Empty ⇒ every camera that has staging tracks. A LIST, not a single
25388
- * `deviceId`, deliberately: `deviceId` would make this device-bound and
25389
- * route it at one camera's owner, and "every camera" would stop being
25390
- * expressible at all. */
25391
- deviceIds: external_exports.array(external_exports.number()).optional(),
25392
- limit: external_exports.number().int().min(1).max(500).optional()
25393
- }), external_exports.array(RetrainTrackSchema).readonly(), {
25394
- kind: "query",
25395
- auth: "admin"
25396
- }),
24621
+ runPipeline: method(external_exports.object({
24622
+ engine: PipelineEngineChoiceSchema.optional(),
24623
+ steps: external_exports.array(PipelineStepInputSchema).min(1),
24624
+ frame: FrameInputSchema.optional(),
24625
+ /**
24626
+ * Process-local lazy frame. Valid only when caller and provider resolve
24627
+ * in the same execution-group process; split/cross-node callers use
24628
+ * `frame`/`image` inline compatibility instead.
24629
+ */
24630
+ frameRef: FrameRefSchema.optional(),
24631
+ /**
24632
+ * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
24633
+ * the decoded pixels live in. One more member of the one-of
24634
+ * frame/frameHandle/image/imageBase64/referenceImage group.
24635
+ */
24636
+ frameHandle: FrameHandleSchema.optional(),
24637
+ imageBase64: external_exports.string().optional(),
24638
+ /**
24639
+ * Binary JPEG bytes — preferred over `imageBase64` on internal
24640
+ * hops (hub → forked worker via Moleculer MsgPack) because it
24641
+ * skips the 33% base64 overhead + the per-call base64 decode on
24642
+ * the detection-pipeline worker. Callers can pass either; exactly
24643
+ * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
24644
+ */
24645
+ image: external_exports.instanceof(Uint8Array).optional(),
24646
+ referenceImage: external_exports.string().optional(),
24647
+ deviceId: external_exports.number().optional(),
24648
+ sessionId: external_exports.string().optional(),
24649
+ /**
24650
+ * Execution plane. 'full' (default) runs the whole tree — benchmark,
24651
+ * reference-image, and detail-subtree calls. 'frame' is the live
24652
+ * per-frame dispatch: ONLY root-plane steps run; crop children
24653
+ * (inputClasses ≠ null) are skipped and served per-track via
24654
+ * pipelineRunner.runDetailSubtree (two-plane design).
24655
+ */
24656
+ plane: external_exports.enum(["full", "frame"]).optional(),
24657
+ /**
24658
+ * Inference-device selector (Phase 2 multi-device). Format
24659
+ * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
24660
+ * Omitted ⇒ the runner's default device (current single-engine
24661
+ * behaviour). Selects WHICH device pool of the node runs the call.
24662
+ */
24663
+ deviceKey: external_exports.string().optional(),
24664
+ /**
24665
+ * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
24666
+ * when the parent crop was resolved from the frame's retained NATIVE
24667
+ * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
24668
+ * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
24669
+ * resolution from that surface — the SAME quality path faces already
24670
+ * had — instead of the downscaled parent tile. `handle` keys the native
24671
+ * surface (node-pinned to its owner); `cropFrameSpace` is the parent
24672
+ * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
24673
+ * the executor's crop-normalized child ROI back into frame-normalized
24674
+ * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
24675
+ * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
24676
+ * (today's behaviour on the fallback path).
24677
+ */
24678
+ nativeCropRef: NativeCropRefSchema.optional()
24679
+ }), PipelineRunResultBridge, { kind: "mutation" }),
25397
24680
  /**
25398
- * What a track can contribute, and what it already has.
25399
- *
25400
- * `candidates` are the track's whole, unannotated frames — index rows only,
25401
- * so this is cheap. `copies` are the frames already inside the dataset, and
25402
- * a candidate whose copy exists is marked `copied: true`: selecting it again
25403
- * is free and CANNOT fail, whatever became of the original.
24681
+ * Batched run N raw frames packed into one cap call. The provider
24682
+ * routes the batch through `SharedInferencePool.inferBatch`
24683
+ * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
24684
+ * envelope cost is amortised N:1 vs N concurrent `runPipeline`
24685
+ * calls. Single root step + uniform model assumed; trees with crop
24686
+ * children fall back to sequential execution.
25404
24687
  *
25405
- * A crop, a thumbnail and `fullFrameBoxed` are never candidates. The last
25406
- * one matters most: it has the model's own rectangle burned into the pixels,
25407
- * and a detector trained on it learns to find a green line.
24688
+ * Used by `scripts/bench-batch-style.mts` for batch benchmarking
24689
+ * N frames in one call to amortise per-call IPC overhead.
25408
24690
  */
25409
- listRetrainFrames: method(external_exports.object({ trackId: external_exports.string() }), RetrainFrameListSchema, {
25410
- kind: "query",
25411
- auth: "admin"
25412
- }),
24691
+ runPipelineBatch: method(external_exports.object({
24692
+ engine: PipelineEngineChoiceSchema.optional(),
24693
+ steps: external_exports.array(PipelineStepInputSchema).min(1),
24694
+ frames: external_exports.array(FrameInputSchema).min(1).max(255),
24695
+ deviceId: external_exports.number().optional(),
24696
+ sessionId: external_exports.string().optional(),
24697
+ /**
24698
+ * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
24699
+ * the batch to the Python pool's bench preprocess cache
24700
+ * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
24701
+ * preprocessed ONCE and every later inference is a pure-inference cache
24702
+ * hit — the sustained-throughput run measures inference, not
24703
+ * decode+preprocess+infer. Omitted/0 for live frames (all different →
24704
+ * full preprocess every call, correct). Fresh per sustained run;
24705
+ * released via `uncacheFrame`.
24706
+ */
24707
+ frameId: external_exports.number().int().nonnegative().optional(),
24708
+ /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
24709
+ deviceKey: external_exports.string().optional()
24710
+ }), external_exports.object({ results: external_exports.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
25413
24711
  /**
25414
- * COPY-ON-SELECT the write that makes `trained` safe to evict.
25415
- *
25416
- * Selecting a frame copies its bytes into retrain storage immediately: not
25417
- * a reference, not a lease. Once the copy exists the dataset no longer
25418
- * depends on the track's media, which is exactly what lets D81 hand a
25419
- * `trained` track back to retention.
25420
- *
25421
- * The order inside is load-bearing and is pinned by a test: an EXISTING
25422
- * copy is returned without touching the source, so an original that
25423
- * evaporated blocks the selection of THAT ORIGINAL and never the copy
25424
- * already taken. Every refusal comes back named — a dropped selection is
25425
- * never silent, on the wire or in the log.
24712
+ * Cache a raw frame inside the Python inference pool's memory.
24713
+ * Returns a numeric `frameId` that `inferCached` references —
24714
+ * subsequent calls send only 5 bytes through the pipe instead of
24715
+ * 1.2MB raw data, eliminating the pipe transfer bottleneck.
25426
24716
  */
25427
- selectRetrainFrames: method(external_exports.object({
25428
- deviceId: external_exports.number(),
25429
- trackId: external_exports.string(),
25430
- mediaKeys: external_exports.array(external_exports.string()).min(1)
25431
- }), RetrainFrameSelectionSchema, {
25432
- kind: "mutation",
25433
- auth: "admin"
25434
- }),
25435
- /** Un-select a frame: its annotations go first, then the copy and its blob.
25436
- * Deliberately destructive and deliberately explicit — it is the only way
25437
- * a frame leaves the dataset before export. */
25438
- deselectRetrainFrame: method(external_exports.object({
25439
- deviceId: external_exports.number(),
25440
- trackId: external_exports.string(),
25441
- frameId: external_exports.string()
24717
+ cacheFrameInPool: method(external_exports.object({
24718
+ data: external_exports.instanceof(Uint8Array),
24719
+ width: external_exports.number().int().positive(),
24720
+ height: external_exports.number().int().positive(),
24721
+ format: external_exports.enum([
24722
+ "rgb",
24723
+ "bgr",
24724
+ "gray"
24725
+ ])
25442
24726
  }), external_exports.object({
25443
- removed: external_exports.boolean(),
25444
- removedAnnotations: external_exports.number().int()
25445
- }), {
25446
- kind: "mutation",
25447
- auth: "admin"
25448
- }),
25449
- /**
25450
- * The pixels of ONE copied frame, base64.
25451
- *
25452
- * Through the cap rather than a data plane because it is genuinely one
25453
- * frame at a time, on demand, at human speed — the shape D9/D18 permit
25454
- * (what they forbid is frames crossing a boundary at frame RATE). The
25455
- * annotation canvas needs the image and its exact dimensions in the same
25456
- * answer: a canvas that places a normalised box against a size it guessed
25457
- * draws every box in the wrong place.
25458
- */
25459
- getRetrainFrameImage: method(external_exports.object({ frameId: external_exports.string() }), external_exports.object({
25460
- base64: external_exports.string(),
25461
- width: external_exports.number().int(),
25462
- height: external_exports.number().int()
25463
- }), {
25464
- kind: "query",
25465
- auth: "admin"
25466
- }),
24727
+ frameId: external_exports.number(),
24728
+ width: external_exports.number(),
24729
+ height: external_exports.number()
24730
+ }), { kind: "mutation" }),
25467
24731
  /**
25468
- * Ask the pipeline what it sees, as a PROPOSAL.
25469
- *
25470
- * Runs through `pipelineRunner.runStatelessStep` on the COPIED frame, and
25471
- * every box comes back as a draft with `source: 'assist'` plus the model and
25472
- * score that produced it. The operator confirms, edits, adds and deletes;
25473
- * nothing is stored until `saveRetrainAnnotations`.
25474
- *
25475
- * For packages the request is `rfdetr-package` on the ZONE CROP at 0.35 —
25476
- * never the whole frame, where a package detector at that threshold proposes
25477
- * furniture. A package request with no zone is REFUSED rather than widened,
25478
- * because the silent widening would look like a bad model for as long as
25479
- * nobody checked which rectangle it ran on.
24732
+ * Run inference on a previously cached frame. Sends only 5 bytes
24733
+ * (model_idx + frameId) through the IPC pipe — eliminates the
24734
+ * ~35ms per-call overhead of transferring 1.2MB raw data.
25480
24735
  */
25481
- proposeRetrainAnnotations: method(external_exports.object({
25482
- deviceId: external_exports.number(),
25483
- trackId: external_exports.string(),
25484
- frameId: external_exports.string(),
25485
- subject: RetrainAssistSubjectSchema,
25486
- /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
25487
- nodeId: external_exports.string().optional()
25488
- }), RetrainAssistResultSchema, {
25489
- kind: "mutation",
25490
- auth: "admin"
25491
- }),
25492
- /** Every annotation on a track, oldest first. */
25493
- listRetrainAnnotations: method(external_exports.object({ trackId: external_exports.string() }), external_exports.array(RetrainAnnotationSchema).readonly(), {
25494
- kind: "query",
25495
- auth: "admin"
25496
- }),
24736
+ inferCached: method(external_exports.object({
24737
+ stepId: external_exports.string(),
24738
+ frameId: external_exports.number().int()
24739
+ }), external_exports.record(external_exports.string(), external_exports.unknown()), { kind: "mutation" }),
25497
24740
  /**
25498
- * Replace EVERY annotation on one frame with the supplied set.
25499
- *
25500
- * Whole-frame replacement, not per-box upsert: the unit of ground truth is
25501
- * the frame, and "the operator deleted a box" must be the same durable
25502
- * outcome as "the operator never drew it". A per-box patch would let a frame
25503
- * keep a box the operator removed on a surface that only knew about the
25504
- * boxes it sent.
25505
- *
25506
- * Refuses a macro class typed into `label` or `subLabel` — the tiers are
25507
- * separate and the guard is at the WRITE, because a mixed taxonomy cannot
25508
- * be un-mixed by reading it.
24741
+ * Release a cached frame from the Python pool's memory.
25509
24742
  */
25510
- saveRetrainAnnotations: method(external_exports.object({
25511
- deviceId: external_exports.number(),
25512
- trackId: external_exports.string(),
25513
- frameId: external_exports.string(),
25514
- annotations: external_exports.array(RetrainAnnotationDraftSchema)
25515
- }), external_exports.array(RetrainAnnotationSchema).readonly(), {
25516
- kind: "mutation",
25517
- auth: "admin"
25518
- }),
24743
+ uncacheFrame: method(external_exports.object({ frameId: external_exports.number().int() }), external_exports.void(), { kind: "mutation" }),
24744
+ /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
24745
+ getEffectiveTuning: method(external_exports.void(), external_exports.object({
24746
+ batchMode: external_exports.string(),
24747
+ windowMs: external_exports.number(),
24748
+ maxBatchSize: external_exports.number(),
24749
+ concurrency: external_exports.number()
24750
+ })),
25519
24751
  /**
25520
- * Finish with a track: `staging trained`. **The only writer of that
25521
- * state** D81 shipped the column with it deliberately unreachable.
25522
- *
25523
- * Refuses a track the dataset holds no copies from. `trained` un-pins the
25524
- * track's media, so completing without a copy is a delete order for material
25525
- * nothing ever extracted anything from; that refusal IS the safety argument
25526
- * of D81, expressed as a precondition.
24752
+ * List every EngineFactory currently loaded in this executor's RAM,
24753
+ * with the models resident and a coarse "in use" marker derived from
24754
+ * ongoing inference activity. Used by the Pipeline page Engines tab.
25527
24755
  */
25528
- completeRetrainTrack: method(external_exports.object({
25529
- deviceId: external_exports.number(),
25530
- trackId: external_exports.string()
25531
- }), RetrainTransitionResultSchema, {
24756
+ listLoadedEngines: method(external_exports.void(), external_exports.array(external_exports.object({
24757
+ engineKey: external_exports.string(),
24758
+ engine: PipelineEngineChoiceSchema,
24759
+ modelsLoaded: external_exports.array(external_exports.string()).readonly(),
24760
+ inUseByCameras: external_exports.array(external_exports.number()).readonly(),
24761
+ /**
24762
+ * Origin of this resident factory.
24763
+ * - `runtime` — main camera-serving engine (no idle TTL).
24764
+ * - `warm-override` — benchmark/test override held in the warm
24765
+ * cache; auto-disposed after the idle TTL.
24766
+ * - `device-pool` — a concurrent per-device pool (Phase 2
24767
+ * multi-device, keyed by `deviceKey`) resolved
24768
+ * via `resolveDeviceFactory`. Runs alongside the
24769
+ * `runtime` engine on a DIFFERENT accelerator
24770
+ * (NPU / iGPU / Coral) — this is how the
24771
+ * Engines tab shows all pools running at once.
24772
+ */
24773
+ kind: external_exports.enum([
24774
+ "runtime",
24775
+ "warm-override",
24776
+ "device-pool"
24777
+ ]),
24778
+ /** Native pid of the underlying Python pool (null when no pool). */
24779
+ poolPid: external_exports.number().nullable(),
24780
+ /** ms since this factory was last used (null when not warm-tracked). */
24781
+ idleMs: external_exports.number().nullable(),
24782
+ /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
24783
+ idleTtlMs: external_exports.number().nullable()
24784
+ })).readonly()),
24785
+ /** Warm up an engine without running a frame. No-op if already loaded. */
24786
+ spinEngine: method(external_exports.object({ engine: PipelineEngineChoiceSchema }), external_exports.object({ success: external_exports.literal(true) }), {
25532
24787
  kind: "mutation",
25533
24788
  auth: "admin"
25534
24789
  }),
25535
24790
  /**
25536
- * The deliberate return: `trained staging`, for the rare case.
25537
- *
25538
- * The generic `setTrackFlags` toggle refuses this in both directions by
25539
- * design (D81) — re-staging from a checkbox is how the same material gets
25540
- * annotated twice under two ground truths. Doing it here means the operator
25541
- * is looking at the annotations that already exist while they decide, and
25542
- * those annotations are LEFT ALONE: "put this back" must not be a
25543
- * destructive act wearing a navigational name.
24791
+ * Unload an engine from RAM. `force:true` unloads even when cameras
24792
+ * are actively using it (they re-spin on next frame). Default is
24793
+ * gated returns `{success:false, reason}` when in use.
25544
24794
  */
25545
- restageRetrainTrack: method(external_exports.object({
25546
- deviceId: external_exports.number(),
25547
- trackId: external_exports.string()
25548
- }), RetrainTransitionResultSchema, {
24795
+ killEngine: method(external_exports.object({
24796
+ engine: PipelineEngineChoiceSchema,
24797
+ force: external_exports.boolean().optional()
24798
+ }), external_exports.object({
24799
+ success: external_exports.boolean(),
24800
+ reason: external_exports.string().optional()
24801
+ }), {
25549
24802
  kind: "mutation",
25550
24803
  auth: "admin"
25551
24804
  }),
25552
- /**
25553
- * Where to download the ANNOTATED dataset.
25554
- *
25555
- * The sibling of `getTrainingExportUrl` and deliberately not the same
25556
- * archive: that one streams a marked track's stored media verbatim, this one
25557
- * streams the retrain COPIES plus an `annotations.json` carrying, for every
25558
- * subject, the canonical full-frame box AND the geometry derived for each
25559
- * model shape (letterboxed root / zone-cropped package / subject-cropped
25560
- * classifier). Derived at export, never stored — one box in, three shapes
25561
- * out, so two crops of the same subject can never end up in one feature
25562
- * space (D52).
25563
- */
25564
- getRetrainExportUrl: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), external_exports.object({ url: external_exports.string() }), {
25565
- kind: "query",
24805
+ listReferenceImages: method(external_exports.void(), external_exports.array(ReferenceImageEntrySchema).readonly()),
24806
+ getReferenceImage: method(external_exports.object({ filename: external_exports.string() }), ReferenceImageBodySchema.nullable()),
24807
+ getReferenceAudioFiles: method(external_exports.void(), external_exports.array(ReferenceAudioEntrySchema).readonly()),
24808
+ getReferenceAudio: method(external_exports.object({ filename: external_exports.string() }), ReferenceAudioBodySchema.nullable()),
24809
+ getAudioCapabilities: method(external_exports.void(), AudioCapabilitiesSchema),
24810
+ runAudioTest: method(external_exports.object({
24811
+ addonId: external_exports.string(),
24812
+ modelId: external_exports.string(),
24813
+ filename: external_exports.string().optional(),
24814
+ settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
24815
+ }), AudioTestResultSchema, { kind: "mutation" }),
24816
+ getDetectionConfigSchema: method(external_exports.void(), ConfigUISchemaNullableBridge)
24817
+ }
24818
+ };
24819
+ var ZoneRuleModeEnum = external_exports.enum(["include", "exclude"]);
24820
+ var ZoneRuleSchema = external_exports.object({
24821
+ /** Stable rule id — survives edits, used by the UI for diffing. */
24822
+ id: external_exports.string(),
24823
+ /** Optional human-readable label rendered in the rule editor. */
24824
+ name: external_exports.string().optional(),
24825
+ /** Zones this rule targets. The rule's `mode` applies to ALL
24826
+ * listed zones (OR-set: a detection in any one of them counts).
24827
+ * At least one zone id required — a rule with no targets is a
24828
+ * configuration mistake and the form validator rejects it. */
24829
+ zoneIds: external_exports.array(external_exports.string()).min(1).readonly(),
24830
+ mode: ZoneRuleModeEnum,
24831
+ /**
24832
+ * Class names this rule applies to. Empty / undefined ⇒ rule
24833
+ * applies to every class. Class strings match the `macroClass`
24834
+ * field on detections (e.g. `person`, `car`, `dog`).
24835
+ */
24836
+ classFilter: external_exports.array(external_exports.string()).readonly().optional(),
24837
+ /**
24838
+ * Minimum bbox/mask overlap (0–1) with any of the rule's zones
24839
+ * required to consider an entity "in the zone". Defaults to the
24840
+ * consumer's stage default when omitted. Kept for back-compat with
24841
+ * existing per-rule overrides; new operators pick the value via
24842
+ * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
24843
+ * set, the lower-level engine reads it as a 0–1 fraction.
24844
+ */
24845
+ overlapThreshold: external_exports.number().min(0).max(1).optional(),
24846
+ /**
24847
+ * Operator-friendly version of `overlapThreshold` — the percentage
24848
+ * of the detection's bbox that must lie inside the zone for the
24849
+ * rule to match. Documented default is 85%; the engine substitutes
24850
+ * that when the field is omitted (kept optional so existing rules
24851
+ * stored without it stay valid).
24852
+ *
24853
+ * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
24854
+ * rule, the engine prefers `bboxInclusionPct` because it's the
24855
+ * field exposed in the UI. Internally both feed the same gate.
24856
+ */
24857
+ bboxInclusionPct: external_exports.number().min(0).max(100).optional(),
24858
+ /**
24859
+ * When `true` and a detection has a segmentation mask, use the
24860
+ * mask for overlap instead of the bbox. Detection-stage only;
24861
+ * motion rules ignore this field.
24862
+ */
24863
+ preferMask: external_exports.boolean().optional(),
24864
+ /**
24865
+ * Soft-toggle: `false` disables the rule without deleting it.
24866
+ * Defaults to `true` so operators creating a rule via the UI
24867
+ * see it active immediately.
24868
+ */
24869
+ enabled: external_exports.boolean().default(true)
24870
+ });
24871
+ var ZoneRulesArraySchema = external_exports.array(ZoneRuleSchema).readonly();
24872
+ var ZoneKindEnum = external_exports.enum(["polygon", "tripwire"]);
24873
+ var PolygonPointSchema = external_exports.object({
24874
+ x: external_exports.number(),
24875
+ y: external_exports.number()
24876
+ });
24877
+ var ZoneSchema = external_exports.object({
24878
+ id: external_exports.string(),
24879
+ name: external_exports.string(),
24880
+ kind: ZoneKindEnum.default("polygon"),
24881
+ /** Polygon vertices, fraction of frame (0–1). */
24882
+ polygon: external_exports.array(PolygonPointSchema).readonly(),
24883
+ /** Visual color for UI rendering. */
24884
+ color: external_exports.string().default("#3b82f6")
24885
+ });
24886
+ var zonesCapability = {
24887
+ name: "zones",
24888
+ scope: "device",
24889
+ mode: "singleton",
24890
+ deviceTypes: [DeviceType.Camera],
24891
+ methods: {
24892
+ listZones: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(ZoneSchema).readonly()),
24893
+ addZone: method(external_exports.object({
24894
+ deviceId: external_exports.number(),
24895
+ zone: ZoneSchema
24896
+ }), external_exports.void(), {
24897
+ kind: "mutation",
25566
24898
  auth: "admin"
25567
24899
  }),
25568
- getEventMedia: method(external_exports.object({
25569
- eventId: external_exports.string(),
25570
- kind: MediaFileKindEnum.optional(),
25571
- deviceId: external_exports.number()
25572
- }), external_exports.array(MediaFileSchema).readonly()),
25573
- /** All media rows owned by a track. `kinds` narrows to a kind subset so a
25574
- * client can fetch the SMALL display variants on open and pull the
25575
- * multi-MB native variants only on demand (mirrors `getEventMedia.kind`).
25576
- * Absent ⇒ every kind (back-compat). */
25577
- getTrackMedia: method(external_exports.object({
25578
- trackId: external_exports.string(),
25579
- kinds: external_exports.array(MediaFileKindEnum).optional(),
25580
- deviceId: external_exports.number()
25581
- }), external_exports.array(MediaFileSchema).readonly()),
25582
- /**
25583
- * What media a track HAS, without any of it.
25584
- *
25585
- * The detail view needs the shape of a track's media to build its strip —
25586
- * which kinds exist, in what order, at what size — and then wants each tile
25587
- * fetched as an image, not as base64 inside this response. Measured: the
25588
- * full `getTrackMedia` is 5.3-7.7 MB and blocks the view; this manifest is
25589
- * ~2 KB.
25590
- *
25591
- * It also restores a fact a `kinds` filter destroys: filtering
25592
- * `getTrackMedia` drops whole ROWS, taking `kind` and `sizeBytes` with
25593
- * them, so a client that fetched only the small variants could no longer
25594
- * tell a full-resolution variant existed — and the affordance that opens it
25595
- * would silently disappear.
25596
- */
25597
- listTrackMedia: method(external_exports.object({
25598
- trackId: external_exports.string(),
25599
- deviceId: external_exports.number()
25600
- }), external_exports.array(MediaFileInfoSchema).readonly()),
25601
- /**
25602
- * Search object events by text query using CLIP cosine similarity.
25603
- * Encodes `text` via the `embedding-encoder` cap, queries the
25604
- * `ObjectEmbeddingStore` with optional prefilters, ranks all matching
25605
- * embeddings by cosine similarity, and joins winners to their
25606
- * ObjectEvents by trackId. Returns up to `limit` events scored ≥
25607
- * `minScore`, sorted descending by score.
25608
- */
25609
- searchObjectEvents: method(SearchObjectEventsInput, external_exports.array(ScoredObjectEventSchema).readonly()),
25610
- wipeObjectEmbeddings: method(external_exports.object({}), WipeObjectEmbeddingsResultSchema, {
24900
+ removeZone: method(external_exports.object({
24901
+ deviceId: external_exports.number(),
24902
+ zoneId: external_exports.string()
24903
+ }), external_exports.void(), {
25611
24904
  kind: "mutation",
25612
24905
  auth: "admin"
25613
24906
  }),
25614
- rebuildObjectEmbeddings: method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
24907
+ updateZone: method(external_exports.object({
24908
+ deviceId: external_exports.number(),
24909
+ zone: ZoneSchema
24910
+ }), external_exports.void(), {
25615
24911
  kind: "mutation",
25616
24912
  auth: "admin"
25617
- }),
25618
- getObjectEmbeddingRebuildStatus: method(external_exports.object({}), RebuildStatusSchema)
24913
+ })
25619
24914
  },
25620
- events: {
25621
- /**
25622
- * Enriched frame emitted after refinement the live-overlay source of
25623
- * truth (two-plane re-injection). Carries the frame's detections in the
25624
- * `ObjectDetection` wire shape: first-level roots (with track info +
25625
- * enrichment labels) plus synthesized `kind:'detail'` face/plate entries
25626
- * re-projected from per-track detail state, so stream overlays render
25627
- * boxes + recognized names without querying full Track state.
25628
- */
25629
- onFrameTracked: { data: external_exports.object({
25630
- deviceId: external_exports.number(),
25631
- timestamp: external_exports.number(),
25632
- frameWidth: external_exports.number(),
25633
- frameHeight: external_exports.number(),
25634
- detections: external_exports.array(OverlayDetectionSchema).readonly()
25635
- }) },
25636
- /** Track entered active state (first-seen). */
25637
- onTrackStarted: { data: external_exports.object({
25638
- deviceId: external_exports.number(),
25639
- trackId: external_exports.string(),
25640
- className: external_exports.string()
25641
- }) },
25642
- /** Track expired (TTL reached after last detection). */
25643
- onTrackEnded: { data: external_exports.object({
25644
- deviceId: external_exports.number(),
25645
- trackId: external_exports.string(),
25646
- className: external_exports.string(),
25647
- durationMs: external_exports.number()
25648
- }) },
25649
- /** Canonical "something happened at device X" event, per-kind. */
25650
- onDetectionEvent: { data: external_exports.object({
25651
- deviceId: external_exports.number(),
25652
- kind: EventKindSchema,
25653
- eventId: external_exports.string(),
25654
- timestamp: external_exports.number()
25655
- }) }
25656
- }
24915
+ /**
24916
+ * Runtime-state slice — the live zone catalogue mirrored by the
24917
+ * orchestrator on every CRUD mutation. Consumers read via
24918
+ * `device.state.zones.value` / `.watch(...)` without round-tripping
24919
+ * the cap, and the codegen DeviceProxy auto-wires the reactive
24920
+ * handle. Slice shape is `{ zones: Zone[] }` so future extensions
24921
+ * (e.g. zone groupings) can sit alongside the polygon list.
24922
+ */
24923
+ runtimeState: external_exports.object({ zones: external_exports.array(ZoneSchema).readonly() }),
24924
+ /**
24925
+ * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
24926
+ *
24927
+ * See `RuntimeStateDurability`. Enforced by
24928
+ * `scripts/check-runtime-state-durability.ts`.
24929
+ */
24930
+ durability: "restored"
24931
+ };
24932
+ var TrackStateSchema = external_exports.enum([
24933
+ "new",
24934
+ "entered",
24935
+ "left",
24936
+ "moving",
24937
+ "idle"
24938
+ ]);
24939
+ var EventKindSchema = external_exports.enum([
24940
+ "motion",
24941
+ "object",
24942
+ "audio"
24943
+ ]);
24944
+ var TrackZoneFilterSchema = external_exports.discriminatedUnion("kind", [MaskRectShapeSchema, MaskPolygonShapeSchema]);
24945
+ var EventKindIconSchema = external_exports.enum([
24946
+ "motion",
24947
+ "audio",
24948
+ "person",
24949
+ "vehicle",
24950
+ "animal",
24951
+ "door",
24952
+ "pir",
24953
+ "smoke",
24954
+ "water",
24955
+ "button",
24956
+ "package",
24957
+ "generic"
24958
+ ]);
24959
+ var EventKindCategorySchema = external_exports.enum([
24960
+ "motion",
24961
+ "audio",
24962
+ "detection",
24963
+ "sensor",
24964
+ "control",
24965
+ "custom",
24966
+ "package"
24967
+ ]);
24968
+ var EventKindLevelSchema = external_exports.enum(["macro", "sub"]);
24969
+ var EventKindDescriptorSchema = external_exports.object({
24970
+ /** Stable kind id (e.g. 'motion', 'vehicle', 'car', 'lock'). */
24971
+ kind: external_exports.string(),
24972
+ /** i18n key resolved on the UI side; `label` is the English fallback. */
24973
+ labelKey: external_exports.string(),
24974
+ /** English fallback label (kept for clients that don't translate). */
24975
+ label: external_exports.string(),
24976
+ /** Hex color for timeline/legend rendering. */
24977
+ color: external_exports.string(),
24978
+ /** Dictionary id → lucide component on the UI side. */
24979
+ iconId: external_exports.string(),
24980
+ /** Legacy closed-vocab glyph — fallback for `iconId`. */
24981
+ icon: EventKindIconSchema,
24982
+ category: EventKindCategorySchema,
24983
+ /** Macro parent for this kind ('car' → 'vehicle'); null for a macro. */
24984
+ parentKind: external_exports.string().nullable(),
24985
+ /** Derived from `parentKind`, explicit for the client tree. */
24986
+ level: EventKindLevelSchema,
24987
+ /** Which cap + device contributes this kind. For built-ins the camera
24988
+ * itself; for sensor kinds the LINKED source device. */
24989
+ source: external_exports.object({
24990
+ capName: external_exports.string(),
24991
+ deviceId: external_exports.number()
24992
+ })
24993
+ });
24994
+ var EventKindsForDeviceSchema = external_exports.object({
24995
+ deviceId: external_exports.number(),
24996
+ kinds: external_exports.array(EventKindDescriptorSchema).readonly()
24997
+ });
24998
+ var SensorEventSchema = external_exports.object({
24999
+ id: external_exports.string(),
25000
+ /** The CAMERA the event is attributed to (a sensor linked to N cameras
25001
+ * yields N rows, one per camera). */
25002
+ deviceId: external_exports.number(),
25003
+ /** The linked sensor device whose state changed. */
25004
+ sourceDeviceId: external_exports.number(),
25005
+ /** Event kind id — matches an `EventKindDescriptor.kind`. */
25006
+ kind: external_exports.string(),
25007
+ /** Snapshot of the sensor cap's runtime-state slice at the change. */
25008
+ value: external_exports.record(external_exports.string(), external_exports.unknown()).nullable(),
25009
+ timestamp: external_exports.number()
25010
+ });
25011
+ var TrackPositionSchema = external_exports.object({
25012
+ x: external_exports.number(),
25013
+ y: external_exports.number(),
25014
+ timestamp: external_exports.number(),
25015
+ bbox: BoundingBoxSchema
25016
+ });
25017
+ var TrackSnapshotSchema = external_exports.object({
25018
+ timestamp: external_exports.number(),
25019
+ position: TrackPositionSchema,
25020
+ /** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
25021
+ mediaKey: external_exports.string()
25022
+ });
25023
+ var TrackEnvelopeSchema = external_exports.object({
25024
+ minX: external_exports.number(),
25025
+ minY: external_exports.number(),
25026
+ maxX: external_exports.number(),
25027
+ maxY: external_exports.number()
25028
+ });
25029
+ var TrackProjectionSchema = external_exports.enum(["full", "slim"]);
25030
+ var TrackAudioLabelSchema = external_exports.object({
25031
+ label: external_exports.string(),
25032
+ /** Highest classification score observed across the label's episodes. */
25033
+ peakScore: external_exports.number(),
25034
+ /** Number of coalesced audio-event episodes carrying this label. */
25035
+ count: external_exports.number(),
25036
+ firstAt: external_exports.number(),
25037
+ lastAt: external_exports.number()
25038
+ });
25039
+ var TrackSourceSchema = external_exports.enum([
25040
+ "pipeline",
25041
+ "sensor",
25042
+ "audio"
25043
+ ]);
25044
+ var RetrainStatusSchema = external_exports.enum([
25045
+ "none",
25046
+ "staging",
25047
+ "trained"
25048
+ ]);
25049
+ var TrackFlagFields = {
25050
+ /** Operator marked this track as training material — i.e. `retrainStatus` is
25051
+ * `'staging'`. */
25052
+ markForTrain: external_exports.boolean().optional(),
25053
+ /** Operator marked this track for diagnostic attention. */
25054
+ debug: external_exports.boolean().optional(),
25055
+ /** Operator favourited this track. Pins it against pruning. */
25056
+ favourited: external_exports.boolean().optional()
25057
+ };
25058
+ var TrackRetrainFields = { retrainStatus: RetrainStatusSchema.optional() };
25059
+ var TrackFlagsPatchSchema = external_exports.object(TrackFlagFields);
25060
+ var TrackFlagsSchema = external_exports.object({
25061
+ trackId: external_exports.string(),
25062
+ markForTrain: external_exports.boolean(),
25063
+ debug: external_exports.boolean(),
25064
+ favourited: external_exports.boolean(),
25065
+ /** The lifecycle state the boolean was derived from. Required here (unlike on
25066
+ * a track row) because this shape is only ever produced by the write body,
25067
+ * which always knows it — and a surface that has just written needs to render
25068
+ * `trained` without a re-fetch. */
25069
+ retrainStatus: RetrainStatusSchema
25070
+ });
25071
+ var LabelTierSchema = external_exports.union([external_exports.literal(1), external_exports.literal(2)]);
25072
+ var LabelAttributionSchema = external_exports.object({
25073
+ stepId: external_exports.string(),
25074
+ modelId: external_exports.string().optional(),
25075
+ decidedAt: external_exports.number(),
25076
+ /**
25077
+ * The GALLERY id behind a recognised tier-2 label — a face-gallery
25078
+ * `Identity.id` or a plate-gallery `Vehicle.id` (both `randomUUID`).
25079
+ *
25080
+ * The text alone is a DISPLAY NAME, and a display name is renameable: a
25081
+ * notification rule authored on "Gianluca" stopped matching the moment the
25082
+ * operator fixed the spelling in the gallery, and nothing said so. The id is
25083
+ * the thing that does not move, so it is what a rule matches on
25084
+ * (`NcConditions.identities`) and the text is what a human is shown.
25085
+ *
25086
+ * Absent when the label names no gallery row — a plate the OCR read but no
25087
+ * vehicle claims, a sub-class, a species, any tier-1 value.
25088
+ */
25089
+ identityId: external_exports.string().optional()
25090
+ });
25091
+ var TieredLabelFields = {
25092
+ /** Tier 1 — the sub-class. See {@link LabelTierSchema}. */
25093
+ label: external_exports.string().optional(),
25094
+ /** Confidence of the tier-1 value, as reported by the deciding step. */
25095
+ labelScore: external_exports.number().optional(),
25096
+ /** Provenance of the tier-1 value. See {@link LabelAttributionSchema}. */
25097
+ labelMeta: LabelAttributionSchema.optional(),
25098
+ /** Tier 2 — the instance. See {@link LabelTierSchema}. */
25099
+ subLabel: external_exports.string().optional(),
25100
+ /** Confidence of the tier-2 value, as reported by the deciding step. */
25101
+ subLabelScore: external_exports.number().optional(),
25102
+ /** Provenance of the tier-2 value. See {@link LabelAttributionSchema}. */
25103
+ subLabelMeta: LabelAttributionSchema.optional()
25104
+ };
25105
+ var TrainingExportDeviceTotalsSchema = external_exports.object({
25106
+ deviceId: external_exports.number(),
25107
+ tracks: external_exports.number().int(),
25108
+ files: external_exports.number().int(),
25109
+ bytes: external_exports.number().int()
25110
+ });
25111
+ var TrainingExportSummarySchema = external_exports.object({
25112
+ generatedAt: external_exports.number(),
25113
+ trackCount: external_exports.number().int(),
25114
+ fileCount: external_exports.number().int(),
25115
+ byteCount: external_exports.number().int(),
25116
+ /** More marked tracks exist than a single pass carries. */
25117
+ truncated: external_exports.boolean(),
25118
+ devices: external_exports.array(TrainingExportDeviceTotalsSchema).readonly()
25119
+ });
25120
+ var TrackSchema = external_exports.object({
25121
+ trackId: external_exports.string(),
25122
+ deviceId: external_exports.number(),
25123
+ className: external_exports.string(),
25124
+ ...TieredLabelFields,
25125
+ producingDeviceName: external_exports.string().optional(),
25126
+ /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
25127
+ source: TrackSourceSchema.optional(),
25128
+ firstSeen: external_exports.number(),
25129
+ lastSeen: external_exports.number(),
25130
+ /** Frame-rate position history (subject to maxPositionHistory cap). */
25131
+ positions: external_exports.array(TrackPositionSchema).readonly(),
25132
+ /** Periodic snapshots at snapshotIntervalMs cadence (subject to
25133
+ * saveThumbnails policy). */
25134
+ snapshots: external_exports.array(TrackSnapshotSchema).readonly(),
25135
+ /** Deduplicated zones the track has entered at least once. Zone IDS. */
25136
+ zonesVisited: external_exports.array(external_exports.string()).readonly(),
25137
+ /**
25138
+ * Human NAMES for {@link zonesVisited}, resolved at READ time against the
25139
+ * `zones` capability.
25140
+ *
25141
+ * `zonesVisited` persists ids (`cfeec78c-8d69-…`), which no operator can type
25142
+ * and no card can render — so every free-text search surface was structurally
25143
+ * unable to answer "show me the tracks in Uscio", and did not fail loudly, it
25144
+ * just returned nothing. Resolving here rather than in each client keeps ONE
25145
+ * derivation and costs the clients no extra call (the `zones` cap is
25146
+ * per-device, so a client-side resolve would be a per-camera fan-out on a
25147
+ * surface built to avoid exactly that).
25148
+ *
25149
+ * Resolved, never invented: a zone deleted since the track was written has no
25150
+ * name and is DROPPED, so this array can be shorter than `zonesVisited` — the
25151
+ * two are not positionally aligned. Absent when the track visited no zone, or
25152
+ * when the zone catalogue could not be read.
25153
+ */
25154
+ zoneNames: external_exports.array(external_exports.string()).readonly().optional(),
25155
+ /** Deduplicated set of detector classes observed for this track over its
25156
+ * life (a track may be reclassified, e.g. person→vehicle). Absent on
25157
+ * legacy rows written before class accumulation shipped. */
25158
+ classes: external_exports.array(external_exports.string()).readonly().optional(),
25159
+ /** Cumulative normalized distance travelled (0..1 units = full frame width). */
25160
+ totalDistance: external_exports.number(),
25161
+ state: TrackStateSchema,
25162
+ active: external_exports.boolean(),
25163
+ /** Deterministic key-event importance score in [0,1] (server-computed at
25164
+ * track expiry, recomputed on late label). Absent on legacy rows written
25165
+ * before scoring shipped — consumers degrade to absence / compute-on-read. */
25166
+ importance: external_exports.number().optional(),
25167
+ /** Id of the track's highest-confidence ObjectEvent (its representative
25168
+ * "best" frame). Absent when the track produced no object events. */
25169
+ bestEventId: external_exports.string().optional(),
25170
+ /** Tag of the importance sub-signal that dominated the score
25171
+ * (identity|dwell|proximity|class|confidence|travel|zone). */
25172
+ importanceReason: external_exports.string().optional(),
25173
+ /** Audio-classification labels heard on the camera during the track's
25174
+ * life (score ≥ device `classificationMinScore`), aggregated per label.
25175
+ * Absent on legacy rows / tracks with no confident audio. */
25176
+ audioLabels: external_exports.array(TrackAudioLabelSchema).readonly().optional(),
25177
+ /** Normalized 0..1 trajectory envelope (see {@link TrackEnvelopeSchema}).
25178
+ * Populated from the persisted envelope columns on historical reads;
25179
+ * absent on legacy rows, dims-less tracks and active (in-RAM) tracks. */
25180
+ envelope: TrackEnvelopeSchema.optional(),
25181
+ /**
25182
+ * A face DETECTOR found a face on this track — nothing more. It says the
25183
+ * detail plane produced a `face` detail; it does NOT say the face was
25184
+ * embedded, matched, above `minFacePx`, or that the recognizer was even
25185
+ * enabled. Set once and never cleared.
25186
+ *
25187
+ * **This exists so "face present but not recognised" is expressible.** A
25188
+ * recognised identity lands in `subLabel` (attributed to the face chain via
25189
+ * `subLabelMeta.stepId`), so before this field a track with an unmatched face
25190
+ * and a track with no face at all were byte-identical on the wire and no
25191
+ * surface could tell them apart. The read is `hasFace === true && subLabel
25192
+ * === undefined`.
25193
+ *
25194
+ * **Absent ≠ false.** Every row written before the column existed omits it,
25195
+ * and so does every server that predates the field — a consumer must test
25196
+ * `=== true` and render nothing otherwise, never infer "no face".
25197
+ */
25198
+ hasFace: external_exports.boolean().optional(),
25199
+ /**
25200
+ * This track has a face row IN THE GALLERY: a crop **and** an embedding — a
25201
+ * face an operator could ASSIGN to an identity.
25202
+ *
25203
+ * The STRICT twin of {@link hasFace}, and the pair only earns its keep
25204
+ * because the two disagree. `hasFace` is stamped at the TOP of the face
25205
+ * branch, before every gate, and means no more than "a face detector produced
25206
+ * a face detail". This one is stamped at the single moment the gallery row
25207
+ * LANDS — after `FaceRecognizer.onTrackEnd` successfully persists it, i.e.
25208
+ * past the embedding-magnitude verdict, the `minFacePx` detection gate, the
25209
+ * candidate gate, the imageless-track drop (no crop was ever captured) and
25210
+ * the crop-store drop. Everything between the detector and that insert can
25211
+ * legitimately refuse the face, so a flag written any earlier promises the
25212
+ * operator something to assign and delivers nothing.
25213
+ *
25214
+ * **Independent of recognition.** A face collected but never auto-matched is
25215
+ * still assignable — it is in fact the face an operator most wants to reach —
25216
+ * so this is NOT gated on `recognizedIdentityId`. Recognition lands in
25217
+ * `subLabel`; this says only that the raw material exists.
25218
+ *
25219
+ * **Set once, never cleared.** A track that produced a gallery row produced
25220
+ * one; deleting the row later is the gallery's business, not this flag's.
25221
+ *
25222
+ * **Absent ≠ false**, the same rule as {@link hasFace}: every row written
25223
+ * before the column omits it, and so does every server that predates the
25224
+ * field. A consumer must test `=== true` and render nothing otherwise —
25225
+ * never infer "no assignable face".
25226
+ */
25227
+ hasEmbeddedFace: external_exports.boolean().optional(),
25228
+ /**
25229
+ * This subject CONTAINS a folded rider — a person the rider-pairing step
25230
+ * ([D34](../decisions/adr-0034.md)) removed from the frame BEFORE the tracker,
25231
+ * so the passage is tracked once and as a VEHICLE.
25232
+ *
25233
+ * It exists because the fold's record was dishonest. D34 and the code both
25234
+ * said "the person is not lost — it is reported so both entities stay on the
25235
+ * record"; in fact the pair went into a per-processor RAM field behind an
25236
+ * accessor nobody called, and every durable surface said `vehicle`, full
25237
+ * stop. This is the composition note that makes the row true.
25238
+ *
25239
+ * A COMPOSITION, never a class and never a label. "This vehicle contains a
25240
+ * person" is not an answer to "what is this" — both label tiers would refuse
25241
+ * a macro token anyway (D89), and correctly. Nothing here changes what the
25242
+ * subject IS: a cyclist stays one vehicle track, occupancy still counts one,
25243
+ * and a `person` rule still does not fire for someone cycling past.
25244
+ *
25245
+ * **Absent ≠ false**, exactly like {@link hasFace}: every row written before
25246
+ * the column, and every hub that predates the field, omits it. Test
25247
+ * `=== true` and render nothing otherwise — never infer "no rider".
25248
+ */
25249
+ hasRider: external_exports.boolean().optional(),
25250
+ ...TrackFlagFields,
25251
+ ...TrackRetrainFields
25252
+ });
25253
+ var BaseEventFields = {
25254
+ id: external_exports.string(),
25255
+ deviceId: external_exports.number(),
25256
+ timestamp: external_exports.number()
25657
25257
  };
25658
- var NativeCropRefSchema = external_exports.object({
25659
- /** Handle keying the retained native surface (node-pinned to its owner). */
25660
- handle: FrameHandleSchema,
25661
- /** The parent crop's padded/clamped rectangle in FRAME-space pixels. */
25662
- cropFrameSpace: external_exports.object({
25663
- x: external_exports.number(),
25664
- y: external_exports.number(),
25665
- w: external_exports.number(),
25666
- h: external_exports.number()
25667
- })
25258
+ var MotionEventSchema = external_exports.object({
25259
+ ...BaseEventFields,
25260
+ kind: external_exports.literal("motion"),
25261
+ regionCount: external_exports.number(),
25262
+ /** Heavy JSON array — omitted in slim projection. */
25263
+ regions: external_exports.array(external_exports.object({
25264
+ bbox: BoundingBoxSchema,
25265
+ pixelCount: external_exports.number(),
25266
+ intensity: external_exports.number()
25267
+ })).readonly().optional(),
25268
+ /** Omitted in slim projection. */
25269
+ frameWidth: external_exports.number().optional(),
25270
+ /** Omitted in slim projection. */
25271
+ frameHeight: external_exports.number().optional(),
25272
+ /** Populated by B5 (recording playback URL for this event). */
25273
+ mediaUrl: external_exports.string().optional()
25668
25274
  });
25669
- external_exports.object({
25670
- crop: external_exports.object({
25671
- left: external_exports.number(),
25672
- top: external_exports.number(),
25673
- width: external_exports.number().positive(),
25674
- height: external_exports.number().positive()
25275
+ var DetectionSourceSchema = external_exports.enum(["pipeline", "onboard"]);
25276
+ var ZoneCrossingDirectionSchema = external_exports.enum(["enter", "exit"]);
25277
+ var ZoneCrossingSchema = external_exports.object({
25278
+ direction: ZoneCrossingDirectionSchema,
25279
+ /** Admin zone id crossed. */
25280
+ zoneId: external_exports.string(),
25281
+ /** Zone display name at crossing time (falls back to the id). */
25282
+ zoneName: external_exports.string().optional()
25283
+ });
25284
+ var ObjectEventSchema = external_exports.object({
25285
+ ...BaseEventFields,
25286
+ kind: external_exports.literal("object"),
25287
+ /** Detection source. Optional for backward-compat; absent ⇒ `pipeline`. */
25288
+ source: DetectionSourceSchema.optional(),
25289
+ /**
25290
+ * Inference-frame id shared by every object event emitted from the SAME frame
25291
+ * — the "scene/parent" key. Lets a consumer group co-occurring detections (e.g.
25292
+ * 2 people + 1 dog) under one full frame, and link a track's frames over time
25293
+ * (group by `trackId`, pick a representative `frameId` as the parent frame).
25294
+ * Optional for backward-compat with pre-existing rows / the slim projection
25295
+ * includes it (it is light). Absent on rows written before this field.
25296
+ */
25297
+ frameId: external_exports.string().optional(),
25298
+ /** Omitted in slim projection. */
25299
+ trackId: external_exports.string().optional(),
25300
+ className: external_exports.string(),
25301
+ ...TieredLabelFields,
25302
+ /** Omitted in slim projection. */
25303
+ confidence: external_exports.number().optional(),
25304
+ /** Heavy JSON — omitted in slim projection. */
25305
+ bbox: BoundingBoxSchema.optional(),
25306
+ /** Heavy JSON — omitted in slim projection. */
25307
+ zones: external_exports.array(external_exports.string()).readonly().optional(),
25308
+ /** Omitted in slim projection. */
25309
+ state: TrackStateSchema.optional(),
25310
+ /**
25311
+ * The zone crossing this event IS, when it is one. Absent on every other
25312
+ * event kind (movement state, appearance, package) — see
25313
+ * {@link ZoneCrossingSchema}. Omitted in slim projection.
25314
+ */
25315
+ zoneCrossing: ZoneCrossingSchema.optional(),
25316
+ /** Detection-frame dimensions in pixels — let consumers normalize the
25317
+ * pixel-space `bbox` onto a displayed image. Omitted in slim projection. */
25318
+ frameWidth: external_exports.number().optional(),
25319
+ frameHeight: external_exports.number().optional(),
25320
+ /** MediaStore key for the crop attached to this event (if any). */
25321
+ mediaKey: external_exports.string().optional(),
25322
+ /** Design B: MediaStore key of the track's native-resolution key frame (the
25323
+ * best-detection full frame). Resolve via the event-media data-plane
25324
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
25325
+ * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
25326
+ * sources — consumers fall back to `mediaKey` (the tight crop). */
25327
+ keyFrameMediaKey: external_exports.string().optional(),
25328
+ /** Populated by B5 (recording playback URL for this event). */
25329
+ mediaUrl: external_exports.string().optional(),
25330
+ /** The parent track's key-event importance [0,1], propagated to every object
25331
+ * event of the track (so an event row can be sorted by importance without a
25332
+ * track join). Absent on legacy rows / before the track was scored. */
25333
+ importance: external_exports.number().optional()
25334
+ });
25335
+ var AudioEventSchema = external_exports.object({
25336
+ ...BaseEventFields,
25337
+ kind: external_exports.literal("audio"),
25338
+ rms: external_exports.number(),
25339
+ dbfs: external_exports.number(),
25340
+ classification: external_exports.object({
25341
+ className: external_exports.string(),
25342
+ originalClass: external_exports.string().optional(),
25343
+ score: external_exports.number()
25675
25344
  }).optional(),
25676
- content: external_exports.object({
25677
- width: external_exports.number().int().positive(),
25678
- height: external_exports.number().int().positive()
25679
- }),
25680
- fit: external_exports.enum(["stretch", "contain"]),
25681
- format: external_exports.enum([
25682
- "rgb",
25683
- "gray",
25684
- "jpeg"
25685
- ])
25345
+ /** Populated by B5 (recording playback URL for this event). */
25346
+ mediaUrl: external_exports.string().optional()
25686
25347
  });
25687
- var FrameRefSchema = external_exports.object({
25688
- registryId: external_exports.string().min(1),
25689
- id: external_exports.string().min(1),
25690
- width: external_exports.number().int().positive(),
25691
- height: external_exports.number().int().positive(),
25692
- format: external_exports.enum(["rgb", "gray"]),
25348
+ var MediaFileKindEnum = external_exports.enum([
25349
+ "crop",
25350
+ "thumbnail",
25351
+ "snapshot",
25352
+ "firstFrame",
25353
+ "lastFrame",
25354
+ "fullFrame",
25355
+ "fullFrameBoxed",
25356
+ "faceCrop",
25357
+ "plateCrop",
25358
+ "keyFrame",
25359
+ "keyFrameSmall",
25360
+ "thumbnailSmall"
25361
+ ]);
25362
+ var MediaFileSchema = external_exports.object({
25363
+ key: external_exports.string(),
25364
+ kind: MediaFileKindEnum,
25365
+ base64: external_exports.string(),
25366
+ sizeBytes: external_exports.number(),
25367
+ timestamp: external_exports.number()
25368
+ });
25369
+ var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
25370
+ var RetrainMacroClassSchema = external_exports.enum([
25371
+ "person",
25372
+ "vehicle",
25373
+ "animal",
25374
+ "package",
25375
+ "face",
25376
+ "plate"
25377
+ ]);
25378
+ var RetrainAnnotationKindSchema = external_exports.enum(["subject", "model_error"]);
25379
+ var RetrainAnnotationSourceSchema = external_exports.enum(["operator", "assist"]);
25380
+ var RetrainBboxSchema = external_exports.object({
25381
+ x: external_exports.number(),
25382
+ y: external_exports.number(),
25383
+ w: external_exports.number(),
25384
+ h: external_exports.number()
25385
+ });
25386
+ var RetrainAnnotationSchema = external_exports.object({
25387
+ id: external_exports.string(),
25388
+ trackId: external_exports.string(),
25389
+ deviceId: external_exports.number(),
25390
+ /** The COPY in retrain storage — never the source track's media key. */
25391
+ mediaKey: external_exports.string(),
25392
+ bbox: RetrainBboxSchema,
25393
+ macroClass: RetrainMacroClassSchema,
25394
+ label: external_exports.string().optional(),
25395
+ subLabel: external_exports.string().optional(),
25396
+ kind: RetrainAnnotationKindSchema,
25397
+ source: RetrainAnnotationSourceSchema,
25398
+ /** Which model proposed this box — or, on a `model_error`, drew the phantom. */
25399
+ assistModelId: external_exports.string().optional(),
25400
+ assistScore: external_exports.number().optional(),
25401
+ exportedInBatch: external_exports.string().optional(),
25402
+ createdAt: external_exports.number()
25403
+ });
25404
+ var RetrainAnnotationDraftSchema = RetrainAnnotationSchema.omit({
25405
+ id: true,
25406
+ trackId: true,
25407
+ deviceId: true,
25408
+ mediaKey: true,
25409
+ createdAt: true,
25410
+ exportedInBatch: true
25411
+ });
25412
+ var RetrainTrackSchema = external_exports.object({
25413
+ trackId: external_exports.string(),
25414
+ deviceId: external_exports.number(),
25415
+ className: external_exports.string(),
25416
+ label: external_exports.string().optional(),
25417
+ firstSeen: external_exports.number(),
25418
+ lastSeen: external_exports.number(),
25419
+ /** How many frames the dataset already holds from this track. */
25420
+ frameCount: external_exports.number().int(),
25421
+ /** How many subjects have been annotated on those frames. `0` with
25422
+ * `frameCount: 0` is exactly "staging, still to work". */
25423
+ annotationCount: external_exports.number().int()
25424
+ });
25425
+ var RetrainFrameCandidateSchema = external_exports.object({
25426
+ mediaKey: external_exports.string(),
25427
+ kind: MediaFileKindEnum,
25693
25428
  timestamp: external_exports.number(),
25694
- capturedAt: external_exports.number().optional()
25429
+ sizeBytes: external_exports.number().int(),
25430
+ /** A copy of this original already exists — selecting it is free and cannot
25431
+ * fail, whatever became of the original. */
25432
+ copied: external_exports.boolean()
25695
25433
  });
25696
- var ModelFormatSchema$1 = external_exports.enum([
25697
- "onnx",
25698
- "coreml",
25699
- "openvino",
25700
- "tflite",
25701
- "pt",
25702
- "gguf"
25703
- ]);
25704
- var PipelineSlotSchema = external_exports.enum([
25705
- "detector",
25706
- "cropper",
25707
- "classifier",
25708
- "refiner",
25709
- "audio-classifier"
25710
- ]);
25711
- var PipelineEngineChoiceSchema = external_exports.object({
25712
- runtime: external_exports.enum(["node", "python"]),
25713
- backend: external_exports.string(),
25714
- format: ModelFormatSchema$1,
25715
- device: external_exports.string().optional()
25434
+ var RetrainFrameSchema = external_exports.object({
25435
+ frameId: external_exports.string(),
25436
+ deviceId: external_exports.number(),
25437
+ trackId: external_exports.string(),
25438
+ /** Provenance only. It may already point at nothing — that is expected. */
25439
+ sourceMediaKey: external_exports.string(),
25440
+ sourceKind: MediaFileKindEnum,
25441
+ sizeBytes: external_exports.number().int(),
25442
+ width: external_exports.number().int(),
25443
+ height: external_exports.number().int(),
25444
+ copiedAt: external_exports.number()
25716
25445
  });
25717
- var EngineDeviceInfoSchema = external_exports.object({
25718
- id: external_exports.string(),
25719
- label: external_exports.string(),
25720
- description: external_exports.string().optional()
25446
+ var RetrainCopyRefusalSchema = external_exports.enum([
25447
+ "source-missing",
25448
+ "unreadable-image",
25449
+ "write-failed"
25450
+ ]);
25451
+ var RetrainFrameSelectionSchema = external_exports.object({
25452
+ copied: external_exports.array(RetrainFrameSchema).readonly(),
25453
+ refused: external_exports.array(external_exports.object({
25454
+ sourceMediaKey: external_exports.string(),
25455
+ reason: RetrainCopyRefusalSchema
25456
+ })).readonly()
25721
25457
  });
25722
- var AvailableEngineSchema = external_exports.object({
25723
- engine: PipelineEngineChoiceSchema,
25724
- devices: external_exports.array(EngineDeviceInfoSchema).readonly(),
25725
- defaultDevice: external_exports.string()
25458
+ var RetrainFrameListSchema = external_exports.object({
25459
+ candidates: external_exports.array(RetrainFrameCandidateSchema).readonly(),
25460
+ copies: external_exports.array(RetrainFrameSchema).readonly(),
25461
+ /** What the page pre-selects — the native key frame when one survives. */
25462
+ autoPickMediaKey: external_exports.string().optional()
25726
25463
  });
25727
- var PipelineDefaultStepSchema = external_exports.lazy(() => external_exports.object({
25728
- addonId: external_exports.string(),
25729
- addonName: external_exports.string(),
25730
- slot: PipelineSlotSchema,
25731
- inputClasses: external_exports.array(external_exports.string()).readonly(),
25732
- outputClasses: external_exports.array(external_exports.string()).readonly(),
25733
- enabled: external_exports.boolean(),
25464
+ var RetrainAssistSubjectSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
25465
+ kind: external_exports.literal("package"),
25466
+ zone: RetrainBboxSchema.optional()
25467
+ }), external_exports.object({
25468
+ kind: external_exports.literal("objects"),
25734
25469
  modelId: external_exports.string(),
25735
- children: external_exports.array(PipelineDefaultStepSchema).readonly(),
25736
- group: external_exports.string().optional(),
25737
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
25738
- }));
25739
- var PipelineTemplateStepSchema = external_exports.lazy(() => external_exports.object({
25740
- addonId: external_exports.string(),
25741
- enabled: external_exports.boolean(),
25470
+ minScore: external_exports.number().optional()
25471
+ })]);
25472
+ var RetrainAssistResultSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
25473
+ kind: external_exports.literal("proposed"),
25742
25474
  modelId: external_exports.string(),
25743
- children: external_exports.array(PipelineTemplateStepSchema).readonly(),
25744
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
25745
- }));
25746
- var PipelineTemplateSchema$1 = external_exports.object({
25747
- id: external_exports.string(),
25748
- name: external_exports.string(),
25749
- createdAt: external_exports.string(),
25750
- updatedAt: external_exports.string(),
25751
- engine: PipelineEngineChoiceSchema,
25752
- steps: external_exports.array(PipelineTemplateStepSchema).readonly()
25475
+ stepId: external_exports.string(),
25476
+ minScore: external_exports.number(),
25477
+ /** Drafts, ready to edit. `source: 'assist'` until the operator touches one. */
25478
+ proposals: external_exports.array(RetrainAnnotationDraftSchema).readonly(),
25479
+ /** Returned by the runner but removed by the threshold. */
25480
+ belowThreshold: external_exports.number().int()
25481
+ }), external_exports.object({
25482
+ kind: external_exports.literal("refused"),
25483
+ /** `no-zone` is ours; the rest are the runner's own refusal vocabulary. */
25484
+ reason: external_exports.string(),
25485
+ detail: external_exports.string().optional()
25486
+ })]);
25487
+ var RetrainTransitionResultSchema = external_exports.object({
25488
+ trackId: external_exports.string(),
25489
+ /** Where the track ended up, whatever happened. */
25490
+ retrainStatus: RetrainStatusSchema,
25491
+ /** `false` ⇒ the move was refused or was a no-op; `reason` says which. */
25492
+ changed: external_exports.boolean(),
25493
+ reason: external_exports.enum([
25494
+ "unknown-track",
25495
+ "no-frames-copied",
25496
+ "not-staging",
25497
+ "not-trained",
25498
+ "unchanged"
25499
+ ]).optional()
25753
25500
  });
25754
- var PipelineModelOptionSchema = external_exports.object({
25755
- id: external_exports.string(),
25756
- name: external_exports.string(),
25757
- formats: external_exports.record(external_exports.string(), external_exports.object({
25758
- downloaded: external_exports.boolean(),
25759
- sizeMB: external_exports.number()
25760
- })),
25761
- group: ModelVariantGroupSchema.optional(),
25762
- legacy: external_exports.boolean().optional(),
25763
- provider: ModelProviderIdSchema.optional()
25501
+ var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
25502
+ var MAX_EVENT_QUERY_LIMIT = 5e3;
25503
+ var DeviceEventQueryInput = external_exports.object({
25504
+ deviceId: external_exports.number(),
25505
+ since: external_exports.number().optional(),
25506
+ until: external_exports.number().optional(),
25507
+ limit: external_exports.number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT),
25508
+ /** `slim` drops heavy JSON fields (regions/bbox/zones) and carries an
25509
+ * optional `mediaUrl` (populated by B5). `full` (default) keeps today's
25510
+ * exact behaviour. Callers may omit this field — the store defaults to
25511
+ * `full` when not provided. */
25512
+ projection: external_exports.enum(["full", "slim"]).optional()
25764
25513
  });
25765
- var ConfigFieldBridge = external_exports.custom();
25766
- var PipelineAddonSchemaSchema = external_exports.object({
25514
+ var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: external_exports.string().optional() });
25515
+ var RecentTracksQueryInput = external_exports.object({
25516
+ /** Devices to merge. An empty array yields `{ tracks: [], nextCursor: null }`. */
25517
+ deviceIds: external_exports.array(external_exports.number()),
25518
+ /** Window lower bound on `lastSeen` (inclusive). */
25519
+ since: external_exports.number().optional(),
25520
+ /** Window upper bound on `lastSeen` (inclusive). */
25521
+ until: external_exports.number().optional(),
25522
+ /** Page size. Default 200, max 1000. */
25523
+ limit: external_exports.number().int().min(1).max(1e3).default(200),
25524
+ /** Opaque continuation cursor from a previous page's `nextCursor`.
25525
+ * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
25526
+ cursor: external_exports.string().optional(),
25527
+ /** See {@link TrackProjectionSchema}. Default `full`. */
25528
+ projection: TrackProjectionSchema.optional(),
25529
+ /** Include stationary-promoted rows (parked objects). Default false: the
25530
+ * feed lists passages; parking records live on the stationary registry. */
25531
+ includeStationary: external_exports.boolean().optional()
25532
+ });
25533
+ var RecentTracksPageSchema = external_exports.object({
25534
+ /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
25535
+ tracks: external_exports.array(TrackSchema).readonly(),
25536
+ /** Cursor for the next page, or null when this page is the last. */
25537
+ nextCursor: external_exports.string().nullable()
25538
+ });
25539
+ var LIST_GROUPS_DEFAULT_LIMIT = 40;
25540
+ var LIST_GROUPS_MAX_LIMIT = 100;
25541
+ var AnalyticsGroupRecordSchema = external_exports.object({
25767
25542
  id: external_exports.string(),
25768
- name: external_exports.string(),
25769
- slot: PipelineSlotSchema,
25770
- inputClasses: external_exports.array(external_exports.string()).readonly(),
25771
- outputClasses: external_exports.array(external_exports.string()).readonly(),
25772
- childSlots: external_exports.array(PipelineSlotSchema).readonly(),
25773
- models: external_exports.array(PipelineModelOptionSchema).readonly(),
25774
- defaultModelId: external_exports.string(),
25775
- defaultModelIdByFormat: external_exports.record(external_exports.string(), external_exports.string()).optional(),
25776
- enabledByDefault: external_exports.boolean().optional(),
25777
- backfillIntoExistingOverrides: external_exports.boolean().optional(),
25778
- defaultConfidence: external_exports.number(),
25779
- group: external_exports.string().optional(),
25780
- configSchema: external_exports.array(ConfigFieldBridge).readonly().optional()
25543
+ deviceId: external_exports.number().int(),
25544
+ openedAt: external_exports.number().int(),
25545
+ closedAt: external_exports.number().int(),
25546
+ timestamp: external_exports.number().int(),
25547
+ memberCount: external_exports.number().int(),
25548
+ memberTrackIds: external_exports.array(external_exports.string()).readonly(),
25549
+ className: external_exports.string(),
25550
+ classes: external_exports.array(external_exports.string()).readonly(),
25551
+ /** Relative event-media path, or null when the group has no picture yet. */
25552
+ mediaUrl: external_exports.string().nullable(),
25553
+ singleton: external_exports.boolean()
25781
25554
  });
25782
- var PipelineSlotSchemaSchema = external_exports.object({
25783
- id: PipelineSlotSchema,
25784
- label: external_exports.string(),
25785
- priority: external_exports.number(),
25786
- parentSlot: PipelineSlotSchema.nullable(),
25787
- addons: external_exports.array(PipelineAddonSchemaSchema).readonly()
25555
+ var AnalyticsGroupMemberSchema = external_exports.object({
25556
+ trackId: external_exports.string(),
25557
+ deviceId: external_exports.number().int(),
25558
+ className: external_exports.string(),
25559
+ firstSeen: external_exports.number().int(),
25560
+ lastSeen: external_exports.number().int(),
25561
+ mediaUrl: external_exports.string().nullable()
25788
25562
  });
25789
- var PipelineSchemaSchema = external_exports.object({
25790
- availableEngines: external_exports.array(AvailableEngineSchema).readonly(),
25791
- selectedEngine: PipelineEngineChoiceSchema,
25792
- slots: external_exports.array(PipelineSlotSchemaSchema).readonly()
25563
+ var AnalyticsGroupDetailSchema = AnalyticsGroupRecordSchema.extend({ members: external_exports.array(AnalyticsGroupMemberSchema).readonly() });
25564
+ var ListGroupsQueryInput = external_exports.object({
25565
+ /** Devices to merge. An empty array yields `{ groups: [], nextCursor: null }`. */
25566
+ deviceIds: external_exports.array(external_exports.number()),
25567
+ /** Window lower bound on `closedAt` (inclusive). */
25568
+ since: external_exports.number().optional(),
25569
+ /** Window upper bound on `openedAt` (inclusive). */
25570
+ until: external_exports.number().optional(),
25571
+ limit: external_exports.number().int().min(1).max(LIST_GROUPS_MAX_LIMIT).default(LIST_GROUPS_DEFAULT_LIMIT),
25572
+ /** Opaque continuation cursor from a previous page's `nextCursor`. */
25573
+ cursor: external_exports.string().optional()
25574
+ });
25575
+ var ListGroupsPageSchema = external_exports.object({
25576
+ groups: external_exports.array(AnalyticsGroupRecordSchema).readonly(),
25577
+ nextCursor: external_exports.string().nullable()
25578
+ });
25579
+ var KeyEventQueryInput = external_exports.object({
25580
+ deviceId: external_exports.number(),
25581
+ /** Window lower bound (track firstSeen ≥ since). */
25582
+ since: external_exports.number(),
25583
+ /** Window upper bound (track firstSeen ≤ until). */
25584
+ until: external_exports.number(),
25585
+ limit: external_exports.number().int().min(1).max(200).default(50),
25586
+ /** Drop tracks scoring below this importance. */
25587
+ minImportance: external_exports.number().min(0).max(1).optional(),
25588
+ /** Restrict to a single class (e.g. 'person'). */
25589
+ classFilter: external_exports.string().optional()
25590
+ });
25591
+ var KeyEventSchema = external_exports.object({
25592
+ /** The representative event id (the track's best ObjectEvent, else its trackId). */
25593
+ id: external_exports.string(),
25594
+ trackId: external_exports.string(),
25595
+ /** Track start time (firstSeen). */
25596
+ timestamp: external_exports.number(),
25597
+ className: external_exports.string(),
25598
+ ...TieredLabelFields,
25599
+ importance: external_exports.number(),
25600
+ /** Highest-confidence ObjectEvent id for the track (empty when none). */
25601
+ bestEventId: external_exports.string(),
25602
+ /** Track lifetime in ms (lastSeen - firstSeen). */
25603
+ windowMs: external_exports.number().optional(),
25604
+ ...TrackFlagFields,
25605
+ ...TrackRetrainFields
25606
+ });
25607
+ var TrackedDetectionSchema = external_exports.object({
25608
+ trackId: external_exports.string(),
25609
+ className: external_exports.string(),
25610
+ confidence: external_exports.number(),
25611
+ bbox: BoundingBoxSchema,
25612
+ zones: external_exports.array(external_exports.string()).readonly(),
25613
+ state: TrackStateSchema
25793
25614
  });
25794
- var EngineProvisioningSchema = external_exports.object({
25795
- runtimeId: external_exports.enum([
25796
- "onnx",
25797
- "openvino",
25798
- "coreml",
25799
- "edgetpu"
25800
- ]).nullable(),
25801
- device: external_exports.string().nullable(),
25802
- state: external_exports.enum([
25803
- "idle",
25804
- "installing",
25805
- "verifying",
25806
- "ready",
25807
- "failed"
25808
- ]),
25809
- progress: external_exports.number().optional(),
25810
- error: external_exports.string().optional(),
25811
- nextRetryAt: external_exports.number().optional(),
25812
- /**
25813
- * Gate A (config-correctness gate at engine change): human-readable
25814
- * config issues surfaced EAGERLY when the node's engine changes — model
25815
- * substitutions ("chose X, running Y") and zero-build steps ("no model
25816
- * has a <format> build"). Additive/optional: informational only, never
25817
- * enforced here — `assertEngineReady` (readiness) still gates inference.
25818
- * Absent/empty when the node-default tree resolves cleanly.
25819
- */
25820
- configIssues: external_exports.array(external_exports.string()).optional()
25615
+ var OverlayDetectionSchema = external_exports.looseObject({
25616
+ id: external_exports.string(),
25617
+ kind: external_exports.enum(["first-level", "detail"]),
25618
+ macroClass: external_exports.string(),
25619
+ score: external_exports.number(),
25620
+ bbox: external_exports.object({
25621
+ x: external_exports.number(),
25622
+ y: external_exports.number(),
25623
+ width: external_exports.number(),
25624
+ height: external_exports.number()
25625
+ }),
25626
+ labels: external_exports.array(external_exports.looseObject({
25627
+ label: external_exports.string(),
25628
+ score: external_exports.number()
25629
+ })).readonly(),
25630
+ parentId: external_exports.string().optional()
25821
25631
  });
25822
- var PipelineStepInputSchema = external_exports.lazy(() => external_exports.object({
25823
- addonId: external_exports.string(),
25824
- modelId: external_exports.string().optional(),
25825
- enabled: external_exports.boolean().default(true),
25826
- children: external_exports.array(PipelineStepInputSchema).optional(),
25827
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
25828
- jumpDeviceKey: external_exports.string().optional()
25829
- }));
25830
- var ModelSubstitutionSchema = external_exports.object({
25831
- addonId: external_exports.string(),
25832
- chosen: external_exports.string(),
25833
- running: external_exports.string(),
25834
- format: external_exports.string()
25632
+ var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: external_exports.number() });
25633
+ var SearchObjectEventsInput = external_exports.object({
25634
+ text: external_exports.string(),
25635
+ deviceId: external_exports.number().optional(),
25636
+ since: external_exports.number().optional(),
25637
+ until: external_exports.number().optional(),
25638
+ classFilter: external_exports.string().optional(),
25639
+ limit: external_exports.number().default(50),
25640
+ minScore: external_exports.number().min(0).max(1).default(0.2)
25835
25641
  });
25836
- var PipelineValidationIssueSchema = external_exports.object({
25837
- addonId: external_exports.string(),
25838
- kind: external_exports.enum(["unknown-addon", "no-format-build"]),
25839
- detail: external_exports.string()
25642
+ var TrackCascadeCountsSchema = external_exports.object({
25643
+ /** Persisted track roots deleted (authoritative). */
25644
+ tracks: external_exports.number().int(),
25645
+ /** Object events removed with their tracks (best-effort; see note above). */
25646
+ events: external_exports.number().int(),
25647
+ /** Track/face/plate-owned media removed (best-effort). Never identity media. */
25648
+ media: external_exports.number().int(),
25649
+ /** Unassigned (non-enrolled) face reads removed (best-effort). */
25650
+ faces: external_exports.number().int(),
25651
+ /** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
25652
+ plates: external_exports.number().int(),
25653
+ /** Per-track CLIP search vectors removed (best-effort). */
25654
+ embeddings: external_exports.number().int(),
25655
+ /** Group membership + group rows removed with their last member (best-effort). */
25656
+ groups: external_exports.number().int()
25840
25657
  });
25841
- var PipelineValidationResultSchema = external_exports.object({
25842
- ok: external_exports.boolean(),
25843
- issues: external_exports.array(PipelineValidationIssueSchema).readonly(),
25844
- substitutions: external_exports.array(ModelSubstitutionSchema).readonly(),
25845
- /** The node's `currentEngine.format` this validation ran against. */
25846
- format: external_exports.string()
25658
+ var DiskReconcileCountsSchema = external_exports.object({
25659
+ mediaDropped: external_exports.number().int(),
25660
+ tracks: external_exports.number().int(),
25661
+ events: external_exports.number().int()
25847
25662
  });
25848
- var ReferenceImageEntrySchema = external_exports.object({
25849
- filename: external_exports.string(),
25850
- stepIds: external_exports.array(external_exports.string()).readonly().optional()
25663
+ var EventStoreDeviceFootprintSchema = external_exports.object({
25664
+ deviceId: external_exports.number(),
25665
+ /** Persisted event rows (motion + object + audio) for the camera. */
25666
+ rows: external_exports.number().int(),
25667
+ /** Event-owned media bytes on disk for the camera. */
25668
+ bytes: external_exports.number().int()
25851
25669
  });
25852
- var ReferenceImageBodySchema = external_exports.object({
25853
- base64: external_exports.string(),
25854
- filename: external_exports.string()
25670
+ var EventStoreFootprintSchema = external_exports.object({
25671
+ totalRows: external_exports.number().int(),
25672
+ totalBytes: external_exports.number().int(),
25673
+ devices: external_exports.array(EventStoreDeviceFootprintSchema).readonly()
25855
25674
  });
25856
- var ReferenceAudioEntrySchema = external_exports.object({
25857
- filename: external_exports.string(),
25858
- sizeKb: external_exports.number()
25675
+ var EventPruneCountsSchema = external_exports.object({
25676
+ motion: external_exports.number().int(),
25677
+ object: external_exports.number().int(),
25678
+ audio: external_exports.number().int()
25859
25679
  });
25860
- var ReferenceAudioBodySchema = external_exports.object({ base64: external_exports.string() });
25861
- var AudioBackendSchema = external_exports.object({
25862
- id: external_exports.string(),
25863
- name: external_exports.string(),
25864
- description: external_exports.string(),
25865
- available: external_exports.boolean(),
25680
+ var RebuildObjectEmbeddingsInput = external_exports.object({
25681
+ /** Restrict to one camera. Omit for the whole fleet. */
25682
+ deviceId: external_exports.number().optional(),
25683
+ since: external_exports.number().optional(),
25684
+ until: external_exports.number().optional(),
25685
+ /** Stop after this many tracks; the result reports whether more remain. */
25686
+ maxTracks: external_exports.number().int().positive().optional(),
25866
25687
  /**
25867
- * Raw classifier labels this backend can emit (e.g. YAMNet's
25868
- * 521-class set or Apple SoundAnalysis's 303-class set). Used by
25869
- * the benchmark UI to populate the `enabledMicroClasses` filter
25870
- * specific to the selected backend without a separate fetch.
25688
+ * Run every embedding on THIS node instead of round-robining the fleet.
25689
+ *
25690
+ * Named `executeOnNodeId` and not `nodeId` on purpose: an inline `nodeId`
25691
+ * field in cap args is read by `parent-unowned-call.ts` as a ROUTING PIN, so
25692
+ * calling it that would pin the rebuild REQUEST itself to that node — the
25693
+ * rebuild orchestration lives on the hub, and only the per-track step runs
25694
+ * remotely. This field is data; the per-track pin is applied inside.
25695
+ *
25696
+ * Absent ⇒ round-robin over every online node whose runner can serve the
25697
+ * pinned model.
25871
25698
  */
25872
- rawLabels: external_exports.array(external_exports.string()).readonly().optional()
25699
+ executeOnNodeId: external_exports.string().optional(),
25700
+ /**
25701
+ * Milliseconds to wait between tracks; omit for the built-in default, `0` to
25702
+ * run flat out.
25703
+ *
25704
+ * A rebuild is bulk maintenance on hub-main's single thread. Measured
25705
+ * 2026-08-06, an unpaced pass held that thread busy 82.2 s out of 120 and
25706
+ * pushed `nodes.topology` from 0.25 s to 26 s for 43 minutes. The value in
25707
+ * force is logged at start and finish so a deliberately slow pass reads
25708
+ * differently from a stalled one.
25709
+ */
25710
+ pacingMs: external_exports.number().int().nonnegative().optional()
25873
25711
  });
25874
- var AudioCapabilitiesSchema = external_exports.object({
25875
- activeBackend: external_exports.string(),
25876
- availableBackends: external_exports.array(AudioBackendSchema).readonly(),
25877
- sampleRate: external_exports.number(),
25878
- chunkDurationMs: external_exports.number()
25712
+ var WipeObjectEmbeddingsResultSchema = external_exports.object({ deleted: external_exports.number() });
25713
+ var RebuildObjectEmbeddingsResultSchema = external_exports.object({
25714
+ started: external_exports.boolean(),
25715
+ /** True when a pass was already running; the new request is ignored. */
25716
+ alreadyRunning: external_exports.boolean()
25879
25717
  });
25880
- var DownloadModelResultSchema = external_exports.object({
25881
- filePath: external_exports.string(),
25882
- sizeMB: external_exports.number(),
25883
- durationMs: external_exports.number()
25718
+ var RebuildStatusSchema = external_exports.object({
25719
+ running: external_exports.boolean(),
25720
+ scanned: external_exports.number(),
25721
+ rebuilt: external_exports.number(),
25722
+ /** Tracks whose key frame is gone — nothing to re-embed from. */
25723
+ missingKeyFrame: external_exports.number(),
25724
+ /** Tracks with no usable detection box. */
25725
+ missingBbox: external_exports.number(),
25726
+ /**
25727
+ * Tracks an executing node REFUSED rather than broke on — an unreadable key
25728
+ * frame, a step that threw. Separate from `failed` because the remedy is
25729
+ * different, and because a whole camera silently contributing zero vectors
25730
+ * is the shape of failure a rebuild must never hide.
25731
+ */
25732
+ notRunnable: external_exports.number(),
25733
+ /**
25734
+ * The pass stopped because NO node could serve the pinned model.
25735
+ *
25736
+ * Distinct from `notRunnable` on purpose: that one says "this track was
25737
+ * refused", this one says "the cluster cannot do this work at all" — every
25738
+ * candidate node either lacks the `clip-embedding` step, lacks a build of the
25739
+ * pinned model for its engine format, or dropped out. The remedy is a model /
25740
+ * engine change, not a per-camera one. Non-zero here always comes with
25741
+ * `complete: false`.
25742
+ */
25743
+ noCapableNode: external_exports.number(),
25744
+ failed: external_exports.number(),
25745
+ /** Set once a pass ends: true only when EVERYTHING was covered. */
25746
+ complete: external_exports.boolean().nullable(),
25747
+ startedAtMs: external_exports.number().nullable(),
25748
+ finishedAtMs: external_exports.number().nullable(),
25749
+ /** Present when the pass ended by throwing. */
25750
+ error: external_exports.string().nullable()
25884
25751
  });
25885
- var AudioTestResultSchema = external_exports.object({
25886
- success: external_exports.boolean(),
25887
- error: external_exports.string().optional(),
25888
- frame: external_exports.custom().optional()
25752
+ var ReplayFrameInputSchema = external_exports.object({
25753
+ timestamp: external_exports.number(),
25754
+ frame: PipelineRunResultBridge
25889
25755
  });
25890
- var PipelineConfigBridge = external_exports.custom();
25891
- var ConfigUISchemaBridge = external_exports.custom();
25892
- var ConfigUISchemaNullableBridge = external_exports.custom();
25893
- var InferenceCapabilitiesBridge = external_exports.custom();
25894
- var ModelAvailabilityListBridge = external_exports.custom();
25895
- var PipelineRunResultBridge = external_exports.custom();
25896
- var pipelineExecutorCapability = {
25897
- name: "pipeline-executor",
25898
- scope: "system",
25756
+ var ReplayTrackSchema = external_exports.object({
25757
+ className: external_exports.string(),
25758
+ firstSeenMs: external_exports.number(),
25759
+ lastSeenMs: external_exports.number(),
25760
+ /** Bbox of the track's FIRST matched detection, pixel-space in the clip's
25761
+ * frame a representative box for the diff's `(className, window, IoU)`
25762
+ * pairing (`replay-diff.ts`). A replay does not need the full per-frame
25763
+ * trajectory production's `Track.positions` keeps. */
25764
+ bbox: BoundingBoxSchema,
25765
+ /** How many of the input frames this track matched a real detection on
25766
+ * (never a coasted/extrapolated frame) — the replay's own signal for "how
25767
+ * solid is this track", cheaper than re-deriving it from a trajectory. */
25768
+ framesMatched: external_exports.number().int()
25769
+ });
25770
+ var RunReplayFrameProcessorResultSchema = external_exports.object({ tracks: external_exports.array(ReplayTrackSchema).readonly() });
25771
+ var pipelineAnalyticsCapability = {
25772
+ name: "pipeline-analytics",
25773
+ scope: "device",
25899
25774
  mode: "singleton",
25775
+ kind: "wrapper",
25776
+ defaultActive: true,
25777
+ deviceTypes: [DeviceType.Camera],
25778
+ exposesDeviceSettings: true,
25900
25779
  methods: {
25901
- getAvailableEngines: method(external_exports.void(), external_exports.array(PipelineEngineChoiceSchema)),
25902
- getSelectedEngine: method(external_exports.void(), PipelineEngineChoiceSchema),
25903
- getDefaultSteps: method(PipelineEngineChoiceSchema, external_exports.array(PipelineDefaultStepSchema)),
25780
+ getActiveTracks: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(TrackSchema).readonly()),
25781
+ getTrack: method(external_exports.object({
25782
+ deviceId: external_exports.number(),
25783
+ trackId: external_exports.string()
25784
+ }), TrackSchema.nullable()),
25785
+ /** Historical completed tracks for a device. Queried from the
25786
+ * persisted `pipeline-analytics:tracks` collection; active tracks
25787
+ * still in RAM are not included. */
25788
+ listTracks: method(external_exports.object({
25789
+ deviceId: external_exports.number(),
25790
+ since: external_exports.number().optional(),
25791
+ until: external_exports.number().optional(),
25792
+ limit: external_exports.number().optional(),
25793
+ /** Spatial filter — only tracks whose trajectory intersects the zone
25794
+ * (normalized 0..1 rect or polygon). SQL-prefiltered on the persisted
25795
+ * envelope columns, then precisely tested per position. Tracks with
25796
+ * an unknown envelope (no frame dims at persist time) always match. */
25797
+ zone: TrackZoneFilterSchema.optional(),
25798
+ /** See {@link TrackProjectionSchema}. Default `full` (backward
25799
+ * compatible — omitting the field keeps today's exact behaviour). */
25800
+ projection: TrackProjectionSchema.optional(),
25801
+ /** Include stationary-promoted rows (parked objects handed to the
25802
+ * stationary registry). Default false: the timeline lists passages,
25803
+ * not parking records (operator decision, 2026-08-15). */
25804
+ includeStationary: external_exports.boolean().optional()
25805
+ }), external_exports.array(TrackSchema).readonly()),
25806
+ /**
25807
+ * Batched cluster-wide track listing — ONE call for the events page /
25808
+ * reel first paint instead of a per-camera `listTracks` fan-out. Merges
25809
+ * the persisted completed tracks of every requested device, sorted by
25810
+ * `lastSeen` DESC with a stable (lastSeen, trackId) cursor. Per-device
25811
+ * indexed pages (`idx_tracks_device_lastSeen`) are k-way merged
25812
+ * provider-side; `projection: 'slim'` drops the heavy `positions[]` /
25813
+ * `snapshots[]` JSON (returned as empty arrays). Active in-RAM tracks
25814
+ * are not included (same contract as `listTracks`).
25815
+ */
25816
+ listRecentTracks: method(RecentTracksQueryInput, RecentTracksPageSchema),
25817
+ /**
25818
+ * Batched co-moving group listing — the Groups feed. Same merge/cursor
25819
+ * contract as {@link listRecentTracks}. A group is a sealed partition of
25820
+ * one session; `getGroup` is the detail with members.
25821
+ */
25822
+ listGroups: method(ListGroupsQueryInput, ListGroupsPageSchema),
25823
+ getGroup: method(external_exports.object({
25824
+ deviceId: external_exports.number(),
25825
+ groupId: external_exports.string().min(1)
25826
+ }), AnalyticsGroupDetailSchema.nullable()),
25827
+ clearTracks: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.void(), {
25828
+ kind: "mutation",
25829
+ auth: "admin"
25830
+ }),
25831
+ getMotionEvents: method(DeviceEventQueryInput, external_exports.array(MotionEventSchema).readonly()),
25832
+ getObjectEvents: method(ObjectEventQueryInput, external_exports.array(ObjectEventSchema).readonly()),
25833
+ getAudioEvents: method(DeviceEventQueryInput, external_exports.array(AudioEventSchema).readonly()),
25834
+ /**
25835
+ * Every event kind the device can produce: built-ins (motion + audio),
25836
+ * detection classes actually observed for the device (from the track
25837
+ * history), and sensor kinds contributed by LINKED devices (resolved via
25838
+ * `device-manager.getLinkedDevices`, mapped through `EVENT_KIND_BY_CAP`).
25839
+ */
25840
+ listEventKinds: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(EventKindDescriptorSchema).readonly()),
25841
+ /**
25842
+ * The same answer for many cameras in ONE call.
25843
+ *
25844
+ * `listEventKinds` is the slowest per-device query on the hub — measured
25845
+ * at ~100 ms against 6-11 ms for its neighbours — because composing it
25846
+ * costs a `getCameraStatus`, a `getLinkedDevices` and one `getBindings`
25847
+ * per linked device. The viewer asks for it once per camera at first
25848
+ * paint, so twelve cameras paid twelve of those round-trips on top of the
25849
+ * per-camera cost.
25850
+ *
25851
+ * Batched, the camera statuses come from ONE `getCameraStatuses` instead
25852
+ * of N, and the per-device composition runs concurrently.
25853
+ *
25854
+ * Per-device rather than pre-merged on purpose: a caller that renders one
25855
+ * camera's lanes needs to know WHICH camera a kind came from, and merging
25856
+ * is three lines the caller can do. `listEventKinds` stays for
25857
+ * single-device callers.
25858
+ */
25859
+ listEventKindsBatch: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).min(1).max(200) }), external_exports.array(EventKindsForDeviceSchema).readonly()),
25860
+ /**
25861
+ * Sensor-event history for a camera: state changes of LINKED sensor
25862
+ * devices, attributed to the camera at ingest time (one row per linked
25863
+ * camera). Mirrors `getAudioEvents` query semantics; `kinds` narrows to
25864
+ * a kind subset.
25865
+ */
25866
+ getSensorEvents: method(external_exports.object({
25867
+ deviceId: external_exports.number(),
25868
+ since: external_exports.number().optional(),
25869
+ until: external_exports.number().optional(),
25870
+ kinds: external_exports.array(external_exports.string()).optional(),
25871
+ limit: external_exports.number().int().min(1).max(MAX_EVENT_QUERY_LIMIT).default(DEFAULT_EVENT_QUERY_LIMIT)
25872
+ }), external_exports.array(SensorEventSchema).readonly()),
25873
+ /**
25874
+ * Importance-ranked highlights for a device+window. Queries completed
25875
+ * tracks by (deviceId, firstSeen ∈ [since,until]), scores each (or reuses
25876
+ * the persisted score), filters by minImportance/classFilter, orders by
25877
+ * importance desc, and returns up to `limit` compact key events mapped to
25878
+ * each track's best event. Legacy tracks lacking a persisted score are
25879
+ * scored on-read (no write). Degrades to `[]` on error.
25880
+ */
25881
+ getKeyEvents: method(KeyEventQueryInput, external_exports.array(KeyEventSchema).readonly()),
25882
+ /** Server-side bucketed event counts for the 24-hour timeline.
25883
+ * Returns one entry per non-empty bucket; empty buckets are omitted. */
25884
+ getEventDensity: method(external_exports.object({
25885
+ deviceId: external_exports.number(),
25886
+ since: external_exports.number(),
25887
+ until: external_exports.number(),
25888
+ bucketMs: external_exports.number().int().positive()
25889
+ }), external_exports.array(external_exports.object({
25890
+ bucketStart: external_exports.number(),
25891
+ motion: external_exports.number().int(),
25892
+ object: external_exports.number().int(),
25893
+ audio: external_exports.number().int()
25894
+ })).readonly()),
25895
+ /**
25896
+ * @deprecated Prefer `pruneTracksBefore` — the track is the ROOT of the
25897
+ * analytics model and retention must cascade from it (design §5.1). This
25898
+ * event-only prune leaves orphaned tracks/faces/plates/embeddings behind.
25899
+ * Retained as a compat shim for the Phase-B3 recorder orchestration caller,
25900
+ * which prunes events to keep history aligned with available footage and
25901
+ * reads the per-kind counts. Delete all events (motion + object + audio)
25902
+ * for the given device older than `cutoffMs` (exclusive) + their thumbnails
25903
+ * in lockstep; returns per-kind deleted counts.
25904
+ */
25905
+ pruneEventsBefore: method(external_exports.object({
25906
+ deviceId: external_exports.number(),
25907
+ cutoffMs: external_exports.number()
25908
+ }), external_exports.object({
25909
+ motion: external_exports.number().int(),
25910
+ object: external_exports.number().int(),
25911
+ audio: external_exports.number().int()
25912
+ }), {
25913
+ kind: "mutation",
25914
+ auth: "admin"
25915
+ }),
25916
+ /**
25917
+ * Track-centric retention entry point (design §5.1). Selects every
25918
+ * persisted track for the device whose `lastSeen < cutoffMs` (paged) and
25919
+ * runs the extensible whole-track deletion cascade (design §3): object
25920
+ * events + their media, track/face/plate-owned media, unassigned face
25921
+ * reads, per-track CLIP embeddings, then the track root LAST. ENROLLED
25922
+ * (assigned) faces/plates and `ownerKind:'identity'` media are exempt —
25923
+ * the durable matching gallery always persists. Fires the per-track
25924
+ * live-state cleanup so a pruned track can't linger in dispatch/overlay
25925
+ * state. Returns per-family deleted counts (`tracks` authoritative).
25926
+ *
25927
+ * Replaces `pruneEventsBefore` as the correct time-based retention prune.
25928
+ */
25929
+ pruneTracksBefore: method(external_exports.object({
25930
+ deviceId: external_exports.number(),
25931
+ cutoffMs: external_exports.number()
25932
+ }), TrackCascadeCountsSchema, {
25933
+ kind: "mutation",
25934
+ auth: "admin"
25935
+ }),
25936
+ /**
25937
+ * Operator "clean slate" for a device (design §5.3): prune EVERY track for
25938
+ * the device via the same cascade as `pruneTracksBefore` with `cutoffMs =
25939
+ * now`. One call per device — no client-side track enumeration. Enrolled
25940
+ * gallery + identity media are exempt (design §4). Returns per-family
25941
+ * deleted counts.
25942
+ */
25943
+ wipeAllAnalytics: method(external_exports.object({ deviceId: external_exports.number() }), TrackCascadeCountsSchema, {
25944
+ kind: "mutation",
25945
+ auth: "admin"
25946
+ }),
25947
+ /**
25948
+ * Disk-wins reconcile for one camera. Drops media index rows whose blobs
25949
+ * are gone, then cascades tracks (including favourited and staging) that
25950
+ * have no remaining files. Enrolled identity/vehicle/scene media is never
25951
+ * probed. Trackless motion/audio events with no remaining file are dropped,
25952
+ * including snapshot-less rows.
25953
+ */
25954
+ reconcileFromDisk: method(external_exports.object({ deviceId: external_exports.number() }), DiskReconcileCountsSchema, {
25955
+ kind: "mutation",
25956
+ auth: "admin"
25957
+ }),
25958
+ /**
25959
+ * Delete whole tracks (object events) by id for the given device,
25960
+ * cascading their media in lockstep. Returns the number of tracks
25961
+ * actually deleted plus the ids that could not be removed.
25962
+ *
25963
+ * Operator-driven from the DI Events page (batch delete). Routes through
25964
+ * the same widened track-deletion cascade (design §5.2).
25965
+ */
25966
+ deleteTracks: method(external_exports.object({
25967
+ deviceId: external_exports.number(),
25968
+ trackIds: external_exports.array(external_exports.string()).min(1)
25969
+ }), external_exports.object({
25970
+ deleted: external_exports.number().int(),
25971
+ failed: external_exports.array(external_exports.string()).readonly()
25972
+ }), {
25973
+ kind: "mutation",
25974
+ auth: "admin"
25975
+ }),
25976
+ /**
25977
+ * Set the per-track operator flags (`markForTrain`, `debug`, `favourited`) on ONE track.
25978
+ * The patch is PARTIAL — an omitted key is left untouched — because the
25979
+ * three surfaces that write it (admin Events grid, viewer track detail,
25980
+ * viewer cluster detail) each own one toggle and must not clobber the other.
25981
+ *
25982
+ * Writes the track ROW: `markForTrain`/`debug` are per-TRACK state, so they
25983
+ * live where `label` and `importance` live, not in any per-device settings
25984
+ * store. Updates the in-RAM active track too, so a flag set on a live track
25985
+ * survives its expiry-time persist.
25986
+ *
25987
+ * `auth: 'protected'` (the default), NOT `admin`: the viewer is an
25988
+ * authenticated non-admin surface and two of the three call sites are
25989
+ * there. The note that used to sit here said to revisit this the day a flag
25990
+ * gained an effect that costs storage, and D81 is that day — `markForTrain`
25991
+ * now pins. It STAYS protected, and the reason is that the alternative
25992
+ * makes the feature pointless: marking a track is something you do while
25993
+ * looking at it, on the surface you were already looking at it on, and that
25994
+ * surface is the viewer. What the storage cost gets instead is a BOUND — a
25995
+ * per-device pin budget enforced in the body, refusing a new pin past the
25996
+ * limit while always allowing un-marking. `deleteTracks` next door is still
25997
+ * admin, because destroying evidence and preserving it are not symmetric.
25998
+ *
25999
+ * `markForTrain` writes the retrain LIFECYCLE, not a boolean column: `true`
26000
+ * is `none → staging`, `false` is `staging → none`. A track already
26001
+ * `trained` refuses BOTH — its frames are copies inside the retrain dataset
26002
+ * and re-staging it from a generic toggle is how the same material gets
26003
+ * annotated twice under two ground truths. Returning a trained track to
26004
+ * staging is a deliberate action of the retrain page, which is also the only
26005
+ * thing that produces `trained` in the first place.
26006
+ *
26007
+ * Returns the RESOLVED state of both flags (absent → `false`) plus the
26008
+ * `retrainStatus` they were derived from, so a caller can drive its toggle
26009
+ * — and render a `trained` badge — without a re-fetch. Rejects an unknown
26010
+ * track, a new staging mark on a device already holding its full budget, and
26011
+ * any `markForTrain` write against a trained track.
26012
+ */
26013
+ setTrackFlags: method(external_exports.object({
26014
+ /** Log/audit scope only — the trackId is globally unique on its own. */
26015
+ deviceId: external_exports.number(),
26016
+ trackId: external_exports.string(),
26017
+ flags: TrackFlagsPatchSchema
26018
+ }), TrackFlagsSchema, { kind: "mutation" }),
26019
+ /**
26020
+ * Durable event-store footprint for the management UI: event rows
26021
+ * (motion + object + audio) counted per camera + total, plus the
26022
+ * event-owned media bytes on disk per camera + total. Stat/count-based,
26023
+ * computed on demand.
26024
+ */
26025
+ getEventStoreFootprint: method(external_exports.object({}), EventStoreFootprintSchema, {
26026
+ kind: "query",
26027
+ auth: "admin"
26028
+ }),
26029
+ /**
26030
+ * Cluster-wide prune of events older than `olderThanMs` (exclusive) across
26031
+ * every camera, deleting each event's media in lockstep. Logged to the
26032
+ * events ops-log with `reason` (default `'retention'`). Returns the summed
26033
+ * per-kind deleted counts.
26034
+ */
26035
+ pruneEvents: method(external_exports.object({
26036
+ olderThanMs: external_exports.number(),
26037
+ reason: OpsLogReasonSchema.optional()
26038
+ }), EventPruneCountsSchema, {
26039
+ kind: "mutation",
26040
+ auth: "admin"
26041
+ }),
26042
+ /**
26043
+ * Manually delete EVERY event (motion + object + audio) for one camera and
26044
+ * its event-owned media in lockstep. Logged to the events ops-log as
26045
+ * `op:'manual-delete', reason:'manual'`. Destructive — the admin UI guards
26046
+ * it behind a confirm.
26047
+ */
26048
+ deleteDeviceEvents: method(external_exports.object({ deviceId: external_exports.number() }), EventPruneCountsSchema, {
26049
+ kind: "mutation",
26050
+ auth: "admin"
26051
+ }),
26052
+ /** The events ops-log rows (newest-first), optionally scoped to one camera.
26053
+ * Backed by a declared pipeline-analytics SQLite collection. */
26054
+ /** Internal migration-participant lease. It drains active MediaStore
26055
+ * writes and refuses new ones without changing analytics bindings. */
26056
+ pauseForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ paused: external_exports.literal(true) }), {
26057
+ kind: "mutation",
26058
+ auth: "admin"
26059
+ }),
26060
+ resumeForStorageMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ resumed: external_exports.literal(true) }), {
26061
+ kind: "mutation",
26062
+ auth: "admin"
26063
+ }),
26064
+ /** Drops cached location roots after the storage coordinator repoints a
26065
+ * default so future event-media writes use the new root. */
26066
+ refreshStorageLocationsForMigration: method(StorageMigrationLeaseInputSchema, external_exports.object({ refreshed: external_exports.literal(true) }), {
26067
+ kind: "mutation",
26068
+ auth: "admin"
26069
+ }),
26070
+ startStorageMigrationMove: method(StorageMigrationMediaMoveInputSchema, external_exports.object({ jobId: external_exports.string() }), {
26071
+ kind: "mutation",
26072
+ auth: "admin"
26073
+ }),
26074
+ getStorageMigrationMoveStatus: method(external_exports.object({ jobId: external_exports.string() }), RelocateJobSchema.nullable(), { auth: "admin" }),
26075
+ cancelStorageMigrationMove: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
26076
+ kind: "mutation",
26077
+ auth: "admin"
26078
+ }),
25904
26079
  /**
25905
- * Per-node detection-engine provisioning snapshot. Returns the live
25906
- * state of the lazy runtime-provisioning machine on `nodeId`
25907
- * (idle / installing / verifying / ready / failed). The UI pairs this
25908
- * one-shot query with the `pipeline.engine-provisioning` live event
25909
- * (emitted on every transition) to drive a per-node "engine ready?"
25910
- * indicator without polling. Phase 2.
26080
+ * Moves event media between locations the OPERATOR's mover, without the
26081
+ * coordinator's lease. Twin of `recording.relocateFootage`: the same
26082
+ * engine the lease-gated coordinated migration uses
26083
+ * (`startStorageMigrationMove`), armable in the background and WITHOUT
26084
+ * pausing anything. Exists because `eventMedia` was the only storage
26085
+ * class whose only move path went through the recorder's global pause.
25911
26086
  */
25912
- getEngineProvisioning: method(external_exports.object({ nodeId: external_exports.string() }), EngineProvisioningSchema),
25913
- getVideoPipelineSteps: method(external_exports.void(), external_exports.record(external_exports.string(), external_exports.object({
25914
- modelId: external_exports.string(),
25915
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).readonly()
25916
- }))),
25917
- setVideoPipelineSteps: method(external_exports.object({ steps: external_exports.record(external_exports.string(), external_exports.object({
25918
- modelId: external_exports.string(),
25919
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).readonly()
25920
- })) }), external_exports.object({ success: external_exports.literal(true) }), {
26087
+ relocateMedia: method(RelocateMediaInputSchema, external_exports.object({ jobId: external_exports.string() }), {
26088
+ kind: "mutation",
26089
+ auth: "admin"
26090
+ }),
26091
+ /** Every relocate job this addon knows about, newest first (in RAM: the
26092
+ * move is resumable, so a lost list costs nothing but the display). */
26093
+ listRelocateMediaJobs: method(external_exports.object({}), external_exports.array(RelocateJobSchema).readonly(), {
26094
+ kind: "query",
26095
+ auth: "admin"
26096
+ }),
26097
+ /** Cancel a running or queued relocate job. */
26098
+ cancelRelocateMedia: method(external_exports.object({ jobId: external_exports.string() }), external_exports.object({ cancelled: external_exports.boolean() }), {
25921
26099
  kind: "mutation",
25922
26100
  auth: "admin"
25923
26101
  }),
26102
+ listOpsLog: method(OpsLogQueryInputSchema, external_exports.array(OpsLogEntrySchema).readonly(), {
26103
+ kind: "query",
26104
+ auth: "admin"
26105
+ }),
25924
26106
  /**
25925
- * Clear THIS node's executor-side PER-DEVICE settings stores (the
25926
- * per-camera step overrides the object-detection root reads via
25927
- * `applyDeviceOverridesToTree`). `nodeId` is the ROUTING key — the
25928
- * generated cap-router strips it (default `nodeIdMode: 'routing'`) and
25929
- * dispatches to that node, so the provider method runs ON the target
25930
- * node and receives no `nodeId`.
26107
+ * The CHEAP QUESTION, asked before any media moves: how big is the dataset
26108
+ * the marked (`markForTrain`) tracks would produce?
25931
26109
  *
25932
- * This is the slimmed executor leg of the orchestrator's
25933
- * `resetNodePipelineDefaults` flow (which owns the real reset: node
25934
- * addonDefaults pins + per-camera orchestrator overrides). The legacy
25935
- * `resetToDefault` which reset a persisted global step-tree seed
25936
- * nothing in the live per-camera path read was removed together with
25937
- * that seed.
26110
+ * Answered from media INDEX rows only key, kind, size, timestamp — so it
26111
+ * costs ~2 KB of reads per track and no blob reads at all. The measured harm
26112
+ * behind D56 was a bulk pass that read and base64'd every blob a track owned
26113
+ * before deciding anything, taking hub-main to 82 s busy out of 120; an
26114
+ * export is that same I/O shape, so it inherits the same discipline: know
26115
+ * the size, then decide.
26116
+ *
26117
+ * `truncated` reports that more marked tracks exist than one pass carries.
26118
+ * Empty `deviceIds` ⇒ every device that has marked tracks.
25938
26119
  */
25939
- clearDeviceOverrides: method(external_exports.object({ nodeId: external_exports.string() }), external_exports.object({
25940
- success: external_exports.literal(true),
25941
- clearedDevices: external_exports.number()
26120
+ getTrainingExportSummary: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), TrainingExportSummarySchema, {
26121
+ kind: "query",
26122
+ auth: "admin"
26123
+ }),
26124
+ /**
26125
+ * Where to download the dataset archive.
26126
+ *
26127
+ * The BYTES do not come back through this cap — they come from the returned
26128
+ * data-plane URL, which streams a tar built entry by entry. A multi-gigabyte
26129
+ * archive base64'd through a unary RPC envelope would be held whole in
26130
+ * memory twice on a hub this repo has already OOM'd once (D9/D18 are the
26131
+ * same lesson about frames). `getDownloadUrl` on `recordingExport` is the
26132
+ * precedent, and this follows it deliberately.
26133
+ *
26134
+ * The archive contains a `manifest.json` FIRST, then the stored media
26135
+ * VERBATIM under `tracks/<deviceId>/<trackId>/…`. No crop is derived and no
26136
+ * model is run: a training set's pixels must be the pixels the pipeline saw.
26137
+ */
26138
+ getTrainingExportUrl: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), external_exports.object({ url: external_exports.string() }), {
26139
+ kind: "query",
26140
+ auth: "admin"
26141
+ }),
26142
+ /**
26143
+ * The staging worklist for one camera, or for every camera that has one.
26144
+ *
26145
+ * Fetched ON DEMAND, over the staging set only — the page never scans
26146
+ * history, because making the working set small is the entire purpose of
26147
+ * the mark. Each row carries how many frames the dataset already holds from
26148
+ * the track and how many subjects were annotated on them, so
26149
+ * `frameCount: 0` reads as "still to work" without a second call per track.
26150
+ *
26151
+ * `auth: 'admin'`, unlike the viewer-level mark itself: marking a track is
26152
+ * curation you do while looking at it, but building the training set the
26153
+ * fleet's models are fine-tuned on is not.
26154
+ */
26155
+ listRetrainStaging: method(external_exports.object({
26156
+ /** Empty ⇒ every camera that has staging tracks. A LIST, not a single
26157
+ * `deviceId`, deliberately: `deviceId` would make this device-bound and
26158
+ * route it at one camera's owner, and "every camera" would stop being
26159
+ * expressible at all. */
26160
+ deviceIds: external_exports.array(external_exports.number()).optional(),
26161
+ limit: external_exports.number().int().min(1).max(500).optional()
26162
+ }), external_exports.array(RetrainTrackSchema).readonly(), {
26163
+ kind: "query",
26164
+ auth: "admin"
26165
+ }),
26166
+ /**
26167
+ * What a track can contribute, and what it already has.
26168
+ *
26169
+ * `candidates` are the track's whole, unannotated frames — index rows only,
26170
+ * so this is cheap. `copies` are the frames already inside the dataset, and
26171
+ * a candidate whose copy exists is marked `copied: true`: selecting it again
26172
+ * is free and CANNOT fail, whatever became of the original.
26173
+ *
26174
+ * A crop, a thumbnail and `fullFrameBoxed` are never candidates. The last
26175
+ * one matters most: it has the model's own rectangle burned into the pixels,
26176
+ * and a detector trained on it learns to find a green line.
26177
+ */
26178
+ listRetrainFrames: method(external_exports.object({ trackId: external_exports.string() }), RetrainFrameListSchema, {
26179
+ kind: "query",
26180
+ auth: "admin"
26181
+ }),
26182
+ /**
26183
+ * COPY-ON-SELECT — the write that makes `trained` safe to evict.
26184
+ *
26185
+ * Selecting a frame copies its bytes into retrain storage immediately: not
26186
+ * a reference, not a lease. Once the copy exists the dataset no longer
26187
+ * depends on the track's media, which is exactly what lets D81 hand a
26188
+ * `trained` track back to retention.
26189
+ *
26190
+ * The order inside is load-bearing and is pinned by a test: an EXISTING
26191
+ * copy is returned without touching the source, so an original that
26192
+ * evaporated blocks the selection of THAT ORIGINAL and never the copy
26193
+ * already taken. Every refusal comes back named — a dropped selection is
26194
+ * never silent, on the wire or in the log.
26195
+ */
26196
+ selectRetrainFrames: method(external_exports.object({
26197
+ deviceId: external_exports.number(),
26198
+ trackId: external_exports.string(),
26199
+ mediaKeys: external_exports.array(external_exports.string()).min(1)
26200
+ }), RetrainFrameSelectionSchema, {
26201
+ kind: "mutation",
26202
+ auth: "admin"
26203
+ }),
26204
+ /** Un-select a frame: its annotations go first, then the copy and its blob.
26205
+ * Deliberately destructive and deliberately explicit — it is the only way
26206
+ * a frame leaves the dataset before export. */
26207
+ deselectRetrainFrame: method(external_exports.object({
26208
+ deviceId: external_exports.number(),
26209
+ trackId: external_exports.string(),
26210
+ frameId: external_exports.string()
26211
+ }), external_exports.object({
26212
+ removed: external_exports.boolean(),
26213
+ removedAnnotations: external_exports.number().int()
25942
26214
  }), {
25943
26215
  kind: "mutation",
25944
26216
  auth: "admin"
25945
26217
  }),
25946
- getSchema: method(external_exports.void(), PipelineSchemaSchema),
25947
- getGlobalSteps: method(external_exports.void(), external_exports.array(PipelineDefaultStepSchema).readonly().nullable()),
25948
- getGlobalPipelineConfig: method(external_exports.void(), PipelineConfigBridge),
25949
- getOrchestratorConfigSchema: method(external_exports.void(), ConfigUISchemaBridge),
25950
26218
  /**
25951
- * Gate B (pre-init config-correctness gate). PURE COMPUTE against this
25952
- * node's `currentEngine.format` — resolves `steps` the same way the
25953
- * runtime dispatch path would, and reports what WOULD happen without
25954
- * touching any node-global state. Called by the orchestrator at attach
25955
- * time (`attachOn`), node-pinned to the TARGET node, so config problems
25956
- * surface BEFORE `pipelineRunner.attachCamera` rather than at the first
25957
- * per-frame resolve. `ok` is false iff `issues` is non-empty (both
25958
- * `unknown-addon` and `no-format-build` are HARD issues); `substitutions`
25959
- * is informational (a degraded-but-loadable model swap) and never
25960
- * affects `ok`. Never throws.
26219
+ * The pixels of ONE copied frame, base64.
26220
+ *
26221
+ * Through the cap rather than a data plane because it is genuinely one
26222
+ * frame at a time, on demand, at human speed — the shape D9/D18 permit
26223
+ * (what they forbid is frames crossing a boundary at frame RATE). The
26224
+ * annotation canvas needs the image and its exact dimensions in the same
26225
+ * answer: a canvas that places a normalised box against a size it guessed
26226
+ * draws every box in the wrong place.
25961
26227
  */
25962
- validatePipeline: method(external_exports.object({ steps: external_exports.array(PipelineStepInputSchema) }), PipelineValidationResultSchema),
25963
- listTemplates: method(external_exports.void(), external_exports.array(PipelineTemplateSchema$1).readonly()),
25964
- saveTemplate: method(external_exports.object({
25965
- name: external_exports.string(),
25966
- steps: external_exports.array(PipelineTemplateStepSchema).readonly(),
25967
- engine: PipelineEngineChoiceSchema
25968
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
25969
- updateTemplate: method(external_exports.object({
25970
- id: external_exports.string(),
25971
- name: external_exports.string().optional(),
25972
- steps: external_exports.array(PipelineTemplateStepSchema).readonly().optional()
25973
- }), PipelineTemplateSchema$1, { kind: "mutation" }),
25974
- deleteTemplate: method(external_exports.object({ id: external_exports.string() }), external_exports.void(), { kind: "mutation" }),
25975
- getCapabilities: method(external_exports.void(), InferenceCapabilitiesBridge),
25976
- getAddonModels: method(external_exports.object({ addonId: external_exports.string() }), ModelAvailabilityListBridge),
25977
- downloadModel: method(external_exports.object({
25978
- addonId: external_exports.string(),
25979
- modelId: external_exports.string(),
25980
- format: ModelFormatSchema$1
25981
- }), DownloadModelResultSchema, { kind: "mutation" }),
25982
- deleteModel: method(external_exports.object({
25983
- addonId: external_exports.string(),
25984
- modelId: external_exports.string(),
25985
- format: ModelFormatSchema$1
25986
- }), external_exports.object({ success: external_exports.literal(true) }), { kind: "mutation" }),
26228
+ getRetrainFrameImage: method(external_exports.object({ frameId: external_exports.string() }), external_exports.object({
26229
+ base64: external_exports.string(),
26230
+ width: external_exports.number().int(),
26231
+ height: external_exports.number().int()
26232
+ }), {
26233
+ kind: "query",
26234
+ auth: "admin"
26235
+ }),
26236
+ /**
26237
+ * Ask the pipeline what it sees, as a PROPOSAL.
26238
+ *
26239
+ * Runs through `pipelineRunner.runStatelessStep` on the COPIED frame, and
26240
+ * every box comes back as a draft with `source: 'assist'` plus the model and
26241
+ * score that produced it. The operator confirms, edits, adds and deletes;
26242
+ * nothing is stored until `saveRetrainAnnotations`.
26243
+ *
26244
+ * For packages the request is `rfdetr-package` on the ZONE CROP at 0.35 —
26245
+ * never the whole frame, where a package detector at that threshold proposes
26246
+ * furniture. A package request with no zone is REFUSED rather than widened,
26247
+ * because the silent widening would look like a bad model for as long as
26248
+ * nobody checked which rectangle it ran on.
26249
+ */
26250
+ proposeRetrainAnnotations: method(external_exports.object({
26251
+ deviceId: external_exports.number(),
26252
+ trackId: external_exports.string(),
26253
+ frameId: external_exports.string(),
26254
+ subject: RetrainAssistSubjectSchema,
26255
+ /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
26256
+ nodeId: external_exports.string().optional()
26257
+ }), RetrainAssistResultSchema, {
26258
+ kind: "mutation",
26259
+ auth: "admin"
26260
+ }),
25987
26261
  /**
25988
- * Stateless single-frame execution. Callers (runner, benchmark) pass
25989
- * the complete `engine` + `steps` tree; the executor holds no state
25990
- * about cameras or saved pipelines.
26262
+ * The FrameProcessor pass of a replay run — see the `Replay` section
26263
+ * above this capability's definition for why this is not the
26264
+ * `processFrame` method this file's header says pipeline-analytics does
26265
+ * not have.
25991
26266
  *
25992
- * `engine` is optional during the migration window to preserve the
25993
- * legacy call shape used by existing benchmark code; once all
25994
- * callers pass it explicitly we make it required.
26267
+ * Constructs a FRESH `FrameProcessor` for `(deviceId, source)`, feeds it
26268
+ * `frames` IN THE ORDER GIVEN (the caller is responsible for time
26269
+ * ordering this method does not sort), and returns the tracks it
26270
+ * produced. Zero persistence: no `TrackStore`, no event bus, no media
26271
+ * capture. `zones` / `detectionRules` are the run's OWN zone set —
26272
+ * typically the camera's real zones plus an ephemeral overlay
26273
+ * (`addon-benchmark`'s `replay-plan.ts`), never read from or written to
26274
+ * the `zones` capability by this method itself.
26275
+ */
26276
+ runReplayFrameProcessor: method(external_exports.object({
26277
+ deviceId: external_exports.number(),
26278
+ source: DetectionSourceSchema,
26279
+ zones: external_exports.array(ZoneSchema).readonly().optional(),
26280
+ detectionRules: external_exports.array(ZoneRuleSchema).readonly().optional(),
26281
+ zoneMembershipMinOverlap: external_exports.number().min(0).max(1).optional(),
26282
+ frames: external_exports.array(ReplayFrameInputSchema).min(1)
26283
+ }), RunReplayFrameProcessorResultSchema, {
26284
+ kind: "mutation",
26285
+ auth: "admin"
26286
+ }),
26287
+ /** Every annotation on a track, oldest first. */
26288
+ listRetrainAnnotations: method(external_exports.object({ trackId: external_exports.string() }), external_exports.array(RetrainAnnotationSchema).readonly(), {
26289
+ kind: "query",
26290
+ auth: "admin"
26291
+ }),
26292
+ /**
26293
+ * Replace EVERY annotation on one frame with the supplied set.
25995
26294
  *
25996
- * Exactly one of `frame`, `frameRef`, `frameHandle`, `imageBase64`,
25997
- * `referenceImage` must be provided:
25998
- * - `frame`: runtime dispatch path (runner decoded broker frame).
25999
- * Carries the raw buffer, dimensions, and format; the executor
26000
- * uses it directly without base64 round-tripping.
26001
- * - `frameHandle` (CB5): a zero-pixel shm `FrameHandle` for the SAME
26002
- * decoded frame. Both runner and executor are hub-local processes
26003
- * sharing `/dev/shm`, so the executor maps the named segment and
26004
- * reads the pixels back zero-copy eliminating the ~1.2MB
26005
- * re-serialisation over UDS/MsgPack the `frame` path pays per call.
26006
- * High-risk: the FrameRing is a latest-wins seqlock with no
26007
- * refcount, so a recycled slot yields a null read; the executor
26008
- * then degrades to an empty result and the runner ships pixels via
26009
- * `frame` as the fallback (queue-depth gated on the runner side).
26010
- * - `imageBase64`: one-shot test path (benchmark ImageTab).
26011
- * - `referenceImage`: named file from the reference-image store.
26295
+ * Whole-frame replacement, not per-box upsert: the unit of ground truth is
26296
+ * the frame, and "the operator deleted a box" must be the same durable
26297
+ * outcome as "the operator never drew it". A per-box patch would let a frame
26298
+ * keep a box the operator removed on a surface that only knew about the
26299
+ * boxes it sent.
26300
+ *
26301
+ * Refuses a macro class typed into `label` or `subLabel` — the tiers are
26302
+ * separate and the guard is at the WRITE, because a mixed taxonomy cannot
26303
+ * be un-mixed by reading it.
26012
26304
  */
26013
- runPipeline: method(external_exports.object({
26014
- engine: PipelineEngineChoiceSchema.optional(),
26015
- steps: external_exports.array(PipelineStepInputSchema).min(1),
26016
- frame: FrameInputSchema.optional(),
26017
- /**
26018
- * Process-local lazy frame. Valid only when caller and provider resolve
26019
- * in the same execution-group process; split/cross-node callers use
26020
- * `frame`/`image` inline compatibility instead.
26021
- */
26022
- frameRef: FrameRefSchema.optional(),
26023
- /**
26024
- * CB5 shm passthrough — a `FrameHandle` naming the same ring slot
26025
- * the decoded pixels live in. One more member of the one-of
26026
- * frame/frameHandle/image/imageBase64/referenceImage group.
26027
- */
26028
- frameHandle: FrameHandleSchema.optional(),
26029
- imageBase64: external_exports.string().optional(),
26030
- /**
26031
- * Binary JPEG bytes — preferred over `imageBase64` on internal
26032
- * hops (hub → forked worker via Moleculer MsgPack) because it
26033
- * skips the 33% base64 overhead + the per-call base64 decode on
26034
- * the detection-pipeline worker. Callers can pass either; exactly
26035
- * one of `frame`/`image`/`imageBase64`/`referenceImage` is required.
26036
- */
26037
- image: external_exports.instanceof(Uint8Array).optional(),
26038
- referenceImage: external_exports.string().optional(),
26039
- deviceId: external_exports.number().optional(),
26040
- sessionId: external_exports.string().optional(),
26041
- /**
26042
- * Execution plane. 'full' (default) runs the whole tree — benchmark,
26043
- * reference-image, and detail-subtree calls. 'frame' is the live
26044
- * per-frame dispatch: ONLY root-plane steps run; crop children
26045
- * (inputClasses ≠ null) are skipped and served per-track via
26046
- * pipelineRunner.runDetailSubtree (two-plane design).
26047
- */
26048
- plane: external_exports.enum(["full", "frame"]).optional(),
26049
- /**
26050
- * Inference-device selector (Phase 2 multi-device). Format
26051
- * `<backend>:<device>` (e.g. `openvino:gpu`, `edgetpu:usb`, `cpu`).
26052
- * Omitted ⇒ the runner's default device (current single-engine
26053
- * behaviour). Selects WHICH device pool of the node runs the call.
26054
- */
26055
- deviceKey: external_exports.string().optional(),
26056
- /**
26057
- * Two-plane NATIVE child-crop reference. Set by `runDetailSubtree` ONLY
26058
- * when the parent crop was resolved from the frame's retained NATIVE
26059
- * surface (a frameHandle HIT). Lets the executor re-cut a LEAF crop
26060
- * child's ROI (plate-ocr, face-embedding, leaf classifiers) at native
26061
- * resolution from that surface — the SAME quality path faces already
26062
- * had — instead of the downscaled parent tile. `handle` keys the native
26063
- * surface (node-pinned to its owner); `cropFrameSpace` is the parent
26064
- * crop's padded/clamped rectangle in FRAME-space pixels, used to compose
26065
- * the executor's crop-normalized child ROI back into frame-normalized
26066
- * coordinates. Auxiliary to the image source (`image`/`frame`/…), NOT one
26067
- * of the mutually-exclusive image inputs. Absent ⇒ tile-crop children
26068
- * (today's behaviour on the fallback path).
26069
- */
26070
- nativeCropRef: NativeCropRefSchema.optional()
26071
- }), PipelineRunResultBridge, { kind: "mutation" }),
26305
+ saveRetrainAnnotations: method(external_exports.object({
26306
+ deviceId: external_exports.number(),
26307
+ trackId: external_exports.string(),
26308
+ frameId: external_exports.string(),
26309
+ annotations: external_exports.array(RetrainAnnotationDraftSchema)
26310
+ }), external_exports.array(RetrainAnnotationSchema).readonly(), {
26311
+ kind: "mutation",
26312
+ auth: "admin"
26313
+ }),
26072
26314
  /**
26073
- * Batched run N raw frames packed into one cap call. The provider
26074
- * routes the batch through `SharedInferencePool.inferBatch`
26075
- * (`MSG_INFER_BATCH = 0x03`) so the IPC framing and JSON response
26076
- * envelope cost is amortised N:1 vs N concurrent `runPipeline`
26077
- * calls. Single root step + uniform model assumed; trees with crop
26078
- * children fall back to sequential execution.
26315
+ * Finish with a track: `staging trained`. **The only writer of that
26316
+ * state** — D81 shipped the column with it deliberately unreachable.
26079
26317
  *
26080
- * Used by `scripts/bench-batch-style.mts` for batch benchmarking —
26081
- * N frames in one call to amortise per-call IPC overhead.
26318
+ * Refuses a track the dataset holds no copies from. `trained` un-pins the
26319
+ * track's media, so completing without a copy is a delete order for material
26320
+ * nothing ever extracted anything from; that refusal IS the safety argument
26321
+ * of D81, expressed as a precondition.
26082
26322
  */
26083
- runPipelineBatch: method(external_exports.object({
26084
- engine: PipelineEngineChoiceSchema.optional(),
26085
- steps: external_exports.array(PipelineStepInputSchema).min(1),
26086
- frames: external_exports.array(FrameInputSchema).min(1).max(255),
26087
- deviceId: external_exports.number().optional(),
26088
- sessionId: external_exports.string().optional(),
26089
- /**
26090
- * Pure-inference benchmark hint. A NONZERO uint32 pins every frame in
26091
- * the batch to the Python pool's bench preprocess cache
26092
- * (`_bench_frame_id`) so a REPEATED benchmark frame is decoded +
26093
- * preprocessed ONCE and every later inference is a pure-inference cache
26094
- * hit — the sustained-throughput run measures inference, not
26095
- * decode+preprocess+infer. Omitted/0 for live frames (all different →
26096
- * full preprocess every call, correct). Fresh per sustained run;
26097
- * released via `uncacheFrame`.
26098
- */
26099
- frameId: external_exports.number().int().nonnegative().optional(),
26100
- /** Inference-device selector (Phase 2 multi-device); see runPipeline. */
26101
- deviceKey: external_exports.string().optional()
26102
- }), external_exports.object({ results: external_exports.array(PipelineRunResultBridge).readonly() }), { kind: "mutation" }),
26323
+ completeRetrainTrack: method(external_exports.object({
26324
+ deviceId: external_exports.number(),
26325
+ trackId: external_exports.string()
26326
+ }), RetrainTransitionResultSchema, {
26327
+ kind: "mutation",
26328
+ auth: "admin"
26329
+ }),
26103
26330
  /**
26104
- * Cache a raw frame inside the Python inference pool's memory.
26105
- * Returns a numeric `frameId` that `inferCached` references —
26106
- * subsequent calls send only 5 bytes through the pipe instead of
26107
- * 1.2MB raw data, eliminating the pipe transfer bottleneck.
26331
+ * The deliberate return: `trained staging`, for the rare case.
26332
+ *
26333
+ * The generic `setTrackFlags` toggle refuses this in both directions by
26334
+ * design (D81) re-staging from a checkbox is how the same material gets
26335
+ * annotated twice under two ground truths. Doing it here means the operator
26336
+ * is looking at the annotations that already exist while they decide, and
26337
+ * those annotations are LEFT ALONE: "put this back" must not be a
26338
+ * destructive act wearing a navigational name.
26108
26339
  */
26109
- cacheFrameInPool: method(external_exports.object({
26110
- data: external_exports.instanceof(Uint8Array),
26111
- width: external_exports.number().int().positive(),
26112
- height: external_exports.number().int().positive(),
26113
- format: external_exports.enum([
26114
- "rgb",
26115
- "bgr",
26116
- "gray"
26117
- ])
26118
- }), external_exports.object({
26119
- frameId: external_exports.number(),
26120
- width: external_exports.number(),
26121
- height: external_exports.number()
26122
- }), { kind: "mutation" }),
26340
+ restageRetrainTrack: method(external_exports.object({
26341
+ deviceId: external_exports.number(),
26342
+ trackId: external_exports.string()
26343
+ }), RetrainTransitionResultSchema, {
26344
+ kind: "mutation",
26345
+ auth: "admin"
26346
+ }),
26123
26347
  /**
26124
- * Run inference on a previously cached frame. Sends only 5 bytes
26125
- * (model_idx + frameId) through the IPC pipe — eliminates the
26126
- * ~35ms per-call overhead of transferring 1.2MB raw data.
26348
+ * Where to download the ANNOTATED dataset.
26349
+ *
26350
+ * The sibling of `getTrainingExportUrl` and deliberately not the same
26351
+ * archive: that one streams a marked track's stored media verbatim, this one
26352
+ * streams the retrain COPIES plus an `annotations.json` carrying, for every
26353
+ * subject, the canonical full-frame box AND the geometry derived for each
26354
+ * model shape (letterboxed root / zone-cropped package / subject-cropped
26355
+ * classifier). Derived at export, never stored — one box in, three shapes
26356
+ * out, so two crops of the same subject can never end up in one feature
26357
+ * space (D52).
26127
26358
  */
26128
- inferCached: method(external_exports.object({
26129
- stepId: external_exports.string(),
26130
- frameId: external_exports.number().int()
26131
- }), external_exports.record(external_exports.string(), external_exports.unknown()), { kind: "mutation" }),
26359
+ getRetrainExportUrl: method(external_exports.object({ deviceIds: external_exports.array(external_exports.number()).optional() }), external_exports.object({ url: external_exports.string() }), {
26360
+ kind: "query",
26361
+ auth: "admin"
26362
+ }),
26363
+ getEventMedia: method(external_exports.object({
26364
+ eventId: external_exports.string(),
26365
+ kind: MediaFileKindEnum.optional(),
26366
+ deviceId: external_exports.number()
26367
+ }), external_exports.array(MediaFileSchema).readonly()),
26368
+ /** All media rows owned by a track. `kinds` narrows to a kind subset so a
26369
+ * client can fetch the SMALL display variants on open and pull the
26370
+ * multi-MB native variants only on demand (mirrors `getEventMedia.kind`).
26371
+ * Absent ⇒ every kind (back-compat). */
26372
+ getTrackMedia: method(external_exports.object({
26373
+ trackId: external_exports.string(),
26374
+ kinds: external_exports.array(MediaFileKindEnum).optional(),
26375
+ deviceId: external_exports.number()
26376
+ }), external_exports.array(MediaFileSchema).readonly()),
26132
26377
  /**
26133
- * Release a cached frame from the Python pool's memory.
26378
+ * What media a track HAS, without any of it.
26379
+ *
26380
+ * The detail view needs the shape of a track's media to build its strip —
26381
+ * which kinds exist, in what order, at what size — and then wants each tile
26382
+ * fetched as an image, not as base64 inside this response. Measured: the
26383
+ * full `getTrackMedia` is 5.3-7.7 MB and blocks the view; this manifest is
26384
+ * ~2 KB.
26385
+ *
26386
+ * It also restores a fact a `kinds` filter destroys: filtering
26387
+ * `getTrackMedia` drops whole ROWS, taking `kind` and `sizeBytes` with
26388
+ * them, so a client that fetched only the small variants could no longer
26389
+ * tell a full-resolution variant existed — and the affordance that opens it
26390
+ * would silently disappear.
26134
26391
  */
26135
- uncacheFrame: method(external_exports.object({ frameId: external_exports.number().int() }), external_exports.void(), { kind: "mutation" }),
26136
- /** Returns the effective pool tuning (resolved from user overrides + backend defaults). */
26137
- getEffectiveTuning: method(external_exports.void(), external_exports.object({
26138
- batchMode: external_exports.string(),
26139
- windowMs: external_exports.number(),
26140
- maxBatchSize: external_exports.number(),
26141
- concurrency: external_exports.number()
26142
- })),
26392
+ listTrackMedia: method(external_exports.object({
26393
+ trackId: external_exports.string(),
26394
+ deviceId: external_exports.number()
26395
+ }), external_exports.array(MediaFileInfoSchema).readonly()),
26143
26396
  /**
26144
- * List every EngineFactory currently loaded in this executor's RAM,
26145
- * with the models resident and a coarse "in use" marker derived from
26146
- * ongoing inference activity. Used by the Pipeline page Engines tab.
26397
+ * Search object events by text query using CLIP cosine similarity.
26398
+ * Encodes `text` via the `embedding-encoder` cap, queries the
26399
+ * `ObjectEmbeddingStore` with optional prefilters, ranks all matching
26400
+ * embeddings by cosine similarity, and joins winners to their
26401
+ * ObjectEvents by trackId. Returns up to `limit` events scored ≥
26402
+ * `minScore`, sorted descending by score.
26147
26403
  */
26148
- listLoadedEngines: method(external_exports.void(), external_exports.array(external_exports.object({
26149
- engineKey: external_exports.string(),
26150
- engine: PipelineEngineChoiceSchema,
26151
- modelsLoaded: external_exports.array(external_exports.string()).readonly(),
26152
- inUseByCameras: external_exports.array(external_exports.number()).readonly(),
26153
- /**
26154
- * Origin of this resident factory.
26155
- * - `runtime` — main camera-serving engine (no idle TTL).
26156
- * - `warm-override` — benchmark/test override held in the warm
26157
- * cache; auto-disposed after the idle TTL.
26158
- * - `device-pool` — a concurrent per-device pool (Phase 2
26159
- * multi-device, keyed by `deviceKey`) resolved
26160
- * via `resolveDeviceFactory`. Runs alongside the
26161
- * `runtime` engine on a DIFFERENT accelerator
26162
- * (NPU / iGPU / Coral) — this is how the
26163
- * Engines tab shows all pools running at once.
26164
- */
26165
- kind: external_exports.enum([
26166
- "runtime",
26167
- "warm-override",
26168
- "device-pool"
26169
- ]),
26170
- /** Native pid of the underlying Python pool (null when no pool). */
26171
- poolPid: external_exports.number().nullable(),
26172
- /** ms since this factory was last used (null when not warm-tracked). */
26173
- idleMs: external_exports.number().nullable(),
26174
- /** Idle TTL after which `warm-override` factories self-evict (null when not applicable). */
26175
- idleTtlMs: external_exports.number().nullable()
26176
- })).readonly()),
26177
- /** Warm up an engine without running a frame. No-op if already loaded. */
26178
- spinEngine: method(external_exports.object({ engine: PipelineEngineChoiceSchema }), external_exports.object({ success: external_exports.literal(true) }), {
26404
+ searchObjectEvents: method(SearchObjectEventsInput, external_exports.array(ScoredObjectEventSchema).readonly()),
26405
+ wipeObjectEmbeddings: method(external_exports.object({}), WipeObjectEmbeddingsResultSchema, {
26179
26406
  kind: "mutation",
26180
26407
  auth: "admin"
26181
26408
  }),
26182
- /**
26183
- * Unload an engine from RAM. `force:true` unloads even when cameras
26184
- * are actively using it (they re-spin on next frame). Default is
26185
- * gated — returns `{success:false, reason}` when in use.
26186
- */
26187
- killEngine: method(external_exports.object({
26188
- engine: PipelineEngineChoiceSchema,
26189
- force: external_exports.boolean().optional()
26190
- }), external_exports.object({
26191
- success: external_exports.boolean(),
26192
- reason: external_exports.string().optional()
26193
- }), {
26409
+ rebuildObjectEmbeddings: method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
26194
26410
  kind: "mutation",
26195
26411
  auth: "admin"
26196
26412
  }),
26197
- listReferenceImages: method(external_exports.void(), external_exports.array(ReferenceImageEntrySchema).readonly()),
26198
- getReferenceImage: method(external_exports.object({ filename: external_exports.string() }), ReferenceImageBodySchema.nullable()),
26199
- getReferenceAudioFiles: method(external_exports.void(), external_exports.array(ReferenceAudioEntrySchema).readonly()),
26200
- getReferenceAudio: method(external_exports.object({ filename: external_exports.string() }), ReferenceAudioBodySchema.nullable()),
26201
- getAudioCapabilities: method(external_exports.void(), AudioCapabilitiesSchema),
26202
- runAudioTest: method(external_exports.object({
26203
- addonId: external_exports.string(),
26204
- modelId: external_exports.string(),
26205
- filename: external_exports.string().optional(),
26206
- settings: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
26207
- }), AudioTestResultSchema, { kind: "mutation" }),
26208
- getDetectionConfigSchema: method(external_exports.void(), ConfigUISchemaNullableBridge)
26413
+ getObjectEmbeddingRebuildStatus: method(external_exports.object({}), RebuildStatusSchema)
26414
+ },
26415
+ events: {
26416
+ /**
26417
+ * Enriched frame emitted after refinement — the live-overlay source of
26418
+ * truth (two-plane re-injection). Carries the frame's detections in the
26419
+ * `ObjectDetection` wire shape: first-level roots (with track info +
26420
+ * enrichment labels) plus synthesized `kind:'detail'` face/plate entries
26421
+ * re-projected from per-track detail state, so stream overlays render
26422
+ * boxes + recognized names without querying full Track state.
26423
+ */
26424
+ onFrameTracked: { data: external_exports.object({
26425
+ deviceId: external_exports.number(),
26426
+ timestamp: external_exports.number(),
26427
+ frameWidth: external_exports.number(),
26428
+ frameHeight: external_exports.number(),
26429
+ detections: external_exports.array(OverlayDetectionSchema).readonly()
26430
+ }) },
26431
+ /** Track entered active state (first-seen). */
26432
+ onTrackStarted: { data: external_exports.object({
26433
+ deviceId: external_exports.number(),
26434
+ trackId: external_exports.string(),
26435
+ className: external_exports.string()
26436
+ }) },
26437
+ /** Track expired (TTL reached after last detection). */
26438
+ onTrackEnded: { data: external_exports.object({
26439
+ deviceId: external_exports.number(),
26440
+ trackId: external_exports.string(),
26441
+ className: external_exports.string(),
26442
+ durationMs: external_exports.number()
26443
+ }) },
26444
+ /** Canonical "something happened at device X" event, per-kind. */
26445
+ onDetectionEvent: { data: external_exports.object({
26446
+ deviceId: external_exports.number(),
26447
+ kind: EventKindSchema,
26448
+ eventId: external_exports.string(),
26449
+ timestamp: external_exports.number()
26450
+ }) }
26209
26451
  }
26210
26452
  };
26211
26453
  var OrchestratorMetricsSchema = external_exports.object({
@@ -26232,66 +26474,6 @@ var CameraMetricsSchema = external_exports.object({
26232
26474
  ])
26233
26475
  });
26234
26476
  var CameraMetricsWithDeviceIdSchema = CameraMetricsSchema.extend({ deviceId: external_exports.number() });
26235
- var ZoneKindEnum = external_exports.enum(["polygon", "tripwire"]);
26236
- var PolygonPointSchema = external_exports.object({
26237
- x: external_exports.number(),
26238
- y: external_exports.number()
26239
- });
26240
- var ZoneSchema = external_exports.object({
26241
- id: external_exports.string(),
26242
- name: external_exports.string(),
26243
- kind: ZoneKindEnum.default("polygon"),
26244
- /** Polygon vertices, fraction of frame (0–1). */
26245
- polygon: external_exports.array(PolygonPointSchema).readonly(),
26246
- /** Visual color for UI rendering. */
26247
- color: external_exports.string().default("#3b82f6")
26248
- });
26249
- var zonesCapability = {
26250
- name: "zones",
26251
- scope: "device",
26252
- mode: "singleton",
26253
- deviceTypes: [DeviceType.Camera],
26254
- methods: {
26255
- listZones: method(external_exports.object({ deviceId: external_exports.number() }), external_exports.array(ZoneSchema).readonly()),
26256
- addZone: method(external_exports.object({
26257
- deviceId: external_exports.number(),
26258
- zone: ZoneSchema
26259
- }), external_exports.void(), {
26260
- kind: "mutation",
26261
- auth: "admin"
26262
- }),
26263
- removeZone: method(external_exports.object({
26264
- deviceId: external_exports.number(),
26265
- zoneId: external_exports.string()
26266
- }), external_exports.void(), {
26267
- kind: "mutation",
26268
- auth: "admin"
26269
- }),
26270
- updateZone: method(external_exports.object({
26271
- deviceId: external_exports.number(),
26272
- zone: ZoneSchema
26273
- }), external_exports.void(), {
26274
- kind: "mutation",
26275
- auth: "admin"
26276
- })
26277
- },
26278
- /**
26279
- * Runtime-state slice — the live zone catalogue mirrored by the
26280
- * orchestrator on every CRUD mutation. Consumers read via
26281
- * `device.state.zones.value` / `.watch(...)` without round-tripping
26282
- * the cap, and the codegen DeviceProxy auto-wires the reactive
26283
- * handle. Slice shape is `{ zones: Zone[] }` so future extensions
26284
- * (e.g. zone groupings) can sit alongside the polygon list.
26285
- */
26286
- runtimeState: external_exports.object({ zones: external_exports.array(ZoneSchema).readonly() }),
26287
- /**
26288
- * Runtime-state durability: **restored** — written only on operator mutation, so a camera that never had one has nothing to re-derive from. This is the slice `zone-mirror-hydration.ts` exists to paper over.
26289
- *
26290
- * See `RuntimeStateDurability`. Enforced by
26291
- * `scripts/check-runtime-state-durability.ts`.
26292
- */
26293
- durability: "restored"
26294
- };
26295
26477
  var NativeCropBboxSchema = external_exports.object({
26296
26478
  x: external_exports.number(),
26297
26479
  y: external_exports.number(),
@@ -35229,6 +35411,23 @@ var ReadGopBytesResultSchema = external_exports.object({
35229
35411
  /** Media ms the returned fragment covers. */
35230
35412
  gopDurMs: external_exports.number()
35231
35413
  });
35414
+ var ReadWindowBytesResultSchema = external_exports.discriminatedUnion("kind", [external_exports.object({
35415
+ kind: external_exports.literal("ok"),
35416
+ data: external_exports.instanceof(Uint8Array),
35417
+ /** Absolute epoch ms of the returned bytes' first sample — at or before
35418
+ * the requested `fromMs` (anchored on the nearest keyframe). */
35419
+ gopStartMs: external_exports.number(),
35420
+ /** Media ms the returned bytes cover, from `gopStartMs`. */
35421
+ gopDurMs: external_exports.number(),
35422
+ /** `false` ⇒ the safety byte cap cut the read short before it reached
35423
+ * the requested `toMs`; the caller got fewer frames than asked for. */
35424
+ reachesRequestedEnd: external_exports.boolean()
35425
+ }), external_exports.object({
35426
+ kind: external_exports.literal("spans-multiple-segments"),
35427
+ /** Where the covering segment's own footage runs out — informational,
35428
+ * not a retry hint (retrying the same window would refuse again). */
35429
+ segmentEndMs: external_exports.number()
35430
+ })]);
35232
35431
  var recordingCapability = {
35233
35432
  name: "recording",
35234
35433
  scope: "system",
@@ -35308,6 +35507,21 @@ var recordingCapability = {
35308
35507
  kind: "query",
35309
35508
  auth: "admin"
35310
35509
  }),
35510
+ /** Read a WINDOW `[fromMs, toMs)` of segment `startMs`, by mfra byte
35511
+ * range — the multi-GOP twin of `readGopBytes`. Refuses (does not
35512
+ * degrade) when the window runs past the covering segment. Used by the
35513
+ * replay clip's `recording` source to fetch several seconds of native
35514
+ * footage for its single ffmpeg decode pass. */
35515
+ readWindowBytes: method(external_exports.object({
35516
+ deviceId: external_exports.number(),
35517
+ profile: external_exports.string(),
35518
+ startMs: external_exports.number(),
35519
+ fromMs: external_exports.number(),
35520
+ toMs: external_exports.number()
35521
+ }), ReadWindowBytesResultSchema, {
35522
+ kind: "query",
35523
+ auth: "admin"
35524
+ }),
35311
35525
  setDeviceConfig: method(external_exports.object({
35312
35526
  deviceId: external_exports.number(),
35313
35527
  config: RecordingConfigSchema
@@ -35872,59 +36086,6 @@ var sceneMonitorCapability = {
35872
36086
  */
35873
36087
  durability: "session"
35874
36088
  };
35875
- var ZoneRuleModeEnum = external_exports.enum(["include", "exclude"]);
35876
- var ZoneRuleSchema = external_exports.object({
35877
- /** Stable rule id — survives edits, used by the UI for diffing. */
35878
- id: external_exports.string(),
35879
- /** Optional human-readable label rendered in the rule editor. */
35880
- name: external_exports.string().optional(),
35881
- /** Zones this rule targets. The rule's `mode` applies to ALL
35882
- * listed zones (OR-set: a detection in any one of them counts).
35883
- * At least one zone id required — a rule with no targets is a
35884
- * configuration mistake and the form validator rejects it. */
35885
- zoneIds: external_exports.array(external_exports.string()).min(1).readonly(),
35886
- mode: ZoneRuleModeEnum,
35887
- /**
35888
- * Class names this rule applies to. Empty / undefined ⇒ rule
35889
- * applies to every class. Class strings match the `macroClass`
35890
- * field on detections (e.g. `person`, `car`, `dog`).
35891
- */
35892
- classFilter: external_exports.array(external_exports.string()).readonly().optional(),
35893
- /**
35894
- * Minimum bbox/mask overlap (0–1) with any of the rule's zones
35895
- * required to consider an entity "in the zone". Defaults to the
35896
- * consumer's stage default when omitted. Kept for back-compat with
35897
- * existing per-rule overrides; new operators pick the value via
35898
- * `bboxInclusionPct` (operator-friendly 0–100). Whichever field is
35899
- * set, the lower-level engine reads it as a 0–1 fraction.
35900
- */
35901
- overlapThreshold: external_exports.number().min(0).max(1).optional(),
35902
- /**
35903
- * Operator-friendly version of `overlapThreshold` — the percentage
35904
- * of the detection's bbox that must lie inside the zone for the
35905
- * rule to match. Documented default is 85%; the engine substitutes
35906
- * that when the field is omitted (kept optional so existing rules
35907
- * stored without it stay valid).
35908
- *
35909
- * When BOTH `overlapThreshold` and `bboxInclusionPct` are set on a
35910
- * rule, the engine prefers `bboxInclusionPct` because it's the
35911
- * field exposed in the UI. Internally both feed the same gate.
35912
- */
35913
- bboxInclusionPct: external_exports.number().min(0).max(100).optional(),
35914
- /**
35915
- * When `true` and a detection has a segmentation mask, use the
35916
- * mask for overlap instead of the bbox. Detection-stage only;
35917
- * motion rules ignore this field.
35918
- */
35919
- preferMask: external_exports.boolean().optional(),
35920
- /**
35921
- * Soft-toggle: `false` disables the rule without deleting it.
35922
- * Defaults to `true` so operators creating a rule via the UI
35923
- * see it active immediately.
35924
- */
35925
- enabled: external_exports.boolean().default(true)
35926
- });
35927
- var ZoneRulesArraySchema = external_exports.array(ZoneRuleSchema).readonly();
35928
36089
  var ScriptRunnerStatusSchema = external_exports.object({
35929
36090
  /** Whether the script is currently executing. */
35930
36091
  isRunning: external_exports.boolean(),
@@ -40179,6 +40340,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40179
40340
  addonId: null,
40180
40341
  access: "view"
40181
40342
  },
40343
+ "notificationRules.resolveArtifactUrl": {
40344
+ capName: "notification-rules",
40345
+ capScope: "system",
40346
+ addonId: null,
40347
+ access: "view"
40348
+ },
40182
40349
  "notificationRules.setAlarmConfig": {
40183
40350
  capName: "notification-rules",
40184
40351
  capScope: "system",
@@ -40623,6 +40790,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40623
40790
  addonId: null,
40624
40791
  access: "create"
40625
40792
  },
40793
+ "pipelineAnalytics.runReplayFrameProcessor": {
40794
+ capName: "pipeline-analytics",
40795
+ capScope: "device",
40796
+ addonId: null,
40797
+ access: "create"
40798
+ },
40626
40799
  "pipelineAnalytics.saveRetrainAnnotations": {
40627
40800
  capName: "pipeline-analytics",
40628
40801
  capScope: "device",
@@ -40755,6 +40928,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40755
40928
  addonId: null,
40756
40929
  access: "view"
40757
40930
  },
40931
+ "pipelineExecutor.getInferenceDeviceHealth": {
40932
+ capName: "pipeline-executor",
40933
+ capScope: "system",
40934
+ addonId: null,
40935
+ access: "view"
40936
+ },
40758
40937
  "pipelineExecutor.getOrchestratorConfigSchema": {
40759
40938
  capName: "pipeline-executor",
40760
40939
  capScope: "system",
@@ -40827,6 +41006,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
40827
41006
  addonId: null,
40828
41007
  access: "view"
40829
41008
  },
41009
+ "pipelineExecutor.rearmInferenceDevice": {
41010
+ capName: "pipeline-executor",
41011
+ capScope: "system",
41012
+ addonId: null,
41013
+ access: "create"
41014
+ },
40830
41015
  "pipelineExecutor.runAudioTest": {
40831
41016
  capName: "pipeline-executor",
40832
41017
  capScope: "system",
@@ -41565,6 +41750,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
41565
41750
  addonId: null,
41566
41751
  access: "view"
41567
41752
  },
41753
+ "recording.readWindowBytes": {
41754
+ capName: "recording",
41755
+ capScope: "system",
41756
+ addonId: null,
41757
+ access: "view"
41758
+ },
41568
41759
  "recording.refreshStorageLocationsForMigration": {
41569
41760
  capName: "recording",
41570
41761
  capScope: "system",
@@ -44075,6 +44266,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
44075
44266
  form: "single",
44076
44267
  optional: false
44077
44268
  }],
44269
+ "pipelineAnalytics.runReplayFrameProcessor": [{
44270
+ name: "deviceId",
44271
+ form: "single",
44272
+ optional: false
44273
+ }],
44078
44274
  "pipelineAnalytics.saveRetrainAnnotations": [{
44079
44275
  name: "deviceId",
44080
44276
  form: "single",
@@ -44390,6 +44586,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
44390
44586
  form: "single",
44391
44587
  optional: false
44392
44588
  }],
44589
+ "recording.readWindowBytes": [{
44590
+ name: "deviceId",
44591
+ form: "single",
44592
+ optional: false
44593
+ }],
44393
44594
  "recording.relocateFootage": [{
44394
44595
  name: "deviceId",
44395
44596
  form: "single",