@camstack/system 1.2.60 → 1.2.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
  12. package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
  13. package/dist/builtins/device-manager/device-aggregation.d.ts +49 -2
  14. package/dist/builtins/device-manager/device-manager.addon.js +60 -5
  15. package/dist/builtins/device-manager/device-manager.addon.mjs +60 -5
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  18. package/dist/builtins/hub-forwarder/index.js +1 -1
  19. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  22. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  23. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  24. package/dist/builtins/local-network/local-network.addon.js +1 -1
  25. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  26. package/dist/builtins/loki-logging/index.js +1 -1
  27. package/dist/builtins/loki-logging/index.mjs +1 -1
  28. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  30. package/dist/builtins/platform-probe/index.js +1 -1
  31. package/dist/builtins/platform-probe/index.mjs +1 -1
  32. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  34. package/dist/builtins/snapshot/index.js +1 -1
  35. package/dist/builtins/snapshot/index.mjs +1 -1
  36. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  38. package/dist/builtins/sqlite-storage/prefix-range.d.ts +50 -0
  39. package/dist/builtins/sqlite-storage/query-bounds.d.ts +116 -0
  40. package/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +12 -0
  41. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -2
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +281 -19
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +281 -19
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  46. package/dist/builtins/system-config/system-config.addon.js +1 -1
  47. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  48. package/dist/builtins/winston-logging/index.js +1 -1
  49. package/dist/builtins/winston-logging/index.mjs +1 -1
  50. package/dist/{dist-Cis_iV-u.js → dist-DgPUarcs.js} +636 -3
  51. package/dist/{dist-C5XpOjbI.mjs → dist-DnKpFaP1.mjs} +636 -3
  52. package/dist/index.d.ts +2 -2
  53. package/dist/index.js +337 -12
  54. package/dist/index.mjs +328 -12
  55. package/dist/kernel/addon-residue.d.ts +49 -0
  56. package/dist/kernel/heap-watch.d.ts +48 -1
  57. package/dist/kernel/index.d.ts +2 -0
  58. package/package.json +1 -1
@@ -5434,6 +5434,69 @@ var StreamFormatSchema = zod.z.enum([
5434
5434
  "mjpeg",
5435
5435
  "rtsp"
5436
5436
  ]);
5437
+ /** A container `produceEventMedia` can emit. */
5438
+ var EventMediaKindSchema = zod.z.enum(["mp4", "gif"]);
5439
+ /**
5440
+ * One produced artifact, referenced by HANDLE.
5441
+ *
5442
+ * Never inline bytes: a produced clip is 200 KB–5 MB and every consumer of this
5443
+ * method is in another runner ([D9](../../../../docs/decisions/adr-0009.md),
5444
+ * [D18](../../../../docs/decisions/adr-0018.md) — cross-process media is fetched
5445
+ * on demand, compressed, by handle). `bytes` is here so a caller can decide
5446
+ * whether it wants the fetch at all.
5447
+ */
5448
+ var EventMediaArtifactSchema = zod.z.object({
5449
+ kind: EventMediaKindSchema,
5450
+ /** Opaque, single-camera, short-lived. Redeem with `fetchEventMedia`. */
5451
+ handle: zod.z.string(),
5452
+ /**
5453
+ * The node holding the bytes — the ROUTING key for `fetchEventMedia`.
5454
+ *
5455
+ * `stream-broker` is a singleton cap and an unpinned call never leaves the
5456
+ * hub, so a handle produced on an agent's broker would be redeemed against
5457
+ * the hub's store and come back `null`. Same contract, same field name and
5458
+ * the same reason as `FrameHandleSchema.nodeId`: the producer stamps where it
5459
+ * lives and the consumer pins to it.
5460
+ */
5461
+ nodeId: zod.z.string(),
5462
+ mime: zod.z.string(),
5463
+ bytes: zod.z.number().int(),
5464
+ width: zod.z.number().int(),
5465
+ height: zod.z.number().int()
5466
+ });
5467
+ /**
5468
+ * What a production actually covered — the answer to the only question an
5469
+ * operator asks about a notification clip.
5470
+ *
5471
+ * `fromTs`/`toTs` are WALL CLOCK, derived from the ring's own packet timeline,
5472
+ * so a caller can state "this clip starts 4.1 s before the event" instead of
5473
+ * inferring it from a duration. A production whose `fromTs` is later than the
5474
+ * event is a production with no pre-roll, and that is exactly the defect this
5475
+ * method exists to make visible rather than plausible.
5476
+ */
5477
+ var EventMediaCoverageSchema = zod.z.object({
5478
+ fromTs: zod.z.number(),
5479
+ toTs: zod.z.number(),
5480
+ /** Encoded packets in the muxed window. */
5481
+ packets: zod.z.number().int()
5482
+ });
5483
+ /**
5484
+ * The result of ONE cut, in every container the caller asked for.
5485
+ *
5486
+ * Every artifact in `media` came out of the SAME window of the SAME rendition —
5487
+ * that is the whole reason this is one method rather than one call per format.
5488
+ * A consumer attaching a gif and a video can no longer show two different
5489
+ * moments, because it never chose two sources.
5490
+ */
5491
+ var EventMediaProductionSchema = zod.z.object({
5492
+ media: zod.z.array(EventMediaArtifactSchema).readonly(),
5493
+ coverage: EventMediaCoverageSchema,
5494
+ /** The rendition actually cut from — what the default or the fallback chose. */
5495
+ profile: CamProfileSchema,
5496
+ /** `copy` = the camera's own H.264, untouched. `encode` = re-encoded (H.265
5497
+ * source, a downscale, or a playback rate other than 1). */
5498
+ video: zod.z.enum(["copy", "encode"])
5499
+ });
5437
5500
  var RtspRestreamEntrySchema = zod.z.object({
5438
5501
  brokerId: zod.z.string(),
5439
5502
  url: zod.z.string(),
@@ -5871,6 +5934,66 @@ var streamBrokerCapability = {
5871
5934
  kind: "mutation",
5872
5935
  auth: "admin"
5873
5936
  }),
5937
+ /**
5938
+ * THE production point for event media — one window, every container.
5939
+ *
5940
+ * `renderPreBufferClip` renders ONE format per call, so a consumer wanting a
5941
+ * gif and a video made two calls and got two windows. On 2026-08-08 the
5942
+ * operator received exactly that: a gif around a doorbell press and an mp4
5943
+ * of the six seconds after it. This method cuts once and derives the rest,
5944
+ * so "the attachments agree" is a property of the service instead of a
5945
+ * discipline every consumer has to keep.
5946
+ *
5947
+ * It is also the only place that owns the packets. The broker already
5948
+ * retains an AnnexB ring per camera (`setClipRetention`, D32) — the
5949
+ * prebuffer — so a production reaches BACKWARDS from the event with no
5950
+ * process to spawn and nothing kept warm. It waits out `postSeconds`, muxes
5951
+ * the window once (COPYING the camera's own H.264 whenever nothing is being
5952
+ * changed), derives any gif from that same mux, and returns HANDLES.
5953
+ *
5954
+ * Intended for every future consumer, not just notifications: a clips API, a
5955
+ * retrain export and an event export all want the same window of the same
5956
+ * camera and must not each grow their own renderer.
5957
+ */
5958
+ produceEventMedia: method(zod.z.object({
5959
+ deviceId: zod.z.number(),
5960
+ /** Absent = the largest H.264 rendition at or below 1080p, which is
5961
+ * also the one that can be copied. Falls back to whatever the ring
5962
+ * actually retained, and the answer says which. */
5963
+ profile: CamProfileSchema.optional(),
5964
+ aroundMs: zod.z.number(),
5965
+ preSeconds: zod.z.number().min(0).max(20).default(4),
5966
+ postSeconds: zod.z.number().min(0).max(20).default(6),
5967
+ kinds: zod.z.array(EventMediaKindSchema).min(1).default(["mp4"]),
5968
+ /** GIF geometry. The video keeps the source's own. */
5969
+ gifMaxWidth: zod.z.number().int().min(120).max(1280).default(640),
5970
+ gifFps: zod.z.number().int().min(1).max(15).default(8),
5971
+ /**
5972
+ * Playback rate, applied to EVERY container so they stay one clip.
5973
+ * `1` is real time and is what allows the copy branch.
5974
+ */
5975
+ speed: zod.z.number().min(1).max(8).default(1)
5976
+ }), EventMediaProductionSchema, {
5977
+ kind: "mutation",
5978
+ auth: "admin"
5979
+ }),
5980
+ /**
5981
+ * Redeem a {@link EventMediaArtifactSchema} handle for its bytes.
5982
+ *
5983
+ * Separate from the production on purpose (D18): the producing runner keeps
5984
+ * the artifact for a few minutes, and a consumer that only wanted the
5985
+ * coverage never moves a megabyte. `null` once it has expired — a handle is
5986
+ * short-lived by design and a caller that waited too long must see that
5987
+ * rather than a zero-length file.
5988
+ */
5989
+ fetchEventMedia: method(zod.z.object({ handle: zod.z.string() }), zod.z.object({
5990
+ base64: zod.z.string(),
5991
+ mime: zod.z.string(),
5992
+ bytes: zod.z.number().int()
5993
+ }).nullable(), {
5994
+ kind: "mutation",
5995
+ auth: "admin"
5996
+ }),
5874
5997
  listAllCameraStreams: method(zod.z.void(), zod.z.array(CameraStreamSchema).readonly()),
5875
5998
  listAllProfileSlots: method(zod.z.void(), zod.z.array(ProfileSlotSchema).readonly()),
5876
5999
  getBrokerStats: method(zod.z.object({ brokerId: zod.z.string() }), BrokerStatsSchema),
@@ -13026,6 +13149,60 @@ var TrackFlagsSchema = zod.z.object({
13026
13149
  * `trained` without a re-fetch. */
13027
13150
  retrainStatus: RetrainStatusSchema
13028
13151
  });
13152
+ zod.z.union([zod.z.literal(1), zod.z.literal(2)]);
13153
+ /**
13154
+ * WHO decided a label, and when. Carried per tier so a value can be traced to
13155
+ * the step and model that produced it — which is what makes the write rule
13156
+ * arguable after the fact ("why is 592's label `dog` and not `Canis lupus`?")
13157
+ * and what lets a migrated, UNATTRIBUTED value be told apart from a real one.
13158
+ *
13159
+ * `stepId` is the pipeline step id (`animal-classifier`, `bird-classifier`,
13160
+ * `plate-ocr`, `face-embedding`, `object-detection`), or the sentinel
13161
+ * `migration:4g` for a value the 4g migration moved from the single-slot era —
13162
+ * that value has no provenance, and the write rule lets ANY properly-attributed
13163
+ * write of the same tier replace it regardless of score.
13164
+ */
13165
+ var LabelAttributionSchema = zod.z.object({
13166
+ stepId: zod.z.string(),
13167
+ modelId: zod.z.string().optional(),
13168
+ decidedAt: zod.z.number()
13169
+ });
13170
+ /**
13171
+ * The TIERED label model (roadmap 4g), spread into `TrackSchema` and
13172
+ * `ObjectEventSchema` from ONE place so the two surfaces cannot drift — a
13173
+ * track and its events always answer the same question the same way.
13174
+ *
13175
+ * Two scalar columns, not an array: every consumer wants "the coarse one" or
13176
+ * "the fine one", and an array made both a scan. `label` is tier 1, `subLabel`
13177
+ * is tier 2, and each carries its own score + attribution.
13178
+ *
13179
+ * **Reading it.** What a human should be shown is `subLabel ?? label` — the
13180
+ * finest thing known. Before 4g the single `label` column held the finest
13181
+ * value, so a consumer that has not been updated reads the tier-1 slot and
13182
+ * shows nothing on a species-only row; that is why the migration puts every
13183
+ * pre-4g value in tier 2 (it cannot regress a display that reads the fallback)
13184
+ * and why the read surfaces were changed in the same train.
13185
+ *
13186
+ * **Writing it.** The slots are independent, which is the whole point: a
13187
+ * tier-1 write (`bird`) can never overwrite a tier-2 value (`Turdus
13188
+ * migratorius`), so fineness cannot regress by construction. Within a tier the
13189
+ * higher score wins. One rule, one implementation — see
13190
+ * `pipeline/label-tier.ts` in addon-post-analysis.
13191
+ */
13192
+ var TieredLabelFields = {
13193
+ /** Tier 1 — the sub-class. See {@link LabelTierSchema}. */
13194
+ label: zod.z.string().optional(),
13195
+ /** Confidence of the tier-1 value, as reported by the deciding step. */
13196
+ labelScore: zod.z.number().optional(),
13197
+ /** Provenance of the tier-1 value. See {@link LabelAttributionSchema}. */
13198
+ labelMeta: LabelAttributionSchema.optional(),
13199
+ /** Tier 2 — the instance. See {@link LabelTierSchema}. */
13200
+ subLabel: zod.z.string().optional(),
13201
+ /** Confidence of the tier-2 value, as reported by the deciding step. */
13202
+ subLabelScore: zod.z.number().optional(),
13203
+ /** Provenance of the tier-2 value. See {@link LabelAttributionSchema}. */
13204
+ subLabelMeta: LabelAttributionSchema.optional()
13205
+ };
13029
13206
  /** Per-camera slice of a training-export estimate. */
13030
13207
  var TrainingExportDeviceTotalsSchema = zod.z.object({
13031
13208
  deviceId: zod.z.number(),
@@ -13050,7 +13227,7 @@ var TrackSchema = zod.z.object({
13050
13227
  trackId: zod.z.string(),
13051
13228
  deviceId: zod.z.number(),
13052
13229
  className: zod.z.string(),
13053
- label: zod.z.string().optional(),
13230
+ ...TieredLabelFields,
13054
13231
  producingDeviceName: zod.z.string().optional(),
13055
13232
  /** Track provenance. Absent ⇒ `pipeline` (legacy rows). */
13056
13233
  source: TrackSourceSchema.optional(),
@@ -13171,7 +13348,7 @@ var ObjectEventSchema = zod.z.object({
13171
13348
  /** Omitted in slim projection. */
13172
13349
  trackId: zod.z.string().optional(),
13173
13350
  className: zod.z.string(),
13174
- label: zod.z.string().optional(),
13351
+ ...TieredLabelFields,
13175
13352
  /** Omitted in slim projection. */
13176
13353
  confidence: zod.z.number().optional(),
13177
13354
  /** Heavy JSON — omitted in slim projection. */
@@ -13252,6 +13429,173 @@ var MediaFileSchema = zod.z.object({
13252
13429
  * stored blob and a `?variant=thumb` rendering without fetching either.
13253
13430
  */
13254
13431
  var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
13432
+ /**
13433
+ * The MACRO tier of an annotation — a CLOSED set.
13434
+ *
13435
+ * This is what the exported detector predicts, so a typo here is a new class
13436
+ * with one example in it. `label` and `subLabel` are open strings by contrast:
13437
+ * the whole point of the page is teaching the model things it does not know
13438
+ * yet, and constraining that vocabulary would make it useless.
13439
+ *
13440
+ * A macro class is NEVER a label. The provider refuses a write whose `label` or
13441
+ * `subLabel` is one of these values, in any casing, because once `person`
13442
+ * exists in both tiers "every person box" stops being answerable without
13443
+ * knowing every string anyone ever typed — and the damage is retroactive.
13444
+ */
13445
+ var RetrainMacroClassSchema = zod.z.enum([
13446
+ "person",
13447
+ "vehicle",
13448
+ "animal",
13449
+ "package",
13450
+ "face",
13451
+ "plate"
13452
+ ]);
13453
+ /** A subject to learn, or a phantom to unlearn (taught by OMISSION). */
13454
+ var RetrainAnnotationKindSchema = zod.z.enum(["subject", "model_error"]);
13455
+ /** Did a human draw this box, or did the assist propose it? */
13456
+ var RetrainAnnotationSourceSchema = zod.z.enum(["operator", "assist"]);
13457
+ /** Normalised `[0,1]` rectangle against the FULL frame — the canonical form. */
13458
+ var RetrainBboxSchema = zod.z.object({
13459
+ x: zod.z.number(),
13460
+ y: zod.z.number(),
13461
+ w: zod.z.number(),
13462
+ h: zod.z.number()
13463
+ });
13464
+ /**
13465
+ * One annotated subject.
13466
+ *
13467
+ * `bbox` is normalised against the full frame, ALWAYS. The per-model shapes
13468
+ * (letterboxed root / zone-cropped package / subject-cropped classifier) are
13469
+ * derived from it at export and never stored — storing them is how one feature
13470
+ * space ends up holding two crops of the same subject (D52).
13471
+ */
13472
+ var RetrainAnnotationSchema = zod.z.object({
13473
+ id: zod.z.string(),
13474
+ trackId: zod.z.string(),
13475
+ deviceId: zod.z.number(),
13476
+ /** The COPY in retrain storage — never the source track's media key. */
13477
+ mediaKey: zod.z.string(),
13478
+ bbox: RetrainBboxSchema,
13479
+ macroClass: RetrainMacroClassSchema,
13480
+ label: zod.z.string().optional(),
13481
+ subLabel: zod.z.string().optional(),
13482
+ kind: RetrainAnnotationKindSchema,
13483
+ source: RetrainAnnotationSourceSchema,
13484
+ /** Which model proposed this box — or, on a `model_error`, drew the phantom. */
13485
+ assistModelId: zod.z.string().optional(),
13486
+ assistScore: zod.z.number().optional(),
13487
+ exportedInBatch: zod.z.string().optional(),
13488
+ createdAt: zod.z.number()
13489
+ });
13490
+ /** The write form — the server owns `id`, `createdAt` and the frame binding. */
13491
+ var RetrainAnnotationDraftSchema = RetrainAnnotationSchema.omit({
13492
+ id: true,
13493
+ trackId: true,
13494
+ deviceId: true,
13495
+ mediaKey: true,
13496
+ createdAt: true,
13497
+ exportedInBatch: true
13498
+ });
13499
+ /** A track sitting in `staging`, with everything the worklist needs to rank it. */
13500
+ var RetrainTrackSchema = zod.z.object({
13501
+ trackId: zod.z.string(),
13502
+ deviceId: zod.z.number(),
13503
+ className: zod.z.string(),
13504
+ label: zod.z.string().optional(),
13505
+ firstSeen: zod.z.number(),
13506
+ lastSeen: zod.z.number(),
13507
+ /** How many frames the dataset already holds from this track. */
13508
+ frameCount: zod.z.number().int(),
13509
+ /** How many subjects have been annotated on those frames. `0` with
13510
+ * `frameCount: 0` is exactly "staging, still to work". */
13511
+ annotationCount: zod.z.number().int()
13512
+ });
13513
+ /** A frame the picker may offer — an index row, no blob was read to produce it. */
13514
+ var RetrainFrameCandidateSchema = zod.z.object({
13515
+ mediaKey: zod.z.string(),
13516
+ kind: MediaFileKindEnum,
13517
+ timestamp: zod.z.number(),
13518
+ sizeBytes: zod.z.number().int(),
13519
+ /** A copy of this original already exists — selecting it is free and cannot
13520
+ * fail, whatever became of the original. */
13521
+ copied: zod.z.boolean()
13522
+ });
13523
+ /** A frame the dataset OWNS: bytes copied at selection time. */
13524
+ var RetrainFrameSchema = zod.z.object({
13525
+ frameId: zod.z.string(),
13526
+ deviceId: zod.z.number(),
13527
+ trackId: zod.z.string(),
13528
+ /** Provenance only. It may already point at nothing — that is expected. */
13529
+ sourceMediaKey: zod.z.string(),
13530
+ sourceKind: MediaFileKindEnum,
13531
+ sizeBytes: zod.z.number().int(),
13532
+ width: zod.z.number().int(),
13533
+ height: zod.z.number().int(),
13534
+ copiedAt: zod.z.number()
13535
+ });
13536
+ /** Why a copy-on-select could not be honoured — named, never a silent skip. */
13537
+ var RetrainCopyRefusalSchema = zod.z.enum([
13538
+ "source-missing",
13539
+ "unreadable-image",
13540
+ "write-failed"
13541
+ ]);
13542
+ var RetrainFrameSelectionSchema = zod.z.object({
13543
+ copied: zod.z.array(RetrainFrameSchema).readonly(),
13544
+ refused: zod.z.array(zod.z.object({
13545
+ sourceMediaKey: zod.z.string(),
13546
+ reason: RetrainCopyRefusalSchema
13547
+ })).readonly()
13548
+ });
13549
+ var RetrainFrameListSchema = zod.z.object({
13550
+ candidates: zod.z.array(RetrainFrameCandidateSchema).readonly(),
13551
+ copies: zod.z.array(RetrainFrameSchema).readonly(),
13552
+ /** What the page pre-selects — the native key frame when one survives. */
13553
+ autoPickMediaKey: zod.z.string().optional()
13554
+ });
13555
+ /** What the operator asked the assist to look for. */
13556
+ var RetrainAssistSubjectSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
13557
+ kind: zod.z.literal("package"),
13558
+ zone: RetrainBboxSchema.optional()
13559
+ }), zod.z.object({
13560
+ kind: zod.z.literal("objects"),
13561
+ modelId: zod.z.string(),
13562
+ minScore: zod.z.number().optional()
13563
+ })]);
13564
+ /**
13565
+ * The assist's answer — a discriminated union, because "the model saw nothing"
13566
+ * and "this node cannot run that model" lead to different next moves and a
13567
+ * nullable result cannot tell them apart.
13568
+ */
13569
+ var RetrainAssistResultSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
13570
+ kind: zod.z.literal("proposed"),
13571
+ modelId: zod.z.string(),
13572
+ stepId: zod.z.string(),
13573
+ minScore: zod.z.number(),
13574
+ /** Drafts, ready to edit. `source: 'assist'` until the operator touches one. */
13575
+ proposals: zod.z.array(RetrainAnnotationDraftSchema).readonly(),
13576
+ /** Returned by the runner but removed by the threshold. */
13577
+ belowThreshold: zod.z.number().int()
13578
+ }), zod.z.object({
13579
+ kind: zod.z.literal("refused"),
13580
+ /** `no-zone` is ours; the rest are the runner's own refusal vocabulary. */
13581
+ reason: zod.z.string(),
13582
+ detail: zod.z.string().optional()
13583
+ })]);
13584
+ /** The outcome of a lifecycle move owned by the retrain page. */
13585
+ var RetrainTransitionResultSchema = zod.z.object({
13586
+ trackId: zod.z.string(),
13587
+ /** Where the track ended up, whatever happened. */
13588
+ retrainStatus: RetrainStatusSchema,
13589
+ /** `false` ⇒ the move was refused or was a no-op; `reason` says which. */
13590
+ changed: zod.z.boolean(),
13591
+ reason: zod.z.enum([
13592
+ "unknown-track",
13593
+ "no-frames-copied",
13594
+ "not-staging",
13595
+ "not-trained",
13596
+ "unchanged"
13597
+ ]).optional()
13598
+ });
13255
13599
  var DEFAULT_EVENT_QUERY_LIMIT = 1e3;
13256
13600
  var MAX_EVENT_QUERY_LIMIT = 5e3;
13257
13601
  var DeviceEventQueryInput = zod.z.object({
@@ -13306,7 +13650,7 @@ var KeyEventSchema = zod.z.object({
13306
13650
  /** Track start time (firstSeen). */
13307
13651
  timestamp: zod.z.number(),
13308
13652
  className: zod.z.string(),
13309
- label: zod.z.string().optional(),
13653
+ ...TieredLabelFields,
13310
13654
  importance: zod.z.number(),
13311
13655
  /** Highest-confidence ObjectEvent id for the track (empty when none). */
13312
13656
  bestEventId: zod.z.string(),
@@ -13797,6 +14141,201 @@ var pipelineAnalyticsCapability = {
13797
14141
  kind: "query",
13798
14142
  auth: "admin"
13799
14143
  }),
14144
+ /**
14145
+ * The staging worklist for one camera, or for every camera that has one.
14146
+ *
14147
+ * Fetched ON DEMAND, over the staging set only — the page never scans
14148
+ * history, because making the working set small is the entire purpose of
14149
+ * the mark. Each row carries how many frames the dataset already holds from
14150
+ * the track and how many subjects were annotated on them, so
14151
+ * `frameCount: 0` reads as "still to work" without a second call per track.
14152
+ *
14153
+ * `auth: 'admin'`, unlike the viewer-level mark itself: marking a track is
14154
+ * curation you do while looking at it, but building the training set the
14155
+ * fleet's models are fine-tuned on is not.
14156
+ */
14157
+ listRetrainStaging: method(zod.z.object({
14158
+ /** Empty ⇒ every camera that has staging tracks. A LIST, not a single
14159
+ * `deviceId`, deliberately: `deviceId` would make this device-bound and
14160
+ * route it at one camera's owner, and "every camera" would stop being
14161
+ * expressible at all. */
14162
+ deviceIds: zod.z.array(zod.z.number()).optional(),
14163
+ limit: zod.z.number().int().min(1).max(500).optional()
14164
+ }), zod.z.array(RetrainTrackSchema).readonly(), {
14165
+ kind: "query",
14166
+ auth: "admin"
14167
+ }),
14168
+ /**
14169
+ * What a track can contribute, and what it already has.
14170
+ *
14171
+ * `candidates` are the track's whole, unannotated frames — index rows only,
14172
+ * so this is cheap. `copies` are the frames already inside the dataset, and
14173
+ * a candidate whose copy exists is marked `copied: true`: selecting it again
14174
+ * is free and CANNOT fail, whatever became of the original.
14175
+ *
14176
+ * A crop, a thumbnail and `fullFrameBoxed` are never candidates. The last
14177
+ * one matters most: it has the model's own rectangle burned into the pixels,
14178
+ * and a detector trained on it learns to find a green line.
14179
+ */
14180
+ listRetrainFrames: method(zod.z.object({ trackId: zod.z.string() }), RetrainFrameListSchema, {
14181
+ kind: "query",
14182
+ auth: "admin"
14183
+ }),
14184
+ /**
14185
+ * COPY-ON-SELECT — the write that makes `trained` safe to evict.
14186
+ *
14187
+ * Selecting a frame copies its bytes into retrain storage immediately: not
14188
+ * a reference, not a lease. Once the copy exists the dataset no longer
14189
+ * depends on the track's media, which is exactly what lets D81 hand a
14190
+ * `trained` track back to retention.
14191
+ *
14192
+ * The order inside is load-bearing and is pinned by a test: an EXISTING
14193
+ * copy is returned without touching the source, so an original that
14194
+ * evaporated blocks the selection of THAT ORIGINAL and never the copy
14195
+ * already taken. Every refusal comes back named — a dropped selection is
14196
+ * never silent, on the wire or in the log.
14197
+ */
14198
+ selectRetrainFrames: method(zod.z.object({
14199
+ deviceId: zod.z.number(),
14200
+ trackId: zod.z.string(),
14201
+ mediaKeys: zod.z.array(zod.z.string()).min(1)
14202
+ }), RetrainFrameSelectionSchema, {
14203
+ kind: "mutation",
14204
+ auth: "admin"
14205
+ }),
14206
+ /** Un-select a frame: its annotations go first, then the copy and its blob.
14207
+ * Deliberately destructive and deliberately explicit — it is the only way
14208
+ * a frame leaves the dataset before export. */
14209
+ deselectRetrainFrame: method(zod.z.object({
14210
+ deviceId: zod.z.number(),
14211
+ trackId: zod.z.string(),
14212
+ frameId: zod.z.string()
14213
+ }), zod.z.object({
14214
+ removed: zod.z.boolean(),
14215
+ removedAnnotations: zod.z.number().int()
14216
+ }), {
14217
+ kind: "mutation",
14218
+ auth: "admin"
14219
+ }),
14220
+ /**
14221
+ * The pixels of ONE copied frame, base64.
14222
+ *
14223
+ * Through the cap rather than a data plane because it is genuinely one
14224
+ * frame at a time, on demand, at human speed — the shape D9/D18 permit
14225
+ * (what they forbid is frames crossing a boundary at frame RATE). The
14226
+ * annotation canvas needs the image and its exact dimensions in the same
14227
+ * answer: a canvas that places a normalised box against a size it guessed
14228
+ * draws every box in the wrong place.
14229
+ */
14230
+ getRetrainFrameImage: method(zod.z.object({ frameId: zod.z.string() }), zod.z.object({
14231
+ base64: zod.z.string(),
14232
+ width: zod.z.number().int(),
14233
+ height: zod.z.number().int()
14234
+ }), {
14235
+ kind: "query",
14236
+ auth: "admin"
14237
+ }),
14238
+ /**
14239
+ * Ask the pipeline what it sees, as a PROPOSAL.
14240
+ *
14241
+ * Runs through `pipelineRunner.runStatelessStep` on the COPIED frame, and
14242
+ * every box comes back as a draft with `source: 'assist'` plus the model and
14243
+ * score that produced it. The operator confirms, edits, adds and deletes;
14244
+ * nothing is stored until `saveRetrainAnnotations`.
14245
+ *
14246
+ * For packages the request is `rfdetr-package` on the ZONE CROP at 0.35 —
14247
+ * never the whole frame, where a package detector at that threshold proposes
14248
+ * furniture. A package request with no zone is REFUSED rather than widened,
14249
+ * because the silent widening would look like a bad model for as long as
14250
+ * nobody checked which rectangle it ran on.
14251
+ */
14252
+ proposeRetrainAnnotations: method(zod.z.object({
14253
+ deviceId: zod.z.number(),
14254
+ trackId: zod.z.string(),
14255
+ frameId: zod.z.string(),
14256
+ subject: RetrainAssistSubjectSchema,
14257
+ /** Which node runs it. Absent ⇒ wherever an unowned call lands. */
14258
+ nodeId: zod.z.string().optional()
14259
+ }), RetrainAssistResultSchema, {
14260
+ kind: "mutation",
14261
+ auth: "admin"
14262
+ }),
14263
+ /** Every annotation on a track, oldest first. */
14264
+ listRetrainAnnotations: method(zod.z.object({ trackId: zod.z.string() }), zod.z.array(RetrainAnnotationSchema).readonly(), {
14265
+ kind: "query",
14266
+ auth: "admin"
14267
+ }),
14268
+ /**
14269
+ * Replace EVERY annotation on one frame with the supplied set.
14270
+ *
14271
+ * Whole-frame replacement, not per-box upsert: the unit of ground truth is
14272
+ * the frame, and "the operator deleted a box" must be the same durable
14273
+ * outcome as "the operator never drew it". A per-box patch would let a frame
14274
+ * keep a box the operator removed on a surface that only knew about the
14275
+ * boxes it sent.
14276
+ *
14277
+ * Refuses a macro class typed into `label` or `subLabel` — the tiers are
14278
+ * separate and the guard is at the WRITE, because a mixed taxonomy cannot
14279
+ * be un-mixed by reading it.
14280
+ */
14281
+ saveRetrainAnnotations: method(zod.z.object({
14282
+ deviceId: zod.z.number(),
14283
+ trackId: zod.z.string(),
14284
+ frameId: zod.z.string(),
14285
+ annotations: zod.z.array(RetrainAnnotationDraftSchema)
14286
+ }), zod.z.array(RetrainAnnotationSchema).readonly(), {
14287
+ kind: "mutation",
14288
+ auth: "admin"
14289
+ }),
14290
+ /**
14291
+ * Finish with a track: `staging → trained`. **The only writer of that
14292
+ * state** — D81 shipped the column with it deliberately unreachable.
14293
+ *
14294
+ * Refuses a track the dataset holds no copies from. `trained` un-pins the
14295
+ * track's media, so completing without a copy is a delete order for material
14296
+ * nothing ever extracted anything from; that refusal IS the safety argument
14297
+ * of D81, expressed as a precondition.
14298
+ */
14299
+ completeRetrainTrack: method(zod.z.object({
14300
+ deviceId: zod.z.number(),
14301
+ trackId: zod.z.string()
14302
+ }), RetrainTransitionResultSchema, {
14303
+ kind: "mutation",
14304
+ auth: "admin"
14305
+ }),
14306
+ /**
14307
+ * The deliberate return: `trained → staging`, for the rare case.
14308
+ *
14309
+ * The generic `setTrackFlags` toggle refuses this in both directions by
14310
+ * design (D81) — re-staging from a checkbox is how the same material gets
14311
+ * annotated twice under two ground truths. Doing it here means the operator
14312
+ * is looking at the annotations that already exist while they decide, and
14313
+ * those annotations are LEFT ALONE: "put this back" must not be a
14314
+ * destructive act wearing a navigational name.
14315
+ */
14316
+ restageRetrainTrack: method(zod.z.object({
14317
+ deviceId: zod.z.number(),
14318
+ trackId: zod.z.string()
14319
+ }), RetrainTransitionResultSchema, {
14320
+ kind: "mutation",
14321
+ auth: "admin"
14322
+ }),
14323
+ /**
14324
+ * Where to download the ANNOTATED dataset.
14325
+ *
14326
+ * The sibling of `getTrainingExportUrl` and deliberately not the same
14327
+ * archive: that one streams a marked track's stored media verbatim, this one
14328
+ * streams the retrain COPIES plus an `annotations.json` carrying, for every
14329
+ * subject, the canonical full-frame box AND the geometry derived for each
14330
+ * model shape (letterboxed root / zone-cropped package / subject-cropped
14331
+ * classifier). Derived at export, never stored — one box in, three shapes
14332
+ * out, so two crops of the same subject can never end up in one feature
14333
+ * space (D52).
14334
+ */
14335
+ getRetrainExportUrl: method(zod.z.object({ deviceIds: zod.z.array(zod.z.number()).optional() }), zod.z.object({ url: zod.z.string() }), {
14336
+ kind: "query",
14337
+ auth: "admin"
14338
+ }),
13800
14339
  getEventMedia: method(zod.z.object({
13801
14340
  eventId: zod.z.string(),
13802
14341
  kind: MediaFileKindEnum.optional()
@@ -14614,6 +15153,22 @@ var DetailResultSchema = zod.z.object({
14614
15153
  bbox: NativeCropBboxSchema.optional(),
14615
15154
  embedding: zod.z.string().optional(),
14616
15155
  label: zod.z.string().optional(),
15156
+ /**
15157
+ * The tier `label` occupies, copied VERBATIM from the producing step's
15158
+ * `StepDefinition.labelTier` (roadmap 4g). Present only when `label` is.
15159
+ *
15160
+ * It rides the wire rather than being resolved by the consumer because the
15161
+ * declaration lives with the step definition, which only the executing node
15162
+ * has: post-analysis holds no step registry, and re-deriving the tier from
15163
+ * `className` there would be exactly the inference this model exists to
15164
+ * forbid. A `label` that arrives WITHOUT this field is refused by the write
15165
+ * rule and logged (`label tier undeclared`) — an older runner therefore
15166
+ * stops enriching rather than guessing, which is why addon-pipeline is
15167
+ * deployed BEFORE addon-post-analysis.
15168
+ */
15169
+ labelTier: zod.z.union([zod.z.literal(1), zod.z.literal(2)]).optional(),
15170
+ /** Model that produced `label` — carried into the tier's attribution. */
15171
+ labelModelId: zod.z.string().optional(),
14617
15172
  alignedCropJpeg: zod.z.string().optional(),
14618
15173
  /** Face short side (px) measured on the NATIVE crop surface. The `bbox`
14619
15174
  * above is detection-frame px (≈6× smaller on a 4K camera) — min-face-size
@@ -29171,6 +29726,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
29171
29726
  addonId: null,
29172
29727
  access: "delete"
29173
29728
  },
29729
+ "pipelineAnalytics.completeRetrainTrack": {
29730
+ capName: "pipeline-analytics",
29731
+ capScope: "device",
29732
+ addonId: null,
29733
+ access: "create"
29734
+ },
29174
29735
  "pipelineAnalytics.deleteDeviceEvents": {
29175
29736
  capName: "pipeline-analytics",
29176
29737
  capScope: "device",
@@ -29183,6 +29744,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
29183
29744
  addonId: null,
29184
29745
  access: "delete"
29185
29746
  },
29747
+ "pipelineAnalytics.deselectRetrainFrame": {
29748
+ capName: "pipeline-analytics",
29749
+ capScope: "device",
29750
+ addonId: null,
29751
+ access: "create"
29752
+ },
29186
29753
  "pipelineAnalytics.getActiveTracks": {
29187
29754
  capName: "pipeline-analytics",
29188
29755
  capScope: "device",
@@ -29243,6 +29810,18 @@ var METHOD_ACCESS_MAP = Object.freeze({
29243
29810
  addonId: null,
29244
29811
  access: "view"
29245
29812
  },
29813
+ "pipelineAnalytics.getRetrainExportUrl": {
29814
+ capName: "pipeline-analytics",
29815
+ capScope: "device",
29816
+ addonId: null,
29817
+ access: "view"
29818
+ },
29819
+ "pipelineAnalytics.getRetrainFrameImage": {
29820
+ capName: "pipeline-analytics",
29821
+ capScope: "device",
29822
+ addonId: null,
29823
+ access: "view"
29824
+ },
29246
29825
  "pipelineAnalytics.getSensorEvents": {
29247
29826
  capName: "pipeline-analytics",
29248
29827
  capScope: "device",
@@ -29297,6 +29876,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
29297
29876
  addonId: null,
29298
29877
  access: "view"
29299
29878
  },
29879
+ "pipelineAnalytics.listRetrainAnnotations": {
29880
+ capName: "pipeline-analytics",
29881
+ capScope: "device",
29882
+ addonId: null,
29883
+ access: "view"
29884
+ },
29885
+ "pipelineAnalytics.listRetrainFrames": {
29886
+ capName: "pipeline-analytics",
29887
+ capScope: "device",
29888
+ addonId: null,
29889
+ access: "view"
29890
+ },
29891
+ "pipelineAnalytics.listRetrainStaging": {
29892
+ capName: "pipeline-analytics",
29893
+ capScope: "device",
29894
+ addonId: null,
29895
+ access: "view"
29896
+ },
29300
29897
  "pipelineAnalytics.listTrackMedia": {
29301
29898
  capName: "pipeline-analytics",
29302
29899
  capScope: "device",
@@ -29309,6 +29906,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
29309
29906
  addonId: null,
29310
29907
  access: "view"
29311
29908
  },
29909
+ "pipelineAnalytics.proposeRetrainAnnotations": {
29910
+ capName: "pipeline-analytics",
29911
+ capScope: "device",
29912
+ addonId: null,
29913
+ access: "create"
29914
+ },
29312
29915
  "pipelineAnalytics.pruneEvents": {
29313
29916
  capName: "pipeline-analytics",
29314
29917
  capScope: "device",
@@ -29339,12 +29942,30 @@ var METHOD_ACCESS_MAP = Object.freeze({
29339
29942
  addonId: null,
29340
29943
  access: "create"
29341
29944
  },
29945
+ "pipelineAnalytics.restageRetrainTrack": {
29946
+ capName: "pipeline-analytics",
29947
+ capScope: "device",
29948
+ addonId: null,
29949
+ access: "create"
29950
+ },
29951
+ "pipelineAnalytics.saveRetrainAnnotations": {
29952
+ capName: "pipeline-analytics",
29953
+ capScope: "device",
29954
+ addonId: null,
29955
+ access: "create"
29956
+ },
29342
29957
  "pipelineAnalytics.searchObjectEvents": {
29343
29958
  capName: "pipeline-analytics",
29344
29959
  capScope: "device",
29345
29960
  addonId: null,
29346
29961
  access: "view"
29347
29962
  },
29963
+ "pipelineAnalytics.selectRetrainFrames": {
29964
+ capName: "pipeline-analytics",
29965
+ capScope: "device",
29966
+ addonId: null,
29967
+ access: "create"
29968
+ },
29348
29969
  "pipelineAnalytics.setTrackFlags": {
29349
29970
  capName: "pipeline-analytics",
29350
29971
  capScope: "device",
@@ -30737,6 +31358,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30737
31358
  addonId: null,
30738
31359
  access: "create"
30739
31360
  },
31361
+ "streamBroker.fetchEventMedia": {
31362
+ capName: "stream-broker",
31363
+ capScope: "system",
31364
+ addonId: null,
31365
+ access: "create"
31366
+ },
30740
31367
  "streamBroker.getAllRtspEntries": {
30741
31368
  capName: "stream-broker",
30742
31369
  capScope: "system",
@@ -30821,6 +31448,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
30821
31448
  addonId: null,
30822
31449
  access: "create"
30823
31450
  },
31451
+ "streamBroker.produceEventMedia": {
31452
+ capName: "stream-broker",
31453
+ capScope: "system",
31454
+ addonId: null,
31455
+ access: "create"
31456
+ },
30824
31457
  "streamBroker.publishCameraStream": {
30825
31458
  capName: "stream-broker",
30826
31459
  capScope: "system",