@camstack/addon-pipeline 1.1.52 → 1.1.54

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 (37) hide show
  1. package/dist/audio-analyzer/index.js +1 -1
  2. package/dist/audio-analyzer/index.mjs +1 -1
  3. package/dist/detection-pipeline/index.js +191 -12
  4. package/dist/detection-pipeline/index.mjs +191 -12
  5. package/dist/{dist-B-pQhc30.js → dist-BbaoC680.js} +111 -7
  6. package/dist/{dist-DM6-eQRs.mjs → dist-CEcTeu1h.mjs} +111 -7
  7. package/dist/motion-wasm/index.js +1 -1
  8. package/dist/motion-wasm/index.mjs +1 -1
  9. package/dist/pipeline-runner/index.js +114 -5
  10. package/dist/pipeline-runner/index.mjs +114 -5
  11. package/dist/recorder/index.js +1 -1
  12. package/dist/recorder/index.mjs +1 -1
  13. package/dist/{remote-source-plane-DivMEw6Y.mjs → remote-source-plane-CNCqC_XG.mjs} +1 -1
  14. package/dist/{remote-source-plane-dSe5NlH9.js → remote-source-plane-vN45KFSm.js} +1 -1
  15. package/dist/session-decode/decode-worker-child.js +319 -35
  16. package/dist/session-decode/decode-worker-child.mjs +319 -35
  17. package/dist/stream-broker/_stub.js +2 -2
  18. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-D6UvBYwr.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-HS0tc6Sa.mjs} +3 -3
  19. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-B3gTdHEh.mjs +26 -0
  20. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-C9fwKMfg.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-B_v5r5ya.mjs} +1 -1
  21. package/dist/stream-broker/{hostInit-DpF_eilj.mjs → hostInit-DgohfEDA.mjs} +3 -3
  22. package/dist/stream-broker/index.js +2 -2
  23. package/dist/stream-broker/index.mjs +2 -2
  24. package/dist/stream-broker/remoteEntry.js +1 -1
  25. package/dist/{worker-protocol-pk7qdYXt.mjs → worker-protocol-DGIt_waM.mjs} +6 -0
  26. package/dist/{worker-protocol-BCfO8gUF.js → worker-protocol-DkL6GDxe.js} +6 -0
  27. package/embed-dist/assets/{MaskShapeCanvas-DI4BY7W2-DRCvljqU.js → MaskShapeCanvas-DI4BY7W2-CA2Hd6xx.js} +1 -1
  28. package/embed-dist/assets/{MotionZonesSettings-NcxxQN8r-DmQAEfWC.js → MotionZonesSettings-NcxxQN8r-DUN1VeC7.js} +1 -1
  29. package/embed-dist/assets/{PrivacyMaskSettings-APgPLF7p-DNJ5RGGN.js → PrivacyMaskSettings-APgPLF7p-BqZIAU5s.js} +1 -1
  30. package/embed-dist/assets/index-CC06JBcl.css +2 -0
  31. package/embed-dist/assets/{index-BHKG2Pw-.js → index-CMkYypxB.js} +10 -10
  32. package/embed-dist/index.html +2 -2
  33. package/package.json +1 -1
  34. package/python/inference_pool.py +42 -1
  35. package/python/test_inference_pool_layout.py +44 -0
  36. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-o4tu_xuc.mjs +0 -26
  37. package/embed-dist/assets/index-JqUY2p33.css +0 -2
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-b4Jf2n33.mjs
4630
+ //#region ../types/dist/sleep-BC9Yqte7.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -11420,6 +11420,7 @@ var PipelineAddonSchemaSchema = object({
11420
11420
  defaultModelId: string(),
11421
11421
  defaultModelIdByFormat: record(string(), string()).optional(),
11422
11422
  enabledByDefault: boolean().optional(),
11423
+ backfillIntoExistingOverrides: boolean().optional(),
11423
11424
  defaultConfidence: number(),
11424
11425
  group: string().optional(),
11425
11426
  configSchema: array(ConfigFieldBridge).readonly().optional()
@@ -11889,6 +11890,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).read
11889
11890
  auth: "admin"
11890
11891
  }), object({ zones: array(ZoneSchema).readonly() });
11891
11892
  /**
11893
+ * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
11894
+ * decode worker resolves it against the RETAINED native frame's real pixel dims,
11895
+ * so the caller supplies only the detection-res bbox divided by the detection
11896
+ * dims — no native resolution to plumb.
11897
+ */
11898
+ var NativeCropBboxSchema = object({
11899
+ x: number(),
11900
+ y: number(),
11901
+ w: number(),
11902
+ h: number()
11903
+ });
11904
+ /** Result of a best-effort native-resolution crop (`getNativeCrop`). */
11905
+ var NativeCropResultSchema = object({
11906
+ /** Packed rgb (24-bit) pixels of the crop. */
11907
+ bytes: _instanceof(Uint8Array),
11908
+ width: number().int().positive(),
11909
+ height: number().int().positive()
11910
+ });
11911
+ /**
11892
11912
  * Per-camera tunable ranges + defaults. Single source of truth used
11893
11913
  * by both the Zod data schema (validation + default fallback) and
11894
11914
  * the device settings UI (slider min/max/step). Touch one place and
@@ -12192,7 +12212,22 @@ var pipelineRunnerCapability = {
12192
12212
  /** All per-camera metrics in one round-trip. */
12193
12213
  getAllCameraMetrics: method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()),
12194
12214
  /** List the deviceIds currently attached to this runner. */
12195
- getLocalCameras: method(_void(), array(number()).readonly())
12215
+ getLocalCameras: method(_void(), array(number()).readonly()),
12216
+ /**
12217
+ * Best-effort NATIVE-resolution crop of a retention-ring frame. Given the
12218
+ * `FrameHandle` that rode an inference-result event and a normalized `bbox`,
12219
+ * the runner asks the decode worker still holding that frame's NATIVE
12220
+ * surface to GPU/CPU-crop ONLY the ROI at native res and download just the
12221
+ * crop. Returns `null` on a miss (handle not registered, or the worker's
12222
+ * tiny native-retention ring already evicted the frame) — the caller falls
12223
+ * back to a detection-frame crop. Routed to the frame's owning node by
12224
+ * `handle.nodeId`; NEVER ships a full native frame.
12225
+ */
12226
+ getNativeCrop: method(object({
12227
+ handle: FrameHandleSchema,
12228
+ bbox: NativeCropBboxSchema,
12229
+ maxWidth: number().int().positive().optional()
12230
+ }), NativeCropResultSchema.nullable())
12196
12231
  }
12197
12232
  };
12198
12233
  object({
@@ -16442,7 +16477,17 @@ var TrackSchema = object({
16442
16477
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
16443
16478
  totalDistance: number(),
16444
16479
  state: TrackStateSchema,
16445
- active: boolean()
16480
+ active: boolean(),
16481
+ /** Deterministic key-event importance score in [0,1] (server-computed at
16482
+ * track expiry, recomputed on late label). Absent on legacy rows written
16483
+ * before scoring shipped — consumers degrade to absence / compute-on-read. */
16484
+ importance: number().optional(),
16485
+ /** Id of the track's highest-confidence ObjectEvent (its representative
16486
+ * "best" frame). Absent when the track produced no object events. */
16487
+ bestEventId: string().optional(),
16488
+ /** Tag of the importance sub-signal that dominated the score
16489
+ * (identity|dwell|proximity|class|confidence|travel|zone). */
16490
+ importanceReason: string().optional()
16446
16491
  });
16447
16492
  var BaseEventFields = {
16448
16493
  id: string(),
@@ -16507,8 +16552,18 @@ var ObjectEventSchema = object({
16507
16552
  frameHeight: number().optional(),
16508
16553
  /** MediaStore key for the crop attached to this event (if any). */
16509
16554
  mediaKey: string().optional(),
16555
+ /** Design B: MediaStore key of the track's native-resolution key frame (the
16556
+ * best-detection full frame). Resolve via the event-media data-plane
16557
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
16558
+ * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
16559
+ * sources — consumers fall back to `mediaKey` (the tight crop). */
16560
+ keyFrameMediaKey: string().optional(),
16510
16561
  /** Populated by B5 (recording playback URL for this event). */
16511
- mediaUrl: string().optional()
16562
+ mediaUrl: string().optional(),
16563
+ /** The parent track's key-event importance [0,1], propagated to every object
16564
+ * event of the track (so an event row can be sorted by importance without a
16565
+ * track join). Absent on legacy rows / before the track was scored. */
16566
+ importance: number().optional()
16512
16567
  });
16513
16568
  var AudioEventSchema = object({
16514
16569
  ...BaseEventFields,
@@ -16532,7 +16587,8 @@ var MediaFileKindEnum = _enum([
16532
16587
  "fullFrame",
16533
16588
  "fullFrameBoxed",
16534
16589
  "faceCrop",
16535
- "plateCrop"
16590
+ "plateCrop",
16591
+ "keyFrame"
16536
16592
  ]);
16537
16593
  var MediaFileSchema = object({
16538
16594
  key: string(),
@@ -16553,6 +16609,32 @@ var DeviceEventQueryInput = object({
16553
16609
  projection: _enum(["full", "slim"]).optional()
16554
16610
  });
16555
16611
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16612
+ var KeyEventQueryInput = object({
16613
+ deviceId: number(),
16614
+ /** Window lower bound (track firstSeen ≥ since). */
16615
+ since: number(),
16616
+ /** Window upper bound (track firstSeen ≤ until). */
16617
+ until: number(),
16618
+ limit: number().int().min(1).max(200).default(50),
16619
+ /** Drop tracks scoring below this importance. */
16620
+ minImportance: number().min(0).max(1).optional(),
16621
+ /** Restrict to a single class (e.g. 'person'). */
16622
+ classFilter: string().optional()
16623
+ });
16624
+ var KeyEventSchema = object({
16625
+ /** The representative event id (the track's best ObjectEvent, else its trackId). */
16626
+ id: string(),
16627
+ trackId: string(),
16628
+ /** Track start time (firstSeen). */
16629
+ timestamp: number(),
16630
+ className: string(),
16631
+ label: string().optional(),
16632
+ importance: number(),
16633
+ /** Highest-confidence ObjectEvent id for the track (empty when none). */
16634
+ bestEventId: string(),
16635
+ /** Track lifetime in ms (lastSeen - firstSeen). */
16636
+ windowMs: number().optional()
16637
+ });
16556
16638
  var TrackedDetectionSchema = object({
16557
16639
  trackId: string(),
16558
16640
  className: string(),
@@ -16582,7 +16664,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16582
16664
  }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16583
16665
  kind: "mutation",
16584
16666
  auth: "admin"
16585
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
16667
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16586
16668
  deviceId: number(),
16587
16669
  since: number(),
16588
16670
  until: number(),
@@ -18594,7 +18676,17 @@ var FaceInfoSchema = object({
18594
18676
  recognizedIdentityId: string().optional(),
18595
18677
  identityName: string().optional(),
18596
18678
  assigned: boolean(),
18597
- base64: string().optional()
18679
+ base64: string().optional(),
18680
+ /** Design B: the face bbox (pixel space) on the key frame — lets a detail
18681
+ * view draw the box over the native `keyFrameMediaKey` frame. Absent on
18682
+ * legacy rows written before design B. */
18683
+ faceBbox: BoundingBoxSchema.optional(),
18684
+ /** Design B: MediaStore key of the track's native-resolution key frame.
18685
+ * Fetch the native JPEG via the event-media data-plane
18686
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
18687
+ * track produced no key frame (e.g. native/onboard source) — the UI falls
18688
+ * back to the inline `base64` face crop. */
18689
+ keyFrameMediaKey: string().optional()
18598
18690
  });
18599
18691
  var FaceFilterEnum = _enum([
18600
18692
  "unassigned",
@@ -22746,6 +22838,12 @@ Object.freeze({
22746
22838
  addonId: null,
22747
22839
  access: "view"
22748
22840
  },
22841
+ "pipelineAnalytics.getKeyEvents": {
22842
+ capName: "pipeline-analytics",
22843
+ capScope: "device",
22844
+ addonId: null,
22845
+ access: "view"
22846
+ },
22749
22847
  "pipelineAnalytics.getMotionEvents": {
22750
22848
  capName: "pipeline-analytics",
22751
22849
  capScope: "device",
@@ -23274,6 +23372,12 @@ Object.freeze({
23274
23372
  addonId: null,
23275
23373
  access: "view"
23276
23374
  },
23375
+ "pipelineRunner.getNativeCrop": {
23376
+ capName: "pipeline-runner",
23377
+ capScope: "system",
23378
+ addonId: null,
23379
+ access: "view"
23380
+ },
23277
23381
  "pipelineRunner.reportMotion": {
23278
23382
  capName: "pipeline-runner",
23279
23383
  capScope: "system",
@@ -4627,7 +4627,7 @@ function _instanceof(cls, params = {}) {
4627
4627
  return inst;
4628
4628
  }
4629
4629
  //#endregion
4630
- //#region ../types/dist/sleep-b4Jf2n33.mjs
4630
+ //#region ../types/dist/sleep-BC9Yqte7.mjs
4631
4631
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4632
4632
  EventCategory["SystemBoot"] = "system.boot";
4633
4633
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -11420,6 +11420,7 @@ var PipelineAddonSchemaSchema = object({
11420
11420
  defaultModelId: string(),
11421
11421
  defaultModelIdByFormat: record(string(), string()).optional(),
11422
11422
  enabledByDefault: boolean().optional(),
11423
+ backfillIntoExistingOverrides: boolean().optional(),
11423
11424
  defaultConfidence: number(),
11424
11425
  group: string().optional(),
11425
11426
  configSchema: array(ConfigFieldBridge).readonly().optional()
@@ -11889,6 +11890,25 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(ZoneSchema).read
11889
11890
  auth: "admin"
11890
11891
  }), object({ zones: array(ZoneSchema).readonly() });
11891
11892
  /**
11893
+ * A bounding box in NORMALIZED [0,1] frame coordinates for `getNativeCrop`. The
11894
+ * decode worker resolves it against the RETAINED native frame's real pixel dims,
11895
+ * so the caller supplies only the detection-res bbox divided by the detection
11896
+ * dims — no native resolution to plumb.
11897
+ */
11898
+ var NativeCropBboxSchema = object({
11899
+ x: number(),
11900
+ y: number(),
11901
+ w: number(),
11902
+ h: number()
11903
+ });
11904
+ /** Result of a best-effort native-resolution crop (`getNativeCrop`). */
11905
+ var NativeCropResultSchema = object({
11906
+ /** Packed rgb (24-bit) pixels of the crop. */
11907
+ bytes: _instanceof(Uint8Array),
11908
+ width: number().int().positive(),
11909
+ height: number().int().positive()
11910
+ });
11911
+ /**
11892
11912
  * Per-camera tunable ranges + defaults. Single source of truth used
11893
11913
  * by both the Zod data schema (validation + default fallback) and
11894
11914
  * the device settings UI (slider min/max/step). Touch one place and
@@ -12192,7 +12212,22 @@ var pipelineRunnerCapability = {
12192
12212
  /** All per-camera metrics in one round-trip. */
12193
12213
  getAllCameraMetrics: method(_void(), array(CameraMetricsWithDeviceIdSchema).readonly()),
12194
12214
  /** List the deviceIds currently attached to this runner. */
12195
- getLocalCameras: method(_void(), array(number()).readonly())
12215
+ getLocalCameras: method(_void(), array(number()).readonly()),
12216
+ /**
12217
+ * Best-effort NATIVE-resolution crop of a retention-ring frame. Given the
12218
+ * `FrameHandle` that rode an inference-result event and a normalized `bbox`,
12219
+ * the runner asks the decode worker still holding that frame's NATIVE
12220
+ * surface to GPU/CPU-crop ONLY the ROI at native res and download just the
12221
+ * crop. Returns `null` on a miss (handle not registered, or the worker's
12222
+ * tiny native-retention ring already evicted the frame) — the caller falls
12223
+ * back to a detection-frame crop. Routed to the frame's owning node by
12224
+ * `handle.nodeId`; NEVER ships a full native frame.
12225
+ */
12226
+ getNativeCrop: method(object({
12227
+ handle: FrameHandleSchema,
12228
+ bbox: NativeCropBboxSchema,
12229
+ maxWidth: number().int().positive().optional()
12230
+ }), NativeCropResultSchema.nullable())
12196
12231
  }
12197
12232
  };
12198
12233
  object({
@@ -16442,7 +16477,17 @@ var TrackSchema = object({
16442
16477
  /** Cumulative normalized distance travelled (0..1 units = full frame width). */
16443
16478
  totalDistance: number(),
16444
16479
  state: TrackStateSchema,
16445
- active: boolean()
16480
+ active: boolean(),
16481
+ /** Deterministic key-event importance score in [0,1] (server-computed at
16482
+ * track expiry, recomputed on late label). Absent on legacy rows written
16483
+ * before scoring shipped — consumers degrade to absence / compute-on-read. */
16484
+ importance: number().optional(),
16485
+ /** Id of the track's highest-confidence ObjectEvent (its representative
16486
+ * "best" frame). Absent when the track produced no object events. */
16487
+ bestEventId: string().optional(),
16488
+ /** Tag of the importance sub-signal that dominated the score
16489
+ * (identity|dwell|proximity|class|confidence|travel|zone). */
16490
+ importanceReason: string().optional()
16446
16491
  });
16447
16492
  var BaseEventFields = {
16448
16493
  id: string(),
@@ -16507,8 +16552,18 @@ var ObjectEventSchema = object({
16507
16552
  frameHeight: number().optional(),
16508
16553
  /** MediaStore key for the crop attached to this event (if any). */
16509
16554
  mediaKey: string().optional(),
16555
+ /** Design B: MediaStore key of the track's native-resolution key frame (the
16556
+ * best-detection full frame). Resolve via the event-media data-plane
16557
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`) for a detail view that
16558
+ * draws `bbox` over the native frame. Absent on legacy rows / non-decoded
16559
+ * sources — consumers fall back to `mediaKey` (the tight crop). */
16560
+ keyFrameMediaKey: string().optional(),
16510
16561
  /** Populated by B5 (recording playback URL for this event). */
16511
- mediaUrl: string().optional()
16562
+ mediaUrl: string().optional(),
16563
+ /** The parent track's key-event importance [0,1], propagated to every object
16564
+ * event of the track (so an event row can be sorted by importance without a
16565
+ * track join). Absent on legacy rows / before the track was scored. */
16566
+ importance: number().optional()
16512
16567
  });
16513
16568
  var AudioEventSchema = object({
16514
16569
  ...BaseEventFields,
@@ -16532,7 +16587,8 @@ var MediaFileKindEnum = _enum([
16532
16587
  "fullFrame",
16533
16588
  "fullFrameBoxed",
16534
16589
  "faceCrop",
16535
- "plateCrop"
16590
+ "plateCrop",
16591
+ "keyFrame"
16536
16592
  ]);
16537
16593
  var MediaFileSchema = object({
16538
16594
  key: string(),
@@ -16553,6 +16609,32 @@ var DeviceEventQueryInput = object({
16553
16609
  projection: _enum(["full", "slim"]).optional()
16554
16610
  });
16555
16611
  var ObjectEventQueryInput = DeviceEventQueryInput.extend({ classFilter: string().optional() });
16612
+ var KeyEventQueryInput = object({
16613
+ deviceId: number(),
16614
+ /** Window lower bound (track firstSeen ≥ since). */
16615
+ since: number(),
16616
+ /** Window upper bound (track firstSeen ≤ until). */
16617
+ until: number(),
16618
+ limit: number().int().min(1).max(200).default(50),
16619
+ /** Drop tracks scoring below this importance. */
16620
+ minImportance: number().min(0).max(1).optional(),
16621
+ /** Restrict to a single class (e.g. 'person'). */
16622
+ classFilter: string().optional()
16623
+ });
16624
+ var KeyEventSchema = object({
16625
+ /** The representative event id (the track's best ObjectEvent, else its trackId). */
16626
+ id: string(),
16627
+ trackId: string(),
16628
+ /** Track start time (firstSeen). */
16629
+ timestamp: number(),
16630
+ className: string(),
16631
+ label: string().optional(),
16632
+ importance: number(),
16633
+ /** Highest-confidence ObjectEvent id for the track (empty when none). */
16634
+ bestEventId: string(),
16635
+ /** Track lifetime in ms (lastSeen - firstSeen). */
16636
+ windowMs: number().optional()
16637
+ });
16556
16638
  var TrackedDetectionSchema = object({
16557
16639
  trackId: string(),
16558
16640
  className: string(),
@@ -16582,7 +16664,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
16582
16664
  }), array(TrackSchema).readonly()), method(object({ deviceId: number() }), _void(), {
16583
16665
  kind: "mutation",
16584
16666
  auth: "admin"
16585
- }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(object({
16667
+ }), method(DeviceEventQueryInput, array(MotionEventSchema).readonly()), method(ObjectEventQueryInput, array(ObjectEventSchema).readonly()), method(DeviceEventQueryInput, array(AudioEventSchema).readonly()), method(KeyEventQueryInput, array(KeyEventSchema).readonly()), method(object({
16586
16668
  deviceId: number(),
16587
16669
  since: number(),
16588
16670
  until: number(),
@@ -18594,7 +18676,17 @@ var FaceInfoSchema = object({
18594
18676
  recognizedIdentityId: string().optional(),
18595
18677
  identityName: string().optional(),
18596
18678
  assigned: boolean(),
18597
- base64: string().optional()
18679
+ base64: string().optional(),
18680
+ /** Design B: the face bbox (pixel space) on the key frame — lets a detail
18681
+ * view draw the box over the native `keyFrameMediaKey` frame. Absent on
18682
+ * legacy rows written before design B. */
18683
+ faceBbox: BoundingBoxSchema.optional(),
18684
+ /** Design B: MediaStore key of the track's native-resolution key frame.
18685
+ * Fetch the native JPEG via the event-media data-plane
18686
+ * (`/addon/<addonId>/event-media/<keyFrameMediaKey>`). Absent when the
18687
+ * track produced no key frame (e.g. native/onboard source) — the UI falls
18688
+ * back to the inline `base64` face crop. */
18689
+ keyFrameMediaKey: string().optional()
18598
18690
  });
18599
18691
  var FaceFilterEnum = _enum([
18600
18692
  "unassigned",
@@ -22746,6 +22838,12 @@ Object.freeze({
22746
22838
  addonId: null,
22747
22839
  access: "view"
22748
22840
  },
22841
+ "pipelineAnalytics.getKeyEvents": {
22842
+ capName: "pipeline-analytics",
22843
+ capScope: "device",
22844
+ addonId: null,
22845
+ access: "view"
22846
+ },
22749
22847
  "pipelineAnalytics.getMotionEvents": {
22750
22848
  capName: "pipeline-analytics",
22751
22849
  capScope: "device",
@@ -23274,6 +23372,12 @@ Object.freeze({
23274
23372
  addonId: null,
23275
23373
  access: "view"
23276
23374
  },
23375
+ "pipelineRunner.getNativeCrop": {
23376
+ capName: "pipeline-runner",
23377
+ capScope: "system",
23378
+ addonId: null,
23379
+ access: "view"
23380
+ },
23277
23381
  "pipelineRunner.reportMotion": {
23278
23382
  capName: "pipeline-runner",
23279
23383
  capScope: "system",
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-B-pQhc30.js");
5
+ const require_dist = require("../dist-BbaoC680.js");
6
6
  let _camstack_shm_ring = require("@camstack/shm-ring");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { B as DeviceType, E as motionDetectionCapability, L as BaseAddon, U as hydrateSchema, x as evaluateZoneRules } from "../dist-DM6-eQRs.mjs";
1
+ import { B as DeviceType, E as motionDetectionCapability, L as BaseAddon, U as hydrateSchema, x as evaluateZoneRules } from "../dist-CEcTeu1h.mjs";
2
2
  import { FrameRingReaderCache } from "@camstack/shm-ring";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -2,10 +2,10 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-B-pQhc30.js");
6
- const require_remote_source_plane = require("../remote-source-plane-dSe5NlH9.js");
5
+ const require_dist = require("../dist-BbaoC680.js");
6
+ const require_remote_source_plane = require("../remote-source-plane-vN45KFSm.js");
7
7
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
8
- const require_worker_protocol = require("../worker-protocol-BCfO8gUF.js");
8
+ const require_worker_protocol = require("../worker-protocol-DkL6GDxe.js");
9
9
  let _camstack_shm_ring = require("@camstack/shm-ring");
10
10
  let node_child_process = require("node:child_process");
11
11
  let node_url = require("node:url");
@@ -1455,6 +1455,9 @@ var SessionDecodeSession = class {
1455
1455
  frameTimeoutMs;
1456
1456
  pendingPull = null;
1457
1457
  pendingToBuffers = /* @__PURE__ */ new Map();
1458
+ /** Outstanding `nativeCrop` requests, keyed by the monotonic `requestId`. */
1459
+ pendingNativeCrops = /* @__PURE__ */ new Map();
1460
+ nextNativeCropId = 1;
1458
1461
  exited = false;
1459
1462
  sigkillTimer = null;
1460
1463
  pullTimeoutTimer = null;
@@ -1519,6 +1522,30 @@ var SessionDecodeSession = class {
1519
1522
  }
1520
1523
  });
1521
1524
  }
1525
+ /**
1526
+ * Send `nativeCrop` for `frameId` and resolve with the crop result, or `null`
1527
+ * on any miss (frame aged out, worker error, or worker already exited). Never
1528
+ * rejects — native crops are strictly best-effort with a free fallback.
1529
+ */
1530
+ requestNativeCrop(frameId, bbox, maxWidth) {
1531
+ if (this.exited) return Promise.resolve(null);
1532
+ const requestId = this.nextNativeCropId++;
1533
+ return new Promise((resolve) => {
1534
+ this.pendingNativeCrops.set(requestId, { resolve });
1535
+ try {
1536
+ this.child.send({
1537
+ kind: "nativeCrop",
1538
+ requestId,
1539
+ frameId,
1540
+ bbox,
1541
+ ...maxWidth !== void 0 ? { maxWidth } : {}
1542
+ });
1543
+ } catch {
1544
+ this.pendingNativeCrops.delete(requestId);
1545
+ resolve(null);
1546
+ }
1547
+ });
1548
+ }
1522
1549
  /** Teardown: `stop`, `kill()`, arm the SIGKILL fallback unless already exited. */
1523
1550
  teardown() {
1524
1551
  this.clearPullTimeout();
@@ -1551,6 +1578,16 @@ var SessionDecodeSession = class {
1551
1578
  case "buffer":
1552
1579
  this.settleToBuffer(raw.frameId, raw.bytes);
1553
1580
  return;
1581
+ case "nativeCropResult":
1582
+ this.settleNativeCrop(raw.requestId, {
1583
+ bytes: raw.bytes,
1584
+ width: raw.width,
1585
+ height: raw.height
1586
+ });
1587
+ return;
1588
+ case "nativeCropMiss":
1589
+ this.settleNativeCrop(raw.requestId, null);
1590
+ return;
1554
1591
  case "error":
1555
1592
  this.settleError(raw.message, raw.frameId);
1556
1593
  return;
@@ -1570,6 +1607,15 @@ var SessionDecodeSession = class {
1570
1607
  }
1571
1608
  for (const waiters of this.pendingToBuffers.values()) for (const waiter of waiters) waiter.reject(/* @__PURE__ */ new Error("session-decode-coordinator: worker exited before toBuffer reply"));
1572
1609
  this.pendingToBuffers.clear();
1610
+ for (const waiter of this.pendingNativeCrops.values()) waiter.resolve(null);
1611
+ this.pendingNativeCrops.clear();
1612
+ }
1613
+ /** Settle the `nativeCrop` waiter for `requestId` (dropping unknown/stale ids). */
1614
+ settleNativeCrop(requestId, result) {
1615
+ const waiter = this.pendingNativeCrops.get(requestId);
1616
+ if (!waiter) return;
1617
+ this.pendingNativeCrops.delete(requestId);
1618
+ waiter.resolve(result);
1573
1619
  }
1574
1620
  settlePendingPull(outcome) {
1575
1621
  const waiter = this.pendingPull;
@@ -1701,10 +1747,12 @@ function buildFrameImage(session, reply) {
1701
1747
  width: reply.width,
1702
1748
  height: reply.height,
1703
1749
  format: reply.format,
1750
+ frameId: reply.frameId,
1704
1751
  toBuffer: async (opts) => {
1705
1752
  if (closed) throw new Error(`session-decode-coordinator: toBuffer() called on a closed frame (frameId=${reply.frameId})`);
1706
1753
  return session.requestBuffer(reply.frameId, opts);
1707
1754
  },
1755
+ nativeCrop: (bbox, maxWidth) => session.requestNativeCrop(reply.frameId, bbox, maxWidth),
1708
1756
  close: () => {
1709
1757
  closed = true;
1710
1758
  }
@@ -1845,7 +1893,9 @@ function startSessionDecodePump(deps) {
1845
1893
  }
1846
1894
  try {
1847
1895
  const decoded = await toDecodedFrame(frame, format);
1848
- deps.onDecodedFrame(decoded);
1896
+ const image = frame.image;
1897
+ const nativeCrop = image.nativeCrop ? (bbox, maxWidth) => image.nativeCrop(bbox, maxWidth) : void 0;
1898
+ deps.onDecodedFrame(decoded, nativeCrop);
1849
1899
  } catch (err) {
1850
1900
  deps.logger.debug("session-decode pump: skipped a frame (decode/consume error)", {
1851
1901
  tags: { deviceId: deps.source.deviceId },
@@ -1878,6 +1928,40 @@ function startSessionDecodePump(deps) {
1878
1928
  iterator.return().catch(() => {});
1879
1929
  };
1880
1930
  }
1931
+ var NativeCropRegistry = class {
1932
+ capacity;
1933
+ entries = /* @__PURE__ */ new Map();
1934
+ constructor(capacity = 64) {
1935
+ this.capacity = capacity;
1936
+ }
1937
+ /** Number of registered handles (tests / metrics). */
1938
+ get size() {
1939
+ return this.entries.size;
1940
+ }
1941
+ /** A retention frame is uniquely identified by its segment + slot + committed seq. */
1942
+ key(handle) {
1943
+ return `${handle.shmId}#${handle.slot}#${handle.seq}`;
1944
+ }
1945
+ /** Bind `request` to `handle`; evicts the oldest entry past the cap. */
1946
+ register(handle, request) {
1947
+ const k = this.key(handle);
1948
+ this.entries.delete(k);
1949
+ this.entries.set(k, request);
1950
+ while (this.entries.size > this.capacity) {
1951
+ const oldest = this.entries.keys().next().value;
1952
+ if (oldest === void 0) break;
1953
+ this.entries.delete(oldest);
1954
+ }
1955
+ }
1956
+ /** The requester for `handle`, or `null` if never registered / already evicted. */
1957
+ get(handle) {
1958
+ return this.entries.get(this.key(handle)) ?? null;
1959
+ }
1960
+ /** Drop every entry (node/addon shutdown). */
1961
+ clear() {
1962
+ this.entries.clear();
1963
+ }
1964
+ };
1881
1965
  //#endregion
1882
1966
  //#region src/pipeline-runner/index.ts
1883
1967
  var DEFAULT_CONFIG = {
@@ -2102,6 +2186,13 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2102
2186
  * `resize()`d live in `onConfigChanged`. Null until `onInitialize`.
2103
2187
  */
2104
2188
  spawnGate = null;
2189
+ /**
2190
+ * Node-level map from a retention-ring `FrameHandle` to the decode worker
2191
+ * still holding that frame's NATIVE surface — backs the `getNativeCrop` cap
2192
+ * so post-analysis can cut native-res crops (best-effort; a miss falls back
2193
+ * to the detection-frame crop). See {@link NativeCropRegistry}.
2194
+ */
2195
+ nativeCropRegistry = new NativeCropRegistry();
2105
2196
  attached = /* @__PURE__ */ new Map();
2106
2197
  nodeId = "unknown";
2107
2198
  metricsSnapshotTimer = null;
@@ -2671,6 +2762,23 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2671
2762
  this.runner?.reportMotion(input.deviceId, input.detected, input.source, input.regions);
2672
2763
  return { success: true };
2673
2764
  }
2765
+ /**
2766
+ * Best-effort native-resolution crop for a retention-ring frame (native-crop
2767
+ * feature). Looks the handle up in {@link nativeCropRegistry}; a miss (never
2768
+ * registered, or the worker's tiny native ring already evicted the frame)
2769
+ * returns `null` so the caller falls back to a detection-frame crop. The
2770
+ * worker downloads ONLY the ROI — a full native frame never travels the wire.
2771
+ */
2772
+ async getNativeCrop(input) {
2773
+ const requester = this.nativeCropRegistry.get(input.handle);
2774
+ if (!requester) return null;
2775
+ const result = await requester(input.bbox, input.maxWidth);
2776
+ return result ? {
2777
+ bytes: result.bytes,
2778
+ width: result.width,
2779
+ height: result.height
2780
+ } : null;
2781
+ }
2674
2782
  detachInternal(deviceId) {
2675
2783
  const attachment = this.attached.get(deviceId);
2676
2784
  if (!attachment) return;
@@ -2990,8 +3098,9 @@ var PipelineRunnerAddon = class extends require_dist.BaseAddon {
2990
3098
  format: "rgb"
2991
3099
  },
2992
3100
  acquireSpawnSlot: this.buildDecodeSpawnAcquire(log, "detect"),
2993
- onDecodedFrame: (frame) => {
3101
+ onDecodedFrame: (frame, nativeCrop) => {
2994
3102
  const handle = retentionSink.write(frame) ?? void 0;
3103
+ if (handle && nativeCrop) this.nativeCropRegistry.register(handle, nativeCrop);
2995
3104
  this.enqueueSharedDetectionFrame(config.deviceId, frame, handle);
2996
3105
  }
2997
3106
  });