@camstack/addon-pipeline 1.2.296 → 1.2.297

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-D1daTtqT.js → default-detection-model-B2n_SFXt.js} +368 -5
  4. package/dist/{default-detection-model-Co578D8C.mjs → default-detection-model-D6DypCkI.mjs} +339 -6
  5. package/dist/detection-pipeline/index.js +521 -217
  6. package/dist/detection-pipeline/index.mjs +520 -216
  7. package/dist/{dist-CCd0Q3nr.mjs → dist-CxHIpsQs.mjs} +53 -123
  8. package/dist/{dist-8up-f2TX.js → dist-DUqr1zyq.js} +64 -140
  9. package/dist/motion-wasm/index.js +1 -1
  10. package/dist/motion-wasm/index.mjs +1 -1
  11. package/dist/{node-lpQgHes9.js → node-Dx6DLQ1j.js} +1 -1
  12. package/dist/{node-DgMSXSWP.mjs → node-DyeWu78a.mjs} +1 -1
  13. package/dist/pipeline-runner/index.js +213 -203
  14. package/dist/pipeline-runner/index.mjs +213 -203
  15. package/dist/{process-memory-DFC_O5zE.mjs → process-memory-BVR4592X.mjs} +1 -1
  16. package/dist/{process-memory-CX_92V_r.js → process-memory-CMJ3NY-s.js} +1 -1
  17. package/dist/recorder/index.js +2 -2
  18. package/dist/recorder/index.mjs +2 -2
  19. package/dist/{segment-demux-js-DzBx6NN2.js → segment-demux-js-CmGIF_uK.js} +1 -1
  20. package/dist/{segment-demux-js-FZbBuk3F.mjs → segment-demux-js-Dzga-XgE.mjs} +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-C_i7oFBl.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-P71ze8cu.mjs} +2 -2
  23. package/dist/stream-broker/{_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-DUGQKsKL.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CZkpFU-J.mjs} +1 -1
  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-Dti8Ex88.mjs +26 -0
  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-BPtppL3W.mjs → hostInit-CRlStbz4.mjs} +2 -2
  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 +1 -1
  32. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CkbplMHA.mjs +0 -26
@@ -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-8up-f2TX.js");
7
- const require_process_memory = require("../process-memory-CX_92V_r.js");
6
+ const require_dist = require("../dist-DUqr1zyq.js");
7
+ const require_process_memory = require("../process-memory-CMJ3NY-s.js");
8
8
  let node_fs = require("node:fs");
9
9
  node_fs = require_chunk.__toESM(node_fs);
10
10
  let node_path = require("node:path");
@@ -1,6 +1,6 @@
1
1
  import { n as __require } from "../chunk-DnnnRqeS.mjs";
2
- import { E as HF_BASE_URL, G as audioAnalyzerCapability, Gt as resolvePoolMemoryPolicy, Sn as nodePin, W as audioAnalysisCapability, _n as expandAudioChunkToF32le, bt as mapAudioLabelToMacro, cn as BaseAddon, f as DEFAULT_AUDIO_ANALYZER_CONFIG, j as PoolMemoryWatchdog, n as AUDIO_BACKEND_CHOICES, pn as audioChunkBytesPerSample, tn as errMsg, vn as hydrateSchema, zn as EventCategory } from "../dist-CCd0Q3nr.mjs";
3
- import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-DFC_O5zE.mjs";
2
+ import { E as HF_BASE_URL, G as audioAnalyzerCapability, Rn as EventCategory, Ut as resolvePoolMemoryPolicy, W as audioAnalysisCapability, _n as hydrateSchema, bt as mapAudioLabelToMacro, en as errMsg, fn as audioChunkBytesPerSample, gn as expandAudioChunkToF32le, j as PoolMemoryWatchdog, n as AUDIO_BACKEND_CHOICES, p as DEFAULT_AUDIO_ANALYZER_CONFIG, sn as BaseAddon, xn as nodePin } from "../dist-CxHIpsQs.mjs";
3
+ import { n as pickNodePlatformArch, t as readProcessMemory } from "../process-memory-BVR4592X.mjs";
4
4
  import * as fs from "node:fs";
5
5
  import * as path$1 from "node:path";
6
6
  import { downloadFile } from "@camstack/system/addon-utils";
@@ -1,4 +1,4 @@
1
- const require_dist = require("./dist-8up-f2TX.js");
1
+ const require_dist = require("./dist-DUqr1zyq.js");
2
2
  let node_crypto = require("node:crypto");
3
3
  //#region src/detection-pipeline/pipeline/landmark-precision-gate.ts
4
4
  /**
@@ -1655,7 +1655,6 @@ var STEP_FACE_DETECTION = new PipelineStepBase({
1655
1655
  outputClasses: ["face"],
1656
1656
  models: [...FACE_DETECTION_MODELS],
1657
1657
  defaultModelId: "yunet-2023mar",
1658
- defaultModelIdByFormat: { tflite: "scrfd-2.5g" },
1659
1658
  defaultConfidence: .5,
1660
1659
  defaultMinParentScore: .7,
1661
1660
  cadence: {
@@ -2080,17 +2079,24 @@ var LocalFrameRegistry = class {
2080
2079
  image,
2081
2080
  estimatedBytes,
2082
2081
  refs: 1,
2082
+ consumerRefs: 1,
2083
2083
  releaseNativeOnClose: false
2084
2084
  });
2085
2085
  this.bytes += estimatedBytes;
2086
2086
  this.evictToBounds();
2087
2087
  return this.entries.has(ref.id) ? ref : null;
2088
2088
  }
2089
- acquire(ref) {
2089
+ /**
2090
+ * Take a lease on `ref`. `holder` says why (D656): a reader that resolves a
2091
+ * view now is a `consumer` (the default); a lease PARKED for a read that may
2092
+ * come later is `retention`, and does not keep a draining worker alive.
2093
+ */
2094
+ acquire(ref, holder = "consumer") {
2090
2095
  if (ref.registryId === this.registryId && !this.entries.has(ref.id)) this.staleMissCount += 1;
2091
2096
  const entry = this.getEntry(ref);
2092
2097
  if (!entry) return null;
2093
2098
  entry.refs += 1;
2099
+ if (holder === "consumer") this.addConsumer(entry);
2094
2100
  this.touch(entry);
2095
2101
  let released = false;
2096
2102
  return {
@@ -2098,17 +2104,40 @@ var LocalFrameRegistry = class {
2098
2104
  release: (reason) => {
2099
2105
  if (released) return;
2100
2106
  released = true;
2101
- this.release(ref, reason);
2107
+ this.releaseRef(ref, reason, holder);
2102
2108
  }
2103
2109
  };
2104
2110
  }
2111
+ /** Release the OWNER reference {@link register} took — a consumer reference. */
2105
2112
  release(ref, reason) {
2113
+ this.releaseRef(ref, reason, "consumer");
2114
+ }
2115
+ releaseRef(ref, reason, holder) {
2106
2116
  const entry = this.getEntry(ref);
2107
2117
  if (!entry) return;
2108
2118
  this.releaseCount += 1;
2109
2119
  if (releasesNative(reason)) entry.releaseNativeOnClose = true;
2110
2120
  entry.refs = Math.max(0, entry.refs - 1);
2111
- if (entry.refs === 0) this.drop(entry);
2121
+ if (entry.refs === 0) {
2122
+ this.drop(entry);
2123
+ return;
2124
+ }
2125
+ if (holder === "consumer") this.removeConsumer(entry);
2126
+ }
2127
+ /**
2128
+ * The frame's source learns whether a consumer still holds it, on each
2129
+ * transition only: the last consumer letting go of a frame that stays
2130
+ * PARKED, and a consumer coming back to one. A drop needs no signal — the
2131
+ * image is closed.
2132
+ */
2133
+ addConsumer(entry) {
2134
+ entry.consumerRefs += 1;
2135
+ if (entry.consumerRefs === 1) entry.image.setPendingConsumer?.(true);
2136
+ }
2137
+ removeConsumer(entry) {
2138
+ if (entry.consumerRefs === 0) return;
2139
+ entry.consumerRefs -= 1;
2140
+ if (entry.consumerRefs === 0) entry.image.setPendingConsumer?.(false);
2112
2141
  }
2113
2142
  clear(reason = "detach") {
2114
2143
  for (const entry of [...this.entries.values()]) {
@@ -2207,6 +2236,310 @@ function rootObjectGate(steps) {
2207
2236
  return (steps?.find((s) => s.enabled !== false))?.settings;
2208
2237
  }
2209
2238
  //#endregion
2239
+ //#region src/pipeline-runner/device-jump.ts
2240
+ /**
2241
+ * Step-tree device jump (same-node, phase 1) — the PURE resolution core.
2242
+ *
2243
+ * Given the camera's elected ("effective") inference device and the node's
2244
+ * enabled-device roster, decide which device ONE enrichment step should run on:
2245
+ *
2246
+ * 1. A manual `jumpDeviceKey` override wins when it names a candidate that can
2247
+ * run the step (bypasses the auto choice; operator intent).
2248
+ * 2. Otherwise, if the effective device's format can run the step's model →
2249
+ * NO jump (the step inherits the effective device — the common case).
2250
+ * 3. Otherwise AUTO-jump: among the node's OTHER enabled devices whose format
2251
+ * can run the step (and that are under their `maxSessions` cap), pick the
2252
+ * weighted-least-loaded one — mirroring the balancer's per-device core.
2253
+ * 4. If nothing on the node can run it → `no-candidate` (the caller emits the
2254
+ * loud once-per-(camera,step) warn that replaces today's silent degrade).
2255
+ *
2256
+ * Framework-free + side-effect-free so the whole decision table is unit-testable
2257
+ * without a live runner/catalog. The `canRun(format)` predicate is injected by
2258
+ * the runner (which owns the model catalog); this module never imports it.
2259
+ */
2260
+ /** The `ModelFormat` of a `<backend>:<device>` key, or `undefined` for an unknown backend. */
2261
+ function formatOf(deviceKey) {
2262
+ const colon = deviceKey.indexOf(":");
2263
+ return require_dist.DEVICE_BACKEND_TO_FORMAT[colon >= 0 ? deviceKey.slice(0, colon) : deviceKey];
2264
+ }
2265
+ /** True when the step's model has a build for `deviceKey`'s format. */
2266
+ function canRunOn(deviceKey, canRun) {
2267
+ const format = formatOf(deviceKey);
2268
+ return format !== void 0 && canRun(format, deviceKey);
2269
+ }
2270
+ /** Effective weighted load of a device (session count ÷ weight); lower = preferred. */
2271
+ function weightedLoad(d) {
2272
+ return d.attachedCameras / (d.weight > 0 ? d.weight : 1);
2273
+ }
2274
+ /**
2275
+ * Weighted-least-loaded pick with a deterministic `deviceKey` tie-break —
2276
+ * mirrors the node/device balancer's decision core (least `load/weight`, ties
2277
+ * broken stably). `devices` is assumed non-empty.
2278
+ */
2279
+ function pickWeightedLeastLoaded(devices) {
2280
+ return devices.reduce((best, d) => {
2281
+ const dScore = weightedLoad(d);
2282
+ const bScore = weightedLoad(best);
2283
+ if (dScore < bScore) return d;
2284
+ if (dScore > bScore) return best;
2285
+ return d.deviceKey < best.deviceKey ? d : best;
2286
+ });
2287
+ }
2288
+ /**
2289
+ * Build the `canRun` predicate for ONE enrichment dispatch. The step and its
2290
+ * enabled descendants ride the SAME `runPipeline` call — one pool, one format
2291
+ * (detail-subtree contract: "its inherited children ride the same call → same
2292
+ * device") — so a format can run the dispatch only when EVERY catalog-known
2293
+ * step in the subtree ships a build for it. Gating only the subtree TOP let a
2294
+ * `face-detection` with a tflite build stay on the Coral while its
2295
+ * `face-embedding` child (no tflite build anywhere) doomed the call.
2296
+ * Unknown/custom addonIds (`getStepDef` → null) have no catalog to gate on
2297
+ * and never block. `getStepDef` is injected — this module stays catalog-free.
2298
+ * Each step is judged by `stepModelRunsOnFormat` (`@camstack/types`), the one
2299
+ * rule every placement reads: a legacy stub build counts only when named (D657).
2300
+ */
2301
+ function buildSubtreeCanRun(step, getStepDef) {
2302
+ const judged = [];
2303
+ const walk = (s) => {
2304
+ if (s.enabled === false) return;
2305
+ const def = getStepDef(s.addonId);
2306
+ if (def !== null) judged.push({
2307
+ def,
2308
+ modelId: s.modelId
2309
+ });
2310
+ for (const child of s.children ?? []) walk(child);
2311
+ };
2312
+ walk(step);
2313
+ return (format) => judged.every(({ def, modelId }) => require_dist.stepModelRunsOnFormat(def.models, modelId, format));
2314
+ }
2315
+ /**
2316
+ * Resolve the device one enrichment step runs on. See the module header for the
2317
+ * decision table. Pure — never throws, no I/O.
2318
+ */
2319
+ function resolveStepDevice(input) {
2320
+ const { effectiveDeviceKey, manualJumpDeviceKey, canRun } = input;
2321
+ if (manualJumpDeviceKey !== void 0 && manualJumpDeviceKey !== effectiveDeviceKey) {
2322
+ const target = input.devices.find((d) => d.deviceKey === manualJumpDeviceKey);
2323
+ if (target && canRunOn(target.deviceKey, canRun)) return {
2324
+ kind: "jumped",
2325
+ deviceKey: target.deviceKey,
2326
+ reason: "manual"
2327
+ };
2328
+ }
2329
+ if (canRunOn(effectiveDeviceKey, canRun)) return {
2330
+ kind: "no-jump",
2331
+ deviceKey: effectiveDeviceKey
2332
+ };
2333
+ const runnable = input.devices.filter((d) => d.deviceKey !== effectiveDeviceKey && (d.maxSessions === null || d.attachedCameras < d.maxSessions) && canRunOn(d.deviceKey, canRun));
2334
+ if (runnable.length === 0) return {
2335
+ kind: "no-candidate",
2336
+ deviceKey: effectiveDeviceKey
2337
+ };
2338
+ return {
2339
+ kind: "jumped",
2340
+ deviceKey: pickWeightedLeastLoaded(runnable).deviceKey,
2341
+ reason: "auto"
2342
+ };
2343
+ }
2344
+ /**
2345
+ * Re-provision a jumped subtree for its TARGET device (D650): every step's
2346
+ * `modelId` / `settings` are REPLACED by the target's entry — the orchestrator
2347
+ * computed it as what the step would carry had the camera been elected onto
2348
+ * that device (its per-(node,device) base with the camera's override for it).
2349
+ * A step absent from the map runs with neither, i.e. the target format's
2350
+ * default model. The elected device's provisioning never follows a step across:
2351
+ * it was chosen for a format this subtree does not run on.
2352
+ */
2353
+ function provisionSubtreeForDevice(step, provisioning) {
2354
+ const own = provisioning[step.addonId];
2355
+ const { modelId: _electedModelId, settings: _electedSettings, ...rest } = step;
2356
+ return {
2357
+ ...rest,
2358
+ ...own?.modelId !== void 0 ? { modelId: own.modelId } : {},
2359
+ ...own?.settings !== void 0 ? { settings: own.settings } : {},
2360
+ ...step.children !== void 0 ? { children: step.children.map((c) => provisionSubtreeForDevice(c, provisioning)) } : {}
2361
+ };
2362
+ }
2363
+ /** The subtree as `deviceKey` would run it: the elected device runs it as sent; another takes its roster entry. */
2364
+ function provisionedFor(step, deviceKey, effectiveDeviceKey, roster) {
2365
+ if (deviceKey === effectiveDeviceKey) return step;
2366
+ const provisioning = roster.find((d) => d.deviceKey === deviceKey)?.steps;
2367
+ return provisioning === void 0 ? step : provisionSubtreeForDevice(step, provisioning);
2368
+ }
2369
+ /**
2370
+ * Place ONE detail subtree (D650): which same-node device runs it
2371
+ * ({@link resolveStepDevice}) and, when it jumps, the provisioning it runs
2372
+ * with there ({@link provisionSubtreeForDevice}). Pure — the caller logs.
2373
+ *
2374
+ * A roster entry without `steps` came from an orchestrator that predates D650:
2375
+ * the step keeps what it was sent with, as it did before.
2376
+ */
2377
+ function placeDetailStep(input) {
2378
+ const { step, effectiveDeviceKey, roster } = input;
2379
+ if (!effectiveDeviceKey) return {
2380
+ deviceKey: void 0,
2381
+ step,
2382
+ decision: null,
2383
+ provisioned: false
2384
+ };
2385
+ if (!roster || roster.length < 2) return {
2386
+ deviceKey: effectiveDeviceKey,
2387
+ step,
2388
+ decision: null,
2389
+ provisioned: false
2390
+ };
2391
+ const decision = resolveStepDevice({
2392
+ effectiveDeviceKey,
2393
+ devices: roster.map((d) => ({
2394
+ deviceKey: d.deviceKey,
2395
+ weight: d.weight,
2396
+ maxSessions: d.maxSessions,
2397
+ attachedCameras: input.attachedByKey.get(d.deviceKey) ?? 0
2398
+ })),
2399
+ ...step.jumpDeviceKey !== void 0 ? { manualJumpDeviceKey: step.jumpDeviceKey } : {},
2400
+ canRun: (format, deviceKey) => buildSubtreeCanRun(provisionedFor(step, deviceKey, effectiveDeviceKey, roster), input.getStepDef)(format)
2401
+ });
2402
+ if (decision.kind !== "jumped") return {
2403
+ deviceKey: decision.deviceKey,
2404
+ step,
2405
+ decision,
2406
+ provisioned: false
2407
+ };
2408
+ const provisioning = roster.find((d) => d.deviceKey === decision.deviceKey)?.steps;
2409
+ return {
2410
+ deviceKey: decision.deviceKey,
2411
+ step: provisioning === void 0 ? step : provisionSubtreeForDevice(step, provisioning),
2412
+ decision,
2413
+ provisioned: provisioning !== void 0
2414
+ };
2415
+ }
2416
+ /**
2417
+ * The steps whose models a camera's elected pool loads (D657).
2418
+ *
2419
+ * The frame-plane dispatch executes only root-plane steps; a detail subtree
2420
+ * rides it solely so its model pre-warms the elected pool. That is right only
2421
+ * when the subtree RUNS there. The decision is {@link placeDetailStep}'s — the
2422
+ * one the detail plane takes for the same subtree — so the pool that loads a
2423
+ * model and the pool that runs it cannot disagree:
2424
+ * - no decision (no elected device, or a roster of one) or `no-jump`: kept;
2425
+ * - `jumped` (auto or manual): left out — the detail dispatch loads it on the
2426
+ * TARGET pool, with the target's provisioning;
2427
+ * - `no-candidate`: left out — nothing on the node can load it, so a
2428
+ * pre-warm could only fail (the detail plane still says so, loudly).
2429
+ * Roots are the elected device's job and always stay. Root-plane children
2430
+ * stay and are walked; a detail subtree is judged whole at its top, because
2431
+ * its children ride its call. Pure — the caller logs.
2432
+ */
2433
+ function stepsForElectedPool(steps, isDetailStep, place) {
2434
+ const delegated = [];
2435
+ const walk = (nodes, topLevel) => {
2436
+ const kept = [];
2437
+ for (const step of nodes) {
2438
+ if (!topLevel && step.enabled !== false && isDetailStep(step.addonId)) {
2439
+ const decision = place(step).decision;
2440
+ if (decision === null || decision.kind === "no-jump") kept.push(step);
2441
+ else delegated.push(step.addonId);
2442
+ continue;
2443
+ }
2444
+ kept.push(step.children !== void 0 && step.children.length > 0 ? {
2445
+ ...step,
2446
+ children: walk(step.children, false)
2447
+ } : step);
2448
+ }
2449
+ return kept;
2450
+ };
2451
+ return {
2452
+ steps: walk(steps, true),
2453
+ delegated
2454
+ };
2455
+ }
2456
+ //#endregion
2457
+ //#region src/detection-pipeline/registry/custom-models.ts
2458
+ /**
2459
+ * Group a flat list of custom-model descriptors (as returned by the
2460
+ * `custom-model-registry` collection cap) into a `stepId → entries` map for
2461
+ * the picker / resolution union. Pure; order within a step preserved.
2462
+ */
2463
+ function groupCustomModelsByStep(descriptors) {
2464
+ const byStep = /* @__PURE__ */ new Map();
2465
+ for (const d of descriptors) {
2466
+ const arr = byStep.get(d.stepId) ?? [];
2467
+ arr.push(d.entry);
2468
+ byStep.set(d.stepId, arr);
2469
+ }
2470
+ return byStep;
2471
+ }
2472
+ /**
2473
+ * Union a step's static catalog models with operator-registered custom
2474
+ * models. On an `id` collision the static catalog entry wins — a custom
2475
+ * model can never shadow a built-in one.
2476
+ *
2477
+ * Pure + side-effect-free so it can be unit-tested in isolation and called
2478
+ * from the (free) `buildSchemaSlots` builder without any addon context.
2479
+ */
2480
+ function mergeCustomModels(staticModels, customModels) {
2481
+ const seen = new Set(staticModels.map((m) => m.id));
2482
+ const merged = [...staticModels];
2483
+ for (const m of customModels) {
2484
+ if (seen.has(m.id)) continue;
2485
+ seen.add(m.id);
2486
+ merged.push({
2487
+ ...m,
2488
+ provider: require_dist.inferModelProvider(m)
2489
+ });
2490
+ }
2491
+ return merged;
2492
+ }
2493
+ /**
2494
+ * A step's definition with its custom models merged in (static wins on an id
2495
+ * collision), or `null` for an unknown addonId. Pure.
2496
+ */
2497
+ function mergedStepDefinition(stepId, customByStep) {
2498
+ let def;
2499
+ try {
2500
+ def = getStepDefinition(stepId);
2501
+ } catch {
2502
+ return null;
2503
+ }
2504
+ const custom = customByStep.get(stepId);
2505
+ if (custom === void 0 || custom.length === 0) return def;
2506
+ return {
2507
+ ...def,
2508
+ models: mergeCustomModels(def.models, custom)
2509
+ };
2510
+ }
2511
+ /** TTL-cached read of the custom-model registry, with a synchronous merged view. */
2512
+ var CustomModelCatalog = class {
2513
+ logger;
2514
+ now;
2515
+ byStep = /* @__PURE__ */ new Map();
2516
+ readAtMs = Number.NEGATIVE_INFINITY;
2517
+ constructor(logger, now = Date.now) {
2518
+ this.logger = logger;
2519
+ this.now = now;
2520
+ }
2521
+ /** Re-read the registry when the snapshot is older than the TTL; answer the snapshot. */
2522
+ async refresh(api) {
2523
+ const now = this.now();
2524
+ if (now - this.readAtMs < 5e3) return this.byStep;
2525
+ this.readAtMs = now;
2526
+ if (api === void 0) return this.byStep;
2527
+ try {
2528
+ const providers = await api.addons.listCapabilityProviders.query({ capName: "custom-model-registry" });
2529
+ this.byStep = providers.some((p) => p.isActive) ? groupCustomModelsByStep(await api.customModelRegistry.listModels.query()) : /* @__PURE__ */ new Map();
2530
+ } catch (err) {
2531
+ this.logger.warn("custom-model-registry query failed — keeping the last custom-model snapshot", { meta: { error: err instanceof Error ? err.message : String(err) } });
2532
+ }
2533
+ return this.byStep;
2534
+ }
2535
+ /** The snapshot, synchronously. */
2536
+ current() {
2537
+ return this.byStep;
2538
+ }
2539
+ /** A step's merged definition from the snapshot, synchronously. */
2540
+ stepDefinition = (stepId) => mergedStepDefinition(stepId, this.byStep);
2541
+ };
2542
+ //#endregion
2210
2543
  //#region src/detection-pipeline/default-detection-model.ts
2211
2544
  /** The object-detection step id — the only slot this resolver applies to. */
2212
2545
  var OBJECT_DETECTION_STEP_ID = "object-detection";
@@ -2301,6 +2634,12 @@ Object.defineProperty(exports, "ALL_STEPS", {
2301
2634
  return ALL_STEPS;
2302
2635
  }
2303
2636
  });
2637
+ Object.defineProperty(exports, "CustomModelCatalog", {
2638
+ enumerable: true,
2639
+ get: function() {
2640
+ return CustomModelCatalog;
2641
+ }
2642
+ });
2304
2643
  Object.defineProperty(exports, "OBJECT_DETECTION_STEP_ID", {
2305
2644
  enumerable: true,
2306
2645
  get: function() {
@@ -2313,6 +2652,12 @@ Object.defineProperty(exports, "RETIRED_MODEL_IDS", {
2313
2652
  return RETIRED_MODEL_IDS;
2314
2653
  }
2315
2654
  });
2655
+ Object.defineProperty(exports, "buildSubtreeCanRun", {
2656
+ enumerable: true,
2657
+ get: function() {
2658
+ return buildSubtreeCanRun;
2659
+ }
2660
+ });
2316
2661
  Object.defineProperty(exports, "getDefaultModelForFormat", {
2317
2662
  enumerable: true,
2318
2663
  get: function() {
@@ -2349,6 +2694,18 @@ Object.defineProperty(exports, "macroGateVerdict", {
2349
2694
  return macroGateVerdict;
2350
2695
  }
2351
2696
  });
2697
+ Object.defineProperty(exports, "mergeCustomModels", {
2698
+ enumerable: true,
2699
+ get: function() {
2700
+ return mergeCustomModels;
2701
+ }
2702
+ });
2703
+ Object.defineProperty(exports, "placeDetailStep", {
2704
+ enumerable: true,
2705
+ get: function() {
2706
+ return placeDetailStep;
2707
+ }
2708
+ });
2352
2709
  Object.defineProperty(exports, "resolveDefaultDetectionModel", {
2353
2710
  enumerable: true,
2354
2711
  get: function() {
@@ -2373,3 +2730,9 @@ Object.defineProperty(exports, "rootObjectGate", {
2373
2730
  return rootObjectGate;
2374
2731
  }
2375
2732
  });
2733
+ Object.defineProperty(exports, "stepsForElectedPool", {
2734
+ enumerable: true,
2735
+ get: function() {
2736
+ return stepsForElectedPool;
2737
+ }
2738
+ });