@camstack/types 1.2.17 → 1.2.19

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 (36) hide show
  1. package/dist/addon.js +2 -2
  2. package/dist/addon.mjs +2 -2
  3. package/dist/capabilities/addons.cap.d.ts +2 -2
  4. package/dist/capabilities/device-manager.cap.d.ts +22 -0
  5. package/dist/capabilities/index.d.ts +2 -2
  6. package/dist/capabilities/llm-runtime.cap.d.ts +3 -3
  7. package/dist/capabilities/llm.cap.d.ts +4 -4
  8. package/dist/capabilities/motion.cap.d.ts +13 -1
  9. package/dist/capabilities/notification-rules.cap.d.ts +142 -0
  10. package/dist/capabilities/pipeline-analytics.cap.d.ts +70 -1
  11. package/dist/capabilities/pipeline-executor.cap.d.ts +2 -2
  12. package/dist/capabilities/pipeline-orchestrator.cap.d.ts +6 -6
  13. package/dist/capabilities/recording-export.cap.d.ts +7 -7
  14. package/dist/capabilities/recording.cap.d.ts +1 -1
  15. package/dist/enums/event-category.d.ts +13 -0
  16. package/dist/enums.js +1 -1
  17. package/dist/enums.mjs +1 -1
  18. package/dist/{event-category-Cdyife4p.js → event-category-39bpf1r_.js} +13 -0
  19. package/dist/{event-category-BLcNejAE.mjs → event-category-Bz24uP1U.mjs} +13 -0
  20. package/dist/generated/addon-api.d.ts +7 -0
  21. package/dist/generated/method-access-map.d.ts +1 -1
  22. package/dist/generated/system-proxy.d.ts +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +148 -5
  25. package/dist/index.mjs +145 -6
  26. package/dist/interfaces/addon.d.ts +10 -2
  27. package/dist/interfaces/readiness.d.ts +5 -1
  28. package/dist/interfaces/relocate.d.ts +2 -2
  29. package/dist/lifecycle/job.d.ts +2 -2
  30. package/dist/readiness/readiness-registry.d.ts +32 -2
  31. package/dist/{sleep-Ct4Is70g.mjs → sleep-BYyv9gUM.mjs} +31 -4
  32. package/dist/{sleep-yfPaamGa.js → sleep-mkNQLVPE.js} +31 -4
  33. package/dist/types/detection.d.ts +49 -1
  34. package/dist/types/engine-output.d.ts +19 -19
  35. package/dist/types/pipeline-step.d.ts +2 -2
  36. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- const require_event_category = require("./event-category-Cdyife4p.js");
1
+ const require_event_category = require("./event-category-39bpf1r_.js");
2
2
  let zod = require("zod");
3
3
  //#region src/disposer-chain.ts
4
4
  var DisposerChain = class {
@@ -1513,6 +1513,7 @@ var ReadinessRegistry = class {
1513
1513
  logger;
1514
1514
  now;
1515
1515
  generation;
1516
+ verboseDeviceScopeLogs;
1516
1517
  snapshot = /* @__PURE__ */ new Map();
1517
1518
  subscriptions = /* @__PURE__ */ new Set();
1518
1519
  unsubscribeBus;
@@ -1523,6 +1524,7 @@ var ReadinessRegistry = class {
1523
1524
  this.logger = options.logger;
1524
1525
  this.now = options.now ?? (() => Date.now());
1525
1526
  this.generation = options.generation ?? randomGeneration();
1527
+ this.verboseDeviceScopeLogs = options.verboseDeviceScopeLogs ?? false;
1526
1528
  this.unsubscribeBus = this.bus.subscribe({ category: "system.ready-state" }, (event) => this.ingest(event.data));
1527
1529
  this.unsubscribeAgentOffline = this.bus.subscribe({ category: "agent.offline" }, (event) => this.synthesizeDownForNode(event.data.agentId));
1528
1530
  if (typeof this.bus.getRecent === "function") try {
@@ -1576,9 +1578,16 @@ var ReadinessRegistry = class {
1576
1578
  * Every applied entry dispatches a one-shot transition to matching
1577
1579
  * subscriptions so pending `awaitReady` callers unblock without having
1578
1580
  * to wait for a fresh event.
1581
+ *
1582
+ * Returns the number of records APPLIED (added or advanced). Callers
1583
+ * doing periodic reconciles use this to stay silent when the snapshot
1584
+ * changed nothing — the every-60s "hydrated 1446 record(s)" heartbeat
1585
+ * from every child process was pure noise (≈2 800 lines/hour observed
1586
+ * live on 2026-08-01).
1579
1587
  */
1580
1588
  hydrate(records) {
1581
1589
  const now = this.now();
1590
+ let applied = 0;
1582
1591
  for (const record of records) {
1583
1592
  const key = readinessKey(record.capName, record.scope);
1584
1593
  const prev = this.snapshot.get(key) ?? null;
@@ -1604,7 +1613,8 @@ var ReadinessRegistry = class {
1604
1613
  sourceNodeId: record.sourceNodeId
1605
1614
  };
1606
1615
  this.snapshot.set(key, hydrated);
1607
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
1616
+ applied++;
1617
+ this.logPerRecord(record.scope, `readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
1608
1618
  const transition = {
1609
1619
  capName: record.capName,
1610
1620
  scope: record.scope,
@@ -1625,6 +1635,23 @@ var ReadinessRegistry = class {
1625
1635
  }
1626
1636
  }
1627
1637
  }
1638
+ return applied;
1639
+ }
1640
+ /**
1641
+ * Per-record debug line, volume-gated: device-scoped records are logged
1642
+ * only when `verboseDeviceScopeLogs` is set (see the option's doc for
1643
+ * the 21 577-lines-per-boot-minute incident that forced the gate);
1644
+ * node/global records always log. Device lines carry `tags.deviceId`
1645
+ * so they group per-camera (CLAUDE.md logging rule).
1646
+ */
1647
+ logPerRecord(scope, message) {
1648
+ if (!this.logger) return;
1649
+ if (scope.type === "device") {
1650
+ if (!this.verboseDeviceScopeLogs) return;
1651
+ this.logger.debug(message, { tags: { deviceId: scope.deviceId } });
1652
+ return;
1653
+ }
1654
+ this.logger.debug(message);
1628
1655
  }
1629
1656
  /**
1630
1657
  * True when THIS process is the origin authority for `record` — a
@@ -1799,7 +1826,7 @@ var ReadinessRegistry = class {
1799
1826
  ts: payload.ts,
1800
1827
  durationInPrevState
1801
1828
  };
1802
- if (this.logger) this.logger.debug(`readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`);
1829
+ this.logPerRecord(payload.scope, `readiness: ${payload.capName} (${scopeKey(payload.scope)}) → ${payload.state} epoch=${epoch} gen=${payload.generation.slice(0, 6)} (prev ${durationInPrevState}ms)`);
1803
1830
  for (const sub of this.subscriptions) {
1804
1831
  if (sub.capName !== payload.capName) continue;
1805
1832
  if (!scopesEqual(sub.scope, payload.scope)) continue;
@@ -1844,7 +1871,7 @@ var ReadinessRegistry = class {
1844
1871
  ts: now,
1845
1872
  durationInPrevState
1846
1873
  };
1847
- if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`);
1874
+ this.logPerRecord(record.scope, `readiness: ${record.capName} (${scopeKey(record.scope)}) → down (synthesized from agent.offline ${offlineNodeId})`);
1848
1875
  for (const sub of this.subscriptions) {
1849
1876
  if (sub.capName !== record.capName) continue;
1850
1877
  if (!scopesEqual(sub.scope, record.scope)) continue;
@@ -1,4 +1,4 @@
1
- import type { BoundingBox as EngineBoundingBox, Landmark, FrameInput } from './io.js';
1
+ import type { BoundingBox as EngineBoundingBox, FrameInput, Landmark } from './io.js';
2
2
  export interface SpatialDetection {
3
3
  readonly class: string;
4
4
  readonly originalClass: string;
@@ -248,6 +248,47 @@ export interface AudioDetection extends DetectionBase {
248
248
  * avoids an import-level collision.
249
249
  */
250
250
  export type DetectionEntry = ObjectDetection | AudioDetection;
251
+ /**
252
+ * Why a POST-NMS box was dropped between the model and the overlay. One code
253
+ * per drop site — the diagnostic overlay shows the code verbatim, so a new
254
+ * drop site MUST add its own code here rather than reuse a neighbour's:
255
+ * - `class-filter`: class not in the step's `enabledMacroClasses`, or a
256
+ * COCO class the `classMap` does not map (both are class-identity drops
257
+ * and both count toward the trace's model-saw/kept gap).
258
+ * - `below-threshold`: score under the per-macro `minConfidence<Macro>`.
259
+ * - `full-frame-guard`: the full-frame phantom guard
260
+ * (`pipeline/full-frame-guard.ts`).
261
+ * - `zone-gate`: dropped by a detection-stage zone rule AFTER the frame
262
+ * result was built (`gateDetectionsByZoneRules`).
263
+ *
264
+ * NMS suppression is NOT a reason here: every engine's NMS runs in-process in
265
+ * the Python pool (`inference_pool.py` postprocessors) and the suppressed set
266
+ * is destroyed before the boundary — the trace's model-saw count is the only
267
+ * NMS visibility. A reason that can never fire does not belong in the union.
268
+ */
269
+ export type DiscardReason = 'class-filter' | 'below-threshold' | 'full-frame-guard' | 'zone-gate';
270
+ /**
271
+ * A post-NMS box the pipeline dropped before it could reach the overlay /
272
+ * tracker / events — always carried on `FrameResult.discarded` when a frame
273
+ * drops at least one box (the discarded set is post-NMS and
274
+ * post-confidence-floor: a handful of boxes per frame, not the raw
275
+ * candidate flood, which never leaves the Python pool).
276
+ *
277
+ * `bbox` is the executor's `[x1, y1, x2, y2]` tuple in ABSOLUTE
278
+ * analysis-frame pixels (the same space the executor's drop sites hold) —
279
+ * consumers normalise against `FrameResult.width`/`height` from the same
280
+ * frame. Deliberately NOT the survivors' `{x, y, width, height}` object:
281
+ * the tuple is what every drop site already holds, and the viewer's
282
+ * `parseDiscardedEvent` accepts it as-is.
283
+ */
284
+ export interface DiscardedDetection {
285
+ /** `[x1, y1, x2, y2]` in absolute analysis-frame pixels. */
286
+ readonly bbox: readonly [number, number, number, number];
287
+ /** Macro class where the classMap resolves one, else the raw model class. */
288
+ readonly macroClass: string;
289
+ readonly score: number;
290
+ readonly reason: DiscardReason;
291
+ }
251
292
  /**
252
293
  * Per-frame pipeline result — the canonical container emitted by
253
294
  * `pipelineExecutor.runPipeline` (one-shot benchmark + runtime dispatch)
@@ -263,6 +304,13 @@ export interface FrameResult {
263
304
  readonly width: number;
264
305
  readonly height: number;
265
306
  readonly detections: readonly ObjectDetection[];
307
+ /**
308
+ * Post-NMS boxes the pipeline dropped on this frame (see
309
+ * {@link DiscardedDetection}). Always populated when at least one box was
310
+ * dropped — absent means "nothing dropped", never "empty array". A frame
311
+ * that drops nothing allocates nothing and puts no field on the wire.
312
+ */
313
+ readonly discarded?: readonly DiscardedDetection[];
266
314
  readonly debug?: FrameDebug;
267
315
  }
268
316
  /**
@@ -1,23 +1,23 @@
1
- export interface LetterboxMeta {
2
- readonly scale: number;
3
- readonly padX: number;
4
- readonly padY: number;
5
- readonly originalWidth: number;
6
- readonly originalHeight: number;
7
- }
1
+ /**
2
+ * Output envelope of an inference engine call.
3
+ *
4
+ * Every engine is a Python pool (`inference_pool.py`) and postprocessing runs
5
+ * in-process there — the pool returns a `structured` StepOutput payload. The
6
+ * former Node.js onnxruntime raw-tensor fields (`tensor` / `tensors` /
7
+ * `letterbox`) were removed with that path: the platform ships no Node ONNX
8
+ * runtime.
9
+ */
8
10
  export interface EngineOutput {
9
- readonly tensor?: Float32Array;
10
- readonly tensors?: Record<string, Float32Array>;
11
11
  readonly structured?: Record<string, unknown>;
12
- readonly letterbox?: LetterboxMeta;
13
12
  readonly inferenceMs: number;
14
- }
15
- export interface ModelInputMeta {
16
- readonly inputSize: {
17
- readonly width: number;
18
- readonly height: number;
19
- };
20
- readonly inputNormalization: 'zero-one' | 'imagenet' | 'none';
21
- readonly inputLayout: 'nchw' | 'nhwc';
22
- readonly preprocessMode: 'letterbox' | 'resize';
13
+ /**
14
+ * Overload shed: the inference pool DELIBERATELY did not run inference for
15
+ * this frame (the Python pool's per-model in-flight bound, or the TS-side
16
+ * worker in-flight cap). A shed output carries NO `structured` payload —
17
+ * consumers skip the frame the way any dropped frame is skipped. It is an
18
+ * expected flow-control outcome, never an error.
19
+ */
20
+ readonly dropped?: true;
21
+ /** Which bound shed the frame (e.g. `'worker-in-flight-cap'`). */
22
+ readonly shedReason?: string;
23
23
  }
@@ -1,7 +1,7 @@
1
- import type { ModelCatalogEntry } from './models.js';
1
+ import type { ConfigField } from '../interfaces/config-ui.js';
2
2
  import type { ClassMapDefinition } from './labels.js';
3
+ import type { ModelCatalogEntry } from './models.js';
3
4
  import type { PipelineSlot } from './pipeline.js';
4
- import type { ConfigField } from '../interfaces/config-ui.js';
5
5
  /**
6
6
  * Each pipeline step implements this interface as a class.
7
7
  * `definition` holds the static metadata; `getConfigSchema()` declares
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/types",
3
- "version": "1.2.17",
3
+ "version": "1.2.19",
4
4
  "description": "Shared types, interfaces, and model catalogs for the CamStack detection ecosystem",
5
5
  "keywords": [
6
6
  "camstack",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "camstack": {
50
50
  "system": false,
51
- "displayName": "CamStack Types",
51
+ "displayName": "Types",
52
52
  "description": "Shared cap definitions, schemas, and runtime contracts. Required at server boot; updating restarts the hub."
53
53
  },
54
54
  "files": [