@camstack/addon-post-analysis 1.2.302 → 1.2.303

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.
@@ -67320,6 +67320,22 @@ function messageOf(err) {
67320
67320
  function isDetailNotAttachedRefusal(err) {
67321
67321
  return messageOf(err).includes(DETAIL_NOT_ATTACHED_MARKER);
67322
67322
  }
67323
+ /**
67324
+ * The runner's TYPED handle-expired refusal (D688): the frame handle a dispatch
67325
+ * named is past the native lease age bound, and the registry will refuse it on
67326
+ * every later ask. TWIN of `addon-pipeline/src/pipeline-runner/detail-park.ts`
67327
+ * `DETAIL_HANDLE_EXPIRED_MARKER` — keep the two identical.
67328
+ *
67329
+ * Why the dispatcher cares: unlike not-attached this is about ONE handle, not
67330
+ * the session — the parked frame still serves — but the handle itself is
67331
+ * finished. Naming it again (next tick, or through the native-strict crop rung)
67332
+ * can only be refused again; on the live fleet that was ~7 refusals per handle.
67333
+ */
67334
+ var DETAIL_HANDLE_EXPIRED_MARKER = "runDetailSubtree-refused:handle-expired";
67335
+ /** Did this `runDetailSubtree` error carry the runner's handle-expired marker? */
67336
+ function isDetailHandleExpiredRefusal(err) {
67337
+ return messageOf(err).includes(DETAIL_HANDLE_EXPIRED_MARKER);
67338
+ }
67323
67339
  //#endregion
67324
67340
  //#region src/pipeline-analytics/detail-scheduler.ts
67325
67341
  /** Default backoff/period when a step's cadence omits `minIntervalMs`. */
@@ -67776,6 +67792,7 @@ var TrackDetailDispatcher = class {
67776
67792
  className: frame.className
67777
67793
  };
67778
67794
  const steps = composeDetailSteps(req, (trackId) => this.deps.hasTrackLabel?.(trackId) ?? false);
67795
+ let handleRefused = false;
67779
67796
  if (frame.frameHandle !== void 0) try {
67780
67797
  const primary = await this.deps.runDetailSubtree({
67781
67798
  deviceId,
@@ -67789,7 +67806,10 @@ var TrackDetailDispatcher = class {
67789
67806
  this.warnNotAttachedThrottled(deviceId, dev, req);
67790
67807
  return null;
67791
67808
  }
67792
- this.deps.logger.debug("detail primary call failed — trying frame fallback", {
67809
+ if (isDetailHandleExpiredRefusal(err)) {
67810
+ handleRefused = true;
67811
+ this.retireRefusedHandle(deviceId, dev, req, frame);
67812
+ } else this.deps.logger.debug("detail primary call failed — trying frame fallback", {
67793
67813
  tags: { deviceId },
67794
67814
  meta: {
67795
67815
  trackId: req.trackId,
@@ -67839,7 +67859,7 @@ var TrackDetailDispatcher = class {
67839
67859
  }
67840
67860
  });
67841
67861
  }
67842
- if (this.deps.captureCropBase64 !== void 0) try {
67862
+ if (this.deps.captureCropBase64 !== void 0 && frame.frameHandle !== void 0 && !handleRefused) try {
67843
67863
  const cropJpeg = await this.deps.captureCropBase64(frame);
67844
67864
  if (cropJpeg !== null) {
67845
67865
  const retry = await this.deps.runDetailSubtree({
@@ -67868,6 +67888,35 @@ var TrackDetailDispatcher = class {
67868
67888
  return null;
67869
67889
  }
67870
67890
  /**
67891
+ * The runner refused `frame`'s handle as past the lease age bound (D688).
67892
+ * That verdict is final for the handle, so the track's stored association
67893
+ * loses it: later dispatches skip rung 1 and the native-strict crop rung
67894
+ * (both would name the same handle and be refused again — ~7 times per
67895
+ * handle on the live fleet) and go straight to the parked frame, until a
67896
+ * newly OBSERVED frame brings a fresh handle via {@link onFrame}.
67897
+ *
67898
+ * Only the refused handle is dropped: if a newer frame replaced it while the
67899
+ * dispatch was in flight, that one stands. The line is therefore emitted once
67900
+ * per (track, handle) — a concurrent dispatch refused on the same handle
67901
+ * finds it already gone and says nothing.
67902
+ */
67903
+ retireRefusedHandle(deviceId, dev, req, frame) {
67904
+ const current = dev.tracks.get(req.trackId);
67905
+ if (current === void 0 || current.frameHandle !== frame.frameHandle) return;
67906
+ const { frameHandle: refused, ...withoutHandle } = current;
67907
+ dev.tracks.set(req.trackId, withoutHandle);
67908
+ this.deps.logger.info("detail dispatch: frame handle refused as expired — retired for this track, parked frame only until a fresh observation", {
67909
+ tags: { deviceId },
67910
+ meta: {
67911
+ trackId: req.trackId,
67912
+ stepId: req.stepId,
67913
+ reason: "handle-expired",
67914
+ handle: refused === void 0 ? null : `${refused.shmId}#${refused.slot}#${refused.seq}`,
67915
+ parkedFrameRung: this.deps.captureParkedFrame !== void 0
67916
+ }
67917
+ });
67918
+ }
67919
+ /**
67871
67920
  * The typed refusal is EXPECTED for a track that outlives the runner's
67872
67921
  * detail park grace (a closing track's last dispatches), so it logs its own
67873
67922
  * line — the reason is knowable, unlike the every-rung-missed warn — and
@@ -67228,6 +67228,22 @@ function messageOf(err) {
67228
67228
  function isDetailNotAttachedRefusal(err) {
67229
67229
  return messageOf(err).includes(DETAIL_NOT_ATTACHED_MARKER);
67230
67230
  }
67231
+ /**
67232
+ * The runner's TYPED handle-expired refusal (D688): the frame handle a dispatch
67233
+ * named is past the native lease age bound, and the registry will refuse it on
67234
+ * every later ask. TWIN of `addon-pipeline/src/pipeline-runner/detail-park.ts`
67235
+ * `DETAIL_HANDLE_EXPIRED_MARKER` — keep the two identical.
67236
+ *
67237
+ * Why the dispatcher cares: unlike not-attached this is about ONE handle, not
67238
+ * the session — the parked frame still serves — but the handle itself is
67239
+ * finished. Naming it again (next tick, or through the native-strict crop rung)
67240
+ * can only be refused again; on the live fleet that was ~7 refusals per handle.
67241
+ */
67242
+ var DETAIL_HANDLE_EXPIRED_MARKER = "runDetailSubtree-refused:handle-expired";
67243
+ /** Did this `runDetailSubtree` error carry the runner's handle-expired marker? */
67244
+ function isDetailHandleExpiredRefusal(err) {
67245
+ return messageOf(err).includes(DETAIL_HANDLE_EXPIRED_MARKER);
67246
+ }
67231
67247
  //#endregion
67232
67248
  //#region src/pipeline-analytics/detail-scheduler.ts
67233
67249
  /** Default backoff/period when a step's cadence omits `minIntervalMs`. */
@@ -67684,6 +67700,7 @@ var TrackDetailDispatcher = class {
67684
67700
  className: frame.className
67685
67701
  };
67686
67702
  const steps = composeDetailSteps(req, (trackId) => this.deps.hasTrackLabel?.(trackId) ?? false);
67703
+ let handleRefused = false;
67687
67704
  if (frame.frameHandle !== void 0) try {
67688
67705
  const primary = await this.deps.runDetailSubtree({
67689
67706
  deviceId,
@@ -67697,7 +67714,10 @@ var TrackDetailDispatcher = class {
67697
67714
  this.warnNotAttachedThrottled(deviceId, dev, req);
67698
67715
  return null;
67699
67716
  }
67700
- this.deps.logger.debug("detail primary call failed — trying frame fallback", {
67717
+ if (isDetailHandleExpiredRefusal(err)) {
67718
+ handleRefused = true;
67719
+ this.retireRefusedHandle(deviceId, dev, req, frame);
67720
+ } else this.deps.logger.debug("detail primary call failed — trying frame fallback", {
67701
67721
  tags: { deviceId },
67702
67722
  meta: {
67703
67723
  trackId: req.trackId,
@@ -67747,7 +67767,7 @@ var TrackDetailDispatcher = class {
67747
67767
  }
67748
67768
  });
67749
67769
  }
67750
- if (this.deps.captureCropBase64 !== void 0) try {
67770
+ if (this.deps.captureCropBase64 !== void 0 && frame.frameHandle !== void 0 && !handleRefused) try {
67751
67771
  const cropJpeg = await this.deps.captureCropBase64(frame);
67752
67772
  if (cropJpeg !== null) {
67753
67773
  const retry = await this.deps.runDetailSubtree({
@@ -67776,6 +67796,35 @@ var TrackDetailDispatcher = class {
67776
67796
  return null;
67777
67797
  }
67778
67798
  /**
67799
+ * The runner refused `frame`'s handle as past the lease age bound (D688).
67800
+ * That verdict is final for the handle, so the track's stored association
67801
+ * loses it: later dispatches skip rung 1 and the native-strict crop rung
67802
+ * (both would name the same handle and be refused again — ~7 times per
67803
+ * handle on the live fleet) and go straight to the parked frame, until a
67804
+ * newly OBSERVED frame brings a fresh handle via {@link onFrame}.
67805
+ *
67806
+ * Only the refused handle is dropped: if a newer frame replaced it while the
67807
+ * dispatch was in flight, that one stands. The line is therefore emitted once
67808
+ * per (track, handle) — a concurrent dispatch refused on the same handle
67809
+ * finds it already gone and says nothing.
67810
+ */
67811
+ retireRefusedHandle(deviceId, dev, req, frame) {
67812
+ const current = dev.tracks.get(req.trackId);
67813
+ if (current === void 0 || current.frameHandle !== frame.frameHandle) return;
67814
+ const { frameHandle: refused, ...withoutHandle } = current;
67815
+ dev.tracks.set(req.trackId, withoutHandle);
67816
+ this.deps.logger.info("detail dispatch: frame handle refused as expired — retired for this track, parked frame only until a fresh observation", {
67817
+ tags: { deviceId },
67818
+ meta: {
67819
+ trackId: req.trackId,
67820
+ stepId: req.stepId,
67821
+ reason: "handle-expired",
67822
+ handle: refused === void 0 ? null : `${refused.shmId}#${refused.slot}#${refused.seq}`,
67823
+ parkedFrameRung: this.deps.captureParkedFrame !== void 0
67824
+ }
67825
+ });
67826
+ }
67827
+ /**
67779
67828
  * The typed refusal is EXPECTED for a track that outlives the runner's
67780
67829
  * detail park grace (a closing track's last dispatches), so it logs its own
67781
67830
  * line — the reason is knowable, unlike the every-rung-missed warn — and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.302",
3
+ "version": "1.2.303",
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",