@camstack/types 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.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-BE4PDZ_3.js");
3
- const require_sleep = require("./sleep-eiC10_cX.js");
3
+ const require_sleep = require("./sleep-CyN9nHr_.js");
4
4
  const require_fmp4_box_splitter = require("./fmp4-box-splitter-BkWH7O3L.js");
5
5
  const require_enums = require("./enums.js");
6
6
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -3793,6 +3793,69 @@ var StreamFormatSchema = zod.z.enum([
3793
3793
  "mjpeg",
3794
3794
  "rtsp"
3795
3795
  ]);
3796
+ /** A container `produceEventMedia` can emit. */
3797
+ var EventMediaKindSchema = zod.z.enum(["mp4", "gif"]);
3798
+ /**
3799
+ * One produced artifact, referenced by HANDLE.
3800
+ *
3801
+ * Never inline bytes: a produced clip is 200 KB–5 MB and every consumer of this
3802
+ * method is in another runner ([D9](../../../../docs/decisions/adr-0009.md),
3803
+ * [D18](../../../../docs/decisions/adr-0018.md) — cross-process media is fetched
3804
+ * on demand, compressed, by handle). `bytes` is here so a caller can decide
3805
+ * whether it wants the fetch at all.
3806
+ */
3807
+ var EventMediaArtifactSchema = zod.z.object({
3808
+ kind: EventMediaKindSchema,
3809
+ /** Opaque, single-camera, short-lived. Redeem with `fetchEventMedia`. */
3810
+ handle: zod.z.string(),
3811
+ /**
3812
+ * The node holding the bytes — the ROUTING key for `fetchEventMedia`.
3813
+ *
3814
+ * `stream-broker` is a singleton cap and an unpinned call never leaves the
3815
+ * hub, so a handle produced on an agent's broker would be redeemed against
3816
+ * the hub's store and come back `null`. Same contract, same field name and
3817
+ * the same reason as `FrameHandleSchema.nodeId`: the producer stamps where it
3818
+ * lives and the consumer pins to it.
3819
+ */
3820
+ nodeId: zod.z.string(),
3821
+ mime: zod.z.string(),
3822
+ bytes: zod.z.number().int(),
3823
+ width: zod.z.number().int(),
3824
+ height: zod.z.number().int()
3825
+ });
3826
+ /**
3827
+ * What a production actually covered — the answer to the only question an
3828
+ * operator asks about a notification clip.
3829
+ *
3830
+ * `fromTs`/`toTs` are WALL CLOCK, derived from the ring's own packet timeline,
3831
+ * so a caller can state "this clip starts 4.1 s before the event" instead of
3832
+ * inferring it from a duration. A production whose `fromTs` is later than the
3833
+ * event is a production with no pre-roll, and that is exactly the defect this
3834
+ * method exists to make visible rather than plausible.
3835
+ */
3836
+ var EventMediaCoverageSchema = zod.z.object({
3837
+ fromTs: zod.z.number(),
3838
+ toTs: zod.z.number(),
3839
+ /** Encoded packets in the muxed window. */
3840
+ packets: zod.z.number().int()
3841
+ });
3842
+ /**
3843
+ * The result of ONE cut, in every container the caller asked for.
3844
+ *
3845
+ * Every artifact in `media` came out of the SAME window of the SAME rendition —
3846
+ * that is the whole reason this is one method rather than one call per format.
3847
+ * A consumer attaching a gif and a video can no longer show two different
3848
+ * moments, because it never chose two sources.
3849
+ */
3850
+ var EventMediaProductionSchema = zod.z.object({
3851
+ media: zod.z.array(EventMediaArtifactSchema).readonly(),
3852
+ coverage: EventMediaCoverageSchema,
3853
+ /** The rendition actually cut from — what the default or the fallback chose. */
3854
+ profile: require_sleep.CamProfileSchema,
3855
+ /** `copy` = the camera's own H.264, untouched. `encode` = re-encoded (H.265
3856
+ * source, a downscale, or a playback rate other than 1). */
3857
+ video: zod.z.enum(["copy", "encode"])
3858
+ });
3796
3859
  var RtspRestreamEntrySchema = zod.z.object({
3797
3860
  brokerId: zod.z.string(),
3798
3861
  url: zod.z.string(),
@@ -4230,6 +4293,66 @@ var streamBrokerCapability = {
4230
4293
  kind: "mutation",
4231
4294
  auth: "admin"
4232
4295
  }),
4296
+ /**
4297
+ * THE production point for event media — one window, every container.
4298
+ *
4299
+ * `renderPreBufferClip` renders ONE format per call, so a consumer wanting a
4300
+ * gif and a video made two calls and got two windows. On 2026-08-08 the
4301
+ * operator received exactly that: a gif around a doorbell press and an mp4
4302
+ * of the six seconds after it. This method cuts once and derives the rest,
4303
+ * so "the attachments agree" is a property of the service instead of a
4304
+ * discipline every consumer has to keep.
4305
+ *
4306
+ * It is also the only place that owns the packets. The broker already
4307
+ * retains an AnnexB ring per camera (`setClipRetention`, D32) — the
4308
+ * prebuffer — so a production reaches BACKWARDS from the event with no
4309
+ * process to spawn and nothing kept warm. It waits out `postSeconds`, muxes
4310
+ * the window once (COPYING the camera's own H.264 whenever nothing is being
4311
+ * changed), derives any gif from that same mux, and returns HANDLES.
4312
+ *
4313
+ * Intended for every future consumer, not just notifications: a clips API, a
4314
+ * retrain export and an event export all want the same window of the same
4315
+ * camera and must not each grow their own renderer.
4316
+ */
4317
+ produceEventMedia: require_sleep.method(zod.z.object({
4318
+ deviceId: zod.z.number(),
4319
+ /** Absent = the largest H.264 rendition at or below 1080p, which is
4320
+ * also the one that can be copied. Falls back to whatever the ring
4321
+ * actually retained, and the answer says which. */
4322
+ profile: require_sleep.CamProfileSchema.optional(),
4323
+ aroundMs: zod.z.number(),
4324
+ preSeconds: zod.z.number().min(0).max(20).default(4),
4325
+ postSeconds: zod.z.number().min(0).max(20).default(6),
4326
+ kinds: zod.z.array(EventMediaKindSchema).min(1).default(["mp4"]),
4327
+ /** GIF geometry. The video keeps the source's own. */
4328
+ gifMaxWidth: zod.z.number().int().min(120).max(1280).default(640),
4329
+ gifFps: zod.z.number().int().min(1).max(15).default(8),
4330
+ /**
4331
+ * Playback rate, applied to EVERY container so they stay one clip.
4332
+ * `1` is real time and is what allows the copy branch.
4333
+ */
4334
+ speed: zod.z.number().min(1).max(8).default(1)
4335
+ }), EventMediaProductionSchema, {
4336
+ kind: "mutation",
4337
+ auth: "admin"
4338
+ }),
4339
+ /**
4340
+ * Redeem a {@link EventMediaArtifactSchema} handle for its bytes.
4341
+ *
4342
+ * Separate from the production on purpose (D18): the producing runner keeps
4343
+ * the artifact for a few minutes, and a consumer that only wanted the
4344
+ * coverage never moves a megabyte. `null` once it has expired — a handle is
4345
+ * short-lived by design and a caller that waited too long must see that
4346
+ * rather than a zero-length file.
4347
+ */
4348
+ fetchEventMedia: require_sleep.method(zod.z.object({ handle: zod.z.string() }), zod.z.object({
4349
+ base64: zod.z.string(),
4350
+ mime: zod.z.string(),
4351
+ bytes: zod.z.number().int()
4352
+ }).nullable(), {
4353
+ kind: "mutation",
4354
+ auth: "admin"
4355
+ }),
4233
4356
  listAllCameraStreams: require_sleep.method(zod.z.void(), zod.z.array(require_sleep.CameraStreamSchema).readonly()),
4234
4357
  listAllProfileSlots: require_sleep.method(zod.z.void(), zod.z.array(require_sleep.ProfileSlotSchema).readonly()),
4235
4358
  getBrokerStats: require_sleep.method(zod.z.object({ brokerId: zod.z.string() }), require_sleep.BrokerStatsSchema),
@@ -12380,6 +12503,76 @@ var TrackFlagsSchema = zod.z.object({
12380
12503
  * `trained` without a re-fetch. */
12381
12504
  retrainStatus: RetrainStatusSchema
12382
12505
  });
12506
+ /**
12507
+ * WHICH tier a label occupies. The slot a label lands in is DECLARED by the
12508
+ * step that produced it (`StepDefinition.labelTier`), never inferred from the
12509
+ * text or the step's name.
12510
+ *
12511
+ * - `1` — a SUB-CLASS: finer than the macro class, still a taxonomy token.
12512
+ * `animal-type` (`dog`, `bird`), `vehicle-type` (`van`), and the root
12513
+ * detector's own raw class when it is finer than the macro it maps to.
12514
+ * - `2` — an INSTANCE: the finest thing said about this subject.
12515
+ * `species` (`Turdus migratorius`), `identity` (`Alice`), `plate-text`.
12516
+ *
12517
+ * The macro class itself (`person`, `vehicle`, `animal`, `package`, `face`,
12518
+ * `plate`, `audio`) is NOT a tier — it is `className`, and a macro token
12519
+ * offered for either label slot is refused (2026-08-07 rule; the refusal is
12520
+ * logged as `label tier collapse refused`).
12521
+ */
12522
+ var LabelTierSchema = zod.z.union([zod.z.literal(1), zod.z.literal(2)]);
12523
+ /**
12524
+ * WHO decided a label, and when. Carried per tier so a value can be traced to
12525
+ * the step and model that produced it — which is what makes the write rule
12526
+ * arguable after the fact ("why is 592's label `dog` and not `Canis lupus`?")
12527
+ * and what lets a migrated, UNATTRIBUTED value be told apart from a real one.
12528
+ *
12529
+ * `stepId` is the pipeline step id (`animal-classifier`, `bird-classifier`,
12530
+ * `plate-ocr`, `face-embedding`, `object-detection`), or the sentinel
12531
+ * `migration:4g` for a value the 4g migration moved from the single-slot era —
12532
+ * that value has no provenance, and the write rule lets ANY properly-attributed
12533
+ * write of the same tier replace it regardless of score.
12534
+ */
12535
+ var LabelAttributionSchema = zod.z.object({
12536
+ stepId: zod.z.string(),
12537
+ modelId: zod.z.string().optional(),
12538
+ decidedAt: zod.z.number()
12539
+ });
12540
+ /**
12541
+ * The TIERED label model (roadmap 4g), spread into `TrackSchema` and
12542
+ * `ObjectEventSchema` from ONE place so the two surfaces cannot drift — a
12543
+ * track and its events always answer the same question the same way.
12544
+ *
12545
+ * Two scalar columns, not an array: every consumer wants "the coarse one" or
12546
+ * "the fine one", and an array made both a scan. `label` is tier 1, `subLabel`
12547
+ * is tier 2, and each carries its own score + attribution.
12548
+ *
12549
+ * **Reading it.** What a human should be shown is `subLabel ?? label` — the
12550
+ * finest thing known. Before 4g the single `label` column held the finest
12551
+ * value, so a consumer that has not been updated reads the tier-1 slot and
12552
+ * shows nothing on a species-only row; that is why the migration puts every
12553
+ * pre-4g value in tier 2 (it cannot regress a display that reads the fallback)
12554
+ * and why the read surfaces were changed in the same train.
12555
+ *
12556
+ * **Writing it.** The slots are independent, which is the whole point: a
12557
+ * tier-1 write (`bird`) can never overwrite a tier-2 value (`Turdus
12558
+ * migratorius`), so fineness cannot regress by construction. Within a tier the
12559
+ * higher score wins. One rule, one implementation — see
12560
+ * `pipeline/label-tier.ts` in addon-post-analysis.
12561
+ */
12562
+ var TieredLabelFields = {
12563
+ /** Tier 1 — the sub-class. See {@link LabelTierSchema}. */
12564
+ label: zod.z.string().optional(),
12565
+ /** Confidence of the tier-1 value, as reported by the deciding step. */
12566
+ labelScore: zod.z.number().optional(),
12567
+ /** Provenance of the tier-1 value. See {@link LabelAttributionSchema}. */
12568
+ labelMeta: LabelAttributionSchema.optional(),
12569
+ /** Tier 2 — the instance. See {@link LabelTierSchema}. */
12570
+ subLabel: zod.z.string().optional(),
12571
+ /** Confidence of the tier-2 value, as reported by the deciding step. */
12572
+ subLabelScore: zod.z.number().optional(),
12573
+ /** Provenance of the tier-2 value. See {@link LabelAttributionSchema}. */
12574
+ subLabelMeta: LabelAttributionSchema.optional()
12575
+ };
12383
12576
  /** Per-camera slice of a training-export estimate. */
12384
12577
  var TrainingExportDeviceTotalsSchema = zod.z.object({
12385
12578
  deviceId: zod.z.number(),
@@ -12404,7 +12597,7 @@ var TrackSchema = zod.z.object({
12404
12597
  trackId: zod.z.string(),
12405
12598
  deviceId: zod.z.number(),
12406
12599
  className: zod.z.string(),
12407
- label: zod.z.string().optional(),
12600
+ ...TieredLabelFields,
12408
12601
  producingDeviceName: zod.z.string().optional(),
12409
12602
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
12410
12603
  source: TrackSourceSchema.optional(),
@@ -12525,7 +12718,7 @@ var ObjectEventSchema = zod.z.object({
12525
12718
  /** Omitted in slim projection. */
12526
12719
  trackId: zod.z.string().optional(),
12527
12720
  className: zod.z.string(),
12528
- label: zod.z.string().optional(),
12721
+ ...TieredLabelFields,
12529
12722
  /** Omitted in slim projection. */
12530
12723
  confidence: zod.z.number().optional(),
12531
12724
  /** Heavy JSON — omitted in slim projection. */
@@ -12606,6 +12799,173 @@ var MediaFileSchema = zod.z.object({
12606
12799
  * stored blob and a `?variant=thumb` rendering without fetching either.
12607
12800
  */
12608
12801
  var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
12802
+ /**
12803
+ * The MACRO tier of an annotation — a CLOSED set.
12804
+ *
12805
+ * This is what the exported detector predicts, so a typo here is a new class
12806
+ * with one example in it. `label` and `subLabel` are open strings by contrast:
12807
+ * the whole point of the page is teaching the model things it does not know
12808
+ * yet, and constraining that vocabulary would make it useless.
12809
+ *
12810
+ * A macro class is NEVER a label. The provider refuses a write whose `label` or
12811
+ * `subLabel` is one of these values, in any casing, because once `person`
12812
+ * exists in both tiers "every person box" stops being answerable without
12813
+ * knowing every string anyone ever typed — and the damage is retroactive.
12814
+ */
12815
+ var RetrainMacroClassSchema = zod.z.enum([
12816
+ "person",
12817
+ "vehicle",
12818
+ "animal",
12819
+ "package",
12820
+ "face",
12821
+ "plate"
12822
+ ]);
12823
+ /** A subject to learn, or a phantom to unlearn (taught by OMISSION). */
12824
+ var RetrainAnnotationKindSchema = zod.z.enum(["subject", "model_error"]);
12825
+ /** Did a human draw this box, or did the assist propose it? */
12826
+ var RetrainAnnotationSourceSchema = zod.z.enum(["operator", "assist"]);
12827
+ /** Normalised `[0,1]` rectangle against the FULL frame — the canonical form. */
12828
+ var RetrainBboxSchema = zod.z.object({
12829
+ x: zod.z.number(),
12830
+ y: zod.z.number(),
12831
+ w: zod.z.number(),
12832
+ h: zod.z.number()
12833
+ });
12834
+ /**
12835
+ * One annotated subject.
12836
+ *
12837
+ * `bbox` is normalised against the full frame, ALWAYS. The per-model shapes
12838
+ * (letterboxed root / zone-cropped package / subject-cropped classifier) are
12839
+ * derived from it at export and never stored — storing them is how one feature
12840
+ * space ends up holding two crops of the same subject (D52).
12841
+ */
12842
+ var RetrainAnnotationSchema = zod.z.object({
12843
+ id: zod.z.string(),
12844
+ trackId: zod.z.string(),
12845
+ deviceId: zod.z.number(),
12846
+ /** The COPY in retrain storage — never the source track's media key. */
12847
+ mediaKey: zod.z.string(),
12848
+ bbox: RetrainBboxSchema,
12849
+ macroClass: RetrainMacroClassSchema,
12850
+ label: zod.z.string().optional(),
12851
+ subLabel: zod.z.string().optional(),
12852
+ kind: RetrainAnnotationKindSchema,
12853
+ source: RetrainAnnotationSourceSchema,
12854
+ /** Which model proposed this box — or, on a `model_error`, drew the phantom. */
12855
+ assistModelId: zod.z.string().optional(),
12856
+ assistScore: zod.z.number().optional(),
12857
+ exportedInBatch: zod.z.string().optional(),
12858
+ createdAt: zod.z.number()
12859
+ });
12860
+ /** The write form — the server owns `id`, `createdAt` and the frame binding. */
12861
+ var RetrainAnnotationDraftSchema = RetrainAnnotationSchema.omit({
12862
+ id: true,
12863
+ trackId: true,
12864
+ deviceId: true,
12865
+ mediaKey: true,
12866
+ createdAt: true,
12867
+ exportedInBatch: true
12868
+ });
12869
+ /** A track sitting in `staging`, with everything the worklist needs to rank it. */
12870
+ var RetrainTrackSchema = zod.z.object({
12871
+ trackId: zod.z.string(),
12872
+ deviceId: zod.z.number(),
12873
+ className: zod.z.string(),
12874
+ label: zod.z.string().optional(),
12875
+ firstSeen: zod.z.number(),
12876
+ lastSeen: zod.z.number(),
12877
+ /** How many frames the dataset already holds from this track. */
12878
+ frameCount: zod.z.number().int(),
12879
+ /** How many subjects have been annotated on those frames. `0` with
12880
+ * `frameCount: 0` is exactly "staging, still to work". */
12881
+ annotationCount: zod.z.number().int()
12882
+ });
12883
+ /** A frame the picker may offer — an index row, no blob was read to produce it. */
12884
+ var RetrainFrameCandidateSchema = zod.z.object({
12885
+ mediaKey: zod.z.string(),
12886
+ kind: MediaFileKindEnum,
12887
+ timestamp: zod.z.number(),
12888
+ sizeBytes: zod.z.number().int(),
12889
+ /** A copy of this original already exists — selecting it is free and cannot
12890
+ * fail, whatever became of the original. */
12891
+ copied: zod.z.boolean()
12892
+ });
12893
+ /** A frame the dataset OWNS: bytes copied at selection time. */
12894
+ var RetrainFrameSchema = zod.z.object({
12895
+ frameId: zod.z.string(),
12896
+ deviceId: zod.z.number(),
12897
+ trackId: zod.z.string(),
12898
+ /** Provenance only. It may already point at nothing — that is expected. */
12899
+ sourceMediaKey: zod.z.string(),
12900
+ sourceKind: MediaFileKindEnum,
12901
+ sizeBytes: zod.z.number().int(),
12902
+ width: zod.z.number().int(),
12903
+ height: zod.z.number().int(),
12904
+ copiedAt: zod.z.number()
12905
+ });
12906
+ /** Why a copy-on-select could not be honoured — named, never a silent skip. */
12907
+ var RetrainCopyRefusalSchema = zod.z.enum([
12908
+ "source-missing",
12909
+ "unreadable-image",
12910
+ "write-failed"
12911
+ ]);
12912
+ var RetrainFrameSelectionSchema = zod.z.object({
12913
+ copied: zod.z.array(RetrainFrameSchema).readonly(),
12914
+ refused: zod.z.array(zod.z.object({
12915
+ sourceMediaKey: zod.z.string(),
12916
+ reason: RetrainCopyRefusalSchema
12917
+ })).readonly()
12918
+ });
12919
+ var RetrainFrameListSchema = zod.z.object({
12920
+ candidates: zod.z.array(RetrainFrameCandidateSchema).readonly(),
12921
+ copies: zod.z.array(RetrainFrameSchema).readonly(),
12922
+ /** What the page pre-selects — the native key frame when one survives. */
12923
+ autoPickMediaKey: zod.z.string().optional()
12924
+ });
12925
+ /** What the operator asked the assist to look for. */
12926
+ var RetrainAssistSubjectSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
12927
+ kind: zod.z.literal("package"),
12928
+ zone: RetrainBboxSchema.optional()
12929
+ }), zod.z.object({
12930
+ kind: zod.z.literal("objects"),
12931
+ modelId: zod.z.string(),
12932
+ minScore: zod.z.number().optional()
12933
+ })]);
12934
+ /**
12935
+ * The assist's answer — a discriminated union, because "the model saw nothing"
12936
+ * and "this node cannot run that model" lead to different next moves and a
12937
+ * nullable result cannot tell them apart.
12938
+ */
12939
+ var RetrainAssistResultSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
12940
+ kind: zod.z.literal("proposed"),
12941
+ modelId: zod.z.string(),
12942
+ stepId: zod.z.string(),
12943
+ minScore: zod.z.number(),
12944
+ /** Drafts, ready to edit. `source: 'assist'` until the operator touches one. */
12945
+ proposals: zod.z.array(RetrainAnnotationDraftSchema).readonly(),
12946
+ /** Returned by the runner but removed by the threshold. */
12947
+ belowThreshold: zod.z.number().int()
12948
+ }), zod.z.object({
12949
+ kind: zod.z.literal("refused"),
12950
+ /** `no-zone` is ours; the rest are the runner's own refusal vocabulary. */
12951
+ reason: zod.z.string(),
12952
+ detail: zod.z.string().optional()
12953
+ })]);
12954
+ /** The outcome of a lifecycle move owned by the retrain page. */
12955
+ var RetrainTransitionResultSchema = zod.z.object({
12956
+ trackId: zod.z.string(),
12957
+ /** Where the track ended up, whatever happened. */
12958
+ retrainStatus: RetrainStatusSchema,
12959
+ /** `false` ⇒ the move was refused or was a no-op; `reason` says which. */
12960
+ changed: zod.z.boolean(),
12961
+ reason: zod.z.enum([
12962
+ "unknown-track",
12963
+ "no-frames-copied",
12964
+ "not-staging",
12965
+ "not-trained",
12966
+ "unchanged"
12967
+ ]).optional()
12968
+ });
12609
12969
  var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
12610
12970
  var MAX_EVENT_QUERY_LIMIT = 5e3;
12611
12971
  var DeviceEventQueryInput = zod.z.object({
@@ -12660,7 +13020,7 @@ var KeyEventSchema = zod.z.object({
12660
13020
  /** Track start time (firstSeen). */
12661
13021
  timestamp: zod.z.number(),
12662
13022
  className: zod.z.string(),
12663
- label: zod.z.string().optional(),
13023
+ ...TieredLabelFields,
12664
13024
  importance: zod.z.number(),
12665
13025
  /** Highest-confidence ObjectEvent id for the track (empty when none). */
12666
13026
  bestEventId: zod.z.string(),
@@ -13151,6 +13511,201 @@ var pipelineAnalyticsCapability = {
13151
13511
  kind: "query",
13152
13512
  auth: "admin"
13153
13513
  }),
13514
+ /**
13515
+ * The staging worklist for one camera, or for every camera that has one.
13516
+ *
13517
+ * Fetched ON DEMAND, over the staging set only — the page never scans
13518
+ * history, because making the working set small is the entire purpose of
13519
+ * the mark. Each row carries how many frames the dataset already holds from
13520
+ * the track and how many subjects were annotated on them, so
13521
+ * `frameCount: 0` reads as "still to work" without a second call per track.
13522
+ *
13523
+ * `auth: 'admin'`, unlike the viewer-level mark itself: marking a track is
13524
+ * curation you do while looking at it, but building the training set the
13525
+ * fleet's models are fine-tuned on is not.
13526
+ */
13527
+ listRetrainStaging: require_sleep.method(zod.z.object({
13528
+ /** Empty ⇒ every camera that has staging tracks. A LIST, not a single
13529
+ * `deviceId`, deliberately: `deviceId` would make this device-bound and
13530
+ * route it at one camera's owner, and "every camera" would stop being
13531
+ * expressible at all. */
13532
+ deviceIds: zod.z.array(zod.z.number()).optional(),
13533
+ limit: zod.z.number().int().min(1).max(500).optional()
13534
+ }), zod.z.array(RetrainTrackSchema).readonly(), {
13535
+ kind: "query",
13536
+ auth: "admin"
13537
+ }),
13538
+ /**
13539
+ * What a track can contribute, and what it already has.
13540
+ *
13541
+ * `candidates` are the track's whole, unannotated frames — index rows only,
13542
+ * so this is cheap. `copies` are the frames already inside the dataset, and
13543
+ * a candidate whose copy exists is marked `copied: true`: selecting it again
13544
+ * is free and CANNOT fail, whatever became of the original.
13545
+ *
13546
+ * A crop, a thumbnail and `fullFrameBoxed` are never candidates. The last
13547
+ * one matters most: it has the model's own rectangle burned into the pixels,
13548
+ * and a detector trained on it learns to find a green line.
13549
+ */
13550
+ listRetrainFrames: require_sleep.method(zod.z.object({ trackId: zod.z.string() }), RetrainFrameListSchema, {
13551
+ kind: "query",
13552
+ auth: "admin"
13553
+ }),
13554
+ /**
13555
+ * COPY-ON-SELECT — the write that makes `trained` safe to evict.
13556
+ *
13557
+ * Selecting a frame copies its bytes into retrain storage immediately: not
13558
+ * a reference, not a lease. Once the copy exists the dataset no longer
13559
+ * depends on the track's media, which is exactly what lets D81 hand a
13560
+ * `trained` track back to retention.
13561
+ *
13562
+ * The order inside is load-bearing and is pinned by a test: an EXISTING
13563
+ * copy is returned without touching the source, so an original that
13564
+ * evaporated blocks the selection of THAT ORIGINAL and never the copy
13565
+ * already taken. Every refusal comes back named — a dropped selection is
13566
+ * never silent, on the wire or in the log.
13567
+ */
13568
+ selectRetrainFrames: require_sleep.method(zod.z.object({
13569
+ deviceId: zod.z.number(),
13570
+ trackId: zod.z.string(),
13571
+ mediaKeys: zod.z.array(zod.z.string()).min(1)
13572
+ }), RetrainFrameSelectionSchema, {
13573
+ kind: "mutation",
13574
+ auth: "admin"
13575
+ }),
13576
+ /** Un-select a frame: its annotations go first, then the copy and its blob.
13577
+ * Deliberately destructive and deliberately explicit — it is the only way
13578
+ * a frame leaves the dataset before export. */
13579
+ deselectRetrainFrame: require_sleep.method(zod.z.object({
13580
+ deviceId: zod.z.number(),
13581
+ trackId: zod.z.string(),
13582
+ frameId: zod.z.string()
13583
+ }), zod.z.object({
13584
+ removed: zod.z.boolean(),
13585
+ removedAnnotations: zod.z.number().int()
13586
+ }), {
13587
+ kind: "mutation",
13588
+ auth: "admin"
13589
+ }),
13590
+ /**
13591
+ * The pixels of ONE copied frame, base64.
13592
+ *
13593
+ * Through the cap rather than a data plane because it is genuinely one
13594
+ * frame at a time, on demand, at human speed — the shape D9/D18 permit
13595
+ * (what they forbid is frames crossing a boundary at frame RATE). The
13596
+ * annotation canvas needs the image and its exact dimensions in the same
13597
+ * answer: a canvas that places a normalised box against a size it guessed
13598
+ * draws every box in the wrong place.
13599
+ */
13600
+ getRetrainFrameImage: require_sleep.method(zod.z.object({ frameId: zod.z.string() }), zod.z.object({
13601
+ base64: zod.z.string(),
13602
+ width: zod.z.number().int(),
13603
+ height: zod.z.number().int()
13604
+ }), {
13605
+ kind: "query",
13606
+ auth: "admin"
13607
+ }),
13608
+ /**
13609
+ * Ask the pipeline what it sees, as a PROPOSAL.
13610
+ *
13611
+ * Runs through `pipelineRunner.runStatelessStep` on the COPIED frame, and
13612
+ * every box comes back as a draft with `source: 'assist'` plus the model and
13613
+ * score that produced it. The operator confirms, edits, adds and deletes;
13614
+ * nothing is stored until `saveRetrainAnnotations`.
13615
+ *
13616
+ * For packages the request is `rfdetr-package` on the ZONE CROP at 0.35 —
13617
+ * never the whole frame, where a package detector at that threshold proposes
13618
+ * furniture. A package request with no zone is REFUSED rather than widened,
13619
+ * because the silent widening would look like a bad model for as long as
13620
+ * nobody checked which rectangle it ran on.
13621
+ */
13622
+ proposeRetrainAnnotations: require_sleep.method(zod.z.object({
13623
+ deviceId: zod.z.number(),
13624
+ trackId: zod.z.string(),
13625
+ frameId: zod.z.string(),
13626
+ subject: RetrainAssistSubjectSchema,
13627
+ /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
13628
+ nodeId: zod.z.string().optional()
13629
+ }), RetrainAssistResultSchema, {
13630
+ kind: "mutation",
13631
+ auth: "admin"
13632
+ }),
13633
+ /** Every annotation on a track, oldest first. */
13634
+ listRetrainAnnotations: require_sleep.method(zod.z.object({ trackId: zod.z.string() }), zod.z.array(RetrainAnnotationSchema).readonly(), {
13635
+ kind: "query",
13636
+ auth: "admin"
13637
+ }),
13638
+ /**
13639
+ * Replace EVERY annotation on one frame with the supplied set.
13640
+ *
13641
+ * Whole-frame replacement, not per-box upsert: the unit of ground truth is
13642
+ * the frame, and "the operator deleted a box" must be the same durable
13643
+ * outcome as "the operator never drew it". A per-box patch would let a frame
13644
+ * keep a box the operator removed on a surface that only knew about the
13645
+ * boxes it sent.
13646
+ *
13647
+ * Refuses a macro class typed into `label` or `subLabel` — the tiers are
13648
+ * separate and the guard is at the WRITE, because a mixed taxonomy cannot
13649
+ * be un-mixed by reading it.
13650
+ */
13651
+ saveRetrainAnnotations: require_sleep.method(zod.z.object({
13652
+ deviceId: zod.z.number(),
13653
+ trackId: zod.z.string(),
13654
+ frameId: zod.z.string(),
13655
+ annotations: zod.z.array(RetrainAnnotationDraftSchema)
13656
+ }), zod.z.array(RetrainAnnotationSchema).readonly(), {
13657
+ kind: "mutation",
13658
+ auth: "admin"
13659
+ }),
13660
+ /**
13661
+ * Finish with a track: `staging → trained`. **The only writer of that
13662
+ * state** — D81 shipped the column with it deliberately unreachable.
13663
+ *
13664
+ * Refuses a track the dataset holds no copies from. `trained` un-pins the
13665
+ * track's media, so completing without a copy is a delete order for material
13666
+ * nothing ever extracted anything from; that refusal IS the safety argument
13667
+ * of D81, expressed as a precondition.
13668
+ */
13669
+ completeRetrainTrack: require_sleep.method(zod.z.object({
13670
+ deviceId: zod.z.number(),
13671
+ trackId: zod.z.string()
13672
+ }), RetrainTransitionResultSchema, {
13673
+ kind: "mutation",
13674
+ auth: "admin"
13675
+ }),
13676
+ /**
13677
+ * The deliberate return: `trained → staging`, for the rare case.
13678
+ *
13679
+ * The generic `setTrackFlags` toggle refuses this in both directions by
13680
+ * design (D81) — re-staging from a checkbox is how the same material gets
13681
+ * annotated twice under two ground truths. Doing it here means the operator
13682
+ * is looking at the annotations that already exist while they decide, and
13683
+ * those annotations are LEFT ALONE: "put this back" must not be a
13684
+ * destructive act wearing a navigational name.
13685
+ */
13686
+ restageRetrainTrack: require_sleep.method(zod.z.object({
13687
+ deviceId: zod.z.number(),
13688
+ trackId: zod.z.string()
13689
+ }), RetrainTransitionResultSchema, {
13690
+ kind: "mutation",
13691
+ auth: "admin"
13692
+ }),
13693
+ /**
13694
+ * Where to download the ANNOTATED dataset.
13695
+ *
13696
+ * The sibling of `getTrainingExportUrl` and deliberately not the same
13697
+ * archive: that one streams a marked track's stored media verbatim, this one
13698
+ * streams the retrain COPIES plus an `annotations.json` carrying, for every
13699
+ * subject, the canonical full-frame box AND the geometry derived for each
13700
+ * model shape (letterboxed root / zone-cropped package / subject-cropped
13701
+ * classifier). Derived at export, never stored — one box in, three shapes
13702
+ * out, so two crops of the same subject can never end up in one feature
13703
+ * space (D52).
13704
+ */
13705
+ getRetrainExportUrl: require_sleep.method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.object({ url: zod.z.string() }), {
13706
+ kind: "query",
13707
+ auth: "admin"
13708
+ }),
13154
13709
  getEventMedia: require_sleep.method(zod.z.object({
13155
13710
  eventId: zod.z.string(),
13156
13711
  kind: MediaFileKindEnum.optional()
@@ -13977,6 +14532,22 @@ var DetailResultSchema = zod.z.object({
13977
14532
  bbox: NativeCropBboxSchema.optional(),
13978
14533
  embedding: zod.z.string().optional(),
13979
14534
  label: zod.z.string().optional(),
14535
+ /**
14536
+ * The tier `label` occupies, copied VERBATIM from the producing step's
14537
+ * `StepDefinition.labelTier` (roadmap 4g). Present only when `label` is.
14538
+ *
14539
+ * It rides the wire rather than being resolved by the consumer because the
14540
+ * declaration lives with the step definition, which only the executing node
14541
+ * has: post-analysis holds no step registry, and re-deriving the tier from
14542
+ * `className` there would be exactly the inference this model exists to
14543
+ * forbid. A `label` that arrives WITHOUT this field is refused by the write
14544
+ * rule and logged (`label tier undeclared`) — an older runner therefore
14545
+ * stops enriching rather than guessing, which is why addon-pipeline is
14546
+ * deployed BEFORE addon-post-analysis.
14547
+ */
14548
+ labelTier: zod.z.union([zod.z.literal(1), zod.z.literal(2)]).optional(),
14549
+ /** Model that produced `label` — carried into the tier's attribution. */
14550
+ labelModelId: zod.z.string().optional(),
13980
14551
  alignedCropJpeg: zod.z.string().optional(),
13981
14552
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
13982
14553
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
@@ -31994,6 +32565,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31994
32565
  addonId: null,
31995
32566
  access: "delete"
31996
32567
  },
32568
+ "pipelineAnalytics.completeRetrainTrack": {
32569
+ capName: "pipeline-analytics",
32570
+ capScope: "device",
32571
+ addonId: null,
32572
+ access: "create"
32573
+ },
31997
32574
  "pipelineAnalytics.deleteDeviceEvents": {
31998
32575
  capName: "pipeline-analytics",
31999
32576
  capScope: "device",
@@ -32006,6 +32583,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
32006
32583
  addonId: null,
32007
32584
  access: "delete"
32008
32585
  },
32586
+ "pipelineAnalytics.deselectRetrainFrame": {
32587
+ capName: "pipeline-analytics",
32588
+ capScope: "device",
32589
+ addonId: null,
32590
+ access: "create"
32591
+ },
32009
32592
  "pipelineAnalytics.getActiveTracks": {
32010
32593
  capName: "pipeline-analytics",
32011
32594
  capScope: "device",
@@ -32066,6 +32649,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
32066
32649
  addonId: null,
32067
32650
  access: "view"
32068
32651
  },
32652
+ "pipelineAnalytics.getRetrainExportUrl": {
32653
+ capName: "pipeline-analytics",
32654
+ capScope: "device",
32655
+ addonId: null,
32656
+ access: "view"
32657
+ },
32658
+ "pipelineAnalytics.getRetrainFrameImage": {
32659
+ capName: "pipeline-analytics",
32660
+ capScope: "device",
32661
+ addonId: null,
32662
+ access: "view"
32663
+ },
32069
32664
  "pipelineAnalytics.getSensorEvents": {
32070
32665
  capName: "pipeline-analytics",
32071
32666
  capScope: "device",
@@ -32120,6 +32715,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
32120
32715
  addonId: null,
32121
32716
  access: "view"
32122
32717
  },
32718
+ "pipelineAnalytics.listRetrainAnnotations": {
32719
+ capName: "pipeline-analytics",
32720
+ capScope: "device",
32721
+ addonId: null,
32722
+ access: "view"
32723
+ },
32724
+ "pipelineAnalytics.listRetrainFrames": {
32725
+ capName: "pipeline-analytics",
32726
+ capScope: "device",
32727
+ addonId: null,
32728
+ access: "view"
32729
+ },
32730
+ "pipelineAnalytics.listRetrainStaging": {
32731
+ capName: "pipeline-analytics",
32732
+ capScope: "device",
32733
+ addonId: null,
32734
+ access: "view"
32735
+ },
32123
32736
  "pipelineAnalytics.listTrackMedia": {
32124
32737
  capName: "pipeline-analytics",
32125
32738
  capScope: "device",
@@ -32132,6 +32745,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
32132
32745
  addonId: null,
32133
32746
  access: "view"
32134
32747
  },
32748
+ "pipelineAnalytics.proposeRetrainAnnotations": {
32749
+ capName: "pipeline-analytics",
32750
+ capScope: "device",
32751
+ addonId: null,
32752
+ access: "create"
32753
+ },
32135
32754
  "pipelineAnalytics.pruneEvents": {
32136
32755
  capName: "pipeline-analytics",
32137
32756
  capScope: "device",
@@ -32162,12 +32781,30 @@ var METHOD_ACCESS_MAP = Object.freeze({
32162
32781
  addonId: null,
32163
32782
  access: "create"
32164
32783
  },
32784
+ "pipelineAnalytics.restageRetrainTrack": {
32785
+ capName: "pipeline-analytics",
32786
+ capScope: "device",
32787
+ addonId: null,
32788
+ access: "create"
32789
+ },
32790
+ "pipelineAnalytics.saveRetrainAnnotations": {
32791
+ capName: "pipeline-analytics",
32792
+ capScope: "device",
32793
+ addonId: null,
32794
+ access: "create"
32795
+ },
32165
32796
  "pipelineAnalytics.searchObjectEvents": {
32166
32797
  capName: "pipeline-analytics",
32167
32798
  capScope: "device",
32168
32799
  addonId: null,
32169
32800
  access: "view"
32170
32801
  },
32802
+ "pipelineAnalytics.selectRetrainFrames": {
32803
+ capName: "pipeline-analytics",
32804
+ capScope: "device",
32805
+ addonId: null,
32806
+ access: "create"
32807
+ },
32171
32808
  "pipelineAnalytics.setTrackFlags": {
32172
32809
  capName: "pipeline-analytics",
32173
32810
  capScope: "device",
@@ -33560,6 +34197,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
33560
34197
  addonId: null,
33561
34198
  access: "create"
33562
34199
  },
34200
+ "streamBroker.fetchEventMedia": {
34201
+ capName: "stream-broker",
34202
+ capScope: "system",
34203
+ addonId: null,
34204
+ access: "create"
34205
+ },
33563
34206
  "streamBroker.getAllRtspEntries": {
33564
34207
  capName: "stream-broker",
33565
34208
  capScope: "system",
@@ -33644,6 +34287,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
33644
34287
  addonId: null,
33645
34288
  access: "create"
33646
34289
  },
34290
+ "streamBroker.produceEventMedia": {
34291
+ capName: "stream-broker",
34292
+ capScope: "system",
34293
+ addonId: null,
34294
+ access: "create"
34295
+ },
33647
34296
  "streamBroker.publishCameraStream": {
33648
34297
  capName: "stream-broker",
33649
34298
  capScope: "system",
@@ -35174,6 +35823,7 @@ function createSystemProxy(api) {
35174
35823
  testConfig: (input) => dispatch("storage", "testConfig", "query", input)
35175
35824
  },
35176
35825
  streamBroker: {
35826
+ fetchEventMedia: (input) => dispatch("streamBroker", "fetchEventMedia", "mutation", input),
35177
35827
  listAllCameraStreams: (input) => dispatch("streamBroker", "listAllCameraStreams", "query", input),
35178
35828
  listAllProfileSlots: (input) => dispatch("streamBroker", "listAllProfileSlots", "query", input),
35179
35829
  getBrokerStats: (input) => dispatch("streamBroker", "getBrokerStats", "query", input),
@@ -37745,6 +38395,10 @@ exports.EventKindDescriptorSchema = EventKindDescriptorSchema;
37745
38395
  exports.EventKindIconSchema = EventKindIconSchema;
37746
38396
  exports.EventKindSchema = EventKindSchema;
37747
38397
  exports.EventKindsForDeviceSchema = EventKindsForDeviceSchema;
38398
+ exports.EventMediaArtifactSchema = EventMediaArtifactSchema;
38399
+ exports.EventMediaCoverageSchema = EventMediaCoverageSchema;
38400
+ exports.EventMediaKindSchema = EventMediaKindSchema;
38401
+ exports.EventMediaProductionSchema = EventMediaProductionSchema;
37748
38402
  exports.EventSourceType = require_enums.EventSourceType$1;
37749
38403
  exports.ExportDownloadSchema = ExportDownloadSchema;
37750
38404
  exports.ExportOptionsSchema = ExportOptionsSchema;
@@ -37799,7 +38453,9 @@ exports.IntercomStatusSchema = IntercomStatusSchema;
37799
38453
  exports.KNOWN_CAP_NAMES = KNOWN_CAP_NAMES;
37800
38454
  exports.KeyEventSchema = KeyEventSchema;
37801
38455
  exports.LOG_LEVEL_RANK = LOG_LEVEL_RANK;
38456
+ exports.LabelAttributionSchema = LabelAttributionSchema;
37802
38457
  exports.LabelDefinitionSchema = LabelDefinitionSchema;
38458
+ exports.LabelTierSchema = LabelTierSchema;
37803
38459
  exports.LawnMowerActivitySchema = LawnMowerActivitySchema;
37804
38460
  exports.LawnMowerControlStatusSchema = LawnMowerControlStatusSchema;
37805
38461
  exports.LinkedDeviceSchema = LinkedDeviceSchema;
@@ -38065,7 +38721,21 @@ exports.RelocateJobStateSchema = RelocateJobStateSchema;
38065
38721
  exports.RelocateMediaInputSchema = RelocateMediaInputSchema;
38066
38722
  exports.RenderedAsSchema = RenderedAsSchema;
38067
38723
  exports.ReportMotionInputSchema = ReportMotionInputSchema;
38724
+ exports.RetrainAnnotationDraftSchema = RetrainAnnotationDraftSchema;
38725
+ exports.RetrainAnnotationKindSchema = RetrainAnnotationKindSchema;
38726
+ exports.RetrainAnnotationSchema = RetrainAnnotationSchema;
38727
+ exports.RetrainAnnotationSourceSchema = RetrainAnnotationSourceSchema;
38728
+ exports.RetrainAssistResultSchema = RetrainAssistResultSchema;
38729
+ exports.RetrainAssistSubjectSchema = RetrainAssistSubjectSchema;
38730
+ exports.RetrainCopyRefusalSchema = RetrainCopyRefusalSchema;
38731
+ exports.RetrainFrameCandidateSchema = RetrainFrameCandidateSchema;
38732
+ exports.RetrainFrameListSchema = RetrainFrameListSchema;
38733
+ exports.RetrainFrameSchema = RetrainFrameSchema;
38734
+ exports.RetrainFrameSelectionSchema = RetrainFrameSelectionSchema;
38735
+ exports.RetrainMacroClassSchema = RetrainMacroClassSchema;
38068
38736
  exports.RetrainStatusSchema = RetrainStatusSchema;
38737
+ exports.RetrainTrackSchema = RetrainTrackSchema;
38738
+ exports.RetrainTransitionResultSchema = RetrainTransitionResultSchema;
38069
38739
  exports.RingBuffer = RingBuffer;
38070
38740
  exports.RtpSourceSchema = RtpSourceSchema;
38071
38741
  exports.RtspRestreamEntrySchema = RtspRestreamEntrySchema;