@camstack/addon-post-analysis 0.1.11 → 0.1.13

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 (34) hide show
  1. package/assets/icon.svg +7 -0
  2. package/dist/embedding-encoder/index.js +6 -6
  3. package/dist/embedding-encoder/index.mjs +1 -1
  4. package/dist/enrichment-engine/index.js +46 -47
  5. package/dist/enrichment-engine/index.js.map +1 -1
  6. package/dist/enrichment-engine/index.mjs +46 -47
  7. package/dist/enrichment-engine/index.mjs.map +1 -1
  8. package/dist/index-DjIIGJS2.js +13728 -0
  9. package/dist/index-DjIIGJS2.js.map +1 -0
  10. package/dist/index-DnAXaymw.mjs +13729 -0
  11. package/dist/index-DnAXaymw.mjs.map +1 -0
  12. package/dist/pipeline-analytics/@mf-types.zip +0 -0
  13. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-p-Z3JTk9.mjs +19 -0
  14. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-BfmtcT51.mjs +15 -0
  15. package/dist/pipeline-analytics/_stub.js +1 -1
  16. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-UzRdrF-t.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-BDtqUbh1.mjs} +6 -6
  17. package/dist/pipeline-analytics/{hostInit-Bs26Ch2I.mjs → hostInit-CtfhvKwy.mjs} +6 -6
  18. package/dist/pipeline-analytics/{index-DyF0fAsr.mjs → index-BgDjUxfg.mjs} +67 -44
  19. package/dist/pipeline-analytics/index-COebxMhm.mjs +13398 -0
  20. package/dist/pipeline-analytics/{index-DkY0uSjx.mjs → index-CzwXKSYE.mjs} +7061 -5699
  21. package/dist/pipeline-analytics/index.js +21 -21
  22. package/dist/pipeline-analytics/index.mjs +1 -1
  23. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  24. package/dist/pipeline-analytics/schemas-ChN4Ih0h.mjs +3584 -0
  25. package/dist/recording/index.js +5 -5
  26. package/dist/recording/index.mjs +2 -2
  27. package/dist/{recording-coordinator-DuP3BUTV.mjs → recording-coordinator-CRJ4yA9t.mjs} +4 -4
  28. package/dist/{recording-coordinator-DuP3BUTV.mjs.map → recording-coordinator-CRJ4yA9t.mjs.map} +1 -1
  29. package/dist/{recording-coordinator-C2sATEhe.js → recording-coordinator-UGTDbTdE.js} +13 -13
  30. package/dist/{recording-coordinator-C2sATEhe.js.map → recording-coordinator-UGTDbTdE.js.map} +1 -1
  31. package/package.json +19 -10
  32. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.mjs-ChoHjdk6.mjs +0 -17
  33. package/dist/pipeline-analytics/__mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.mjs-DXo9zkw7.mjs +0 -15
  34. package/dist/pipeline-analytics/index-DUJwOcGq.mjs +0 -16616
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="#9333ea" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
2
+ <path d="m3 7 2 2 4-4"/>
3
+ <path d="m3 17 2 2 4-4"/>
4
+ <path d="M13 6h8"/>
5
+ <path d="M13 12h8"/>
6
+ <path d="M13 18h8"/>
7
+ </svg>
@@ -22,7 +22,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  mod
23
23
  ));
24
24
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
25
- const types = require("@camstack/types");
25
+ const index = require("../index-DjIIGJS2.js");
26
26
  const core = require("@camstack/core");
27
27
  const path = require("node:path");
28
28
  const fs = require("node:fs");
@@ -533,8 +533,8 @@ class PythonInferenceEngine {
533
533
  }
534
534
  }
535
535
  const AUTO_BACKEND_PRIORITY = ["coreml", "cuda", "tensorrt", "cpu"];
536
- const BACKEND_TO_FORMAT = types.BACKEND_TO_FORMAT;
537
- const RUNTIME_TO_FORMAT = types.RUNTIME_TO_FORMAT;
536
+ const BACKEND_TO_FORMAT = index.BACKEND_TO_FORMAT;
537
+ const RUNTIME_TO_FORMAT = index.RUNTIME_TO_FORMAT;
538
538
  function extractModelMeta(entry) {
539
539
  return {
540
540
  inputSize: entry.inputSize,
@@ -631,7 +631,7 @@ async function resolveEngine(options) {
631
631
  }
632
632
  }
633
633
  }
634
- const scriptName = types.PYTHON_SCRIPT[effectiveRuntime];
634
+ const scriptName = index.PYTHON_SCRIPT[effectiveRuntime];
635
635
  if (scriptName && pythonPath) {
636
636
  const candidates = [
637
637
  path__namespace.join(__dirname, "../../python", scriptName),
@@ -736,7 +736,7 @@ function l2Normalize(vec) {
736
736
  }
737
737
  return vec;
738
738
  }
739
- class EmbeddingEncoderAddon extends types.BaseAddon {
739
+ class EmbeddingEncoderAddon extends index.BaseAddon {
740
740
  imageRawEngine = null;
741
741
  textRawEngine = null;
742
742
  imagePythonEngine = null;
@@ -749,7 +749,7 @@ class EmbeddingEncoderAddon extends types.BaseAddon {
749
749
  async onInitialize() {
750
750
  const modelsDir = await this.ctx.api.storage.resolve.query({ location: "models", relativePath: "" }).catch(() => "camstack-data/models");
751
751
  this.models = new core.ModelDownloadService(modelsDir, []);
752
- return [{ capability: types.embeddingEncoderCapability, provider: this }];
752
+ return [{ capability: index.embeddingEncoderCapability, provider: this }];
753
753
  }
754
754
  async encode(input) {
755
755
  const { crop, width, height } = input;
@@ -1,4 +1,4 @@
1
- import { BACKEND_TO_FORMAT as BACKEND_TO_FORMAT$1, RUNTIME_TO_FORMAT as RUNTIME_TO_FORMAT$1, PYTHON_SCRIPT, BaseAddon, embeddingEncoderCapability } from "@camstack/types";
1
+ import { R as RUNTIME_TO_FORMAT$1, P as PYTHON_SCRIPT, f as BACKEND_TO_FORMAT$1, B as BaseAddon, g as embeddingEncoderCapability } from "../index-DnAXaymw.mjs";
2
2
  import { ModelDownloadService } from "@camstack/core";
3
3
  import * as path from "node:path";
4
4
  import * as fs from "node:fs";
@@ -1,8 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
- const types = require("@camstack/types");
3
+ const index = require("../index-DjIIGJS2.js");
4
4
  const sharp = require("sharp");
5
- const zod = require("zod");
6
5
  const DEFAULT_ENRICHMENT_CONFIG = {
7
6
  enabled: false,
8
7
  embedding: {
@@ -71,7 +70,7 @@ class EmbeddingDispatcher {
71
70
  return;
72
71
  }
73
72
  this.unsubscribe = this.eventBus.subscribe(
74
- { category: types.EventCategory.DetectionResult },
73
+ { category: index.EventCategory.DetectionResult },
75
74
  (event) => {
76
75
  void this.handleDetectionResult(event);
77
76
  }
@@ -202,8 +201,8 @@ class EmbeddingDispatcher {
202
201
  inferenceMs,
203
202
  timestamp: Date.now()
204
203
  };
205
- this.eventBus.emit(types.createEvent(
206
- types.EventCategory.EnrichmentEmbeddingStored,
204
+ this.eventBus.emit(index.createEvent(
205
+ index.EventCategory.EnrichmentEmbeddingStored,
207
206
  { type: "addon", id: "enrichment-engine" },
208
207
  payload
209
208
  ));
@@ -219,45 +218,45 @@ class EmbeddingDispatcher {
219
218
  }
220
219
  }
221
220
  }
222
- const TrackPositionSchema = zod.z.object({
223
- x: zod.z.number(),
224
- y: zod.z.number(),
225
- timestamp: zod.z.number(),
226
- bbox: zod.z.tuple([zod.z.number(), zod.z.number(), zod.z.number(), zod.z.number()])
221
+ const TrackPositionSchema = index.object({
222
+ x: index.number(),
223
+ y: index.number(),
224
+ timestamp: index.number(),
225
+ bbox: index.tuple([index.number(), index.number(), index.number(), index.number()])
227
226
  });
228
- const TrackSnapshotSchema = zod.z.object({
229
- timestamp: zod.z.number(),
227
+ const TrackSnapshotSchema = index.object({
228
+ timestamp: index.number(),
230
229
  position: TrackPositionSchema,
231
- thumbnailPath: zod.z.string()
230
+ thumbnailPath: index.string()
232
231
  });
233
- zod.z.object({
234
- trackId: zod.z.string(),
235
- deviceId: zod.z.string(),
236
- className: zod.z.string(),
237
- label: zod.z.string().optional(),
238
- firstSeen: zod.z.number(),
239
- lastSeen: zod.z.number(),
240
- positions: zod.z.array(TrackPositionSchema),
241
- snapshots: zod.z.array(TrackSnapshotSchema),
242
- totalDistance: zod.z.number(),
243
- zonesVisited: zod.z.array(zod.z.string()),
244
- active: zod.z.boolean()
232
+ index.object({
233
+ trackId: index.string(),
234
+ deviceId: index.string(),
235
+ className: index.string(),
236
+ label: index.string().optional(),
237
+ firstSeen: index.number(),
238
+ lastSeen: index.number(),
239
+ positions: index.array(TrackPositionSchema),
240
+ snapshots: index.array(TrackSnapshotSchema),
241
+ totalDistance: index.number(),
242
+ zonesVisited: index.array(index.string()),
243
+ active: index.boolean()
245
244
  });
246
- const SceneMonitorConfigSchema = zod.z.object({
247
- monitors: zod.z.array(zod.z.object({
248
- id: zod.z.string(),
249
- label: zod.z.string(),
250
- roi: zod.z.object({ x: zod.z.number(), y: zod.z.number(), w: zod.z.number(), h: zod.z.number() }),
251
- enabled: zod.z.boolean(),
252
- states: zod.z.array(zod.z.object({
253
- id: zod.z.string(),
254
- label: zod.z.string(),
255
- prompt: zod.z.string().optional(),
256
- textPrompts: zod.z.array(zod.z.string()).optional(),
257
- referenceEmbeddings: zod.z.array(zod.z.array(zod.z.number())).optional(),
258
- threshold: zod.z.number().optional(),
259
- notify: zod.z.boolean().optional(),
260
- severity: zod.z.enum(["info", "warning", "alert"]).optional()
245
+ const SceneMonitorConfigSchema = index.object({
246
+ monitors: index.array(index.object({
247
+ id: index.string(),
248
+ label: index.string(),
249
+ roi: index.object({ x: index.number(), y: index.number(), w: index.number(), h: index.number() }),
250
+ enabled: index.boolean(),
251
+ states: index.array(index.object({
252
+ id: index.string(),
253
+ label: index.string(),
254
+ prompt: index.string().optional(),
255
+ textPrompts: index.array(index.string()).optional(),
256
+ referenceEmbeddings: index.array(index.array(index.number())).optional(),
257
+ threshold: index.number().optional(),
258
+ notify: index.boolean().optional(),
259
+ severity: index._enum(["info", "warning", "alert"]).optional()
261
260
  }))
262
261
  }))
263
262
  });
@@ -440,7 +439,7 @@ class SceneStateWorker {
440
439
  const data = { ...payload };
441
440
  this.eventBus.emit({
442
441
  id: `scene-state-${deviceId}-${monitor.id}-${Date.now()}`,
443
- category: types.EventCategory.EnrichmentSceneStateChanged,
442
+ category: index.EventCategory.EnrichmentSceneStateChanged,
444
443
  source: { type: "device", id: deviceId },
445
444
  timestamp: /* @__PURE__ */ new Date(),
446
445
  data
@@ -484,13 +483,13 @@ class ActivitySummaryWorker {
484
483
  return;
485
484
  }
486
485
  this.unsubDetection = this.eventBus.subscribe(
487
- { category: types.EventCategory.DetectionResult },
486
+ { category: index.EventCategory.DetectionResult },
488
487
  (event) => {
489
488
  this.handleDetection(event);
490
489
  }
491
490
  );
492
491
  this.unsubSceneState = this.eventBus.subscribe(
493
- { category: types.EventCategory.EnrichmentSceneStateChanged },
492
+ { category: index.EventCategory.EnrichmentSceneStateChanged },
494
493
  (event) => {
495
494
  this.handleSceneStateChange(event);
496
495
  }
@@ -611,8 +610,8 @@ class ActivitySummaryWorker {
611
610
  activityLevel
612
611
  };
613
612
  this._lastSummary = summary;
614
- this.eventBus.emit(types.createEvent(
615
- types.EventCategory.EnrichmentActivitySummary,
613
+ this.eventBus.emit(index.createEvent(
614
+ index.EventCategory.EnrichmentActivitySummary,
616
615
  { type: "device", id: deviceId },
617
616
  summary
618
617
  ));
@@ -629,7 +628,7 @@ class ActivitySummaryWorker {
629
628
  }
630
629
  }
631
630
  }
632
- class EnrichmentEngineAddon extends types.BaseAddon {
631
+ class EnrichmentEngineAddon extends index.BaseAddon {
633
632
  embeddingDispatcher = null;
634
633
  sceneStateWorker = null;
635
634
  activitySummary = null;
@@ -759,7 +758,7 @@ class EnrichmentEngineAddon extends types.BaseAddon {
759
758
  }
760
759
  async loadConfig() {
761
760
  try {
762
- const stored = types.asJsonObject(await this.ctx.api?.settingsStore.get.query({ collection: "addon-settings", key: "enrichment:global" }));
761
+ const stored = index.asJsonObject(await this.ctx.api?.settingsStore.get.query({ collection: "addon-settings", key: "enrichment:global" }));
763
762
  if (stored) {
764
763
  const merged = { ...DEFAULT_ENRICHMENT_CONFIG, ...stored };
765
764
  return merged;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/enrichment-engine/types.ts","../../src/enrichment-engine/services/crop-extractor.ts","../../src/enrichment-engine/workers/embedding-dispatcher.ts","../../src/_analytics-schemas/persistence-records.ts","../../src/enrichment-engine/services/text-embedding-cache.ts","../../src/enrichment-engine/workers/scene-state-worker.ts","../../src/enrichment-engine/workers/activity-summary.ts","../../src/enrichment-engine/index.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Enrichment Engine — Types\n// ---------------------------------------------------------------------------\n\n// --- Normalized Rectangle (0-1 coordinate space) ---\n\nexport interface NormalizedRect {\n readonly x: number // 0-1, left edge\n readonly y: number // 0-1, top edge\n readonly w: number // 0-1, width\n readonly h: number // 0-1, height\n}\n\n// --- Scene State ---\n\nexport interface SceneStateDefinition {\n readonly id: string\n readonly label: string // e.g. \"open\", \"closed\"\n readonly textPrompts: readonly string[] // zero-shot: [\"an open gate\"]\n readonly referenceEmbeddings: readonly (readonly number[])[] // optional: higher accuracy\n readonly notify: boolean\n readonly severity: 'info' | 'warning' | 'alert'\n}\n\nexport interface SceneMonitor {\n readonly id: string\n readonly label: string // e.g. \"Main Gate\"\n readonly roi: NormalizedRect // region of interest\n readonly enabled: boolean\n readonly states: readonly SceneStateDefinition[]\n}\n\nexport interface SceneMonitorConfig {\n readonly monitors: readonly SceneMonitor[]\n}\n\n// --- Enrichment Config (global) ---\n\nexport interface EnrichmentConfig {\n readonly enabled: boolean\n\n readonly embedding: {\n readonly enabled: boolean\n readonly modelId: string // 'clip-vit-b32' | 'clip-vit-b16' | 'siglip2-b16-256'\n readonly agentId: string | 'local'\n readonly runtime: 'node' | 'python'\n readonly backend: 'cpu' | 'coreml' | 'cuda'\n readonly classes: readonly string[] // [] = all classes\n readonly minConfidence: number\n readonly maxPerSecPerCamera: number\n readonly cropStrategy: 'first' | 'best-confidence' | 'track-end'\n readonly retentionDays: number\n }\n\n readonly sceneMonitor: {\n readonly enabled: boolean\n readonly modelId: string // can differ from embedding model\n readonly pollIntervalSec: number\n readonly hysteresisCount: number\n }\n\n readonly activitySummary: {\n readonly enabled: boolean\n readonly intervalSec: number\n readonly activityThresholds: {\n readonly low: number\n readonly medium: number\n readonly high: number\n }\n }\n}\n\n// --- Events ---\n\nexport interface EmbeddingStoredEvent {\n readonly deviceId: number\n readonly trackId: string\n readonly class: string\n readonly embeddingId: string // reference in vector store\n readonly modelId: string\n readonly embeddingDim: number\n readonly inferenceMs: number\n readonly timestamp: number\n}\n\nexport interface SceneStateChangedEvent {\n readonly deviceId: number\n readonly monitorId: string\n readonly monitorLabel: string\n readonly previousState: string\n readonly currentState: string\n readonly confidence: number\n readonly timestamp: number\n}\n\nexport interface ZoneActivityEntry {\n readonly zoneId: string\n readonly entries: number\n readonly exits: number\n readonly avgDwellMs: number\n}\n\nexport interface StateChangeEntry {\n readonly monitorId: string\n readonly from: string\n readonly to: string\n readonly timestamp: number\n}\n\nexport interface ActivitySummaryEvent {\n readonly deviceId: number\n readonly periodStart: number\n readonly periodEnd: number\n readonly objectCounts: Readonly<Record<string, number>>\n readonly zoneActivity: readonly ZoneActivityEntry[]\n readonly stateChanges: readonly StateChangeEntry[]\n readonly activityLevel: 'none' | 'low' | 'medium' | 'high'\n}\n\n// --- Defaults ---\n\nexport const DEFAULT_ENRICHMENT_CONFIG: EnrichmentConfig = {\n enabled: false,\n\n embedding: {\n enabled: false,\n modelId: 'clip-vit-b32',\n agentId: 'local',\n runtime: 'node',\n backend: 'cpu',\n classes: [],\n minConfidence: 0.5,\n maxPerSecPerCamera: 1,\n cropStrategy: 'first',\n retentionDays: 30,\n },\n\n sceneMonitor: {\n enabled: false,\n modelId: 'clip-vit-b32',\n pollIntervalSec: 10,\n hysteresisCount: 3,\n },\n\n activitySummary: {\n enabled: false,\n intervalSec: 60,\n activityThresholds: {\n low: 2,\n medium: 10,\n high: 30,\n },\n },\n}\n","import sharp from 'sharp'\n\n/**\n * Extracts a JPEG-encoded crop from a raw frame buffer using a normalized bounding box.\n * Coordinates are clamped to frame bounds to avoid out-of-range errors.\n */\nexport async function extractCrop(\n frameData: Buffer,\n frameWidth: number,\n frameHeight: number,\n bbox: { readonly x: number; readonly y: number; readonly w: number; readonly h: number },\n): Promise<{ crop: Buffer; width: number; height: number }> {\n // Convert normalized coordinates to pixel values\n const rawLeft = Math.round(bbox.x * frameWidth)\n const rawTop = Math.round(bbox.y * frameHeight)\n const rawWidth = Math.round(bbox.w * frameWidth)\n const rawHeight = Math.round(bbox.h * frameHeight)\n\n // Clamp to frame bounds\n const left = Math.max(0, Math.min(rawLeft, frameWidth - 1))\n const top = Math.max(0, Math.min(rawTop, frameHeight - 1))\n const width = Math.max(1, Math.min(rawWidth, frameWidth - left))\n const height = Math.max(1, Math.min(rawHeight, frameHeight - top))\n\n const crop = await sharp(frameData, {\n raw: { width: frameWidth, height: frameHeight, channels: 3 },\n })\n .extract({ left, top, width, height })\n .jpeg({ quality: 90 })\n .toBuffer()\n\n return { crop, width, height }\n}\n","import { EventCategory, createEvent } from '@camstack/types'\nimport type { IEventBus, IScopedLogger, TypedSystemEvent } from '@camstack/types'\nimport type { EnrichmentConfig, EmbeddingStoredEvent } from '../types.js'\nimport { extractCrop } from '../services/crop-extractor.js'\n\ninterface EmbeddingEncoder {\n encode(crop: Buffer, width: number, height: number): Promise<{ embedding: Float32Array; inferenceMs: number }>\n getInfo(): { modelId: string; embeddingDim: number; ready: boolean }\n}\n\ninterface PendingCrop {\n readonly trackId: string\n readonly deviceId: string\n readonly class: string\n readonly confidence: number\n readonly frameData: Buffer\n readonly frameWidth: number\n readonly frameHeight: number\n readonly bbox: { readonly x: number; readonly y: number; readonly w: number; readonly h: number }\n readonly receivedAt: number\n}\n\n/** Local narrow of a pipeline analysis result item (runtime shape from detection pipeline). */\ninterface PipelineAnalysisItem {\n readonly detection?: {\n readonly class: string\n readonly score: number\n readonly trackId?: string\n readonly bbox: { readonly x: number; readonly y: number; readonly w: number; readonly h: number }\n }\n readonly objectState?: {\n readonly state: string\n }\n readonly zoneEvents?: readonly { readonly zoneId: string; readonly type: string; readonly timestamp?: number }[]\n}\n\n/** Extended pipeline result with frame data attached by the pipeline stage. */\ninterface PipelineResultWithFrame {\n readonly frameData?: Buffer\n readonly imageWidth?: number\n readonly imageHeight?: number\n [key: string]: unknown\n}\n\nexport interface EmbeddingDispatcherDeps {\n readonly config: EnrichmentConfig['embedding']\n readonly encoders: readonly EmbeddingEncoder[]\n readonly eventBus: IEventBus\n readonly logger: IScopedLogger\n}\n\nexport class EmbeddingDispatcher {\n private readonly config: EnrichmentConfig['embedding']\n private readonly encoders: readonly EmbeddingEncoder[]\n private readonly eventBus: IEventBus\n private readonly logger: IScopedLogger\n\n private readonly lastEmbedTime = new Map<string, number>()\n private readonly pendingCrops = new Map<string, PendingCrop>()\n private flushTimer: ReturnType<typeof setInterval> | null = null\n private unsubscribe: (() => void) | null = null\n\n private _processedCount = 0\n private _totalInferenceMs = 0\n private _encoderIndex = 0\n\n constructor(deps: EmbeddingDispatcherDeps) {\n this.config = deps.config\n this.encoders = deps.encoders\n this.eventBus = deps.eventBus\n this.logger = deps.logger\n }\n\n async start(): Promise<void> {\n if (!this.config.enabled) {\n this.logger.info('EmbeddingDispatcher disabled')\n return\n }\n\n this.unsubscribe = this.eventBus.subscribe(\n { category: EventCategory.DetectionResult },\n (event) => { void this.handleDetectionResult(event) },\n )\n\n // Flush timer for best-confidence strategy\n if (this.config.cropStrategy === 'best-confidence') {\n this.flushTimer = setInterval(() => { void this.flushPending() }, 1000)\n }\n\n this.logger.info('EmbeddingDispatcher started', { meta: { strategy: this.config.cropStrategy, maxPerSec: this.config.maxPerSecPerCamera } })\n }\n\n async stop(): Promise<void> {\n this.unsubscribe?.()\n this.unsubscribe = null\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n this.flushTimer = null\n }\n // Flush remaining\n await this.flushPending()\n }\n\n get processedCount(): number { return this._processedCount }\n get avgInferenceMs(): number { return this._processedCount > 0 ? this._totalInferenceMs / this._processedCount : 0 }\n get queueDepth(): number { return this.pendingCrops.size }\n\n private async handleDetectionResult(event: TypedSystemEvent<'detection.result'>): Promise<void> {\n const data = event.data\n const deviceId = event.source.id !== undefined ? String(event.source.id) : ''\n if (!deviceId) return\n\n // analysisResults items are untyped pipeline outputs — narrow to local interface at boundary\n const detections = (data.analysisResults as readonly PipelineAnalysisItem[]) ?? []\n // pipelineResult may carry frame data attached by the analysis stage — narrow at boundary\n const pipelineResult = data.pipelineResult as unknown as PipelineResultWithFrame\n\n for (const det of detections) {\n const detection = det.detection\n if (!detection) continue\n\n // Class filter\n if (this.config.classes.length > 0 && !this.config.classes.includes(detection.class)) continue\n\n // Confidence filter\n if (detection.score < this.config.minConfidence) continue\n\n // Rate limit\n const now = Date.now()\n const minInterval = 1000 / this.config.maxPerSecPerCamera\n const lastTime = this.lastEmbedTime.get(deviceId) ?? 0\n if (now - lastTime < minInterval) continue\n\n const trackId = detection.trackId ?? `${deviceId}-${now}`\n const frameData = pipelineResult.frameData\n const frameWidth = pipelineResult.imageWidth\n const frameHeight = pipelineResult.imageHeight\n\n if (!frameData || !frameWidth || !frameHeight) continue\n\n const pending: PendingCrop = {\n trackId,\n deviceId,\n class: detection.class,\n confidence: detection.score,\n frameData,\n frameWidth,\n frameHeight,\n bbox: detection.bbox,\n receivedAt: now,\n }\n\n switch (this.config.cropStrategy) {\n case 'first': {\n if (!this.pendingCrops.has(trackId)) {\n this.pendingCrops.set(trackId, pending)\n void this.processOne(pending)\n }\n break\n }\n case 'best-confidence': {\n const existing = this.pendingCrops.get(trackId)\n if (!existing || pending.confidence > existing.confidence) {\n this.pendingCrops.set(trackId, pending)\n }\n break\n }\n case 'track-end': {\n const state = det.objectState?.state\n if (state === 'leaving') {\n this.pendingCrops.set(trackId, pending)\n void this.processOne(pending)\n } else {\n const existing = this.pendingCrops.get(trackId)\n if (!existing || pending.confidence > existing.confidence) {\n this.pendingCrops.set(trackId, pending)\n }\n }\n break\n }\n }\n }\n }\n\n private async flushPending(): Promise<void> {\n const now = Date.now()\n const toFlush: PendingCrop[] = []\n\n for (const [trackId, pending] of this.pendingCrops) {\n if (now - pending.receivedAt > 3000) {\n toFlush.push(pending)\n this.pendingCrops.delete(trackId)\n }\n }\n\n await Promise.all(toFlush.map(p => this.processOne(p)))\n }\n\n private async processOne(pending: PendingCrop): Promise<void> {\n if (this.encoders.length === 0) return\n\n try {\n const { crop, width, height } = await extractCrop(\n pending.frameData, pending.frameWidth, pending.frameHeight, pending.bbox,\n )\n\n // Round-robin across encoders\n const encoder = this.encoders[this._encoderIndex % this.encoders.length]!\n this._encoderIndex++\n\n const { embedding: _embedding, inferenceMs } = await encoder.encode(crop, width, height)\n const info = encoder.getInfo()\n\n this._processedCount++\n this._totalInferenceMs += inferenceMs\n this.lastEmbedTime.set(pending.deviceId, Date.now())\n this.pendingCrops.delete(pending.trackId)\n\n const embeddingId = `${pending.deviceId}/${pending.trackId}/${Date.now()}`\n\n const payload: EmbeddingStoredEvent = {\n deviceId: Number(pending.deviceId),\n trackId: pending.trackId,\n class: pending.class,\n embeddingId,\n modelId: info.modelId,\n embeddingDim: info.embeddingDim,\n inferenceMs,\n timestamp: Date.now(),\n }\n\n this.eventBus.emit(createEvent(\n EventCategory.EnrichmentEmbeddingStored,\n { type: 'addon', id: 'enrichment-engine' },\n payload,\n ))\n\n this.logger.debug('Embedded track', {\n tags: { deviceId: Number(pending.deviceId) },\n meta: { class: pending.class, trackId: pending.trackId, inferenceMs: Number(inferenceMs.toFixed(1)) },\n })\n } catch (err) {\n this.logger.warn('Failed to embed track', {\n tags: { deviceId: Number(pending.deviceId) },\n meta: { trackId: pending.trackId, error: String(err) },\n })\n }\n }\n}\n","/**\n * Zod schemas for analytics-suite persisted record types.\n * Used by persistence services to parse settings-store query results.\n */\nimport { z } from 'zod'\n\n// ── Track Trail ─────────────────────────────────────────────────────\n\nexport const TrackPositionSchema = z.object({\n x: z.number(),\n y: z.number(),\n timestamp: z.number(),\n bbox: z.tuple([z.number(), z.number(), z.number(), z.number()]),\n})\n\nexport const TrackSnapshotSchema = z.object({\n timestamp: z.number(),\n position: TrackPositionSchema,\n thumbnailPath: z.string(),\n})\n\nexport const TrackTrailSchema = z.object({\n trackId: z.string(),\n deviceId: z.string(),\n className: z.string(),\n label: z.string().optional(),\n firstSeen: z.number(),\n lastSeen: z.number(),\n positions: z.array(TrackPositionSchema),\n snapshots: z.array(TrackSnapshotSchema),\n totalDistance: z.number(),\n zonesVisited: z.array(z.string()),\n active: z.boolean(),\n})\n\nexport type TrackTrail = z.infer<typeof TrackTrailSchema>\n\n// ── Scene Monitor ───────────────────────────────────────────────────\n\nexport const SceneMonitorConfigSchema = z.object({\n monitors: z.array(z.object({\n id: z.string(),\n label: z.string(),\n roi: z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() }),\n enabled: z.boolean(),\n states: z.array(z.object({\n id: z.string(),\n label: z.string(),\n prompt: z.string().optional(),\n textPrompts: z.array(z.string()).optional(),\n referenceEmbeddings: z.array(z.array(z.number())).optional(),\n threshold: z.number().optional(),\n notify: z.boolean().optional(),\n severity: z.enum(['info', 'warning', 'alert']).optional(),\n })),\n })),\n})\n\nexport type SceneMonitorConfig = z.infer<typeof SceneMonitorConfigSchema>\n\n/** Single scene monitor entry (element of SceneMonitorConfig.monitors). */\nexport type SceneMonitor = SceneMonitorConfig['monitors'][number]\n","/**\n * In-memory cache for text prompt embeddings.\n *\n * Keys follow the convention `${monitorId}:${text}` so that all entries\n * belonging to a specific scene monitor can be invalidated in one call.\n */\nexport class TextEmbeddingCache {\n private readonly cache = new Map<string, Float32Array>()\n\n get(key: string): Float32Array | undefined {\n return this.cache.get(key)\n }\n\n set(key: string, embedding: Float32Array): void {\n this.cache.set(key, embedding)\n }\n\n has(key: string): boolean {\n return this.cache.has(key)\n }\n\n /**\n * Deletes all entries whose key starts with `${monitorId}:`.\n * Used when a scene monitor's text prompts are updated.\n */\n invalidateMonitor(monitorId: string): void {\n const prefix = `${monitorId}:`\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n }\n\n get size(): number {\n return this.cache.size\n }\n}\n","import { EventCategory } from '@camstack/types'\nimport type { IEventBus, IScopedLogger, SettingsStoreClient } from '@camstack/types'\nimport type { EnrichmentConfig, SceneStateChangedEvent } from '../types.js'\nimport { SceneMonitorConfigSchema, type SceneMonitor } from '../../_analytics-schemas/persistence-records.js'\nimport { TextEmbeddingCache } from '../services/text-embedding-cache.js'\nimport { extractCrop } from '../services/crop-extractor.js'\n\ninterface EmbeddingEncoder {\n encode(crop: Buffer, width: number, height: number): Promise<{ embedding: Float32Array; inferenceMs: number }>\n encodeText(text: string): Promise<{ embedding: Float32Array }>\n getInfo(): { modelId: string; embeddingDim: number; ready: boolean }\n}\n\ninterface StreamBrokerRegistry {\n getSnapshot(deviceId: string): Promise<{ data: Buffer; width: number; height: number } | null>\n}\n\ninterface CameraMonitorState {\n currentState: string | null\n pendingState: string | null\n pendingCount: number\n pendingConfidence: number\n}\n\nexport interface SceneStateWorkerDeps {\n readonly config: EnrichmentConfig['sceneMonitor']\n readonly encoders: readonly EmbeddingEncoder[]\n readonly eventBus: IEventBus\n readonly store: SettingsStoreClient\n readonly streamBrokerRegistry: StreamBrokerRegistry\n readonly logger: IScopedLogger\n}\n\nexport class SceneStateWorker {\n private readonly config: EnrichmentConfig['sceneMonitor']\n private readonly encoders: readonly EmbeddingEncoder[]\n private readonly eventBus: IEventBus\n private readonly store: SettingsStoreClient\n private readonly streamBrokerRegistry: StreamBrokerRegistry\n private readonly logger: IScopedLogger\n\n private readonly textCache = new TextEmbeddingCache()\n private readonly monitorStates = new Map<string, CameraMonitorState>()\n private pollTimer: ReturnType<typeof setInterval> | null = null\n private _activeCount = 0\n\n constructor(deps: SceneStateWorkerDeps) {\n this.config = deps.config\n this.encoders = deps.encoders\n this.eventBus = deps.eventBus\n this.store = deps.store\n this.streamBrokerRegistry = deps.streamBrokerRegistry\n this.logger = deps.logger\n }\n\n async start(): Promise<void> {\n if (!this.config.enabled) {\n this.logger.info('SceneStateWorker disabled')\n return\n }\n\n this.pollTimer = setInterval(\n () => { void this.pollAll() },\n this.config.pollIntervalSec * 1000,\n )\n this.logger.info('SceneStateWorker started', { meta: { pollIntervalSec: this.config.pollIntervalSec, hysteresis: this.config.hysteresisCount } })\n }\n\n async stop(): Promise<void> {\n if (this.pollTimer) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n get activeCount(): number { return this._activeCount }\n\n private async pollAll(): Promise<void> {\n // Load all scene monitor configs from settings\n const rawConfigs = await this.store.query.query({ collection: 'device-settings', filter: {\n where: { id: 'enrichment:scene-monitor:%' },\n } })\n const allConfigs = rawConfigs.map(r => ({ id: r.id, data: SceneMonitorConfigSchema.parse(r.data) }))\n\n const deviceMonitors = new Map<string, SceneMonitor[]>()\n for (const record of allConfigs) {\n const deviceId = record.id.replace('enrichment:scene-monitor:', '')\n const config = record.data\n const active = config.monitors.filter(m => m.enabled)\n if (active.length > 0) {\n deviceMonitors.set(deviceId, active)\n }\n }\n\n this._activeCount = [...deviceMonitors.values()].reduce((sum, ms) => sum + ms.length, 0)\n\n // Process each camera (1 snapshot per camera)\n await Promise.all(\n [...deviceMonitors.entries()].map(([deviceId, monitors]) =>\n this.pollCamera(deviceId, monitors),\n ),\n )\n }\n\n private async pollCamera(deviceId: string, monitors: SceneMonitor[]): Promise<void> {\n if (this.encoders.length === 0) return\n\n try {\n // Capture 1 snapshot\n const snapshot = await this.streamBrokerRegistry.getSnapshot(deviceId)\n if (!snapshot) return\n\n const encoder = this.encoders[0]!\n\n // Process each monitor (crop from same snapshot)\n for (const monitor of monitors) {\n try {\n await this.processMonitor(deviceId, monitor, snapshot, encoder)\n } catch (err) {\n this.logger.warn('SceneState error', { tags: { deviceId: Number(deviceId) }, meta: { monitorLabel: monitor.label, error: String(err) } })\n }\n }\n } catch (err) {\n this.logger.warn('Failed to capture snapshot', { tags: { deviceId: Number(deviceId) }, meta: { error: String(err) } })\n }\n }\n\n private async processMonitor(\n deviceId: string,\n monitor: SceneMonitor,\n snapshot: { data: Buffer; width: number; height: number },\n encoder: EmbeddingEncoder,\n ): Promise<void> {\n // Crop ROI (normalized → pixel coords)\n const bbox = {\n x: monitor.roi.x * snapshot.width,\n y: monitor.roi.y * snapshot.height,\n w: monitor.roi.w * snapshot.width,\n h: monitor.roi.h * snapshot.height,\n }\n const { crop, width, height } = await extractCrop(snapshot.data, snapshot.width, snapshot.height, bbox)\n\n // Encode crop\n const { embedding: imageEmb } = await encoder.encode(crop, width, height)\n\n // Find best matching state\n let bestState: string | null = null\n let bestScore = -1\n\n for (const state of monitor.states) {\n let score = 0\n\n if (state.referenceEmbeddings && state.referenceEmbeddings.length > 0) {\n for (const refEmb of state.referenceEmbeddings) {\n const ref = new Float32Array(refEmb)\n const sim = cosineSimilarity(imageEmb, ref)\n score = Math.max(score, sim)\n }\n } else if (state.textPrompts && state.textPrompts.length > 0) {\n for (const prompt of state.textPrompts) {\n const cacheKey = `${monitor.id}:${state.id}:${prompt}`\n let textEmb = this.textCache.get(cacheKey)\n if (!textEmb) {\n const result = await encoder.encodeText(prompt)\n textEmb = result.embedding\n this.textCache.set(cacheKey, textEmb)\n }\n const sim = cosineSimilarity(imageEmb, textEmb)\n score = Math.max(score, sim)\n }\n }\n\n if (score > bestScore) {\n bestScore = score\n bestState = state.label\n }\n }\n\n if (!bestState) return\n\n // Hysteresis\n const key = `${deviceId}:${monitor.id}`\n let ms = this.monitorStates.get(key)\n if (!ms) {\n ms = { currentState: null, pendingState: null, pendingCount: 0, pendingConfidence: 0 }\n this.monitorStates.set(key, ms)\n }\n\n if (bestState === ms.pendingState) {\n ms.pendingCount++\n ms.pendingConfidence = bestScore\n } else {\n ms.pendingState = bestState\n ms.pendingCount = 1\n ms.pendingConfidence = bestScore\n }\n\n if (ms.pendingCount < this.config.hysteresisCount) return\n if (bestState === ms.currentState) return\n\n // State changed!\n const previousState = ms.currentState ?? 'unknown'\n ms.currentState = bestState\n ms.pendingState = null\n ms.pendingCount = 0\n\n const payload: SceneStateChangedEvent = {\n deviceId: Number(deviceId),\n monitorId: monitor.id,\n monitorLabel: monitor.label,\n previousState,\n currentState: bestState,\n confidence: bestScore,\n timestamp: Date.now(),\n }\n\n const data: Record<string, unknown> = { ...payload }\n this.eventBus.emit({\n id: `scene-state-${deviceId}-${monitor.id}-${Date.now()}`,\n category: EventCategory.EnrichmentSceneStateChanged,\n source: { type: 'device', id: deviceId },\n timestamp: new Date(),\n data,\n })\n\n this.logger.info('Scene state changed', {\n tags: { deviceId: Number(deviceId) },\n meta: { monitorLabel: monitor.label, previousState, currentState: bestState, confidence: Number(bestScore.toFixed(2)) },\n })\n }\n}\n\nfunction cosineSimilarity(a: Float32Array, b: Float32Array): number {\n let dot = 0\n let normA = 0\n let normB = 0\n for (let i = 0; i < a.length; i++) {\n dot += a[i]! * b[i]!\n normA += a[i]! * a[i]!\n normB += b[i]! * b[i]!\n }\n return dot / (Math.sqrt(normA) * Math.sqrt(normB))\n}\n","import { EventCategory, createEvent } from '@camstack/types'\nimport type { IEventBus, IScopedLogger, SettingsStoreClient, TypedSystemEvent } from '@camstack/types'\nimport type { EnrichmentConfig, ActivitySummaryEvent, StateChangeEntry } from '../types.js'\n\n/** Local narrow of a pipeline analysis result item (runtime shape from detection pipeline). */\ninterface PipelineAnalysisItem {\n readonly detection?: {\n readonly class: string\n readonly trackId?: string\n }\n readonly zoneEvents?: readonly { readonly zoneId: string; readonly type: string; readonly timestamp?: number }[]\n}\n\ninterface TrackInfo {\n class: string\n deviceId: string\n firstSeen: number\n lastSeen: number\n zones: Set<string>\n}\n\ninterface ZoneEventInfo {\n type: 'enter' | 'exit'\n zoneId: string\n trackId: string\n timestamp: number\n}\n\ninterface ActivityBuffer {\n tracks: Map<string, TrackInfo>\n zoneEvents: ZoneEventInfo[]\n stateChanges: StateChangeEntry[]\n}\n\nexport interface ActivitySummaryWorkerDeps {\n readonly config: EnrichmentConfig['activitySummary']\n readonly eventBus: IEventBus\n readonly store: SettingsStoreClient\n readonly logger: IScopedLogger\n}\n\nexport class ActivitySummaryWorker {\n private readonly config: EnrichmentConfig['activitySummary']\n private readonly eventBus: IEventBus\n private readonly store: SettingsStoreClient\n private readonly logger: IScopedLogger\n\n private readonly buffers = new Map<string, ActivityBuffer>()\n private summaryTimer: ReturnType<typeof setInterval> | null = null\n private unsubDetection: (() => void) | null = null\n private unsubSceneState: (() => void) | null = null\n private _lastSummary: ActivitySummaryEvent | null = null\n\n constructor(deps: ActivitySummaryWorkerDeps) {\n this.config = deps.config\n this.eventBus = deps.eventBus\n this.store = deps.store\n this.logger = deps.logger\n }\n\n async start(): Promise<void> {\n if (!this.config.enabled) {\n this.logger.info('ActivitySummaryWorker disabled')\n return\n }\n\n this.unsubDetection = this.eventBus.subscribe(\n { category: EventCategory.DetectionResult },\n (event) => { this.handleDetection(event) },\n )\n\n this.unsubSceneState = this.eventBus.subscribe(\n { category: EventCategory.EnrichmentSceneStateChanged },\n (event) => { this.handleSceneStateChange(event) },\n )\n\n this.summaryTimer = setInterval(\n () => { void this.emitSummaries() },\n this.config.intervalSec * 1000,\n )\n\n this.logger.info('ActivitySummaryWorker started', { meta: { intervalSec: this.config.intervalSec } })\n }\n\n async stop(): Promise<void> {\n this.unsubDetection?.()\n this.unsubSceneState?.()\n if (this.summaryTimer) {\n clearInterval(this.summaryTimer)\n this.summaryTimer = null\n }\n // Emit final summaries\n await this.emitSummaries()\n }\n\n get lastSummary(): ActivitySummaryEvent | null { return this._lastSummary }\n\n private getBuffer(deviceId: string): ActivityBuffer {\n let buf = this.buffers.get(deviceId)\n if (!buf) {\n buf = { tracks: new Map(), zoneEvents: [], stateChanges: [] }\n this.buffers.set(deviceId, buf)\n }\n return buf\n }\n\n private handleDetection(event: TypedSystemEvent<'detection.result'>): void {\n const deviceId = event.source.id !== undefined ? String(event.source.id) : ''\n if (!deviceId) return\n\n // analysisResults items are untyped pipeline outputs — narrow to local interface at boundary\n const results = (event.data.analysisResults as readonly PipelineAnalysisItem[]) ?? []\n const buf = this.getBuffer(deviceId)\n const now = Date.now()\n\n for (const det of results) {\n const detection = det.detection\n if (!detection) continue\n\n const trackId = detection.trackId ?? `unknown-${now}`\n const existing = buf.tracks.get(trackId)\n\n if (existing) {\n existing.lastSeen = now\n } else {\n buf.tracks.set(trackId, {\n class: detection.class,\n deviceId,\n firstSeen: now,\n lastSeen: now,\n zones: new Set(),\n })\n }\n\n // Zone events\n const zoneEvents = det.zoneEvents ?? []\n for (const ze of zoneEvents) {\n const track = buf.tracks.get(trackId)\n if (track) track.zones.add(ze.zoneId)\n\n if (ze.type === 'zone-enter' || ze.type === 'zone-exit') {\n buf.zoneEvents.push({\n type: ze.type === 'zone-enter' ? 'enter' : 'exit',\n zoneId: ze.zoneId,\n trackId,\n timestamp: ze.timestamp ?? now,\n })\n }\n }\n }\n }\n\n private handleSceneStateChange(event: TypedSystemEvent<'enrichment.scene.state-changed'>): void {\n const deviceId = event.source.id !== undefined ? String(event.source.id) : ''\n if (!deviceId) return\n\n const data = event.data\n const buf = this.getBuffer(deviceId)\n buf.stateChanges.push({\n monitorId: data.monitorId,\n from: data.previousState,\n to: data.currentState,\n timestamp: data.timestamp,\n })\n }\n\n private async emitSummaries(): Promise<void> {\n const now = Date.now()\n\n for (const [deviceId, buf] of this.buffers) {\n if (buf.tracks.size === 0 && buf.zoneEvents.length === 0 && buf.stateChanges.length === 0) {\n continue\n }\n\n // Object counts by class\n const objectCounts: Record<string, number> = {}\n for (const track of buf.tracks.values()) {\n objectCounts[track.class] = (objectCounts[track.class] ?? 0) + 1\n }\n\n // Zone activity\n const zoneMap = new Map<string, { entries: number; exits: number; dwellTimes: number[] }>()\n for (const ze of buf.zoneEvents) {\n let z = zoneMap.get(ze.zoneId)\n if (!z) {\n z = { entries: 0, exits: 0, dwellTimes: [] }\n zoneMap.set(ze.zoneId, z)\n }\n if (ze.type === 'enter') z.entries++\n else z.exits++\n }\n\n const zoneActivity = [...zoneMap.entries()].map(([zoneId, z]) => ({\n zoneId,\n entries: z.entries,\n exits: z.exits,\n avgDwellMs: z.dwellTimes.length > 0\n ? z.dwellTimes.reduce((a, b) => a + b, 0) / z.dwellTimes.length\n : 0,\n }))\n\n // Activity level\n const totalEvents = buf.tracks.size + buf.zoneEvents.length\n const eventsPerMin = totalEvents / (this.config.intervalSec / 60)\n const activityLevel = eventsPerMin >= this.config.activityThresholds.high ? 'high'\n : eventsPerMin >= this.config.activityThresholds.medium ? 'medium'\n : eventsPerMin >= this.config.activityThresholds.low ? 'low'\n : 'none' as const\n\n const summary: ActivitySummaryEvent = {\n deviceId: Number(deviceId),\n periodStart: now - this.config.intervalSec * 1000,\n periodEnd: now,\n objectCounts,\n zoneActivity,\n stateChanges: [...buf.stateChanges],\n activityLevel,\n }\n\n this._lastSummary = summary\n\n this.eventBus.emit(createEvent(\n EventCategory.EnrichmentActivitySummary,\n { type: 'device', id: deviceId },\n summary,\n ))\n\n // Persist for timeline UI\n try {\n await this.store.insert.mutate({ collection: 'addon-settings', record: {\n id: `${deviceId}:${now}`,\n data: { ...summary },\n } })\n } catch {\n // Best-effort persistence\n }\n\n // Reset buffer\n buf.tracks.clear()\n buf.zoneEvents.length = 0\n buf.stateChanges.length = 0\n }\n }\n}\n","import { BaseAddon, asJsonObject } from '@camstack/types'\nimport type { EnrichmentConfig } from './types.js'\nimport { DEFAULT_ENRICHMENT_CONFIG } from './types.js'\nimport { EmbeddingDispatcher } from './workers/embedding-dispatcher.js'\nimport type { EmbeddingDispatcherDeps } from './workers/embedding-dispatcher.js'\nimport { SceneStateWorker } from './workers/scene-state-worker.js'\nimport type { SceneStateWorkerDeps } from './workers/scene-state-worker.js'\nimport { ActivitySummaryWorker } from './workers/activity-summary.js'\n\n/**\n * Extended context shape injected at runtime by the server's capability wiring.\n * Not part of the base AddonContext interface because capabilities are resolved\n * after addon initialization.\n */\n\nexport class EnrichmentEngineAddon extends BaseAddon {\n private embeddingDispatcher: EmbeddingDispatcher | null = null\n private sceneStateWorker: SceneStateWorker | null = null\n private activitySummary: ActivitySummaryWorker | null = null\n private currentFlags = {\n embeddingEnabled: true,\n sceneMonitorEnabled: true,\n activitySummaryEnabled: true,\n }\n constructor() { super({}) }\n\n protected async onInitialize(): Promise<void> {\n const config = await this.loadConfig()\n\n // Encoders will be injected by the server via capability wiring.\n // The server extends AddonContext with a `capabilities` object at runtime.\n // Runtime bridges: capabilities.getCollection/get return unknown because\n // the registry holds heterogeneous providers. The shapes are guaranteed\n // by the capability declaration — see project_cast_elimination_checkpoint.md\n // pattern #7.\n const encoderCollection = this.capabilities?.getCollection?.('embedding-encoder') ?? []\n const streamBrokerRaw = this.capabilities?.get?.('stream-broker')\n\n this.embeddingDispatcher = new EmbeddingDispatcher({\n config: config.embedding,\n encoders: encoderCollection as EmbeddingDispatcherDeps['encoders'],\n eventBus: this.ctx.eventBus,\n logger: this.ctx.logger.child('EmbeddingDispatcher'),\n })\n\n this.sceneStateWorker = new SceneStateWorker({\n config: config.sceneMonitor,\n encoders: encoderCollection as SceneStateWorkerDeps['encoders'],\n eventBus: this.ctx.eventBus,\n store: this.ctx.api!.settingsStore,\n streamBrokerRegistry: (streamBrokerRaw ?? { getSnapshot: async () => null }) as SceneStateWorkerDeps['streamBrokerRegistry'],\n logger: this.ctx.logger.child('SceneStateWorker'),\n })\n\n this.activitySummary = new ActivitySummaryWorker({\n config: config.activitySummary,\n eventBus: this.ctx.eventBus,\n store: this.ctx.api!.settingsStore,\n logger: this.ctx.logger.child('ActivitySummary'),\n })\n\n this.currentFlags = {\n embeddingEnabled: config.embedding.enabled,\n sceneMonitorEnabled: config.sceneMonitor.enabled,\n activitySummaryEnabled: config.activitySummary.enabled,\n }\n\n await this.embeddingDispatcher.start()\n await this.sceneStateWorker.start()\n await this.activitySummary.start()\n\n this.ctx.logger.info('Enrichment engine initialized with 3 workers')\n }\n\n protected async onShutdown(): Promise<void> {\n await this.embeddingDispatcher?.stop()\n await this.sceneStateWorker?.stop()\n await this.activitySummary?.stop()\n this.embeddingDispatcher = null\n this.sceneStateWorker = null\n this.activitySummary = null\n }\n\n // ── Three-level settings API (Phase 3) ─────────────────────────────\n //\n // Enrichment engine is post-detection infra. The three boolean toggle\n // flags (embedding/scene/activity) live in `getGlobalSettings` until\n // the dedicated post-detection UI exists. The underlying\n // EnrichmentConfig blob (stored opaquely in 'addon-settings' →\n // 'enrichment:global') is a separate concern — the flags below mirror\n // the `.enabled` field of each worker's config.\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'enrichment-engine-settings',\n title: 'Enrichment Engine',\n columns: 2,\n fields: [\n {\n type: 'boolean',\n key: 'embeddingEnabled',\n label: 'Embedding Enabled',\n description: 'Compute face/object embeddings from detection crops.',\n default: true,\n },\n {\n type: 'boolean',\n key: 'sceneMonitorEnabled',\n label: 'Scene Monitor Enabled',\n description: 'Run periodic scene state capture for change detection.',\n default: true,\n },\n {\n type: 'boolean',\n key: 'activitySummaryEnabled',\n label: 'Activity Summary Enabled',\n description: 'Aggregate hourly activity summaries per camera.',\n default: true,\n },\n ],\n },\n ],\n })\n }\n\n async updateGlobalSettings(patch: Record<string, unknown>): Promise<void> {\n await this.ctx?.settings?.writeAddonStore(patch)\n const prev = this.currentFlags\n const next = {\n embeddingEnabled: typeof patch['embeddingEnabled'] === 'boolean' ? patch['embeddingEnabled'] : prev.embeddingEnabled,\n sceneMonitorEnabled: typeof patch['sceneMonitorEnabled'] === 'boolean' ? patch['sceneMonitorEnabled'] : prev.sceneMonitorEnabled,\n activitySummaryEnabled: typeof patch['activitySummaryEnabled'] === 'boolean' ? patch['activitySummaryEnabled'] : prev.activitySummaryEnabled,\n }\n this.currentFlags = next\n\n // Toggle workers based on flag changes\n if (prev.embeddingEnabled && !next.embeddingEnabled) {\n this.ctx?.logger.info('Stopping embedding dispatcher (disabled via config)')\n await this.embeddingDispatcher?.stop()\n } else if (!prev.embeddingEnabled && next.embeddingEnabled) {\n this.ctx?.logger.info('Starting embedding dispatcher (enabled via config)')\n await this.embeddingDispatcher?.start()\n }\n\n if (prev.sceneMonitorEnabled && !next.sceneMonitorEnabled) {\n this.ctx?.logger.info('Stopping scene state worker (disabled via config)')\n await this.sceneStateWorker?.stop()\n } else if (!prev.sceneMonitorEnabled && next.sceneMonitorEnabled) {\n this.ctx?.logger.info('Starting scene state worker (enabled via config)')\n await this.sceneStateWorker?.start()\n }\n\n if (prev.activitySummaryEnabled && !next.activitySummaryEnabled) {\n this.ctx?.logger.info('Stopping activity summary worker (disabled via config)')\n await this.activitySummary?.stop()\n } else if (!prev.activitySummaryEnabled && next.activitySummaryEnabled) {\n this.ctx?.logger.info('Starting activity summary worker (enabled via config)')\n await this.activitySummary?.start()\n }\n\n this.ctx?.logger.info('Enrichment engine flags updated', { meta: { flags: this.currentFlags } })\n }\n\n private async loadConfig(): Promise<EnrichmentConfig> {\n try {\n const stored = asJsonObject(await this.ctx.api?.settingsStore.get.query({ collection: 'addon-settings', key: 'enrichment:global' }))\n if (stored) {\n // Shallow structural merge: only keys from DEFAULT_ENRICHMENT_CONFIG\n // are preserved; stored values override their counterparts when\n // present with a matching type.\n const merged: EnrichmentConfig = { ...DEFAULT_ENRICHMENT_CONFIG, ...stored }\n return merged\n }\n } catch {\n // First boot — use defaults\n }\n return DEFAULT_ENRICHMENT_CONFIG\n }\n}\n\n// Default export — kernel addon-loader prefers mod.default to identify the addon class\nexport default EnrichmentEngineAddon\n"],"names":["EventCategory","createEvent","z","BaseAddon","asJsonObject"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/enrichment-engine/types.ts","../../src/enrichment-engine/services/crop-extractor.ts","../../src/enrichment-engine/workers/embedding-dispatcher.ts","../../src/_analytics-schemas/persistence-records.ts","../../src/enrichment-engine/services/text-embedding-cache.ts","../../src/enrichment-engine/workers/scene-state-worker.ts","../../src/enrichment-engine/workers/activity-summary.ts","../../src/enrichment-engine/index.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Enrichment Engine — Types\n// ---------------------------------------------------------------------------\n\n// --- Normalized Rectangle (0-1 coordinate space) ---\n\nexport interface NormalizedRect {\n readonly x: number // 0-1, left edge\n readonly y: number // 0-1, top edge\n readonly w: number // 0-1, width\n readonly h: number // 0-1, height\n}\n\n// --- Scene State ---\n\nexport interface SceneStateDefinition {\n readonly id: string\n readonly label: string // e.g. \"open\", \"closed\"\n readonly textPrompts: readonly string[] // zero-shot: [\"an open gate\"]\n readonly referenceEmbeddings: readonly (readonly number[])[] // optional: higher accuracy\n readonly notify: boolean\n readonly severity: 'info' | 'warning' | 'alert'\n}\n\nexport interface SceneMonitor {\n readonly id: string\n readonly label: string // e.g. \"Main Gate\"\n readonly roi: NormalizedRect // region of interest\n readonly enabled: boolean\n readonly states: readonly SceneStateDefinition[]\n}\n\nexport interface SceneMonitorConfig {\n readonly monitors: readonly SceneMonitor[]\n}\n\n// --- Enrichment Config (global) ---\n\nexport interface EnrichmentConfig {\n readonly enabled: boolean\n\n readonly embedding: {\n readonly enabled: boolean\n readonly modelId: string // 'clip-vit-b32' | 'clip-vit-b16' | 'siglip2-b16-256'\n readonly agentId: string | 'local'\n readonly runtime: 'node' | 'python'\n readonly backend: 'cpu' | 'coreml' | 'cuda'\n readonly classes: readonly string[] // [] = all classes\n readonly minConfidence: number\n readonly maxPerSecPerCamera: number\n readonly cropStrategy: 'first' | 'best-confidence' | 'track-end'\n readonly retentionDays: number\n }\n\n readonly sceneMonitor: {\n readonly enabled: boolean\n readonly modelId: string // can differ from embedding model\n readonly pollIntervalSec: number\n readonly hysteresisCount: number\n }\n\n readonly activitySummary: {\n readonly enabled: boolean\n readonly intervalSec: number\n readonly activityThresholds: {\n readonly low: number\n readonly medium: number\n readonly high: number\n }\n }\n}\n\n// --- Events ---\n\nexport interface EmbeddingStoredEvent {\n readonly deviceId: number\n readonly trackId: string\n readonly class: string\n readonly embeddingId: string // reference in vector store\n readonly modelId: string\n readonly embeddingDim: number\n readonly inferenceMs: number\n readonly timestamp: number\n}\n\nexport interface SceneStateChangedEvent {\n readonly deviceId: number\n readonly monitorId: string\n readonly monitorLabel: string\n readonly previousState: string\n readonly currentState: string\n readonly confidence: number\n readonly timestamp: number\n}\n\nexport interface ZoneActivityEntry {\n readonly zoneId: string\n readonly entries: number\n readonly exits: number\n readonly avgDwellMs: number\n}\n\nexport interface StateChangeEntry {\n readonly monitorId: string\n readonly from: string\n readonly to: string\n readonly timestamp: number\n}\n\nexport interface ActivitySummaryEvent {\n readonly deviceId: number\n readonly periodStart: number\n readonly periodEnd: number\n readonly objectCounts: Readonly<Record<string, number>>\n readonly zoneActivity: readonly ZoneActivityEntry[]\n readonly stateChanges: readonly StateChangeEntry[]\n readonly activityLevel: 'none' | 'low' | 'medium' | 'high'\n}\n\n// --- Defaults ---\n\nexport const DEFAULT_ENRICHMENT_CONFIG: EnrichmentConfig = {\n enabled: false,\n\n embedding: {\n enabled: false,\n modelId: 'clip-vit-b32',\n agentId: 'local',\n runtime: 'node',\n backend: 'cpu',\n classes: [],\n minConfidence: 0.5,\n maxPerSecPerCamera: 1,\n cropStrategy: 'first',\n retentionDays: 30,\n },\n\n sceneMonitor: {\n enabled: false,\n modelId: 'clip-vit-b32',\n pollIntervalSec: 10,\n hysteresisCount: 3,\n },\n\n activitySummary: {\n enabled: false,\n intervalSec: 60,\n activityThresholds: {\n low: 2,\n medium: 10,\n high: 30,\n },\n },\n}\n","import sharp from 'sharp'\n\n/**\n * Extracts a JPEG-encoded crop from a raw frame buffer using a normalized bounding box.\n * Coordinates are clamped to frame bounds to avoid out-of-range errors.\n */\nexport async function extractCrop(\n frameData: Buffer,\n frameWidth: number,\n frameHeight: number,\n bbox: { readonly x: number; readonly y: number; readonly w: number; readonly h: number },\n): Promise<{ crop: Buffer; width: number; height: number }> {\n // Convert normalized coordinates to pixel values\n const rawLeft = Math.round(bbox.x * frameWidth)\n const rawTop = Math.round(bbox.y * frameHeight)\n const rawWidth = Math.round(bbox.w * frameWidth)\n const rawHeight = Math.round(bbox.h * frameHeight)\n\n // Clamp to frame bounds\n const left = Math.max(0, Math.min(rawLeft, frameWidth - 1))\n const top = Math.max(0, Math.min(rawTop, frameHeight - 1))\n const width = Math.max(1, Math.min(rawWidth, frameWidth - left))\n const height = Math.max(1, Math.min(rawHeight, frameHeight - top))\n\n const crop = await sharp(frameData, {\n raw: { width: frameWidth, height: frameHeight, channels: 3 },\n })\n .extract({ left, top, width, height })\n .jpeg({ quality: 90 })\n .toBuffer()\n\n return { crop, width, height }\n}\n","import { EventCategory, createEvent } from '@camstack/types'\nimport type { IEventBus, IScopedLogger, TypedSystemEvent } from '@camstack/types'\nimport type { EnrichmentConfig, EmbeddingStoredEvent } from '../types.js'\nimport { extractCrop } from '../services/crop-extractor.js'\n\ninterface EmbeddingEncoder {\n encode(crop: Buffer, width: number, height: number): Promise<{ embedding: Float32Array; inferenceMs: number }>\n getInfo(): { modelId: string; embeddingDim: number; ready: boolean }\n}\n\ninterface PendingCrop {\n readonly trackId: string\n readonly deviceId: string\n readonly class: string\n readonly confidence: number\n readonly frameData: Buffer\n readonly frameWidth: number\n readonly frameHeight: number\n readonly bbox: { readonly x: number; readonly y: number; readonly w: number; readonly h: number }\n readonly receivedAt: number\n}\n\n/** Local narrow of a pipeline analysis result item (runtime shape from detection pipeline). */\ninterface PipelineAnalysisItem {\n readonly detection?: {\n readonly class: string\n readonly score: number\n readonly trackId?: string\n readonly bbox: { readonly x: number; readonly y: number; readonly w: number; readonly h: number }\n }\n readonly objectState?: {\n readonly state: string\n }\n readonly zoneEvents?: readonly { readonly zoneId: string; readonly type: string; readonly timestamp?: number }[]\n}\n\n/** Extended pipeline result with frame data attached by the pipeline stage. */\ninterface PipelineResultWithFrame {\n readonly frameData?: Buffer\n readonly imageWidth?: number\n readonly imageHeight?: number\n [key: string]: unknown\n}\n\nexport interface EmbeddingDispatcherDeps {\n readonly config: EnrichmentConfig['embedding']\n readonly encoders: readonly EmbeddingEncoder[]\n readonly eventBus: IEventBus\n readonly logger: IScopedLogger\n}\n\nexport class EmbeddingDispatcher {\n private readonly config: EnrichmentConfig['embedding']\n private readonly encoders: readonly EmbeddingEncoder[]\n private readonly eventBus: IEventBus\n private readonly logger: IScopedLogger\n\n private readonly lastEmbedTime = new Map<string, number>()\n private readonly pendingCrops = new Map<string, PendingCrop>()\n private flushTimer: ReturnType<typeof setInterval> | null = null\n private unsubscribe: (() => void) | null = null\n\n private _processedCount = 0\n private _totalInferenceMs = 0\n private _encoderIndex = 0\n\n constructor(deps: EmbeddingDispatcherDeps) {\n this.config = deps.config\n this.encoders = deps.encoders\n this.eventBus = deps.eventBus\n this.logger = deps.logger\n }\n\n async start(): Promise<void> {\n if (!this.config.enabled) {\n this.logger.info('EmbeddingDispatcher disabled')\n return\n }\n\n this.unsubscribe = this.eventBus.subscribe(\n { category: EventCategory.DetectionResult },\n (event) => { void this.handleDetectionResult(event) },\n )\n\n // Flush timer for best-confidence strategy\n if (this.config.cropStrategy === 'best-confidence') {\n this.flushTimer = setInterval(() => { void this.flushPending() }, 1000)\n }\n\n this.logger.info('EmbeddingDispatcher started', { meta: { strategy: this.config.cropStrategy, maxPerSec: this.config.maxPerSecPerCamera } })\n }\n\n async stop(): Promise<void> {\n this.unsubscribe?.()\n this.unsubscribe = null\n if (this.flushTimer) {\n clearInterval(this.flushTimer)\n this.flushTimer = null\n }\n // Flush remaining\n await this.flushPending()\n }\n\n get processedCount(): number { return this._processedCount }\n get avgInferenceMs(): number { return this._processedCount > 0 ? this._totalInferenceMs / this._processedCount : 0 }\n get queueDepth(): number { return this.pendingCrops.size }\n\n private async handleDetectionResult(event: TypedSystemEvent<'detection.result'>): Promise<void> {\n const data = event.data\n const deviceId = event.source.id !== undefined ? String(event.source.id) : ''\n if (!deviceId) return\n\n // analysisResults items are untyped pipeline outputs — narrow to local interface at boundary\n const detections = (data.analysisResults as readonly PipelineAnalysisItem[]) ?? []\n // pipelineResult may carry frame data attached by the analysis stage — narrow at boundary\n const pipelineResult = data.pipelineResult as unknown as PipelineResultWithFrame\n\n for (const det of detections) {\n const detection = det.detection\n if (!detection) continue\n\n // Class filter\n if (this.config.classes.length > 0 && !this.config.classes.includes(detection.class)) continue\n\n // Confidence filter\n if (detection.score < this.config.minConfidence) continue\n\n // Rate limit\n const now = Date.now()\n const minInterval = 1000 / this.config.maxPerSecPerCamera\n const lastTime = this.lastEmbedTime.get(deviceId) ?? 0\n if (now - lastTime < minInterval) continue\n\n const trackId = detection.trackId ?? `${deviceId}-${now}`\n const frameData = pipelineResult.frameData\n const frameWidth = pipelineResult.imageWidth\n const frameHeight = pipelineResult.imageHeight\n\n if (!frameData || !frameWidth || !frameHeight) continue\n\n const pending: PendingCrop = {\n trackId,\n deviceId,\n class: detection.class,\n confidence: detection.score,\n frameData,\n frameWidth,\n frameHeight,\n bbox: detection.bbox,\n receivedAt: now,\n }\n\n switch (this.config.cropStrategy) {\n case 'first': {\n if (!this.pendingCrops.has(trackId)) {\n this.pendingCrops.set(trackId, pending)\n void this.processOne(pending)\n }\n break\n }\n case 'best-confidence': {\n const existing = this.pendingCrops.get(trackId)\n if (!existing || pending.confidence > existing.confidence) {\n this.pendingCrops.set(trackId, pending)\n }\n break\n }\n case 'track-end': {\n const state = det.objectState?.state\n if (state === 'leaving') {\n this.pendingCrops.set(trackId, pending)\n void this.processOne(pending)\n } else {\n const existing = this.pendingCrops.get(trackId)\n if (!existing || pending.confidence > existing.confidence) {\n this.pendingCrops.set(trackId, pending)\n }\n }\n break\n }\n }\n }\n }\n\n private async flushPending(): Promise<void> {\n const now = Date.now()\n const toFlush: PendingCrop[] = []\n\n for (const [trackId, pending] of this.pendingCrops) {\n if (now - pending.receivedAt > 3000) {\n toFlush.push(pending)\n this.pendingCrops.delete(trackId)\n }\n }\n\n await Promise.all(toFlush.map(p => this.processOne(p)))\n }\n\n private async processOne(pending: PendingCrop): Promise<void> {\n if (this.encoders.length === 0) return\n\n try {\n const { crop, width, height } = await extractCrop(\n pending.frameData, pending.frameWidth, pending.frameHeight, pending.bbox,\n )\n\n // Round-robin across encoders\n const encoder = this.encoders[this._encoderIndex % this.encoders.length]!\n this._encoderIndex++\n\n const { embedding: _embedding, inferenceMs } = await encoder.encode(crop, width, height)\n const info = encoder.getInfo()\n\n this._processedCount++\n this._totalInferenceMs += inferenceMs\n this.lastEmbedTime.set(pending.deviceId, Date.now())\n this.pendingCrops.delete(pending.trackId)\n\n const embeddingId = `${pending.deviceId}/${pending.trackId}/${Date.now()}`\n\n const payload: EmbeddingStoredEvent = {\n deviceId: Number(pending.deviceId),\n trackId: pending.trackId,\n class: pending.class,\n embeddingId,\n modelId: info.modelId,\n embeddingDim: info.embeddingDim,\n inferenceMs,\n timestamp: Date.now(),\n }\n\n this.eventBus.emit(createEvent(\n EventCategory.EnrichmentEmbeddingStored,\n { type: 'addon', id: 'enrichment-engine' },\n payload,\n ))\n\n this.logger.debug('Embedded track', {\n tags: { deviceId: Number(pending.deviceId) },\n meta: { class: pending.class, trackId: pending.trackId, inferenceMs: Number(inferenceMs.toFixed(1)) },\n })\n } catch (err) {\n this.logger.warn('Failed to embed track', {\n tags: { deviceId: Number(pending.deviceId) },\n meta: { trackId: pending.trackId, error: String(err) },\n })\n }\n }\n}\n","/**\n * Zod schemas for analytics-suite persisted record types.\n * Used by persistence services to parse settings-store query results.\n */\nimport { z } from 'zod'\n\n// ── Track Trail ─────────────────────────────────────────────────────\n\nexport const TrackPositionSchema = z.object({\n x: z.number(),\n y: z.number(),\n timestamp: z.number(),\n bbox: z.tuple([z.number(), z.number(), z.number(), z.number()]),\n})\n\nexport const TrackSnapshotSchema = z.object({\n timestamp: z.number(),\n position: TrackPositionSchema,\n thumbnailPath: z.string(),\n})\n\nexport const TrackTrailSchema = z.object({\n trackId: z.string(),\n deviceId: z.string(),\n className: z.string(),\n label: z.string().optional(),\n firstSeen: z.number(),\n lastSeen: z.number(),\n positions: z.array(TrackPositionSchema),\n snapshots: z.array(TrackSnapshotSchema),\n totalDistance: z.number(),\n zonesVisited: z.array(z.string()),\n active: z.boolean(),\n})\n\nexport type TrackTrail = z.infer<typeof TrackTrailSchema>\n\n// ── Scene Monitor ───────────────────────────────────────────────────\n\nexport const SceneMonitorConfigSchema = z.object({\n monitors: z.array(z.object({\n id: z.string(),\n label: z.string(),\n roi: z.object({ x: z.number(), y: z.number(), w: z.number(), h: z.number() }),\n enabled: z.boolean(),\n states: z.array(z.object({\n id: z.string(),\n label: z.string(),\n prompt: z.string().optional(),\n textPrompts: z.array(z.string()).optional(),\n referenceEmbeddings: z.array(z.array(z.number())).optional(),\n threshold: z.number().optional(),\n notify: z.boolean().optional(),\n severity: z.enum(['info', 'warning', 'alert']).optional(),\n })),\n })),\n})\n\nexport type SceneMonitorConfig = z.infer<typeof SceneMonitorConfigSchema>\n\n/** Single scene monitor entry (element of SceneMonitorConfig.monitors). */\nexport type SceneMonitor = SceneMonitorConfig['monitors'][number]\n","/**\n * In-memory cache for text prompt embeddings.\n *\n * Keys follow the convention `${monitorId}:${text}` so that all entries\n * belonging to a specific scene monitor can be invalidated in one call.\n */\nexport class TextEmbeddingCache {\n private readonly cache = new Map<string, Float32Array>()\n\n get(key: string): Float32Array | undefined {\n return this.cache.get(key)\n }\n\n set(key: string, embedding: Float32Array): void {\n this.cache.set(key, embedding)\n }\n\n has(key: string): boolean {\n return this.cache.has(key)\n }\n\n /**\n * Deletes all entries whose key starts with `${monitorId}:`.\n * Used when a scene monitor's text prompts are updated.\n */\n invalidateMonitor(monitorId: string): void {\n const prefix = `${monitorId}:`\n for (const key of this.cache.keys()) {\n if (key.startsWith(prefix)) {\n this.cache.delete(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n }\n\n get size(): number {\n return this.cache.size\n }\n}\n","import { EventCategory } from '@camstack/types'\nimport type { IEventBus, IScopedLogger, SettingsStoreClient } from '@camstack/types'\nimport type { EnrichmentConfig, SceneStateChangedEvent } from '../types.js'\nimport { SceneMonitorConfigSchema, type SceneMonitor } from '../../_analytics-schemas/persistence-records.js'\nimport { TextEmbeddingCache } from '../services/text-embedding-cache.js'\nimport { extractCrop } from '../services/crop-extractor.js'\n\ninterface EmbeddingEncoder {\n encode(crop: Buffer, width: number, height: number): Promise<{ embedding: Float32Array; inferenceMs: number }>\n encodeText(text: string): Promise<{ embedding: Float32Array }>\n getInfo(): { modelId: string; embeddingDim: number; ready: boolean }\n}\n\ninterface StreamBrokerRegistry {\n getSnapshot(deviceId: string): Promise<{ data: Buffer; width: number; height: number } | null>\n}\n\ninterface CameraMonitorState {\n currentState: string | null\n pendingState: string | null\n pendingCount: number\n pendingConfidence: number\n}\n\nexport interface SceneStateWorkerDeps {\n readonly config: EnrichmentConfig['sceneMonitor']\n readonly encoders: readonly EmbeddingEncoder[]\n readonly eventBus: IEventBus\n readonly store: SettingsStoreClient\n readonly streamBrokerRegistry: StreamBrokerRegistry\n readonly logger: IScopedLogger\n}\n\nexport class SceneStateWorker {\n private readonly config: EnrichmentConfig['sceneMonitor']\n private readonly encoders: readonly EmbeddingEncoder[]\n private readonly eventBus: IEventBus\n private readonly store: SettingsStoreClient\n private readonly streamBrokerRegistry: StreamBrokerRegistry\n private readonly logger: IScopedLogger\n\n private readonly textCache = new TextEmbeddingCache()\n private readonly monitorStates = new Map<string, CameraMonitorState>()\n private pollTimer: ReturnType<typeof setInterval> | null = null\n private _activeCount = 0\n\n constructor(deps: SceneStateWorkerDeps) {\n this.config = deps.config\n this.encoders = deps.encoders\n this.eventBus = deps.eventBus\n this.store = deps.store\n this.streamBrokerRegistry = deps.streamBrokerRegistry\n this.logger = deps.logger\n }\n\n async start(): Promise<void> {\n if (!this.config.enabled) {\n this.logger.info('SceneStateWorker disabled')\n return\n }\n\n this.pollTimer = setInterval(\n () => { void this.pollAll() },\n this.config.pollIntervalSec * 1000,\n )\n this.logger.info('SceneStateWorker started', { meta: { pollIntervalSec: this.config.pollIntervalSec, hysteresis: this.config.hysteresisCount } })\n }\n\n async stop(): Promise<void> {\n if (this.pollTimer) {\n clearInterval(this.pollTimer)\n this.pollTimer = null\n }\n }\n\n get activeCount(): number { return this._activeCount }\n\n private async pollAll(): Promise<void> {\n // Load all scene monitor configs from settings\n const rawConfigs = await this.store.query.query({ collection: 'device-settings', filter: {\n where: { id: 'enrichment:scene-monitor:%' },\n } })\n const allConfigs = rawConfigs.map(r => ({ id: r.id, data: SceneMonitorConfigSchema.parse(r.data) }))\n\n const deviceMonitors = new Map<string, SceneMonitor[]>()\n for (const record of allConfigs) {\n const deviceId = record.id.replace('enrichment:scene-monitor:', '')\n const config = record.data\n const active = config.monitors.filter(m => m.enabled)\n if (active.length > 0) {\n deviceMonitors.set(deviceId, active)\n }\n }\n\n this._activeCount = [...deviceMonitors.values()].reduce((sum, ms) => sum + ms.length, 0)\n\n // Process each camera (1 snapshot per camera)\n await Promise.all(\n [...deviceMonitors.entries()].map(([deviceId, monitors]) =>\n this.pollCamera(deviceId, monitors),\n ),\n )\n }\n\n private async pollCamera(deviceId: string, monitors: SceneMonitor[]): Promise<void> {\n if (this.encoders.length === 0) return\n\n try {\n // Capture 1 snapshot\n const snapshot = await this.streamBrokerRegistry.getSnapshot(deviceId)\n if (!snapshot) return\n\n const encoder = this.encoders[0]!\n\n // Process each monitor (crop from same snapshot)\n for (const monitor of monitors) {\n try {\n await this.processMonitor(deviceId, monitor, snapshot, encoder)\n } catch (err) {\n this.logger.warn('SceneState error', { tags: { deviceId: Number(deviceId) }, meta: { monitorLabel: monitor.label, error: String(err) } })\n }\n }\n } catch (err) {\n this.logger.warn('Failed to capture snapshot', { tags: { deviceId: Number(deviceId) }, meta: { error: String(err) } })\n }\n }\n\n private async processMonitor(\n deviceId: string,\n monitor: SceneMonitor,\n snapshot: { data: Buffer; width: number; height: number },\n encoder: EmbeddingEncoder,\n ): Promise<void> {\n // Crop ROI (normalized → pixel coords)\n const bbox = {\n x: monitor.roi.x * snapshot.width,\n y: monitor.roi.y * snapshot.height,\n w: monitor.roi.w * snapshot.width,\n h: monitor.roi.h * snapshot.height,\n }\n const { crop, width, height } = await extractCrop(snapshot.data, snapshot.width, snapshot.height, bbox)\n\n // Encode crop\n const { embedding: imageEmb } = await encoder.encode(crop, width, height)\n\n // Find best matching state\n let bestState: string | null = null\n let bestScore = -1\n\n for (const state of monitor.states) {\n let score = 0\n\n if (state.referenceEmbeddings && state.referenceEmbeddings.length > 0) {\n for (const refEmb of state.referenceEmbeddings) {\n const ref = new Float32Array(refEmb)\n const sim = cosineSimilarity(imageEmb, ref)\n score = Math.max(score, sim)\n }\n } else if (state.textPrompts && state.textPrompts.length > 0) {\n for (const prompt of state.textPrompts) {\n const cacheKey = `${monitor.id}:${state.id}:${prompt}`\n let textEmb = this.textCache.get(cacheKey)\n if (!textEmb) {\n const result = await encoder.encodeText(prompt)\n textEmb = result.embedding\n this.textCache.set(cacheKey, textEmb)\n }\n const sim = cosineSimilarity(imageEmb, textEmb)\n score = Math.max(score, sim)\n }\n }\n\n if (score > bestScore) {\n bestScore = score\n bestState = state.label\n }\n }\n\n if (!bestState) return\n\n // Hysteresis\n const key = `${deviceId}:${monitor.id}`\n let ms = this.monitorStates.get(key)\n if (!ms) {\n ms = { currentState: null, pendingState: null, pendingCount: 0, pendingConfidence: 0 }\n this.monitorStates.set(key, ms)\n }\n\n if (bestState === ms.pendingState) {\n ms.pendingCount++\n ms.pendingConfidence = bestScore\n } else {\n ms.pendingState = bestState\n ms.pendingCount = 1\n ms.pendingConfidence = bestScore\n }\n\n if (ms.pendingCount < this.config.hysteresisCount) return\n if (bestState === ms.currentState) return\n\n // State changed!\n const previousState = ms.currentState ?? 'unknown'\n ms.currentState = bestState\n ms.pendingState = null\n ms.pendingCount = 0\n\n const payload: SceneStateChangedEvent = {\n deviceId: Number(deviceId),\n monitorId: monitor.id,\n monitorLabel: monitor.label,\n previousState,\n currentState: bestState,\n confidence: bestScore,\n timestamp: Date.now(),\n }\n\n const data: Record<string, unknown> = { ...payload }\n this.eventBus.emit({\n id: `scene-state-${deviceId}-${monitor.id}-${Date.now()}`,\n category: EventCategory.EnrichmentSceneStateChanged,\n source: { type: 'device', id: deviceId },\n timestamp: new Date(),\n data,\n })\n\n this.logger.info('Scene state changed', {\n tags: { deviceId: Number(deviceId) },\n meta: { monitorLabel: monitor.label, previousState, currentState: bestState, confidence: Number(bestScore.toFixed(2)) },\n })\n }\n}\n\nfunction cosineSimilarity(a: Float32Array, b: Float32Array): number {\n let dot = 0\n let normA = 0\n let normB = 0\n for (let i = 0; i < a.length; i++) {\n dot += a[i]! * b[i]!\n normA += a[i]! * a[i]!\n normB += b[i]! * b[i]!\n }\n return dot / (Math.sqrt(normA) * Math.sqrt(normB))\n}\n","import { EventCategory, createEvent } from '@camstack/types'\nimport type { IEventBus, IScopedLogger, SettingsStoreClient, TypedSystemEvent } from '@camstack/types'\nimport type { EnrichmentConfig, ActivitySummaryEvent, StateChangeEntry } from '../types.js'\n\n/** Local narrow of a pipeline analysis result item (runtime shape from detection pipeline). */\ninterface PipelineAnalysisItem {\n readonly detection?: {\n readonly class: string\n readonly trackId?: string\n }\n readonly zoneEvents?: readonly { readonly zoneId: string; readonly type: string; readonly timestamp?: number }[]\n}\n\ninterface TrackInfo {\n class: string\n deviceId: string\n firstSeen: number\n lastSeen: number\n zones: Set<string>\n}\n\ninterface ZoneEventInfo {\n type: 'enter' | 'exit'\n zoneId: string\n trackId: string\n timestamp: number\n}\n\ninterface ActivityBuffer {\n tracks: Map<string, TrackInfo>\n zoneEvents: ZoneEventInfo[]\n stateChanges: StateChangeEntry[]\n}\n\nexport interface ActivitySummaryWorkerDeps {\n readonly config: EnrichmentConfig['activitySummary']\n readonly eventBus: IEventBus\n readonly store: SettingsStoreClient\n readonly logger: IScopedLogger\n}\n\nexport class ActivitySummaryWorker {\n private readonly config: EnrichmentConfig['activitySummary']\n private readonly eventBus: IEventBus\n private readonly store: SettingsStoreClient\n private readonly logger: IScopedLogger\n\n private readonly buffers = new Map<string, ActivityBuffer>()\n private summaryTimer: ReturnType<typeof setInterval> | null = null\n private unsubDetection: (() => void) | null = null\n private unsubSceneState: (() => void) | null = null\n private _lastSummary: ActivitySummaryEvent | null = null\n\n constructor(deps: ActivitySummaryWorkerDeps) {\n this.config = deps.config\n this.eventBus = deps.eventBus\n this.store = deps.store\n this.logger = deps.logger\n }\n\n async start(): Promise<void> {\n if (!this.config.enabled) {\n this.logger.info('ActivitySummaryWorker disabled')\n return\n }\n\n this.unsubDetection = this.eventBus.subscribe(\n { category: EventCategory.DetectionResult },\n (event) => { this.handleDetection(event) },\n )\n\n this.unsubSceneState = this.eventBus.subscribe(\n { category: EventCategory.EnrichmentSceneStateChanged },\n (event) => { this.handleSceneStateChange(event) },\n )\n\n this.summaryTimer = setInterval(\n () => { void this.emitSummaries() },\n this.config.intervalSec * 1000,\n )\n\n this.logger.info('ActivitySummaryWorker started', { meta: { intervalSec: this.config.intervalSec } })\n }\n\n async stop(): Promise<void> {\n this.unsubDetection?.()\n this.unsubSceneState?.()\n if (this.summaryTimer) {\n clearInterval(this.summaryTimer)\n this.summaryTimer = null\n }\n // Emit final summaries\n await this.emitSummaries()\n }\n\n get lastSummary(): ActivitySummaryEvent | null { return this._lastSummary }\n\n private getBuffer(deviceId: string): ActivityBuffer {\n let buf = this.buffers.get(deviceId)\n if (!buf) {\n buf = { tracks: new Map(), zoneEvents: [], stateChanges: [] }\n this.buffers.set(deviceId, buf)\n }\n return buf\n }\n\n private handleDetection(event: TypedSystemEvent<'detection.result'>): void {\n const deviceId = event.source.id !== undefined ? String(event.source.id) : ''\n if (!deviceId) return\n\n // analysisResults items are untyped pipeline outputs — narrow to local interface at boundary\n const results = (event.data.analysisResults as readonly PipelineAnalysisItem[]) ?? []\n const buf = this.getBuffer(deviceId)\n const now = Date.now()\n\n for (const det of results) {\n const detection = det.detection\n if (!detection) continue\n\n const trackId = detection.trackId ?? `unknown-${now}`\n const existing = buf.tracks.get(trackId)\n\n if (existing) {\n existing.lastSeen = now\n } else {\n buf.tracks.set(trackId, {\n class: detection.class,\n deviceId,\n firstSeen: now,\n lastSeen: now,\n zones: new Set(),\n })\n }\n\n // Zone events\n const zoneEvents = det.zoneEvents ?? []\n for (const ze of zoneEvents) {\n const track = buf.tracks.get(trackId)\n if (track) track.zones.add(ze.zoneId)\n\n if (ze.type === 'zone-enter' || ze.type === 'zone-exit') {\n buf.zoneEvents.push({\n type: ze.type === 'zone-enter' ? 'enter' : 'exit',\n zoneId: ze.zoneId,\n trackId,\n timestamp: ze.timestamp ?? now,\n })\n }\n }\n }\n }\n\n private handleSceneStateChange(event: TypedSystemEvent<'enrichment.scene.state-changed'>): void {\n const deviceId = event.source.id !== undefined ? String(event.source.id) : ''\n if (!deviceId) return\n\n const data = event.data\n const buf = this.getBuffer(deviceId)\n buf.stateChanges.push({\n monitorId: data.monitorId,\n from: data.previousState,\n to: data.currentState,\n timestamp: data.timestamp,\n })\n }\n\n private async emitSummaries(): Promise<void> {\n const now = Date.now()\n\n for (const [deviceId, buf] of this.buffers) {\n if (buf.tracks.size === 0 && buf.zoneEvents.length === 0 && buf.stateChanges.length === 0) {\n continue\n }\n\n // Object counts by class\n const objectCounts: Record<string, number> = {}\n for (const track of buf.tracks.values()) {\n objectCounts[track.class] = (objectCounts[track.class] ?? 0) + 1\n }\n\n // Zone activity\n const zoneMap = new Map<string, { entries: number; exits: number; dwellTimes: number[] }>()\n for (const ze of buf.zoneEvents) {\n let z = zoneMap.get(ze.zoneId)\n if (!z) {\n z = { entries: 0, exits: 0, dwellTimes: [] }\n zoneMap.set(ze.zoneId, z)\n }\n if (ze.type === 'enter') z.entries++\n else z.exits++\n }\n\n const zoneActivity = [...zoneMap.entries()].map(([zoneId, z]) => ({\n zoneId,\n entries: z.entries,\n exits: z.exits,\n avgDwellMs: z.dwellTimes.length > 0\n ? z.dwellTimes.reduce((a, b) => a + b, 0) / z.dwellTimes.length\n : 0,\n }))\n\n // Activity level\n const totalEvents = buf.tracks.size + buf.zoneEvents.length\n const eventsPerMin = totalEvents / (this.config.intervalSec / 60)\n const activityLevel = eventsPerMin >= this.config.activityThresholds.high ? 'high'\n : eventsPerMin >= this.config.activityThresholds.medium ? 'medium'\n : eventsPerMin >= this.config.activityThresholds.low ? 'low'\n : 'none' as const\n\n const summary: ActivitySummaryEvent = {\n deviceId: Number(deviceId),\n periodStart: now - this.config.intervalSec * 1000,\n periodEnd: now,\n objectCounts,\n zoneActivity,\n stateChanges: [...buf.stateChanges],\n activityLevel,\n }\n\n this._lastSummary = summary\n\n this.eventBus.emit(createEvent(\n EventCategory.EnrichmentActivitySummary,\n { type: 'device', id: deviceId },\n summary,\n ))\n\n // Persist for timeline UI\n try {\n await this.store.insert.mutate({ collection: 'addon-settings', record: {\n id: `${deviceId}:${now}`,\n data: { ...summary },\n } })\n } catch {\n // Best-effort persistence\n }\n\n // Reset buffer\n buf.tracks.clear()\n buf.zoneEvents.length = 0\n buf.stateChanges.length = 0\n }\n }\n}\n","import { BaseAddon, asJsonObject } from '@camstack/types'\nimport type { EnrichmentConfig } from './types.js'\nimport { DEFAULT_ENRICHMENT_CONFIG } from './types.js'\nimport { EmbeddingDispatcher } from './workers/embedding-dispatcher.js'\nimport type { EmbeddingDispatcherDeps } from './workers/embedding-dispatcher.js'\nimport { SceneStateWorker } from './workers/scene-state-worker.js'\nimport type { SceneStateWorkerDeps } from './workers/scene-state-worker.js'\nimport { ActivitySummaryWorker } from './workers/activity-summary.js'\n\n/**\n * Extended context shape injected at runtime by the server's capability wiring.\n * Not part of the base AddonContext interface because capabilities are resolved\n * after addon initialization.\n */\n\nexport class EnrichmentEngineAddon extends BaseAddon {\n private embeddingDispatcher: EmbeddingDispatcher | null = null\n private sceneStateWorker: SceneStateWorker | null = null\n private activitySummary: ActivitySummaryWorker | null = null\n private currentFlags = {\n embeddingEnabled: true,\n sceneMonitorEnabled: true,\n activitySummaryEnabled: true,\n }\n constructor() { super({}) }\n\n protected async onInitialize(): Promise<void> {\n const config = await this.loadConfig()\n\n // Encoders will be injected by the server via capability wiring.\n // The server extends AddonContext with a `capabilities` object at runtime.\n // Runtime bridges: capabilities.getCollection/get return unknown because\n // the registry holds heterogeneous providers. The shapes are guaranteed\n // by the capability declaration — see project_cast_elimination_checkpoint.md\n // pattern #7.\n const encoderCollection = this.capabilities?.getCollection?.('embedding-encoder') ?? []\n const streamBrokerRaw = this.capabilities?.get?.('stream-broker')\n\n this.embeddingDispatcher = new EmbeddingDispatcher({\n config: config.embedding,\n encoders: encoderCollection as EmbeddingDispatcherDeps['encoders'],\n eventBus: this.ctx.eventBus,\n logger: this.ctx.logger.child('EmbeddingDispatcher'),\n })\n\n this.sceneStateWorker = new SceneStateWorker({\n config: config.sceneMonitor,\n encoders: encoderCollection as SceneStateWorkerDeps['encoders'],\n eventBus: this.ctx.eventBus,\n store: this.ctx.api!.settingsStore,\n streamBrokerRegistry: (streamBrokerRaw ?? { getSnapshot: async () => null }) as SceneStateWorkerDeps['streamBrokerRegistry'],\n logger: this.ctx.logger.child('SceneStateWorker'),\n })\n\n this.activitySummary = new ActivitySummaryWorker({\n config: config.activitySummary,\n eventBus: this.ctx.eventBus,\n store: this.ctx.api!.settingsStore,\n logger: this.ctx.logger.child('ActivitySummary'),\n })\n\n this.currentFlags = {\n embeddingEnabled: config.embedding.enabled,\n sceneMonitorEnabled: config.sceneMonitor.enabled,\n activitySummaryEnabled: config.activitySummary.enabled,\n }\n\n await this.embeddingDispatcher.start()\n await this.sceneStateWorker.start()\n await this.activitySummary.start()\n\n this.ctx.logger.info('Enrichment engine initialized with 3 workers')\n }\n\n protected async onShutdown(): Promise<void> {\n await this.embeddingDispatcher?.stop()\n await this.sceneStateWorker?.stop()\n await this.activitySummary?.stop()\n this.embeddingDispatcher = null\n this.sceneStateWorker = null\n this.activitySummary = null\n }\n\n // ── Three-level settings API (Phase 3) ─────────────────────────────\n //\n // Enrichment engine is post-detection infra. The three boolean toggle\n // flags (embedding/scene/activity) live in `getGlobalSettings` until\n // the dedicated post-detection UI exists. The underlying\n // EnrichmentConfig blob (stored opaquely in 'addon-settings' →\n // 'enrichment:global') is a separate concern — the flags below mirror\n // the `.enabled` field of each worker's config.\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [\n {\n id: 'enrichment-engine-settings',\n title: 'Enrichment Engine',\n columns: 2,\n fields: [\n {\n type: 'boolean',\n key: 'embeddingEnabled',\n label: 'Embedding Enabled',\n description: 'Compute face/object embeddings from detection crops.',\n default: true,\n },\n {\n type: 'boolean',\n key: 'sceneMonitorEnabled',\n label: 'Scene Monitor Enabled',\n description: 'Run periodic scene state capture for change detection.',\n default: true,\n },\n {\n type: 'boolean',\n key: 'activitySummaryEnabled',\n label: 'Activity Summary Enabled',\n description: 'Aggregate hourly activity summaries per camera.',\n default: true,\n },\n ],\n },\n ],\n })\n }\n\n async updateGlobalSettings(patch: Record<string, unknown>): Promise<void> {\n await this.ctx?.settings?.writeAddonStore(patch)\n const prev = this.currentFlags\n const next = {\n embeddingEnabled: typeof patch['embeddingEnabled'] === 'boolean' ? patch['embeddingEnabled'] : prev.embeddingEnabled,\n sceneMonitorEnabled: typeof patch['sceneMonitorEnabled'] === 'boolean' ? patch['sceneMonitorEnabled'] : prev.sceneMonitorEnabled,\n activitySummaryEnabled: typeof patch['activitySummaryEnabled'] === 'boolean' ? patch['activitySummaryEnabled'] : prev.activitySummaryEnabled,\n }\n this.currentFlags = next\n\n // Toggle workers based on flag changes\n if (prev.embeddingEnabled && !next.embeddingEnabled) {\n this.ctx?.logger.info('Stopping embedding dispatcher (disabled via config)')\n await this.embeddingDispatcher?.stop()\n } else if (!prev.embeddingEnabled && next.embeddingEnabled) {\n this.ctx?.logger.info('Starting embedding dispatcher (enabled via config)')\n await this.embeddingDispatcher?.start()\n }\n\n if (prev.sceneMonitorEnabled && !next.sceneMonitorEnabled) {\n this.ctx?.logger.info('Stopping scene state worker (disabled via config)')\n await this.sceneStateWorker?.stop()\n } else if (!prev.sceneMonitorEnabled && next.sceneMonitorEnabled) {\n this.ctx?.logger.info('Starting scene state worker (enabled via config)')\n await this.sceneStateWorker?.start()\n }\n\n if (prev.activitySummaryEnabled && !next.activitySummaryEnabled) {\n this.ctx?.logger.info('Stopping activity summary worker (disabled via config)')\n await this.activitySummary?.stop()\n } else if (!prev.activitySummaryEnabled && next.activitySummaryEnabled) {\n this.ctx?.logger.info('Starting activity summary worker (enabled via config)')\n await this.activitySummary?.start()\n }\n\n this.ctx?.logger.info('Enrichment engine flags updated', { meta: { flags: this.currentFlags } })\n }\n\n private async loadConfig(): Promise<EnrichmentConfig> {\n try {\n const stored = asJsonObject(await this.ctx.api?.settingsStore.get.query({ collection: 'addon-settings', key: 'enrichment:global' }))\n if (stored) {\n // Shallow structural merge: only keys from DEFAULT_ENRICHMENT_CONFIG\n // are preserved; stored values override their counterparts when\n // present with a matching type.\n const merged: EnrichmentConfig = { ...DEFAULT_ENRICHMENT_CONFIG, ...stored }\n return merged\n }\n } catch {\n // First boot — use defaults\n }\n return DEFAULT_ENRICHMENT_CONFIG\n }\n}\n\n// Default export — kernel addon-loader prefers mod.default to identify the addon class\nexport default EnrichmentEngineAddon\n"],"names":["EventCategory","createEvent","z.object","z.number","z.tuple","z.string","z.array","z.boolean","z.enum","BaseAddon","asJsonObject"],"mappings":"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"}