@camstack/addon-post-analysis 1.2.649 → 1.2.651

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 (16) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{clip-model-registry-DLCu17dL.mjs → clip-model-registry-BWY8P-AK.mjs} +620 -7
  3. package/dist/{clip-model-registry-BweA9zEK.js → clip-model-registry-Dte3QQEt.js} +620 -7
  4. package/dist/embedding-encoder/index.js +1 -1
  5. package/dist/embedding-encoder/index.mjs +1 -1
  6. package/dist/pipeline-analytics/_stub.js +2 -2
  7. package/dist/pipeline-analytics/{_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DFq-lnbD.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-_vVfXkDx.mjs} +2 -2
  8. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6PIugMbv.mjs +26 -0
  9. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-Bk6VlAsZ.mjs +26 -0
  10. package/dist/pipeline-analytics/{hostInit-Dog0wpFI.mjs → hostInit-A7m5uu3n.mjs} +2 -2
  11. package/dist/pipeline-analytics/index.js +148 -3
  12. package/dist/pipeline-analytics/index.mjs +148 -4
  13. package/dist/pipeline-analytics/remoteEntry.js +1 -1
  14. package/package.json +1 -1
  15. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-YDf26qsN.mjs +0 -26
  16. package/dist/pipeline-analytics/_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DQTr4r2P.mjs +0 -26
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_clip_model_registry = require("../clip-model-registry-BweA9zEK.js");
5
+ const require_clip_model_registry = require("../clip-model-registry-Dte3QQEt.js");
6
6
  let node_fs = require("node:fs");
7
7
  node_fs = require_clip_model_registry.__toESM(node_fs, 1);
8
8
  let node_path = require("node:path");
@@ -54676,6 +54676,8 @@ var FrameState = class {
54676
54676
  gatedNodeByDevice = /* @__PURE__ */ new Map();
54677
54677
  /** Throttle state for the "overlay synthesis failed" warn, one per device. */
54678
54678
  overlaySynthesisWarnAt = /* @__PURE__ */ new Map();
54679
+ /** Throttle state for the "overlay skipped a stale frame" debug line, one per device. */
54680
+ overlayStaleLogAt = /* @__PURE__ */ new Map();
54679
54681
  /** Per-device throttle for the inactive-binding frame-drop warn (5 min). */
54680
54682
  inactiveBindingDropWarnAt = /* @__PURE__ */ new Map();
54681
54683
  lastAudioInsertByDevice = /* @__PURE__ */ new Map();
@@ -70436,6 +70438,11 @@ Object.freeze({
70436
70438
  "setDeviceConfig": { "config": { "__nested__": { "bands": [] } } }
70437
70439
  },
70438
70440
  "records": { "query": { "limit": 100 } },
70441
+ "remote": {
70442
+ "sendCommand": { "repeat": 1 },
70443
+ "sendMediaCommand": { "repeat": 1 },
70444
+ "setConfig": { "config": { "__nested__": { "defaultTransmitterId": null } } }
70445
+ },
70439
70446
  "storage": { "upsertLocation": { "isSystem": false } },
70440
70447
  "storage-admin": { "readRecords": { "limit": 100 } },
70441
70448
  "storage-provider": {
@@ -70527,6 +70534,7 @@ Object.freeze({
70527
70534
  "recording": ["listSources"],
70528
70535
  "storage-occupancy": ["getOccupancy"],
70529
70536
  "storage-provider": ["list"],
70537
+ "template": ["list"],
70530
70538
  "training-provider": [
70531
70539
  "describe",
70532
70540
  "describeTypes",
@@ -87284,6 +87292,7 @@ var NcDispatcher = class {
87284
87292
  ...target.ownerUserId !== void 0 ? { ownerUserId: target.ownerUserId } : {},
87285
87293
  ...entry.payload.subject.className !== void 0 ? { className: entry.payload.subject.className } : {},
87286
87294
  ...entry.payload.subject.trackId !== void 0 ? { trackId: entry.payload.subject.trackId } : {},
87295
+ eventAtMs: entry.payload.subject.timestamp,
87287
87296
  ...target.maxActions !== void 0 ? { maxActions: target.maxActions } : {},
87288
87297
  ...entry.payload.subject.systemEvent !== void 0 ? { systemEvent: entry.payload.subject.systemEvent } : {}
87289
87298
  });
@@ -100056,6 +100065,7 @@ async function mintButtons(host, input) {
100056
100065
  userId: verdictOwner,
100057
100066
  ...spec.suggestedClass !== void 0 ? { suggestedClass: spec.suggestedClass } : {},
100058
100067
  ...input.maxActions === 0 ? { viaLink: true } : {},
100068
+ ...input.eventAtMs !== void 0 ? { eventAtMs: input.eventAtMs } : {},
100059
100069
  deviceId: input.deviceId,
100060
100070
  actionId: spec.actionId,
100061
100071
  targetId: input.targetId,
@@ -100517,6 +100527,57 @@ var VERDICT_REFUSAL_COPY = {
100517
100527
  budget: "This camera already holds as many tracks for retraining as it may — nothing was changed",
100518
100528
  "unknown-track": "This track is no longer available — nothing was changed"
100519
100529
  };
100530
+ /** What a person is told when the recording fallback refuses too. */
100531
+ var VERDICT_FRAME_REFUSAL_COPY = {
100532
+ "still-unavailable": "The track is gone and the recording has no frame for that moment — nothing was changed",
100533
+ "still-busy": "The track is gone and the recorder is busy — nothing was changed, try again later",
100534
+ failed: "The track is gone and its frame could not be saved — nothing was changed"
100535
+ };
100536
+ /**
100537
+ * The track is gone (aged out, or discarded as a still object): the camera's
100538
+ * RECORDING still covers the instant, so the verdict lands a frame there
100539
+ * instead. Returns when the frame landed; throws a named refusal otherwise —
100540
+ * every branch that drops the example logs.
100541
+ */
100542
+ async function landFrameFromRecording(host, grant) {
100543
+ const tags = { deviceId: grant.deviceId };
100544
+ const addFrame = host.deps.addVerdictFrameFromRecording;
100545
+ if (addFrame === void 0 || grant.eventAtMs === void 0) {
100546
+ host.logger.warn("detection verdict dropped — the track is gone and no recording fallback", {
100547
+ tags,
100548
+ meta: {
100549
+ trackId: grant.trackId,
100550
+ verdict: grant.verdict,
100551
+ wired: addFrame !== void 0
100552
+ }
100553
+ });
100554
+ throw new NcActionRefusedError(VERDICT_REFUSAL_COPY["unknown-track"]);
100555
+ }
100556
+ const landed = await addFrame({
100557
+ deviceId: grant.deviceId,
100558
+ epochMs: grant.eventAtMs
100559
+ });
100560
+ if (!landed.ok) {
100561
+ host.logger.warn("detection verdict dropped — the track is gone and the recording refused", {
100562
+ tags,
100563
+ meta: {
100564
+ trackId: grant.trackId,
100565
+ verdict: grant.verdict,
100566
+ reason: landed.reason,
100567
+ ...landed.detail !== void 0 ? { detail: landed.detail } : {}
100568
+ }
100569
+ });
100570
+ throw new NcActionRefusedError(VERDICT_FRAME_REFUSAL_COPY[landed.reason]);
100571
+ }
100572
+ host.logger.info("detection verdict landed a recording frame — the track is gone", {
100573
+ tags,
100574
+ meta: {
100575
+ trackId: grant.trackId,
100576
+ verdict: grant.verdict,
100577
+ epochMs: grant.eventAtMs
100578
+ }
100579
+ });
100580
+ }
100520
100581
  /**
100521
100582
  * Mark the track one notification reported for retraining, and record who was
100522
100583
  * asked and when.
@@ -100533,7 +100594,8 @@ async function applyGrantedDetectionVerdict(host, grant) {
100533
100594
  deviceId: grant.deviceId,
100534
100595
  trackId: grant.trackId
100535
100596
  });
100536
- if (!outcome.ok) {
100597
+ if (!outcome.ok && outcome.reason === "unknown-track") await landFrameFromRecording(host, grant);
100598
+ else if (!outcome.ok) {
100537
100599
  host.logger.info("detection verdict changed nothing — the retrain flow refused the mark", {
100538
100600
  tags: { deviceId: grant.deviceId },
100539
100601
  meta: {
@@ -108524,6 +108586,39 @@ function buildOccupancyDepartureDeps(input) {
108524
108586
  };
108525
108587
  }
108526
108588
  //#endregion
108589
+ //#region src/pipeline-analytics/boot/verdict-recording-frame.ts
108590
+ var VERDICT_COLLECTION_NAME = "Notification verdicts";
108591
+ async function findOrCreate(api) {
108592
+ const wanted = VERDICT_COLLECTION_NAME.toLowerCase();
108593
+ const find = async () => (await api.listRetrainCollections()).find((c) => c.name.toLowerCase() === wanted)?.id ?? null;
108594
+ const existing = await find();
108595
+ if (existing !== null) return existing;
108596
+ const created = await api.createRetrainCollection(VERDICT_COLLECTION_NAME);
108597
+ if (created.kind === "created") return created.collection.id;
108598
+ return created.reason === "duplicate-name" ? find() : null;
108599
+ }
108600
+ async function addVerdictFrameFromRecording(api, logger, input) {
108601
+ const collectionId = await findOrCreate(api);
108602
+ if (collectionId === null) {
108603
+ logger.warn("verdict frame dropped — the \"Notification verdicts\" collection is unavailable", { tags: { deviceId: input.deviceId } });
108604
+ return {
108605
+ ok: false,
108606
+ reason: "failed",
108607
+ detail: "collection-unavailable"
108608
+ };
108609
+ }
108610
+ const result = await api.addRetrainFrameFromRecording({
108611
+ collectionId,
108612
+ ...input
108613
+ });
108614
+ if (result.kind === "added") return { ok: true };
108615
+ return {
108616
+ ok: false,
108617
+ reason: result.reason === "still-unavailable" || result.reason === "still-busy" ? result.reason : "failed",
108618
+ detail: result.detail ?? result.reason
108619
+ };
108620
+ }
108621
+ //#endregion
108527
108622
  //#region src/pipeline-analytics/recognition-outlook.ts
108528
108623
  /** The step ids whose announce means "a recogniser may name this class". */
108529
108624
  var FACE_DETAIL_STEP_ID = "face-detection";
@@ -108885,6 +108980,26 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
108885
108980
  };
108886
108981
  }
108887
108982
  },
108983
+ addVerdictFrameFromRecording: async (input) => {
108984
+ const pa = this.ctx.api.pipelineAnalytics;
108985
+ try {
108986
+ return await addVerdictFrameFromRecording({
108987
+ listRetrainCollections: () => pa.listRetrainCollections.query({}),
108988
+ createRetrainCollection: (name) => pa.createRetrainCollection.mutate({ name }),
108989
+ addRetrainFrameFromRecording: (frame) => pa.addRetrainFrameFromRecording.mutate(frame)
108990
+ }, this.ctx.logger, input);
108991
+ } catch (err) {
108992
+ this.ctx.logger.warn("verdict frame dropped — the recording fallback threw", {
108993
+ tags: { deviceId: input.deviceId },
108994
+ meta: { error: require_clip_model_registry.errMsg(err) }
108995
+ });
108996
+ return {
108997
+ ok: false,
108998
+ reason: "failed",
108999
+ detail: require_clip_model_registry.errMsg(err)
109000
+ };
109001
+ }
109002
+ },
108888
109003
  readDeviceStates: async (ids) => {
108889
109004
  const out = /* @__PURE__ */ new Map();
108890
109005
  for (const id of ids) try {
@@ -120587,6 +120702,16 @@ var LTS_FLUSH_INTERVAL_MS = 6e4;
120587
120702
  var OPS_LOG_RETENTION_FALLBACK_MS = 720 * 60 * 6e4;
120588
120703
  /** Throttle for the "overlay synthesis failed" warn — one line / minute / device. */
120589
120704
  var OVERLAY_SYNTHESIS_WARN_THROTTLE_MS = 6e4;
120705
+ /**
120706
+ * A frame older than this is not drawn on the live overlay. On-motion session
120707
+ * cameras replay the GOP pre-roll when a session starts, so frames 2-9 s old
120708
+ * reach the tracker (they still feed tracking and analytics); drawing them
120709
+ * paints boxes for where the subject WAS. 1 s is generous for a live frame's
120710
+ * decode + inference latency.
120711
+ */
120712
+ var OVERLAY_MAX_FRAME_AGE_MS = 1e3;
120713
+ /** The stale-overlay-frame skip is logged at most once per device per this window. */
120714
+ var OVERLAY_STALE_LOG_THROTTLE_MS = 6e4;
120590
120715
  /** How recently a spatial track must have been alive to count as "visual
120591
120716
  * happening" — stamp audio labels on that track instead of opening a
120592
120717
  * synthetic audio episode. */
@@ -123725,6 +123850,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
123725
123850
  this.frameState.packageLookDropReasons.delete(data.deviceId);
123726
123851
  this.overlayState.clearDevice(data.deviceId);
123727
123852
  this.frameState.overlaySynthesisWarnAt.delete(data.deviceId);
123853
+ this.frameState.overlayStaleLogAt.delete(data.deviceId);
123728
123854
  this.forgetDeviceProcessors(data.deviceId);
123729
123855
  this.frameState.levelStateByDevice.delete(data.deviceId);
123730
123856
  this.frameState.audioConfirmByDevice.delete(data.deviceId);
@@ -123747,6 +123873,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
123747
123873
  this.frameState.packageLookDropReasons.delete(deviceId);
123748
123874
  this.overlayState.clearDevice(deviceId);
123749
123875
  this.frameState.overlaySynthesisWarnAt.delete(deviceId);
123876
+ this.frameState.overlayStaleLogAt.delete(deviceId);
123750
123877
  this.forgetDeviceProcessors(deviceId);
123751
123878
  this.frameState.levelStateByDevice.delete(deviceId);
123752
123879
  this.frameState.motionOnsets.forgetDevice(deviceId);
@@ -124361,6 +124488,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
124361
124488
  this.parkedFrames?.dispose();
124362
124489
  this.overlayState.clear();
124363
124490
  this.frameState.overlaySynthesisWarnAt.clear();
124491
+ this.frameState.overlayStaleLogAt.clear();
124364
124492
  const tapeRecorder = this.tapeRecorder;
124365
124493
  this.tapeRecorder = null;
124366
124494
  this.tapeShelf = null;
@@ -125452,6 +125580,21 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
125452
125580
  config: stillnessConfig
125453
125581
  }).still;
125454
125582
  });
125583
+ const frameAgeMs = Date.now() - result.timestamp;
125584
+ const frameIsStale = frameAgeMs > OVERLAY_MAX_FRAME_AGE_MS;
125585
+ if (frameIsStale) {
125586
+ const nowMs = Date.now();
125587
+ if (nowMs - (this.frameState.overlayStaleLogAt.get(deviceId) ?? 0) >= OVERLAY_STALE_LOG_THROTTLE_MS) {
125588
+ this.frameState.overlayStaleLogAt.set(deviceId, nowMs);
125589
+ this.ctx.logger.debug("overlay boxes withheld for a stale frame (replayed pre-roll); presence still emitted", {
125590
+ tags: { deviceId },
125591
+ meta: {
125592
+ frameAgeMs,
125593
+ maxFrameAgeMs: OVERLAY_MAX_FRAME_AGE_MS
125594
+ }
125595
+ });
125596
+ }
125597
+ }
125455
125598
  this.ctx.eventBus.emit({
125456
125599
  id: `pa-${(0, node_crypto.randomUUID)()}`,
125457
125600
  timestamp: new Date(result.timestamp),
@@ -125466,7 +125609,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
125466
125609
  timestamp: result.timestamp,
125467
125610
  frameWidth: result.frameWidth,
125468
125611
  frameHeight: result.frameHeight,
125469
- detections: overlayDetections,
125612
+ detections: frameIsStale ? [] : overlayDetections,
125470
125613
  hasLiveTrack,
125471
125614
  liveTrackCount: result.tracked.length
125472
125615
  }
@@ -127929,6 +128072,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_clip_m
127929
128072
  this.packageBackground?.clearDevice(input.deviceId);
127930
128073
  this.overlayState.clearDevice(input.deviceId);
127931
128074
  this.frameState.overlaySynthesisWarnAt.delete(input.deviceId);
128075
+ this.frameState.overlayStaleLogAt.delete(input.deviceId);
127932
128076
  const prefix = `${input.deviceId}:`;
127933
128077
  for (const k of this.frameState.lastActiveTrackIds.keys()) if (k.startsWith(prefix)) this.frameState.lastActiveTrackIds.delete(k);
127934
128078
  }
@@ -130909,6 +131053,7 @@ function withMean(row) {
130909
131053
  //#endregion
130910
131054
  exports.CONFIRMATION_CROP_MAX_WIDTH = CONFIRMATION_CROP_MAX_WIDTH;
130911
131055
  exports.DETECTION_PIPELINE_SECTION_IDS = DETECTION_PIPELINE_SECTION_IDS;
131056
+ exports.OVERLAY_MAX_FRAME_AGE_MS = OVERLAY_MAX_FRAME_AGE_MS;
130912
131057
  exports.customActions = customActions;
130913
131058
  exports.default = PipelineAnalyticsAddon;
130914
131059
  exports.ncActions = ncActions;
@@ -1,4 +1,4 @@
1
- import { $ as MediaFileKindEnum, $i as BaseAddon, $n as cloneAlarmSettings, $r as paintHex, $t as OpsLogEntrySchema, A as DeclaredDevices, Aa as unknown, Ai as storageOccupancyCapability, An as TrackSideSchema, Ar as isAudioClassName, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as systemEventSummarySplitOf, Bn as applyModeDelaysEdits, Br as isRootSuppliableStepId, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as partialRecord, Ci as ruleGroupingApplies, Cn as TimelapseRuleInputSchema, Cr as failureContributionCapability, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Da as templateLiteral, Di as sensorRaisedOf, Dn as TrackPuritySchema, Dr as isAlertEpisodeOpen, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ea as string, Ei as secureActuationFor, En as TrackPlacementSchema, Er as isAlarmControllableCap, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as syncActiveAlarmProfileSettings, Fn as alarmPanelCapability, Fr as isEventOwnerType, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as withSystemEventClasses, Gn as audioMetricsCapability, Gr as kebabToCamel, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as templateVarsFor, Hn as applySecureOnArmEdit, Hr as isSystemDelivery, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as systemEventDeliveryProblems, In as alertEpisodeId, Ir as isKnownTemplateVar, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as canonicalHash, Jn as buildSubjectPath, Jr as memoizePinnedFfmpeg, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as zoneAnalyticsCapability, Kn as audioModeOf, Kr as linkedStepOf, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as systemEventFamilyProblems, Ln as alertEpisodeKind, Lr as isLabelSupplyStepId, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as subjectPathSampleFromBox, Mn as activateAlarmProfile, Mr as isDetectionMacroClass, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as subjectPathSvgPoints, Nn as addonWidgetsSourceCapability, Nr as isDrawableSubjectPath, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oa as tuple, Oi as servedTemplateVars, On as TrackRelationKindSchema, Or as isAlertEpisodeSeverity, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as summarizeReview, Pn as alarmDeviceSecureOnArm, Pr as isEventKindLabelKey, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as errMsg, Qn as classifyRetrainReview, Qr as occupancyScope, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as systemEventFilterApplies, Rn as alertEpisodePayload, Rr as isMediaOwnerType, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as object, Si as ruleDeviceCompatCapQueries, Sn as TRACK_RELATIONS_DEFAULT_LIMIT, Sr as faceGalleryCapability, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ta as strictObject, Ti as sceneMonitorCapability, Tn as TimelapseRuleSchema, Tr as gifFromMp4Args, Tt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, U as LabelAttributionSchema, Ui as vectorDimFromBase64, Un as armRemovalReconcile, Ur as isSystemEventSummaryRule, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as templateCarriesSummary, Vn as applyNonBlockingEdit, Vr as isScheduleActive, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as viewerLinkBaseOf, Wn as assertTimelapseCadences, Wr as isVectorIndexDimConflict, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as isSourceCap, Xn as bundledCamstack, Xr as normalizeNcMediaPolicy, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as evaluateSensorEdge, Yn as buildViewerLink, Yr as ncMediaPolicyError, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as DeviceType, Zn as calendarEventLocalSpan, Zr as notificationRulesCapability, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as boolean, _n as SceneMonitorSchema, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as createEvent, ai as plateGalleryCapability, an as RetrainDismissedSchema, ar as deleteAlarmProfile, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as looseObject, bi as ruleAutoTriggersAlarm, bn as SetTrackRelationInputSchema, br as eventKindLabel, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as defineRecordKinds, ci as readMotionTriggeredBy, cn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, cr as deriveRecordingMode, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, da as nodePin, di as resolveClipSearchMinScore, dn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, dr as deviceAttentionKind, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as CAP_ADVANCE_FIELDS, ei as paletteResultSchema, en as PackageLlmVerdictSchema, er as consumableFormOf, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fi as resolveDeliveryParams, fn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, fr as deviceRemovalHookCapability, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as array, gi as resolveNcMediaImage, gn as SCENE_RESET_RECAPTURES, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as _enum, hi as resolveModeNotifyPolicy, hn as SCENE_DIVERGED, hr as embeddingRefusalOf, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ia as RECORD_QUERY_MAX, ii as pipelineAnalyticsCapability, in as RebuildObjectEmbeddingsRefusalSchema, ir as createAlarmProfile, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ja as EventCategory, ji as subKindsOf, jn as TrackSourceSchema, jr as isDefaultAlarmProfileId, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ka as union, ki as startMoverUnderClearance, kn as TrackRelationSchema, kr as isAppLinkBase, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as hydrateSchema, li as readTimelapseGeneratedAt, ln as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, lr as describeSystemEventDeliveryProblem, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as ZodObject, mi as resolveModeDelays, mn as SCENE_DEFAULT_UNCOVERED_POLICY, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as InvalidRecordError, nn as RECORDING_EXPORT_MAX_READ_BYTES, nr as coreBlockIdFromAddonId, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as customAction, oi as projectSubjectPath, on as RetrainOriginSchema, or as deriveDetailCropRect, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as sleep$1, pi as resolveLocationMode, pn as SCENE_DEFAULT_QUIET_SECONDS, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, qn as buildEventKindDescriptor, qr as mayWriteToLocation, qt as NcSystemEventKindSchema, ra as RECORD_MAX_DATA_BYTES, ri as pickFaceRescoreIntervalMinutes, rn as RECORDING_SOURCE_CAMSTACK_ADDON, rr as cosineSimilarity$1, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, sa as defineCustomActions, si as readDeviceStateFrom, sn as RetrainStatusSchema, sr as derivePostTriggeredAction, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as CamProfileSchema, ti as parseAlertEpisodePayload, tr as consumableLowPercentOf, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as isDeviceScopedCap, ui as renameAlarmProfile, un as SCENE_DEFAULT_ANCHOR_THRESHOLD, ur as describeSystemEventFamilyProblem, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as discriminatedUnion, vi as retrainRevisionConflictMessage, vn as SetTrackPurityInputSchema, vr as encodeVectorBase64, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wa as record, wi as ruleKindOf, wn as TimelapseRulePatchSchema, wr as gifDeriveSpeed, wt as NC_STATE_SUMMARY_SECTION_IDS, x as COCO_TO_MACRO, xa as number, xi as ruleBelongsToAlarm$1, xn as TIMELAPSE_DENSE_FLOOR_SEC, xr as evictionPolicyOfLocation, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as literal, yi as rootSupplyServes, yn as SetTrackPurityResultSchema, yr as ensureAlarmProfiles, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as systemEventSummaryScheduleOf, zn as applyBlockingEdit, zr as isRetiredClearKey, zt as NcRuleTargetSchema } from "../clip-model-registry-DLCu17dL.mjs";
1
+ import { $ as MediaFileKindEnum, $i as BaseAddon, $n as cloneAlarmSettings, $r as paintHex, $t as OpsLogEntrySchema, A as DeclaredDevices, Aa as unknown, Ai as storageOccupancyCapability, An as TrackSideSchema, Ar as isAudioClassName, At as NcAlarmSensorsInputSchema, B as FULL_IMAGE_BBOX, Bi as systemEventSummarySplitOf, Bn as applyModeDelaysEdits, Br as isRootSuppliableStepId, Bt as NcRuleUpdateInputSchema, C as ClearTrackRelationResultSchema, Ca as partialRecord, Ci as ruleGroupingApplies, Cn as TimelapseRuleInputSchema, Cr as failureContributionCapability, Ct as NC_SNOOZE_MAX_MINUTES, D as DEFAULT_TIMELAPSE_PREVIEW_TEXT, Da as templateLiteral, Di as sensorRaisedOf, Dn as TrackPuritySchema, Dr as isAlertEpisodeOpen, Dt as NC_TEMPLATE_VARS, E as DEFAULT_EVENT_COLOR, Ea as string, Ei as secureActuationFor, En as TrackPlacementSchema, Er as isAlarmControllableCap, Et as NC_TAXONOMY, F as EVENT_DENSITY_BATCH_MAX, Fi as syncActiveAlarmProfileSettings, Fn as alarmPanelCapability, Fr as isEventOwnerType, Ft as NcRuleActionSchema, G as ListTrackRelationsResultSchema, Gi as withSystemEventClasses, Gn as audioMetricsCapability, Gr as kebabToCamel, Gt as NcSnoozeSuppressedSchema, H as LABEL_SUPPLY_MACRO, Hi as templateVarsFor, Hn as applySecureOnArmEdit, Hr as isSystemDelivery, Ht as NcSharedSequenceSchema, I as EVENT_KIND_BY_CAP, Ii as systemEventDeliveryProblems, In as alertEpisodeId, Ir as isKnownTemplateVar, It as NcRuleInputSchema, J as MAX_BIRTH_DECISION_RECORDS, Ji as canonicalHash, Jn as buildSubjectPath, Jr as memoizePinnedFfmpeg, Jt as NcTaxonomySchema, K as LockStateSchema, Ki as zoneAnalyticsCapability, Kn as audioModeOf, Kr as linkedStepOf, Kt as NcStateSummarySectionIdSchema, L as EVENT_OWNER_TYPES, Li as systemEventFamilyProblems, Ln as alertEpisodeKind, Lr as isLabelSupplyStepId, Lt as NcRulePatchSchema, M as EMBEDDING_MODEL_FAILED, Mi as subjectPathSampleFromBox, Mn as activateAlarmProfile, Mr as isDetectionMacroClass, Mt as NcAlarmSettingsPatchSchema, N as EMBEDDING_REQUIREMENTS_FAILED, Ni as subjectPathSvgPoints, Nn as addonWidgetsSourceCapability, Nr as isDrawableSubjectPath, Nt as NcConditionDescriptorSchema, O as DETECTION_MACRO_CLASSES, Oa as tuple, Oi as servedTemplateVars, On as TrackRelationKindSchema, Or as isAlertEpisodeSeverity, Ot as NcAlarmConfigSchema, P as EMBEDDING_REQUIREMENTS_UNAVAILABLE, Pi as summarizeReview, Pn as alarmDeviceSecureOnArm, Pr as isEventKindLabelKey, Pt as NcConditionsSchema, Q as MOTION_CLOSE_AFTER_MS, Qi as errMsg, Qn as classifyRetrainReview, Qr as occupancyScope, Qt as OPS_LOG_DEFAULT_LIMIT, R as EXPORT_DENSE_MAX_RANGES, Ri as systemEventFilterApplies, Rn as alertEpisodePayload, Rr as isMediaOwnerType, Rt as NcRuleSchema, S as ClearTrackRelationInputSchema, Sa as object, Si as ruleDeviceCompatCapQueries, Sn as TRACK_RELATIONS_DEFAULT_LIMIT, Sr as faceGalleryCapability, St as NC_SENSOR_TRANSITION_KINDS, T as DEFAULT_DETAIL_CROP_CONVENTION, Ta as strictObject, Ti as sceneMonitorCapability, Tn as TimelapseRuleSchema, Tr as gifFromMp4Args, Tt as NC_SYSTEM_EVENT_KIND_GROUP_SPECS, U as LabelAttributionSchema, Ui as vectorDimFromBase64, Un as armRemovalReconcile, Ur as isSystemEventSummaryRule, Ut as NcSnoozeInputSchema, V as FailureCounters, Vi as templateCarriesSummary, Vn as applyNonBlockingEdit, Vr as isScheduleActive, Vt as NcScheduleSchema, W as ListTrackRelationsInputSchema, Wi as viewerLinkBaseOf, Wn as assertTimelapseCadences, Wr as isVectorIndexDimConflict, Wt as NcSnoozeSchema, X as MAX_TRACK_PURITIES, Xi as isSourceCap, Xn as bundledCamstack, Xr as normalizeNcMediaPolicy, Xt as NcTemplatePreviewSchema, Y as MAX_BIRTH_LATENCY_PROXY_MS, Yi as evaluateSensorEdge, Yn as buildViewerLink, Yr as ncMediaPolicyError, Yt as NcTemplatePreviewInputSchema, Z as MAX_TRACK_RELATIONS, Zi as DeviceType, Zn as calendarEventLocalSpan, Zr as notificationRulesCapability, Zt as NcTemplateVarDescriptorSchema, _ as BIRTH_DECISION_DEFAULT_LIMIT, _a as boolean, _n as SceneMonitorSchema, _t as NC_CONSUMABLE_SYSTEM_EVENT_KINDS, a as CLIP_EMBEDDING_STEP_ID, aa as createEvent, ai as plateGalleryCapability, an as RetrainDismissedSchema, ar as deleteAlarmProfile, at as NC_ALARM_NOTIFY_FIELDS, b as BucketAccumulator, ba as looseObject, bi as ruleAutoTriggersAlarm, bn as SetTrackRelationInputSchema, br as eventKindLabel, bt as NC_GROUPING_MAX_SEC, c as readClusterModelPinStrict, ca as defineRecordKinds, ci as readMotionTriggeredBy, cn as SCENE_CONFIRM_DEFAULT_MAX_IMAGE_PX, cr as deriveRecordingMode, ct as NC_AUDIO_CONFIRM_HITS_DEFAULT, d as APPEARANCE_BLOCKS, da as nodePin, di as resolveClipSearchMinScore, dn as SCENE_DEFAULT_CHECK_INTERVAL_SEC, dr as deviceAttentionKind, dt as NC_CLIP_PRE_ROLL_DEFAULT_SEC, ea as CAP_ADVANCE_FIELDS, ei as paletteResultSchema, en as PackageLlmVerdictSchema, er as consumableFormOf, et as MotionTriggeredBySchema, f as APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT, fi as resolveDeliveryParams, fn as SCENE_DEFAULT_OBSERVATION_SPACING_SEC, fr as deviceRemovalHookCapability, ft as NC_CLIP_RETENTION_MAX_SEC, g as ArchivedDebugNoteSchema, ga as array, gi as resolveNcMediaImage, gn as SCENE_RESET_RECAPTURES, gt as NC_CONSUMABLE_LOW_PERCENT_DEFAULT, h as AlarmArmModeSchema, ha as _enum, hi as resolveModeNotifyPolicy, hn as SCENE_DIVERGED, hr as embeddingRefusalOf, ht as NC_CONFIRM_DEFAULT_TIMEOUT_MS, ia as RECORD_QUERY_MAX, ii as pipelineAnalyticsCapability, in as RebuildObjectEmbeddingsRefusalSchema, ir as createAlarmProfile, it as NC_ALARM_DELAY_MAX_SEC, j as DetectionSourceSchema, ja as EventCategory, ji as subKindsOf, jn as TrackSourceSchema, jr as isDefaultAlarmProfileId, jt as NcAlarmSensorsSchema, k as DETECTION_PIPELINE_CAP_NAME, ka as union, ki as startMoverUnderClearance, kn as TrackRelationSchema, kr as isAppLinkBase, kt as NcAlarmPostTriggeredActionSchema, l as resolveClusterModelPin, la as hydrateSchema, li as readTimelapseGeneratedAt, ln as SCENE_CONFIRM_DEFAULT_TIMEOUT_MS, lr as describeSystemEventDeliveryProblem, lt as NC_AUDIO_CONFIRM_WINDOW_SEC_DEFAULT, m as AUDIO_MACRO_LABELS, ma as ZodObject, mi as resolveModeDelays, mn as SCENE_DEFAULT_UNCOVERED_POLICY, mt as NC_CONFIRM_DEFAULT_MAX_IMAGE_PX, na as InvalidRecordError, nn as RECORDING_EXPORT_MAX_READ_BYTES, nr as coreBlockIdFromAddonId, nt as NC_ALARM_DEFAULT_TRIGGERED_DURATION_SEC, o as CLUSTER_MODEL_OWNER_ADDON_ID, oa as customAction, oi as projectSubjectPath, on as RetrainOriginSchema, or as deriveDetailCropRect, ot as NC_ALARM_SYSTEM_EVENT_KINDS, p as ARCHIVED_DEBUG_NOTES_DEFAULT_LIMIT, pa as sleep$1, pi as resolveLocationMode, pn as SCENE_DEFAULT_QUIET_SECONDS, pt as NC_CONDITION_CATALOG, q as MAX_ARCHIVED_DEBUG_NOTES, qi as DEFAULT_FIRST_SIGHTING_FRESHNESS_MS, qn as buildEventKindDescriptor, qr as mayWriteToLocation, qt as NcSystemEventKindSchema, ra as RECORD_MAX_DATA_BYTES, ri as pickFaceRescoreIntervalMinutes, rn as RECORDING_SOURCE_CAMSTACK_ADDON, rr as cosineSimilarity$1, rt as NC_ALARM_DELAY_FIELDS, s as FACE_EMBEDDING_STEP_ID, sa as defineCustomActions, si as readDeviceStateFrom, sn as RetrainStatusSchema, sr as derivePostTriggeredAction, st as NC_ALARM_TRIGGERED_MAX_SEC, t as BUILTIN_CLIP_MODELS, ta as CamProfileSchema, ti as parseAlertEpisodePayload, tr as consumableLowPercentOf, tt as NC_ALARM_DEFAULT_PROFILE_ID, u as APPEARANCE_BIN_COUNT, ua as isDeviceScopedCap, ui as renameAlarmProfile, un as SCENE_DEFAULT_ANCHOR_THRESHOLD, ur as describeSystemEventFamilyProblem, ut as NC_CLIP_POST_ROLL_DEFAULT_SEC, v as BaseDevice, va as discriminatedUnion, vi as retrainRevisionConflictMessage, vn as SetTrackPurityInputSchema, vr as encodeVectorBase64, vt as NC_DEFAULT_SNOOZE_MINUTES, w as CoverStateSchema, wa as record, wi as ruleKindOf, wn as TimelapseRulePatchSchema, wr as gifDeriveSpeed, wt as NC_STATE_SUMMARY_SECTION_IDS, x as COCO_TO_MACRO, xa as number, xi as ruleBelongsToAlarm$1, xn as TIMELAPSE_DENSE_FLOOR_SEC, xr as evictionPolicyOfLocation, xt as NC_GROUPING_MIN_SEC, y as BirthDecisionRecordSchema, ya as literal, yi as rootSupplyServes, yn as SetTrackPurityResultSchema, yr as ensureAlarmProfiles, yt as NC_GROUPING_DEFAULT_SEC, z as FIRST_LEVEL_MACRO_CLASSES, zi as systemEventSummaryScheduleOf, zn as applyBlockingEdit, zr as isRetiredClearKey, zt as NcRuleTargetSchema } from "../clip-model-registry-BWY8P-AK.mjs";
2
2
  import { t as __exportAll } from "../embedding-encoder/index.mjs";
3
3
  import * as fs$1 from "node:fs";
4
4
  import { createWriteStream, promises } from "node:fs";
@@ -54581,6 +54581,8 @@ var FrameState = class {
54581
54581
  gatedNodeByDevice = /* @__PURE__ */ new Map();
54582
54582
  /** Throttle state for the "overlay synthesis failed" warn, one per device. */
54583
54583
  overlaySynthesisWarnAt = /* @__PURE__ */ new Map();
54584
+ /** Throttle state for the "overlay skipped a stale frame" debug line, one per device. */
54585
+ overlayStaleLogAt = /* @__PURE__ */ new Map();
54584
54586
  /** Per-device throttle for the inactive-binding frame-drop warn (5 min). */
54585
54587
  inactiveBindingDropWarnAt = /* @__PURE__ */ new Map();
54586
54588
  lastAudioInsertByDevice = /* @__PURE__ */ new Map();
@@ -70341,6 +70343,11 @@ Object.freeze({
70341
70343
  "setDeviceConfig": { "config": { "__nested__": { "bands": [] } } }
70342
70344
  },
70343
70345
  "records": { "query": { "limit": 100 } },
70346
+ "remote": {
70347
+ "sendCommand": { "repeat": 1 },
70348
+ "sendMediaCommand": { "repeat": 1 },
70349
+ "setConfig": { "config": { "__nested__": { "defaultTransmitterId": null } } }
70350
+ },
70344
70351
  "storage": { "upsertLocation": { "isSystem": false } },
70345
70352
  "storage-admin": { "readRecords": { "limit": 100 } },
70346
70353
  "storage-provider": {
@@ -70432,6 +70439,7 @@ Object.freeze({
70432
70439
  "recording": ["listSources"],
70433
70440
  "storage-occupancy": ["getOccupancy"],
70434
70441
  "storage-provider": ["list"],
70442
+ "template": ["list"],
70435
70443
  "training-provider": [
70436
70444
  "describe",
70437
70445
  "describeTypes",
@@ -87189,6 +87197,7 @@ var NcDispatcher = class {
87189
87197
  ...target.ownerUserId !== void 0 ? { ownerUserId: target.ownerUserId } : {},
87190
87198
  ...entry.payload.subject.className !== void 0 ? { className: entry.payload.subject.className } : {},
87191
87199
  ...entry.payload.subject.trackId !== void 0 ? { trackId: entry.payload.subject.trackId } : {},
87200
+ eventAtMs: entry.payload.subject.timestamp,
87192
87201
  ...target.maxActions !== void 0 ? { maxActions: target.maxActions } : {},
87193
87202
  ...entry.payload.subject.systemEvent !== void 0 ? { systemEvent: entry.payload.subject.systemEvent } : {}
87194
87203
  });
@@ -99961,6 +99970,7 @@ async function mintButtons(host, input) {
99961
99970
  userId: verdictOwner,
99962
99971
  ...spec.suggestedClass !== void 0 ? { suggestedClass: spec.suggestedClass } : {},
99963
99972
  ...input.maxActions === 0 ? { viaLink: true } : {},
99973
+ ...input.eventAtMs !== void 0 ? { eventAtMs: input.eventAtMs } : {},
99964
99974
  deviceId: input.deviceId,
99965
99975
  actionId: spec.actionId,
99966
99976
  targetId: input.targetId,
@@ -100422,6 +100432,57 @@ var VERDICT_REFUSAL_COPY = {
100422
100432
  budget: "This camera already holds as many tracks for retraining as it may — nothing was changed",
100423
100433
  "unknown-track": "This track is no longer available — nothing was changed"
100424
100434
  };
100435
+ /** What a person is told when the recording fallback refuses too. */
100436
+ var VERDICT_FRAME_REFUSAL_COPY = {
100437
+ "still-unavailable": "The track is gone and the recording has no frame for that moment — nothing was changed",
100438
+ "still-busy": "The track is gone and the recorder is busy — nothing was changed, try again later",
100439
+ failed: "The track is gone and its frame could not be saved — nothing was changed"
100440
+ };
100441
+ /**
100442
+ * The track is gone (aged out, or discarded as a still object): the camera's
100443
+ * RECORDING still covers the instant, so the verdict lands a frame there
100444
+ * instead. Returns when the frame landed; throws a named refusal otherwise —
100445
+ * every branch that drops the example logs.
100446
+ */
100447
+ async function landFrameFromRecording(host, grant) {
100448
+ const tags = { deviceId: grant.deviceId };
100449
+ const addFrame = host.deps.addVerdictFrameFromRecording;
100450
+ if (addFrame === void 0 || grant.eventAtMs === void 0) {
100451
+ host.logger.warn("detection verdict dropped — the track is gone and no recording fallback", {
100452
+ tags,
100453
+ meta: {
100454
+ trackId: grant.trackId,
100455
+ verdict: grant.verdict,
100456
+ wired: addFrame !== void 0
100457
+ }
100458
+ });
100459
+ throw new NcActionRefusedError(VERDICT_REFUSAL_COPY["unknown-track"]);
100460
+ }
100461
+ const landed = await addFrame({
100462
+ deviceId: grant.deviceId,
100463
+ epochMs: grant.eventAtMs
100464
+ });
100465
+ if (!landed.ok) {
100466
+ host.logger.warn("detection verdict dropped — the track is gone and the recording refused", {
100467
+ tags,
100468
+ meta: {
100469
+ trackId: grant.trackId,
100470
+ verdict: grant.verdict,
100471
+ reason: landed.reason,
100472
+ ...landed.detail !== void 0 ? { detail: landed.detail } : {}
100473
+ }
100474
+ });
100475
+ throw new NcActionRefusedError(VERDICT_FRAME_REFUSAL_COPY[landed.reason]);
100476
+ }
100477
+ host.logger.info("detection verdict landed a recording frame — the track is gone", {
100478
+ tags,
100479
+ meta: {
100480
+ trackId: grant.trackId,
100481
+ verdict: grant.verdict,
100482
+ epochMs: grant.eventAtMs
100483
+ }
100484
+ });
100485
+ }
100425
100486
  /**
100426
100487
  * Mark the track one notification reported for retraining, and record who was
100427
100488
  * asked and when.
@@ -100438,7 +100499,8 @@ async function applyGrantedDetectionVerdict(host, grant) {
100438
100499
  deviceId: grant.deviceId,
100439
100500
  trackId: grant.trackId
100440
100501
  });
100441
- if (!outcome.ok) {
100502
+ if (!outcome.ok && outcome.reason === "unknown-track") await landFrameFromRecording(host, grant);
100503
+ else if (!outcome.ok) {
100442
100504
  host.logger.info("detection verdict changed nothing — the retrain flow refused the mark", {
100443
100505
  tags: { deviceId: grant.deviceId },
100444
100506
  meta: {
@@ -108429,6 +108491,39 @@ function buildOccupancyDepartureDeps(input) {
108429
108491
  };
108430
108492
  }
108431
108493
  //#endregion
108494
+ //#region src/pipeline-analytics/boot/verdict-recording-frame.ts
108495
+ var VERDICT_COLLECTION_NAME = "Notification verdicts";
108496
+ async function findOrCreate(api) {
108497
+ const wanted = VERDICT_COLLECTION_NAME.toLowerCase();
108498
+ const find = async () => (await api.listRetrainCollections()).find((c) => c.name.toLowerCase() === wanted)?.id ?? null;
108499
+ const existing = await find();
108500
+ if (existing !== null) return existing;
108501
+ const created = await api.createRetrainCollection(VERDICT_COLLECTION_NAME);
108502
+ if (created.kind === "created") return created.collection.id;
108503
+ return created.reason === "duplicate-name" ? find() : null;
108504
+ }
108505
+ async function addVerdictFrameFromRecording(api, logger, input) {
108506
+ const collectionId = await findOrCreate(api);
108507
+ if (collectionId === null) {
108508
+ logger.warn("verdict frame dropped — the \"Notification verdicts\" collection is unavailable", { tags: { deviceId: input.deviceId } });
108509
+ return {
108510
+ ok: false,
108511
+ reason: "failed",
108512
+ detail: "collection-unavailable"
108513
+ };
108514
+ }
108515
+ const result = await api.addRetrainFrameFromRecording({
108516
+ collectionId,
108517
+ ...input
108518
+ });
108519
+ if (result.kind === "added") return { ok: true };
108520
+ return {
108521
+ ok: false,
108522
+ reason: result.reason === "still-unavailable" || result.reason === "still-busy" ? result.reason : "failed",
108523
+ detail: result.detail ?? result.reason
108524
+ };
108525
+ }
108526
+ //#endregion
108432
108527
  //#region src/pipeline-analytics/recognition-outlook.ts
108433
108528
  /** The step ids whose announce means "a recogniser may name this class". */
108434
108529
  var FACE_DETAIL_STEP_ID = "face-detection";
@@ -108790,6 +108885,26 @@ var BuildNotificationCenter = class extends BuildNotificationCenterBase {
108790
108885
  };
108791
108886
  }
108792
108887
  },
108888
+ addVerdictFrameFromRecording: async (input) => {
108889
+ const pa = this.ctx.api.pipelineAnalytics;
108890
+ try {
108891
+ return await addVerdictFrameFromRecording({
108892
+ listRetrainCollections: () => pa.listRetrainCollections.query({}),
108893
+ createRetrainCollection: (name) => pa.createRetrainCollection.mutate({ name }),
108894
+ addRetrainFrameFromRecording: (frame) => pa.addRetrainFrameFromRecording.mutate(frame)
108895
+ }, this.ctx.logger, input);
108896
+ } catch (err) {
108897
+ this.ctx.logger.warn("verdict frame dropped — the recording fallback threw", {
108898
+ tags: { deviceId: input.deviceId },
108899
+ meta: { error: errMsg(err) }
108900
+ });
108901
+ return {
108902
+ ok: false,
108903
+ reason: "failed",
108904
+ detail: errMsg(err)
108905
+ };
108906
+ }
108907
+ },
108793
108908
  readDeviceStates: async (ids) => {
108794
108909
  const out = /* @__PURE__ */ new Map();
108795
108910
  for (const id of ids) try {
@@ -120492,6 +120607,16 @@ var LTS_FLUSH_INTERVAL_MS = 6e4;
120492
120607
  var OPS_LOG_RETENTION_FALLBACK_MS = 720 * 60 * 6e4;
120493
120608
  /** Throttle for the "overlay synthesis failed" warn — one line / minute / device. */
120494
120609
  var OVERLAY_SYNTHESIS_WARN_THROTTLE_MS = 6e4;
120610
+ /**
120611
+ * A frame older than this is not drawn on the live overlay. On-motion session
120612
+ * cameras replay the GOP pre-roll when a session starts, so frames 2-9 s old
120613
+ * reach the tracker (they still feed tracking and analytics); drawing them
120614
+ * paints boxes for where the subject WAS. 1 s is generous for a live frame's
120615
+ * decode + inference latency.
120616
+ */
120617
+ var OVERLAY_MAX_FRAME_AGE_MS = 1e3;
120618
+ /** The stale-overlay-frame skip is logged at most once per device per this window. */
120619
+ var OVERLAY_STALE_LOG_THROTTLE_MS = 6e4;
120495
120620
  /** How recently a spatial track must have been alive to count as "visual
120496
120621
  * happening" — stamp audio labels on that track instead of opening a
120497
120622
  * synthetic audio episode. */
@@ -123630,6 +123755,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
123630
123755
  this.frameState.packageLookDropReasons.delete(data.deviceId);
123631
123756
  this.overlayState.clearDevice(data.deviceId);
123632
123757
  this.frameState.overlaySynthesisWarnAt.delete(data.deviceId);
123758
+ this.frameState.overlayStaleLogAt.delete(data.deviceId);
123633
123759
  this.forgetDeviceProcessors(data.deviceId);
123634
123760
  this.frameState.levelStateByDevice.delete(data.deviceId);
123635
123761
  this.frameState.audioConfirmByDevice.delete(data.deviceId);
@@ -123652,6 +123778,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
123652
123778
  this.frameState.packageLookDropReasons.delete(deviceId);
123653
123779
  this.overlayState.clearDevice(deviceId);
123654
123780
  this.frameState.overlaySynthesisWarnAt.delete(deviceId);
123781
+ this.frameState.overlayStaleLogAt.delete(deviceId);
123655
123782
  this.forgetDeviceProcessors(deviceId);
123656
123783
  this.frameState.levelStateByDevice.delete(deviceId);
123657
123784
  this.frameState.motionOnsets.forgetDevice(deviceId);
@@ -124266,6 +124393,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
124266
124393
  this.parkedFrames?.dispose();
124267
124394
  this.overlayState.clear();
124268
124395
  this.frameState.overlaySynthesisWarnAt.clear();
124396
+ this.frameState.overlayStaleLogAt.clear();
124269
124397
  const tapeRecorder = this.tapeRecorder;
124270
124398
  this.tapeRecorder = null;
124271
124399
  this.tapeShelf = null;
@@ -125357,6 +125485,21 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
125357
125485
  config: stillnessConfig
125358
125486
  }).still;
125359
125487
  });
125488
+ const frameAgeMs = Date.now() - result.timestamp;
125489
+ const frameIsStale = frameAgeMs > OVERLAY_MAX_FRAME_AGE_MS;
125490
+ if (frameIsStale) {
125491
+ const nowMs = Date.now();
125492
+ if (nowMs - (this.frameState.overlayStaleLogAt.get(deviceId) ?? 0) >= OVERLAY_STALE_LOG_THROTTLE_MS) {
125493
+ this.frameState.overlayStaleLogAt.set(deviceId, nowMs);
125494
+ this.ctx.logger.debug("overlay boxes withheld for a stale frame (replayed pre-roll); presence still emitted", {
125495
+ tags: { deviceId },
125496
+ meta: {
125497
+ frameAgeMs,
125498
+ maxFrameAgeMs: OVERLAY_MAX_FRAME_AGE_MS
125499
+ }
125500
+ });
125501
+ }
125502
+ }
125360
125503
  this.ctx.eventBus.emit({
125361
125504
  id: `pa-${randomUUID()}`,
125362
125505
  timestamp: new Date(result.timestamp),
@@ -125371,7 +125514,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
125371
125514
  timestamp: result.timestamp,
125372
125515
  frameWidth: result.frameWidth,
125373
125516
  frameHeight: result.frameHeight,
125374
- detections: overlayDetections,
125517
+ detections: frameIsStale ? [] : overlayDetections,
125375
125518
  hasLiveTrack,
125376
125519
  liveTrackCount: result.tracked.length
125377
125520
  }
@@ -127834,6 +127977,7 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
127834
127977
  this.packageBackground?.clearDevice(input.deviceId);
127835
127978
  this.overlayState.clearDevice(input.deviceId);
127836
127979
  this.frameState.overlaySynthesisWarnAt.delete(input.deviceId);
127980
+ this.frameState.overlayStaleLogAt.delete(input.deviceId);
127837
127981
  const prefix = `${input.deviceId}:`;
127838
127982
  for (const k of this.frameState.lastActiveTrackIds.keys()) if (k.startsWith(prefix)) this.frameState.lastActiveTrackIds.delete(k);
127839
127983
  }
@@ -130812,4 +130956,4 @@ function withMean(row) {
130812
130956
  };
130813
130957
  }
130814
130958
  //#endregion
130815
- export { CONFIRMATION_CROP_MAX_WIDTH, DETECTION_PIPELINE_SECTION_IDS, customActions, PipelineAnalyticsAddon as default, ncActions, pickCleanMedia, retagDetectionSections, retagRetentionSection, stripDeviceOnlyFields, stripGlobalOnlyFields, toAnalyticsDeviceSections };
130959
+ export { CONFIRMATION_CROP_MAX_WIDTH, DETECTION_PIPELINE_SECTION_IDS, OVERLAY_MAX_FRAME_AGE_MS, customActions, PipelineAnalyticsAddon as default, ncActions, pickCleanMedia, retagDetectionSections, retagRetentionSection, stripDeviceOnlyFields, stripGlobalOnlyFields, toAnalyticsDeviceSections };
@@ -30,7 +30,7 @@ async function d(e) {
30
30
  }
31
31
  }
32
32
  async function f() {
33
- return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-DFq-lnbD.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_analytics_widgets-_vVfXkDx.mjs")).catch((e) => {
34
34
  throw l = void 0, e;
35
35
  }), l;
36
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.649",
3
+ "version": "1.2.651",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",