@camstack/addon-pipeline 1.2.312 → 1.2.314

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 (32) hide show
  1. package/dist/audio-analyzer/index.js +2 -2
  2. package/dist/audio-analyzer/index.mjs +2 -2
  3. package/dist/{default-detection-model-BZMS04Wn.js → default-detection-model-BvxtQEcb.js} +1 -1
  4. package/dist/{default-detection-model-ClQ1gcZ9.mjs → default-detection-model-Es1-dvFU.mjs} +1 -1
  5. package/dist/detection-pipeline/index.js +4 -4
  6. package/dist/detection-pipeline/index.mjs +4 -4
  7. package/dist/{dist-B3_sbBDM.mjs → dist-E1Z_K22_.mjs} +404 -194
  8. package/dist/{dist-BLf0YjAa.js → dist-Jvi2SOxI.js} +409 -193
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-DJFKTJ0B.mjs → node-DHe-9_tm.mjs} +1 -1
  12. package/dist/{node-Vwf2PuIT.js → node-EjcZQh8D.js} +1 -1
  13. package/dist/pipeline-runner/index.js +187 -17
  14. package/dist/pipeline-runner/index.mjs +187 -17
  15. package/dist/{process-memory-DGTVVYlX.js → process-memory-BUtnSPYK.js} +1 -1
  16. package/dist/{process-memory-BvwauBlX.mjs → process-memory-IQcwHauP.mjs} +1 -1
  17. package/dist/recorder/index.js +2 -2
  18. package/dist/recorder/index.mjs +2 -2
  19. package/dist/{segment-demux-js-C0oz7IVm.mjs → segment-demux-js-CR58wpzs.mjs} +1 -1
  20. package/dist/{segment-demux-js-BUAAuSVF.js → segment-demux-js-Il0EkSOb.js} +1 -1
  21. package/dist/stream-broker/_stub.js +2 -2
  22. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DZh9jQi-.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-CBvGGDX_.mjs} +3 -3
  23. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D0BR9vFO.mjs +26 -0
  24. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BhO-szf8.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DddXyc17.mjs} +1 -1
  25. package/dist/stream-broker/demux-worker-child.js +1 -1
  26. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  27. package/dist/stream-broker/{hostInit-BJYQvQ3e.mjs → hostInit-CjvrBwfh.mjs} +3 -3
  28. package/dist/stream-broker/index.js +3 -3
  29. package/dist/stream-broker/index.mjs +3 -3
  30. package/dist/stream-broker/remoteEntry.js +1 -1
  31. package/package.json +2 -2
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C6EaWavG.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_dist = require("../dist-BLf0YjAa.js");
5
+ const require_dist = require("../dist-Jvi2SOxI.js");
6
6
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
7
7
  let node_fs = require("node:fs");
8
8
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Ot as motionDetectionCapability, pn as BaseAddon, st as evaluateZoneRules, un as DeviceType, xn as hydrateSchema } from "../dist-B3_sbBDM.mjs";
1
+ import { Sn as hydrateSchema, ct as evaluateZoneRules, dn as DeviceType, kt as motionDetectionCapability, mn as BaseAddon } from "../dist-E1Z_K22_.mjs";
2
2
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { an as buildFfmpegArgs, cn as isSoftwareDecode, dn as errMsg, in as Fmp4BoxSplitter } from "./dist-B3_sbBDM.mjs";
1
+ import { an as Fmp4BoxSplitter, fn as errMsg, ln as isSoftwareDecode, on as buildFfmpegArgs } from "./dist-E1Z_K22_.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { realpathSync } from "node:fs";
4
4
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-BLf0YjAa.js");
2
+ const require_dist = require("./dist-Jvi2SOxI.js");
3
3
  let node_crypto = require("node:crypto");
4
4
  let node_fs = require("node:fs");
5
5
  node_fs = require_chunk.__toESM(node_fs, 1);
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-BLf0YjAa.js");
7
- const require_node = require("../node-Vwf2PuIT.js");
8
- const require_default_detection_model = require("../default-detection-model-BZMS04Wn.js");
6
+ const require_dist = require("../dist-Jvi2SOxI.js");
7
+ const require_node = require("../node-EjcZQh8D.js");
8
+ const require_default_detection_model = require("../default-detection-model-BvxtQEcb.js");
9
9
  const require_worker_protocol = require("../worker-protocol-C-G8qmye.js");
10
10
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
11
11
  const require_lazy_sharp = require("../lazy-sharp-BuwKlQLR.js");
@@ -3412,6 +3412,39 @@ function iou(a, b) {
3412
3412
  return union <= 0 ? 0 : intersection / union;
3413
3413
  }
3414
3414
  //#endregion
3415
+ //#region src/pipeline-runner/appearance-descriptor.ts
3416
+ /**
3417
+ * The model-free appearance descriptor of one box (D683 §8.1).
3418
+ *
3419
+ * Input: a small RGB view of the box (`APPEARANCE_VIEW`, resolved from the
3420
+ * frame lease the runner already holds, D379). Output: 96 integers — per
3421
+ * upper/lower half, a hue×saturation histogram (8×4), a value histogram (8)
3422
+ * and a magnitude-weighted gradient-orientation histogram (8) — each block
3423
+ * L1-normalised and quantised to 0–255. `gray` when the mean of saturation ×
3424
+ * value is under 0.08: IR, greyscale, and near-black noise (whose HSV
3425
+ * saturation is high but meaningless). No learned weights (O6).
3426
+ */
3427
+ var APPEARANCE_VIEW = {
3428
+ width: 16,
3429
+ height: 32
3430
+ };
3431
+ /**
3432
+ * Which boxes get a descriptor. `'all'` unless the local cost gate (Task 10
3433
+ * Step 9) failed.
3434
+ *
3435
+ * `'all'` failed the local gate (`appearance-cost-bench.ts`, sharp-fallback
3436
+ * path, synthetic clip -- no live D681 tape was fetched in this session, see
3437
+ * the Task 10 commit message and report): frame p95 3.375 ms against a
3438
+ * 1.0 ms budget. `'candidates'` is cheaper but ALSO exceeded budget on the
3439
+ * same synthetic input (1.622 ms). Per the brief this is a STOP -- the
3440
+ * descriptor must not ship over budget on unconfirmed numbers. `'candidates'`
3441
+ * is kept here as the documented, safer fallback pending Task 24's hub
3442
+ * measurement over the REAL native-lease path (the one this bench does not
3443
+ * exercise); that number decides, and may move this back to `'all'` or
3444
+ * confirm `'candidates'`.
3445
+ */
3446
+ var APPEARANCE_SCOPE = "candidates";
3447
+ //#endregion
3415
3448
  //#region src/pipeline-runner/capacity/capacity-runner.ts
3416
3449
  /**
3417
3450
  * Run ONE ramp step: `cameras` independent open-loop pacers for
@@ -8294,6 +8327,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8294
8327
  /** Per-camera cost of the carried evidence, drained by its report line. */
8295
8328
  birthEvidenceCost = /* @__PURE__ */ new Map();
8296
8329
  birthEvidenceReportedAt = /* @__PURE__ */ new Map();
8330
+ /** Per-camera descriptor cost since the last report (D683). */
8331
+ appearanceCost = /* @__PURE__ */ new Map();
8332
+ appearanceReportedAt = /* @__PURE__ */ new Map();
8297
8333
  /**
8298
8334
  * Node-level RAM store of retained rgb detection frames, keyed by the
8299
8335
  * `FrameHandle` that rides the inference-result event (shm-ring removal
@@ -8619,8 +8655,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
8619
8655
  this.runner.onResult(async (deviceId, frame, result, _streamType, handle, source, occasion) => {
8620
8656
  this.retention?.noteInferenceResult(deviceId, handle, result);
8621
8657
  this.cutSubjectTiles(deviceId, result, handle);
8622
- const carried = await this.attachBirthEvidence(deviceId, frame, result);
8623
- this.emitInferenceResult(deviceId, frame, carried, handle, source, occasion);
8658
+ const { result: carried, novel } = await this.attachBirthEvidence(deviceId, frame, result);
8659
+ const described = await this.attachAppearance(deviceId, frame, carried, novel);
8660
+ this.emitInferenceResult(deviceId, frame, described, handle, source, occasion);
8624
8661
  const detectionCount = result.detections?.length ?? 0;
8625
8662
  this.runner?.noteDetectionResult(deviceId, detectionCount);
8626
8663
  if (detectionCount > 0 && this.occupancyBurstInFlight.has(deviceId)) this.occupancyBurstFound.add(deviceId);
@@ -9802,10 +9839,17 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9802
9839
  * dropped the evidence would be indistinguishable from a camera with no
9803
9840
  * births at all, which is the shape of failure this whole change exists to
9804
9841
  * remove.
9842
+ *
9843
+ * Also returns `novel`: the set of `plan[].index` this pass planned as
9844
+ * birth candidates — reused by `attachAppearance` so the 'candidates'
9845
+ * appearance scope (D683) does not recompute the same novelty question.
9805
9846
  */
9806
9847
  async attachBirthEvidence(deviceId, frame, result) {
9807
9848
  const detections = result.detections;
9808
- if (detections.length === 0) return result;
9849
+ if (detections.length === 0) return {
9850
+ result,
9851
+ novel: /* @__PURE__ */ new Set()
9852
+ };
9809
9853
  const plan = planBirthEvidence(deviceId, detections.map((d) => ({
9810
9854
  bbox: d.bbox,
9811
9855
  score: d.score,
@@ -9814,7 +9858,11 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9814
9858
  width: result.width,
9815
9859
  height: result.height
9816
9860
  }, this.birthNovelty, Date.now());
9817
- if (plan.length === 0) return result;
9861
+ if (plan.length === 0) return {
9862
+ result,
9863
+ novel: /* @__PURE__ */ new Set()
9864
+ };
9865
+ const novel = new Set(plan.map((entry) => entry.index));
9818
9866
  const cost = this.birthEvidenceCost.get(deviceId) ?? {
9819
9867
  cut: 0,
9820
9868
  failed: 0,
@@ -9827,7 +9875,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9827
9875
  if (!(frame.frameRef !== void 0 || frame.format === "rgb" && frame.data.byteLength >= result.width * result.height * 3)) {
9828
9876
  cost.skippedNoPixels += plan.length;
9829
9877
  this.reportBirthEvidenceCost(deviceId);
9830
- return result;
9878
+ return {
9879
+ result,
9880
+ novel
9881
+ };
9831
9882
  }
9832
9883
  for (const entry of plan) {
9833
9884
  const startedAt = Date.now();
@@ -9846,16 +9897,22 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9846
9897
  });
9847
9898
  }
9848
9899
  this.reportBirthEvidenceCost(deviceId);
9849
- if (byIndex.size === 0) return result;
9900
+ if (byIndex.size === 0) return {
9901
+ result,
9902
+ novel
9903
+ };
9850
9904
  return {
9851
- ...result,
9852
- detections: detections.map((d, i) => {
9853
- const evidence = byIndex.get(i);
9854
- return evidence === void 0 ? d : {
9855
- ...d,
9856
- birthEvidence: evidence
9857
- };
9858
- })
9905
+ result: {
9906
+ ...result,
9907
+ detections: detections.map((d, i) => {
9908
+ const evidence = byIndex.get(i);
9909
+ return evidence === void 0 ? d : {
9910
+ ...d,
9911
+ birthEvidence: evidence
9912
+ };
9913
+ })
9914
+ },
9915
+ novel
9859
9916
  };
9860
9917
  }
9861
9918
  /**
@@ -9961,6 +10018,117 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
9961
10018
  cost.bytes = 0;
9962
10019
  cost.encodeMs = 0;
9963
10020
  }
10021
+ /**
10022
+ * The v2 tracker's appearance descriptor (D683 §8.1), cut from the same
10023
+ * raster the birth evidence uses (lease first, inline RGB second). A frame
10024
+ * with no raster attaches nothing and is counted as `skippedNoPixels`.
10025
+ *
10026
+ * OFF by default (fix round 1, coordinator ruling 2026-09-30): appearance
10027
+ * is unproven cost until Task 23 calibrates it, so a disabled camera must
10028
+ * add NOTHING to the frame path — no crop, no resample, no descriptor.
10029
+ * `APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT` is the SAME authority the v2
10030
+ * tracker's own default reads (`@camstack/types`, never a second flag that
10031
+ * could disagree with it — CLAUDE.md D62). Checked here, before any
10032
+ * per-camera bookkeeping, AND inside {@link attachAppearanceIfEnabled} —
10033
+ * one symbol, checked defensively in two frames, not two settings.
10034
+ */
10035
+ async attachAppearance(deviceId, frame, result, novel) {
10036
+ return result;
10037
+ }
10038
+ foldAppearanceCost(into, add) {
10039
+ into.attached += add.attached;
10040
+ into.tooSmall += add.tooSmall;
10041
+ into.failed += add.failed;
10042
+ into.skippedScope += add.skippedScope;
10043
+ into.ms += add.ms;
10044
+ }
10045
+ /** A 16×32 RGB view of `crop`: the lease when there is one, else the inline buffer. */
10046
+ async readAppearanceView(frame, result, crop) {
10047
+ if (frame.frameRef !== void 0) {
10048
+ const lease = require_default_detection_model.localFrameRegistry.acquire(frame.frameRef);
10049
+ if (lease) {
10050
+ let reason = "error";
10051
+ try {
10052
+ const view = await lease.resolve({
10053
+ crop,
10054
+ content: {
10055
+ width: APPEARANCE_VIEW.width,
10056
+ height: APPEARANCE_VIEW.height
10057
+ },
10058
+ fit: "stretch",
10059
+ format: "rgb"
10060
+ });
10061
+ if (view.kind !== "view") {
10062
+ reason = "drop";
10063
+ return null;
10064
+ }
10065
+ if (view.data.byteLength !== APPEARANCE_VIEW.width * APPEARANCE_VIEW.height * 3) {
10066
+ reason = "error";
10067
+ return null;
10068
+ }
10069
+ reason = "success";
10070
+ return {
10071
+ data: new Uint8Array(view.data),
10072
+ width: APPEARANCE_VIEW.width,
10073
+ height: APPEARANCE_VIEW.height
10074
+ };
10075
+ } catch {
10076
+ return null;
10077
+ } finally {
10078
+ lease.release(reason);
10079
+ }
10080
+ }
10081
+ }
10082
+ if (frame.format !== "rgb" || frame.data.byteLength < result.width * result.height * 3) return null;
10083
+ try {
10084
+ const data = await (await require_lazy_sharp.getSharp())(frame.data, { raw: {
10085
+ width: result.width,
10086
+ height: result.height,
10087
+ channels: 3
10088
+ } }).extract(crop).resize(APPEARANCE_VIEW.width, APPEARANCE_VIEW.height, { fit: "fill" }).raw().toBuffer();
10089
+ return {
10090
+ data: new Uint8Array(data),
10091
+ width: APPEARANCE_VIEW.width,
10092
+ height: APPEARANCE_VIEW.height
10093
+ };
10094
+ } catch {
10095
+ return null;
10096
+ }
10097
+ }
10098
+ /**
10099
+ * State, per camera, what attaching the descriptor cost over the window —
10100
+ * and drain it (D683, mirrors {@link reportBirthEvidenceCost}). The cost
10101
+ * gate (Step 9) is measured offline; this line is what proves the shipped
10102
+ * runner matches the bench.
10103
+ */
10104
+ reportAppearanceCost(deviceId) {
10105
+ const cost = this.appearanceCost.get(deviceId);
10106
+ if (!cost) return;
10107
+ const now = Date.now();
10108
+ const lastAt = this.appearanceReportedAt.get(deviceId) ?? 0;
10109
+ if (lastAt !== 0 && now - lastAt < BIRTH_EVIDENCE_REPORT_INTERVAL_MS) return;
10110
+ const windowMs = lastAt === 0 ? BIRTH_EVIDENCE_REPORT_INTERVAL_MS : now - lastAt;
10111
+ this.appearanceReportedAt.set(deviceId, now);
10112
+ this.ctx.logger.info("appearance descriptors attached", {
10113
+ tags: { deviceId },
10114
+ meta: {
10115
+ attached: cost.attached,
10116
+ tooSmall: cost.tooSmall,
10117
+ failed: cost.failed,
10118
+ skippedNoPixels: cost.skippedNoPixels,
10119
+ skippedScope: cost.skippedScope,
10120
+ meanMs: cost.attached > 0 ? Number((cost.ms / cost.attached).toFixed(2)) : 0,
10121
+ scope: APPEARANCE_SCOPE,
10122
+ windowMs
10123
+ }
10124
+ });
10125
+ cost.attached = 0;
10126
+ cost.tooSmall = 0;
10127
+ cost.failed = 0;
10128
+ cost.skippedNoPixels = 0;
10129
+ cost.skippedScope = 0;
10130
+ cost.ms = 0;
10131
+ }
9964
10132
  async resolveNativeCropPixels(input) {
9965
10133
  const b = input.bbox;
9966
10134
  const isFullFrame = b.x <= .001 && b.y <= .001 && b.w >= .999 && b.h >= .999;
@@ -10636,6 +10804,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends require_dist.BaseAdd
10636
10804
  this.birthNovelty.clearDevice(deviceId);
10637
10805
  this.birthEvidenceCost.delete(deviceId);
10638
10806
  this.birthEvidenceReportedAt.delete(deviceId);
10807
+ this.appearanceCost.delete(deviceId);
10808
+ this.appearanceReportedAt.delete(deviceId);
10639
10809
  this.runner?.unregisterCamera(deviceId);
10640
10810
  this.ctx?.logger.info("detachCamera", {
10641
10811
  tags: { deviceId },
@@ -1,6 +1,6 @@
1
- import { An as _enum, Bn as string, C as FailureCounters, D as NativeLeaseAdmissionSchema, E as MOTION_CLOSE_AFTER_MS, Fn as literal, Ft as pipelineRunnerCapability, Hn as EventCategory, In as number, Ln as object, Mn as boolean, Mt as pickDetailCropConvention, Nt as pickNativeLeaseOverride, O as NativeLeaseSettingsSchema, Pn as lazy, Tn as nodePin, _ as DEFAULT_NATIVE_LEASE_SETTINGS, _n as createEvent, dn as errMsg, dt as failureContributionCapability, h as DEFAULT_DETAIL_CROP_CONVENTION, i as BackgroundRunRegistry, jn as array, pn as BaseAddon, tt as deriveDetailCropRect, vn as customAction, yn as defineCustomActions, yt as loadContributionCapability } from "../dist-B3_sbBDM.mjs";
2
- import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DJFKTJ0B.mjs";
3
- import { c as macroGateVerdict, d as resolveFrameViewGeometry, g as getStepDefinition, l as rootObjectGate, o as placeDetailStep, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as RETIRED_MODEL_IDS } from "../default-detection-model-ClQ1gcZ9.mjs";
1
+ import { D as MOTION_CLOSE_AFTER_MS, En as nodePin, Fn as lazy, In as literal, It as pipelineRunnerCapability, Ln as number, Mn as array, Nn as boolean, Nt as pickDetailCropConvention, O as NativeLeaseAdmissionSchema, Pt as pickNativeLeaseOverride, Rn as object, Un as EventCategory, Vn as string, a as BackgroundRunRegistry, bn as defineCustomActions, bt as loadContributionCapability, fn as errMsg, ft as failureContributionCapability, g as DEFAULT_DETAIL_CROP_CONVENTION, jn as _enum, k as NativeLeaseSettingsSchema, mn as BaseAddon, nt as deriveDetailCropRect, v as DEFAULT_NATIVE_LEASE_SETTINGS, vn as createEvent, w as FailureCounters, yn as customAction } from "../dist-E1Z_K22_.mjs";
2
+ import { i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DHe-9_tm.mjs";
3
+ import { c as macroGateVerdict, d as resolveFrameViewGeometry, g as getStepDefinition, l as rootObjectGate, o as placeDetailStep, r as CustomModelCatalog, s as stepsForElectedPool, u as localFrameRegistry, v as RETIRED_MODEL_IDS } from "../default-detection-model-Es1-dvFU.mjs";
4
4
  import { t as isWorkerReply } from "../worker-protocol-D_NzPcnh.mjs";
5
5
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
6
6
  import { t as getSharp } from "../lazy-sharp-DXsqwpph.mjs";
@@ -3404,6 +3404,39 @@ function iou(a, b) {
3404
3404
  return union <= 0 ? 0 : intersection / union;
3405
3405
  }
3406
3406
  //#endregion
3407
+ //#region src/pipeline-runner/appearance-descriptor.ts
3408
+ /**
3409
+ * The model-free appearance descriptor of one box (D683 §8.1).
3410
+ *
3411
+ * Input: a small RGB view of the box (`APPEARANCE_VIEW`, resolved from the
3412
+ * frame lease the runner already holds, D379). Output: 96 integers — per
3413
+ * upper/lower half, a hue×saturation histogram (8×4), a value histogram (8)
3414
+ * and a magnitude-weighted gradient-orientation histogram (8) — each block
3415
+ * L1-normalised and quantised to 0–255. `gray` when the mean of saturation ×
3416
+ * value is under 0.08: IR, greyscale, and near-black noise (whose HSV
3417
+ * saturation is high but meaningless). No learned weights (O6).
3418
+ */
3419
+ var APPEARANCE_VIEW = {
3420
+ width: 16,
3421
+ height: 32
3422
+ };
3423
+ /**
3424
+ * Which boxes get a descriptor. `'all'` unless the local cost gate (Task 10
3425
+ * Step 9) failed.
3426
+ *
3427
+ * `'all'` failed the local gate (`appearance-cost-bench.ts`, sharp-fallback
3428
+ * path, synthetic clip -- no live D681 tape was fetched in this session, see
3429
+ * the Task 10 commit message and report): frame p95 3.375 ms against a
3430
+ * 1.0 ms budget. `'candidates'` is cheaper but ALSO exceeded budget on the
3431
+ * same synthetic input (1.622 ms). Per the brief this is a STOP -- the
3432
+ * descriptor must not ship over budget on unconfirmed numbers. `'candidates'`
3433
+ * is kept here as the documented, safer fallback pending Task 24's hub
3434
+ * measurement over the REAL native-lease path (the one this bench does not
3435
+ * exercise); that number decides, and may move this back to `'all'` or
3436
+ * confirm `'candidates'`.
3437
+ */
3438
+ var APPEARANCE_SCOPE = "candidates";
3439
+ //#endregion
3407
3440
  //#region src/pipeline-runner/capacity/capacity-runner.ts
3408
3441
  /**
3409
3442
  * Run ONE ramp step: `cameras` independent open-loop pacers for
@@ -8286,6 +8319,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
8286
8319
  /** Per-camera cost of the carried evidence, drained by its report line. */
8287
8320
  birthEvidenceCost = /* @__PURE__ */ new Map();
8288
8321
  birthEvidenceReportedAt = /* @__PURE__ */ new Map();
8322
+ /** Per-camera descriptor cost since the last report (D683). */
8323
+ appearanceCost = /* @__PURE__ */ new Map();
8324
+ appearanceReportedAt = /* @__PURE__ */ new Map();
8289
8325
  /**
8290
8326
  * Node-level RAM store of retained rgb detection frames, keyed by the
8291
8327
  * `FrameHandle` that rides the inference-result event (shm-ring removal
@@ -8611,8 +8647,9 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
8611
8647
  this.runner.onResult(async (deviceId, frame, result, _streamType, handle, source, occasion) => {
8612
8648
  this.retention?.noteInferenceResult(deviceId, handle, result);
8613
8649
  this.cutSubjectTiles(deviceId, result, handle);
8614
- const carried = await this.attachBirthEvidence(deviceId, frame, result);
8615
- this.emitInferenceResult(deviceId, frame, carried, handle, source, occasion);
8650
+ const { result: carried, novel } = await this.attachBirthEvidence(deviceId, frame, result);
8651
+ const described = await this.attachAppearance(deviceId, frame, carried, novel);
8652
+ this.emitInferenceResult(deviceId, frame, described, handle, source, occasion);
8616
8653
  const detectionCount = result.detections?.length ?? 0;
8617
8654
  this.runner?.noteDetectionResult(deviceId, detectionCount);
8618
8655
  if (detectionCount > 0 && this.occupancyBurstInFlight.has(deviceId)) this.occupancyBurstFound.add(deviceId);
@@ -9794,10 +9831,17 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
9794
9831
  * dropped the evidence would be indistinguishable from a camera with no
9795
9832
  * births at all, which is the shape of failure this whole change exists to
9796
9833
  * remove.
9834
+ *
9835
+ * Also returns `novel`: the set of `plan[].index` this pass planned as
9836
+ * birth candidates — reused by `attachAppearance` so the 'candidates'
9837
+ * appearance scope (D683) does not recompute the same novelty question.
9797
9838
  */
9798
9839
  async attachBirthEvidence(deviceId, frame, result) {
9799
9840
  const detections = result.detections;
9800
- if (detections.length === 0) return result;
9841
+ if (detections.length === 0) return {
9842
+ result,
9843
+ novel: /* @__PURE__ */ new Set()
9844
+ };
9801
9845
  const plan = planBirthEvidence(deviceId, detections.map((d) => ({
9802
9846
  bbox: d.bbox,
9803
9847
  score: d.score,
@@ -9806,7 +9850,11 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
9806
9850
  width: result.width,
9807
9851
  height: result.height
9808
9852
  }, this.birthNovelty, Date.now());
9809
- if (plan.length === 0) return result;
9853
+ if (plan.length === 0) return {
9854
+ result,
9855
+ novel: /* @__PURE__ */ new Set()
9856
+ };
9857
+ const novel = new Set(plan.map((entry) => entry.index));
9810
9858
  const cost = this.birthEvidenceCost.get(deviceId) ?? {
9811
9859
  cut: 0,
9812
9860
  failed: 0,
@@ -9819,7 +9867,10 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
9819
9867
  if (!(frame.frameRef !== void 0 || frame.format === "rgb" && frame.data.byteLength >= result.width * result.height * 3)) {
9820
9868
  cost.skippedNoPixels += plan.length;
9821
9869
  this.reportBirthEvidenceCost(deviceId);
9822
- return result;
9870
+ return {
9871
+ result,
9872
+ novel
9873
+ };
9823
9874
  }
9824
9875
  for (const entry of plan) {
9825
9876
  const startedAt = Date.now();
@@ -9838,16 +9889,22 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
9838
9889
  });
9839
9890
  }
9840
9891
  this.reportBirthEvidenceCost(deviceId);
9841
- if (byIndex.size === 0) return result;
9892
+ if (byIndex.size === 0) return {
9893
+ result,
9894
+ novel
9895
+ };
9842
9896
  return {
9843
- ...result,
9844
- detections: detections.map((d, i) => {
9845
- const evidence = byIndex.get(i);
9846
- return evidence === void 0 ? d : {
9847
- ...d,
9848
- birthEvidence: evidence
9849
- };
9850
- })
9897
+ result: {
9898
+ ...result,
9899
+ detections: detections.map((d, i) => {
9900
+ const evidence = byIndex.get(i);
9901
+ return evidence === void 0 ? d : {
9902
+ ...d,
9903
+ birthEvidence: evidence
9904
+ };
9905
+ })
9906
+ },
9907
+ novel
9851
9908
  };
9852
9909
  }
9853
9910
  /**
@@ -9953,6 +10010,117 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
9953
10010
  cost.bytes = 0;
9954
10011
  cost.encodeMs = 0;
9955
10012
  }
10013
+ /**
10014
+ * The v2 tracker's appearance descriptor (D683 §8.1), cut from the same
10015
+ * raster the birth evidence uses (lease first, inline RGB second). A frame
10016
+ * with no raster attaches nothing and is counted as `skippedNoPixels`.
10017
+ *
10018
+ * OFF by default (fix round 1, coordinator ruling 2026-09-30): appearance
10019
+ * is unproven cost until Task 23 calibrates it, so a disabled camera must
10020
+ * add NOTHING to the frame path — no crop, no resample, no descriptor.
10021
+ * `APPEARANCE_RESOLVES_AMBIGUITY_DEFAULT` is the SAME authority the v2
10022
+ * tracker's own default reads (`@camstack/types`, never a second flag that
10023
+ * could disagree with it — CLAUDE.md D62). Checked here, before any
10024
+ * per-camera bookkeeping, AND inside {@link attachAppearanceIfEnabled} —
10025
+ * one symbol, checked defensively in two frames, not two settings.
10026
+ */
10027
+ async attachAppearance(deviceId, frame, result, novel) {
10028
+ return result;
10029
+ }
10030
+ foldAppearanceCost(into, add) {
10031
+ into.attached += add.attached;
10032
+ into.tooSmall += add.tooSmall;
10033
+ into.failed += add.failed;
10034
+ into.skippedScope += add.skippedScope;
10035
+ into.ms += add.ms;
10036
+ }
10037
+ /** A 16×32 RGB view of `crop`: the lease when there is one, else the inline buffer. */
10038
+ async readAppearanceView(frame, result, crop) {
10039
+ if (frame.frameRef !== void 0) {
10040
+ const lease = localFrameRegistry.acquire(frame.frameRef);
10041
+ if (lease) {
10042
+ let reason = "error";
10043
+ try {
10044
+ const view = await lease.resolve({
10045
+ crop,
10046
+ content: {
10047
+ width: APPEARANCE_VIEW.width,
10048
+ height: APPEARANCE_VIEW.height
10049
+ },
10050
+ fit: "stretch",
10051
+ format: "rgb"
10052
+ });
10053
+ if (view.kind !== "view") {
10054
+ reason = "drop";
10055
+ return null;
10056
+ }
10057
+ if (view.data.byteLength !== APPEARANCE_VIEW.width * APPEARANCE_VIEW.height * 3) {
10058
+ reason = "error";
10059
+ return null;
10060
+ }
10061
+ reason = "success";
10062
+ return {
10063
+ data: new Uint8Array(view.data),
10064
+ width: APPEARANCE_VIEW.width,
10065
+ height: APPEARANCE_VIEW.height
10066
+ };
10067
+ } catch {
10068
+ return null;
10069
+ } finally {
10070
+ lease.release(reason);
10071
+ }
10072
+ }
10073
+ }
10074
+ if (frame.format !== "rgb" || frame.data.byteLength < result.width * result.height * 3) return null;
10075
+ try {
10076
+ const data = await (await getSharp())(frame.data, { raw: {
10077
+ width: result.width,
10078
+ height: result.height,
10079
+ channels: 3
10080
+ } }).extract(crop).resize(APPEARANCE_VIEW.width, APPEARANCE_VIEW.height, { fit: "fill" }).raw().toBuffer();
10081
+ return {
10082
+ data: new Uint8Array(data),
10083
+ width: APPEARANCE_VIEW.width,
10084
+ height: APPEARANCE_VIEW.height
10085
+ };
10086
+ } catch {
10087
+ return null;
10088
+ }
10089
+ }
10090
+ /**
10091
+ * State, per camera, what attaching the descriptor cost over the window —
10092
+ * and drain it (D683, mirrors {@link reportBirthEvidenceCost}). The cost
10093
+ * gate (Step 9) is measured offline; this line is what proves the shipped
10094
+ * runner matches the bench.
10095
+ */
10096
+ reportAppearanceCost(deviceId) {
10097
+ const cost = this.appearanceCost.get(deviceId);
10098
+ if (!cost) return;
10099
+ const now = Date.now();
10100
+ const lastAt = this.appearanceReportedAt.get(deviceId) ?? 0;
10101
+ if (lastAt !== 0 && now - lastAt < BIRTH_EVIDENCE_REPORT_INTERVAL_MS) return;
10102
+ const windowMs = lastAt === 0 ? BIRTH_EVIDENCE_REPORT_INTERVAL_MS : now - lastAt;
10103
+ this.appearanceReportedAt.set(deviceId, now);
10104
+ this.ctx.logger.info("appearance descriptors attached", {
10105
+ tags: { deviceId },
10106
+ meta: {
10107
+ attached: cost.attached,
10108
+ tooSmall: cost.tooSmall,
10109
+ failed: cost.failed,
10110
+ skippedNoPixels: cost.skippedNoPixels,
10111
+ skippedScope: cost.skippedScope,
10112
+ meanMs: cost.attached > 0 ? Number((cost.ms / cost.attached).toFixed(2)) : 0,
10113
+ scope: APPEARANCE_SCOPE,
10114
+ windowMs
10115
+ }
10116
+ });
10117
+ cost.attached = 0;
10118
+ cost.tooSmall = 0;
10119
+ cost.failed = 0;
10120
+ cost.skippedNoPixels = 0;
10121
+ cost.skippedScope = 0;
10122
+ cost.ms = 0;
10123
+ }
9956
10124
  async resolveNativeCropPixels(input) {
9957
10125
  const b = input.bbox;
9958
10126
  const isFullFrame = b.x <= .001 && b.y <= .001 && b.w >= .999 && b.h >= .999;
@@ -10628,6 +10796,8 @@ var PipelineRunnerAddon = class PipelineRunnerAddon extends BaseAddon {
10628
10796
  this.birthNovelty.clearDevice(deviceId);
10629
10797
  this.birthEvidenceCost.delete(deviceId);
10630
10798
  this.birthEvidenceReportedAt.delete(deviceId);
10799
+ this.appearanceCost.delete(deviceId);
10800
+ this.appearanceReportedAt.delete(deviceId);
10631
10801
  this.runner?.unregisterCamera(deviceId);
10632
10802
  this.ctx?.logger.info("detachCamera", {
10633
10803
  tags: { deviceId },
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-BLf0YjAa.js");
2
+ const require_dist = require("./dist-Jvi2SOxI.js");
3
3
  let node_fs = require("node:fs");
4
4
  node_fs = require_chunk.__toESM(node_fs);
5
5
  let node_child_process = require("node:child_process");
@@ -1,4 +1,4 @@
1
- import { At as parseProcStatus } from "./dist-B3_sbBDM.mjs";
1
+ import { jt as parseProcStatus } from "./dist-E1Z_K22_.mjs";
2
2
  import * as fs from "node:fs";
3
3
  import { execFile } from "node:child_process";
4
4
  //#region src/shared/node-topology-platform.ts
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../chunk-emK7D4bc.js");
6
- const require_dist = require("../dist-BLf0YjAa.js");
7
- const require_node = require("../node-Vwf2PuIT.js");
6
+ const require_dist = require("../dist-Jvi2SOxI.js");
7
+ const require_node = require("../node-EjcZQh8D.js");
8
8
  const require_hub_hostname = require("../hub-hostname-DAJXlOgV.js");
9
9
  const require_restream_intent = require("../restream-intent-7uDNqMKs.js");
10
10
  const require_retire_root_keys = require("../retire-root-keys-Csb8z2f3.js");
@@ -1,5 +1,5 @@
1
- import { An as _enum, B as VISIT_MERGE_GAP_MS, Bn as string, Bt as recordingSignalCapability, Ct as mayReadLocation, Dt as modeMayWrite, Et as migrateLegacyDeviceSignalConfig, F as RECORDING_SOURCE_CAMSTACK_ADDON, Hn as EventCategory, Ht as resolveCreateExportSubject, I as RecordingConfigSchema, In as number, J as clipExportFailure, K as batteryCapability, Kt as resolveRecordingProfiles, L as RecordingSignalStatusSchema, Ln as object, Lt as recordingArchiveCapability, Mn as boolean, N as RECORDING_EXPORT_MAX_READ_BYTES, On as selectAssignedProfileSlots, P as RECORDING_SOURCE_CAMSTACK, Qt as stripRetiredBandPreBufferSec, Rn as preprocess, Rt as recordingCapability, S as ExportRecordSchema, Tn as nodePin, Tt as memoizePinnedFfmpeg, Wt as resolveLocationMode, Xt as storageOccupancyCapability, Y as clipExportFailureOf, Yt as storageEvictableCapability, a as BatteryStatusSchema, b as EVENT_PAD_MS, ct as evictionPolicyForMode, dn as errMsg$1, en as videoclipsCapability, et as deriveBatteryPresence, g as DEFAULT_EVENTS_BAND_BUFFER_SEC, jn as array, k as OpsLogEntrySchema, kn as sleep$1, ln as DeviceFeature, lt as evictionPolicyOfLocation, nt as deriveRecordingMode, o as CLIP_BROKER_PACED_RATES, on as evaluateSensorEdge, pn as BaseAddon, pt as gbToBytes, un as DeviceType, ut as exportSubjectOf, vn as customAction, vt as isFirstLevelMacroClass, wt as mayWriteToLocation, xn as hydrateSchema, y as EVENT_OWNER_TYPES, yn as defineCustomActions, yt as loadContributionCapability, z as VIDEOCLIPS_MAX_OFFER_BYTES, zn as record, zt as recordingExportCapability } from "../dist-B3_sbBDM.mjs";
2
- import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DJFKTJ0B.mjs";
1
+ import { $t as stripRetiredBandPreBufferSec, A as OpsLogEntrySchema, An as sleep$1, B as VIDEOCLIPS_MAX_OFFER_BYTES, Bn as record, Bt as recordingExportCapability, C as ExportRecordSchema, Dt as migrateLegacyDeviceSignalConfig, En as nodePin, Et as memoizePinnedFfmpeg, F as RECORDING_SOURCE_CAMSTACK, Gt as resolveLocationMode, I as RECORDING_SOURCE_CAMSTACK_ADDON, L as RecordingConfigSchema, Ln as number, Mn as array, Nn as boolean, Ot as modeMayWrite, P as RECORDING_EXPORT_MAX_READ_BYTES, R as RecordingSignalStatusSchema, Rn as object, Rt as recordingArchiveCapability, Sn as hydrateSchema, Tt as mayWriteToLocation, Un as EventCategory, Ut as resolveCreateExportSubject, V as VISIT_MERGE_GAP_MS, Vn as string, Vt as recordingSignalCapability, X as clipExportFailureOf, Xt as storageEvictableCapability, Y as clipExportFailure, Zt as storageOccupancyCapability, _ as DEFAULT_EVENTS_BAND_BUFFER_SEC, b as EVENT_OWNER_TYPES, bn as defineCustomActions, bt as loadContributionCapability, dn as DeviceType, dt as exportSubjectOf, fn as errMsg$1, jn as _enum, kn as selectAssignedProfileSlots, lt as evictionPolicyForMode, mn as BaseAddon, mt as gbToBytes, o as BatteryStatusSchema, q as batteryCapability, qt as resolveRecordingProfiles, rt as deriveRecordingMode, s as CLIP_BROKER_PACED_RATES, sn as evaluateSensorEdge, tn as videoclipsCapability, tt as deriveBatteryPresence, un as DeviceFeature, ut as evictionPolicyOfLocation, wt as mayReadLocation, x as EVENT_PAD_MS, yn as customAction, yt as isFirstLevelMacroClass, zn as preprocess, zt as recordingCapability } from "../dist-E1Z_K22_.mjs";
2
+ import { a as physicalRootOf, i as NO_COST_CLAIM, t as ChildCostRegistry } from "../node-DHe-9_tm.mjs";
3
3
  import { t as resolveHubHostname } from "../hub-hostname-cCknRYKj.mjs";
4
4
  import { d as PREBUFFER_MAX_MS, l as withRecordingIntent, r as RECORDING_PRE_ROLL_REQUEST_MS } from "../restream-intent-DyGmLAU9.mjs";
5
5
  import { n as RowMapStore, t as retireRootKeys } from "../retire-root-keys-DZvlso2l.mjs";
@@ -1,5 +1,5 @@
1
1
  import { r as __toESM, t as __commonJSMin } from "./chunk-DnnnRqeS.mjs";
2
- import { dn as errMsg } from "./dist-B3_sbBDM.mjs";
2
+ import { fn as errMsg } from "./dist-E1Z_K22_.mjs";
3
3
  import { spawn } from "node:child_process";
4
4
  //#region src/stream-broker/recorded/avcc-annexb.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-emK7D4bc.js");
2
- const require_dist = require("./dist-BLf0YjAa.js");
2
+ const require_dist = require("./dist-Jvi2SOxI.js");
3
3
  let node_child_process = require("node:child_process");
4
4
  //#region src/stream-broker/recorded/avcc-annexb.ts
5
5
  /**
@@ -1,7 +1,7 @@
1
- import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BhO-szf8.mjs";
1
+ import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, p as c, r as l, s as u, t as d, u as f } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DddXyc17.mjs";
2
2
  import { a as p, i as m, n as h, o as g, r as _, t as v } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react__loadShare__.js-C9j-2lBe.mjs";
3
3
  import { n as y, r as b, t as x } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-XO0-Pyu6.mjs";
4
- import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-C6EaWavG.mjs";
4
+ import { t as S } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-D0BR9vFO.mjs";
5
5
  import { n as C, t as w } from "./_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-BO7TIbJV.mjs";
6
6
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.js
7
7
  var T = (...e) => e.filter((e, t, n) => !!e && e.trim() !== "" && n.indexOf(e) === t).join(" ").trim(), E = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), D = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), O = (e) => {