@camstack/addon-pipeline-orchestrator 1.2.126 → 1.2.128

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.
package/dist/index.mjs CHANGED
@@ -13847,6 +13847,114 @@ method(object({
13847
13847
  height: number()
13848
13848
  }), EmbeddingResultSchema, { auth: "admin" }), method(object({ text: string() }), EmbeddingResultSchema, { auth: "admin" }), method(_void(), EmbeddingInfoSchema, { auth: "admin" });
13849
13849
  /**
13850
+ * `failure-contribution` — the capability an addon reports its OWN losses
13851
+ * through, per camera, with the denominator attached. It stores nothing.
13852
+ *
13853
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
13854
+ *
13855
+ * `load-contribution` answers *what did this camera COST*. This answers *what
13856
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
13857
+ * copied: the contributor reports what it already knows, hub-main adds only
13858
+ * `addonId`, nothing needs global knowledge, and there is no central list for
13859
+ * somebody to forget to edit.
13860
+ *
13861
+ * They are not merged, because their invariants are opposites:
13862
+ *
13863
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
13864
+ * claim a camera cost nothing, which is a measurement nobody made;
13865
+ * - a `failure-contribution` zero is the **most valuable value on the
13866
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
13867
+ * and it is exactly what an absent entry cannot say.
13868
+ *
13869
+ * Putting a loss counter on a cost entry would also break the reconciliation
13870
+ * that gives `load-contribution` its point: contributions are subtracted from
13871
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
13872
+ * has no process.
13873
+ *
13874
+ * ## Why not a log line, since the counters already exist
13875
+ *
13876
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
13877
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
13878
+ * ends in a log line, and a log line is the thing the operator asked to stop
13879
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
13880
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
13881
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
13882
+ * media blackout were both diagnosed. The counters stay; this is where they can
13883
+ * be READ.
13884
+ *
13885
+ * ## The rate is served with its denominator or not at all
13886
+ *
13887
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
13888
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
13889
+ * than yesterday" and was **flat across twelve hours** once divided by the
13890
+ * successes on the same path. A surface that publishes only the numerator
13891
+ * reproduces that mistake on every read.
13892
+ *
13893
+ * ## Shape
13894
+ *
13895
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
13896
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
13897
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
13898
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
13899
+ * a forked runner's entries reach hub-main over transport that already exists.
13900
+ * No new UDS message, no second registry (D3). The operator reads the assembled
13901
+ * result through `system.getFailureContributions`.
13902
+ */
13903
+ var FailureReasonCountSchema = object({
13904
+ /**
13905
+ * Why the attempt did not land, in the contributor's own vocabulary —
13906
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
13907
+ * strings that already appear in this repo's logs and, where one exists, the
13908
+ * same string the per-track `previewMissReason` records (D276): a second
13909
+ * vocabulary for the same loss would make the row and the counter
13910
+ * un-joinable.
13911
+ */
13912
+ reason: string(),
13913
+ count: number().int().nonnegative()
13914
+ });
13915
+ var FailureContributionSchema = object({
13916
+ /**
13917
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
13918
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
13919
+ * `unit` free: the families are owned by different addons and a shared enum
13920
+ * is a central list that rots invisibly.
13921
+ */
13922
+ family: string(),
13923
+ /**
13924
+ * The NUMERIC device id — the same value every log line carries as
13925
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
13926
+ * cannot name the camera must not emit the entry, because a fleet total
13927
+ * cannot answer the only question anybody asks of this surface.
13928
+ */
13929
+ deviceId: number().int().positive(),
13930
+ /**
13931
+ * A second dimension inside the family: the model / step id for an inference
13932
+ * timeout, so "which camera AND which model" is one read. Absent when the
13933
+ * family has a single variant.
13934
+ */
13935
+ variant: string().optional(),
13936
+ /**
13937
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
13938
+ * differencing two reads must drop the interval when it changes, because the
13939
+ * counter restarted from zero in a respawned runner. Same discipline as
13940
+ * `LoadContribution.startedAtMs`.
13941
+ */
13942
+ sinceMs: number(),
13943
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
13944
+ atMs: number(),
13945
+ /**
13946
+ * THE DENOMINATOR — every attempt on this path for this camera in the
13947
+ * window. A failure count published without it is the mistake this schema
13948
+ * exists to make impossible.
13949
+ */
13950
+ attempts: number().int().nonnegative(),
13951
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
13952
+ succeeded: number().int().nonnegative(),
13953
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
13954
+ reasons: array(FailureReasonCountSchema).readonly()
13955
+ });
13956
+ method(_void(), array(FailureContributionSchema).readonly());
13957
+ /**
13850
13958
  * filesystem-browse — per-node capability for browsing the node's local
13851
13959
  * filesystem. Reads are unconfined (whole filesystem, from `/` down); WRITES
13852
13960
  * are sandboxed to operator-configured allowed roots (D115). Used by the
@@ -14368,6 +14476,68 @@ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }
14368
14476
  kind: "mutation",
14369
14477
  auth: "admin"
14370
14478
  });
14479
+ var LoadContributionSchema = object({
14480
+ role: _enum([
14481
+ "decode",
14482
+ "transcode",
14483
+ "recording",
14484
+ "streaming",
14485
+ "detection"
14486
+ ]),
14487
+ /**
14488
+ * The NUMERIC device id — the same value every log line carries as
14489
+ * `tags.deviceId`. `null` means this cost genuinely belongs to no single
14490
+ * camera (a shared pool), NOT that the contributor forgot to look it up: a
14491
+ * contributor that cannot name its camera must not emit the entry at all,
14492
+ * because an unnamed per-camera entry is indistinguishable from a shared one
14493
+ * and would quietly turn one camera's cost into everybody's.
14494
+ */
14495
+ deviceId: number().int().positive().nullable(),
14496
+ attribution: _enum([
14497
+ "measured",
14498
+ "accounted",
14499
+ "unattributable"
14500
+ ]),
14501
+ /**
14502
+ * What ONE entry is, in the contributor's own words — `615/high`,
14503
+ * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
14504
+ * family and inventing a common one would lose the only information that
14505
+ * makes two entries for the same camera distinguishable.
14506
+ */
14507
+ unit: string(),
14508
+ /**
14509
+ * The OS process this cost lives in, when there is one. Present so a
14510
+ * consumer can (a) tell two generations of the same unit apart across a
14511
+ * restart, and (b) subtract claimed processes from the node's process
14512
+ * snapshot to see what NOBODY claimed. Absent for an entry that owns no
14513
+ * process of its own.
14514
+ */
14515
+ pid: number().int().positive().optional(),
14516
+ /**
14517
+ * When this generation started. The pid's incarnation marker: a consumer
14518
+ * differencing {@link LoadContributionSchema.shape.cpuSeconds} must drop the
14519
+ * window when this changes, because the counter restarted from zero in a new
14520
+ * process.
14521
+ */
14522
+ startedAtMs: number().optional(),
14523
+ /**
14524
+ * CUMULATIVE CPU seconds this unit has consumed since it started — user +
14525
+ * system, read from the child's own `/proc/<pid>/stat` at the moment the
14526
+ * contribution is asked for.
14527
+ *
14528
+ * Cumulative and not a rate on purpose: a rate needs a window, a window
14529
+ * needs a sampler, and a new per-node sampler is the defect half of
14530
+ * `docs/architecture/load-ledger.md` documents. A counter can be differenced
14531
+ * by whoever already keeps a history; a rate cannot be un-averaged.
14532
+ *
14533
+ * Absent — never zero — on a node with no `/proc`, on a read failure, and on
14534
+ * an entry with no process.
14535
+ */
14536
+ cpuSeconds: number().optional(),
14537
+ /** Resident bytes of this unit's process, same source and same rules. */
14538
+ rssBytes: number().optional()
14539
+ });
14540
+ method(_void(), array(LoadContributionSchema).readonly());
14371
14541
  /**
14372
14542
  * `log-channels` — the capability an addon DECLARES its diagnostic channels
14373
14543
  * through. It stores nothing.
@@ -14444,176 +14614,6 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
14444
14614
  tags: record(string(), string()).optional()
14445
14615
  }), array(LogEntrySchema).readonly());
14446
14616
  /**
14447
- * `failure-contribution` — the capability an addon reports its OWN losses
14448
- * through, per camera, with the denominator attached. It stores nothing.
14449
- *
14450
- * ## The twin of `load-contribution`, and why it is a twin and not a field
14451
- *
14452
- * `load-contribution` answers *what did this camera COST*. This answers *what
14453
- * did this camera LOSE*. The reporting discipline is identical and deliberately
14454
- * copied: the contributor reports what it already knows, hub-main adds only
14455
- * `addonId`, nothing needs global knowledge, and there is no central list for
14456
- * somebody to forget to edit.
14457
- *
14458
- * They are not merged, because their invariants are opposites:
14459
- *
14460
- * - a `load-contribution` measurement is **absent, never zero** — a zero would
14461
- * claim a camera cost nothing, which is a measurement nobody made;
14462
- * - a `failure-contribution` zero is the **most valuable value on the
14463
- * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
14464
- * and it is exactly what an absent entry cannot say.
14465
- *
14466
- * Putting a loss counter on a cost entry would also break the reconciliation
14467
- * that gives `load-contribution` its point: contributions are subtracted from
14468
- * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
14469
- * has no process.
14470
- *
14471
- * ## Why not a log line, since the counters already exist
14472
- *
14473
- * Several of these paths already counted themselves — `CaptureScheduler`'s
14474
- * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
14475
- * ends in a log line, and a log line is the thing the operator asked to stop
14476
- * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
14477
- * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
14478
- * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
14479
- * media blackout were both diagnosed. The counters stay; this is where they can
14480
- * be READ.
14481
- *
14482
- * ## The rate is served with its denominator or not at all
14483
- *
14484
- * Every entry carries `attempts` and `succeeded`. A miss count alone is
14485
- * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
14486
- * than yesterday" and was **flat across twelve hours** once divided by the
14487
- * successes on the same path. A surface that publishes only the numerator
14488
- * reproduces that mistake on every read.
14489
- *
14490
- * ## Shape
14491
- *
14492
- * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
14493
- * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
14494
- * generated hooks, while `addons.listCapabilityProviders` still enumerates it
14495
- * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
14496
- * a forked runner's entries reach hub-main over transport that already exists.
14497
- * No new UDS message, no second registry (D3). The operator reads the assembled
14498
- * result through `system.getFailureContributions`.
14499
- */
14500
- var FailureReasonCountSchema = object({
14501
- /**
14502
- * Why the attempt did not land, in the contributor's own vocabulary —
14503
- * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
14504
- * strings that already appear in this repo's logs and, where one exists, the
14505
- * same string the per-track `previewMissReason` records (D276): a second
14506
- * vocabulary for the same loss would make the row and the counter
14507
- * un-joinable.
14508
- */
14509
- reason: string(),
14510
- count: number().int().nonnegative()
14511
- });
14512
- var FailureContributionSchema = object({
14513
- /**
14514
- * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
14515
- * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
14516
- * `unit` free: the families are owned by different addons and a shared enum
14517
- * is a central list that rots invisibly.
14518
- */
14519
- family: string(),
14520
- /**
14521
- * The NUMERIC device id — the same value every log line carries as
14522
- * `tags.deviceId`. Never nullable and never absent: a contributor that
14523
- * cannot name the camera must not emit the entry, because a fleet total
14524
- * cannot answer the only question anybody asks of this surface.
14525
- */
14526
- deviceId: number().int().positive(),
14527
- /**
14528
- * A second dimension inside the family: the model / step id for an inference
14529
- * timeout, so "which camera AND which model" is one read. Absent when the
14530
- * family has a single variant.
14531
- */
14532
- variant: string().optional(),
14533
- /**
14534
- * Epoch ms this counter started — the INCARNATION MARKER. A consumer
14535
- * differencing two reads must drop the interval when it changes, because the
14536
- * counter restarted from zero in a respawned runner. Same discipline as
14537
- * `LoadContribution.startedAtMs`.
14538
- */
14539
- sinceMs: number(),
14540
- /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
14541
- atMs: number(),
14542
- /**
14543
- * THE DENOMINATOR — every attempt on this path for this camera in the
14544
- * window. A failure count published without it is the mistake this schema
14545
- * exists to make impossible.
14546
- */
14547
- attempts: number().int().nonnegative(),
14548
- /** Attempts that landed. `attempts - succeeded` is the loss. */
14549
- succeeded: number().int().nonnegative(),
14550
- /** The loss, partitioned. Sums to `attempts - succeeded`. */
14551
- reasons: array(FailureReasonCountSchema).readonly()
14552
- });
14553
- method(_void(), array(FailureContributionSchema).readonly());
14554
- var LoadContributionSchema = object({
14555
- role: _enum([
14556
- "decode",
14557
- "transcode",
14558
- "recording",
14559
- "streaming",
14560
- "detection"
14561
- ]),
14562
- /**
14563
- * The NUMERIC device id — the same value every log line carries as
14564
- * `tags.deviceId`. `null` means this cost genuinely belongs to no single
14565
- * camera (a shared pool), NOT that the contributor forgot to look it up: a
14566
- * contributor that cannot name its camera must not emit the entry at all,
14567
- * because an unnamed per-camera entry is indistinguishable from a shared one
14568
- * and would quietly turn one camera's cost into everybody's.
14569
- */
14570
- deviceId: number().int().positive().nullable(),
14571
- attribution: _enum([
14572
- "measured",
14573
- "accounted",
14574
- "unattributable"
14575
- ]),
14576
- /**
14577
- * What ONE entry is, in the contributor's own words — `615/high`,
14578
- * `617/native`, `cuda:0 shared pool`. Free text because the unit differs per
14579
- * family and inventing a common one would lose the only information that
14580
- * makes two entries for the same camera distinguishable.
14581
- */
14582
- unit: string(),
14583
- /**
14584
- * The OS process this cost lives in, when there is one. Present so a
14585
- * consumer can (a) tell two generations of the same unit apart across a
14586
- * restart, and (b) subtract claimed processes from the node's process
14587
- * snapshot to see what NOBODY claimed. Absent for an entry that owns no
14588
- * process of its own.
14589
- */
14590
- pid: number().int().positive().optional(),
14591
- /**
14592
- * When this generation started. The pid's incarnation marker: a consumer
14593
- * differencing {@link LoadContributionSchema.shape.cpuSeconds} must drop the
14594
- * window when this changes, because the counter restarted from zero in a new
14595
- * process.
14596
- */
14597
- startedAtMs: number().optional(),
14598
- /**
14599
- * CUMULATIVE CPU seconds this unit has consumed since it started — user +
14600
- * system, read from the child's own `/proc/<pid>/stat` at the moment the
14601
- * contribution is asked for.
14602
- *
14603
- * Cumulative and not a rate on purpose: a rate needs a window, a window
14604
- * needs a sampler, and a new per-node sampler is the defect half of
14605
- * `docs/architecture/load-ledger.md` documents. A counter can be differenced
14606
- * by whoever already keeps a history; a rate cannot be un-averaged.
14607
- *
14608
- * Absent — never zero — on a node with no `/proc`, on a read failure, and on
14609
- * an entry with no process.
14610
- */
14611
- cpuSeconds: number().optional(),
14612
- /** Resident bytes of this unit's process, same source and same rules. */
14613
- rssBytes: number().optional()
14614
- });
14615
- method(_void(), array(LoadContributionSchema).readonly());
14616
- /**
14617
14617
  * `login-method` — collection cap through which auth addons contribute
14618
14618
  * their pre-auth login surfaces to the login page. This is the SINGLE,
14619
14619
  * generic mechanism that supersedes the dead `auth.listProviders` reader:
@@ -19497,12 +19497,53 @@ var MediaFileKindEnum = _enum([
19497
19497
  "keyFrameSmall",
19498
19498
  "thumbnailSmall"
19499
19499
  ]);
19500
+ /**
19501
+ * One media row ON THE WIRE: what it is, how big it is, and WHERE ITS BYTES
19502
+ * ARE — never the bytes themselves.
19503
+ *
19504
+ * ## Why `url` and not `base64`
19505
+ *
19506
+ * Measured on the live hub 2026-08-30: `getTrackMedia {trackId, deviceId}`
19507
+ * with no `kinds` returned 6 rows / **3 597 219 B**, of which `keyFrame` alone
19508
+ * was **2 824 077 B** — one full-resolution frame, base64, so +33 % on the
19509
+ * wire. Forty events is ~144 MB. Every byte of it was read off disk,
19510
+ * base64-encoded, held whole in a unary tRPC envelope, and materialised in
19511
+ * hub-main's heap on the way past — for an `<img>` that would have cached it.
19512
+ *
19513
+ * `url` points at the `event-media` data plane
19514
+ * (`/addon/<addonId>/event-media/<storedKey>`), which serves the same blob
19515
+ * with an ETag and `Cache-Control: immutable`, honours conditional GETs, can
19516
+ * render a `?variant=thumb`, and streams. The hub gate in front of it requires
19517
+ * a bearer or the session cookie (`access: 'authenticated'`), so the bytes are
19518
+ * no less protected than they were inside a `view`-level cap response — see
19519
+ * `data-plane-access.ts` for the rule and the one gap it does not close
19520
+ * (per-device scoping).
19521
+ *
19522
+ * The URL is built from the row's **stored** key, which is not always its
19523
+ * published `kind`: a track's face/plate crop is stored as `crop` under
19524
+ * `('face'|'plate', '<prefix>-<trackId>')` and published as
19525
+ * `faceCrop`/`plateCrop`. `MediaStore.getByKey` knows only the stored key.
19526
+ *
19527
+ * ## `base64` is TRANSITIONAL and is going away
19528
+ *
19529
+ * It is still populated for one reason: the deployed viewer's track-detail
19530
+ * HERO tile reads it (`use-track-media-entry.ts` → `parseMediaFiles`, which
19531
+ * REQUIRES the field), and a row without it parses as a FAILED read — the red
19532
+ * triangle — not as absence. Removing the field before that viewer ships is an
19533
+ * outage, not a cleanup. Once the viewer takes its hero bytes from `url`,
19534
+ * delete this line and the `withBytes` pass-through in
19535
+ * `analytics-query-facade.ts`; nothing else reads it.
19536
+ */
19500
19537
  var MediaFileSchema = object({
19501
19538
  key: string(),
19502
19539
  kind: MediaFileKindEnum,
19503
- base64: string(),
19504
19540
  sizeBytes: number(),
19505
19541
  timestamp: number()
19542
+ }).extend({
19543
+ /** `/addon/<addonId>/event-media/<encoded stored key>`. Always present. */
19544
+ url: string(),
19545
+ /** @deprecated Transitional — see the schema docblock. Use {@link url}. */
19546
+ base64: string()
19506
19547
  });
19507
19548
  /**
19508
19549
  * One media row WITHOUT its bytes.
@@ -19514,7 +19555,9 @@ var MediaFileSchema = object({
19514
19555
  * blocks the whole view.
19515
19556
  *
19516
19557
  * `sizeBytes` is carried because it is what lets a client decide between the
19517
- * stored blob and a `?variant=thumb` rendering without fetching either.
19558
+ * stored blob and a `?variant=thumb` rendering without fetching either, and
19559
+ * `url` because a client that had to build the plane path itself is a second
19560
+ * copy of a route — the embed, the viewer and the admin UI each grew one.
19518
19561
  */
19519
19562
  var MediaFileInfoSchema = MediaFileSchema.omit({ base64: true });
19520
19563
  /**
@@ -19861,6 +19904,50 @@ var EventStoreFootprintSchema = object({
19861
19904
  totalBytes: number().int(),
19862
19905
  devices: array(EventStoreDeviceFootprintSchema).readonly()
19863
19906
  });
19907
+ /** Event-media footprint for one {@link MediaFileKind}. */
19908
+ var EventMediaKindFootprintSchema = object({
19909
+ kind: MediaFileKindEnum,
19910
+ /** Media rows of this kind. */
19911
+ rows: number().int(),
19912
+ /** Bytes on disk held by those rows. */
19913
+ bytes: number().int()
19914
+ });
19915
+ /**
19916
+ * The media footprint broken down by KIND — the axis a deletion decision
19917
+ * actually turns on.
19918
+ *
19919
+ * A byte total says how much there is; it cannot say what is safe to remove.
19920
+ * The deletable set (the periodic `snapshot` filmstrip, the surplus per-edge
19921
+ * motion stills) and the keep set (`firstFrame`, rolling `lastFrame`,
19922
+ * `thumbnail`/`thumbnailSmall`, `keyFrame`/`keyFrameSmall`, the face/plate
19923
+ * buffers, gallery media, the CLIP `crop`) are distinguished by `kind` and by
19924
+ * nothing else, so sizing a deletion means summing per kind.
19925
+ *
19926
+ * ## Why `unaccounted*` exists
19927
+ *
19928
+ * `kinds` is enumerated from {@link MediaFileKindEnum} — the closed set the
19929
+ * writers use — and summed one kind at a time. `totalRows` / `totalBytes` come
19930
+ * from a SEPARATE unfiltered aggregate over the same rows, never from adding
19931
+ * `kinds` up. A row whose stored `kind` is not in the enum (written by a
19932
+ * retired code path, or by a version that knew a kind this one does not) would
19933
+ * otherwise vanish from the total silently, and an operator would delete
19934
+ * against a denominator smaller than the disk.
19935
+ *
19936
+ * `unaccountedRows` / `unaccountedBytes` are the difference. They are normally
19937
+ * zero; a non-zero value is a real finding and must be shown, not rounded away.
19938
+ */
19939
+ var EventMediaKindBreakdownSchema = object({
19940
+ /** Every media row in scope, from one unfiltered aggregate. */
19941
+ totalRows: number().int(),
19942
+ /** Every media byte in scope, from that same aggregate. */
19943
+ totalBytes: number().int(),
19944
+ /** Per-kind footprint, ordered by bytes descending. */
19945
+ kinds: array(EventMediaKindFootprintSchema).readonly(),
19946
+ /** `totalRows` minus the summed `kinds` rows — see the schema note. */
19947
+ unaccountedRows: number().int(),
19948
+ /** `totalBytes` minus the summed `kinds` bytes — see the schema note. */
19949
+ unaccountedBytes: number().int()
19950
+ });
19864
19951
  /** Per-kind counts returned by the event-prune / device-delete mutations. */
19865
19952
  var EventPruneCountsSchema = object({
19866
19953
  motion: number().int(),
@@ -20064,6 +20151,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20064
20151
  }), TrackFlagsSchema, { kind: "mutation" }), method(object({}), EventStoreFootprintSchema, {
20065
20152
  kind: "query",
20066
20153
  auth: "admin"
20154
+ }), method(object({ deviceId: number().int().optional() }), EventMediaKindBreakdownSchema, {
20155
+ kind: "query",
20156
+ auth: "admin"
20067
20157
  }), method(object({
20068
20158
  olderThanMs: number(),
20069
20159
  reason: OpsLogReasonSchema.optional()
@@ -20203,6 +20293,9 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
20203
20293
  }), array(MediaFileSchema).readonly()), method(object({
20204
20294
  trackId: string(),
20205
20295
  deviceId: number()
20296
+ }), array(MediaFileInfoSchema).readonly()), method(object({
20297
+ eventId: string(),
20298
+ deviceId: number()
20206
20299
  }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
20207
20300
  kind: "mutation",
20208
20301
  auth: "admin"
@@ -22493,6 +22586,20 @@ method(object({
22493
22586
  error: string().optional()
22494
22587
  }), { auth: "admin" }), method(_void(), array(ProviderListEntrySchema).readonly()), method(object({
22495
22588
  providerId: string(),
22589
+ /**
22590
+ * The location this config is an UNSAVED edit of, when there is one.
22591
+ *
22592
+ * `listLocations` replaces every declared secret with the redaction
22593
+ * sentinel, so the edit modal's form state holds the sentinel for any
22594
+ * credential the operator did not retype — and posting that here
22595
+ * without a way to resolve it makes the provider try to authenticate
22596
+ * as `__camstack_redacted__` and report the operator's own working
22597
+ * password as wrong. Given this id, the orchestrator restores each
22598
+ * sentinel from the stored config (same rule as `upsertLocation`)
22599
+ * before dispatching. Omitted by the "Add location" wizard, where
22600
+ * every value was typed just now and nothing is stored yet.
22601
+ */
22602
+ locationId: string().optional(),
22496
22603
  config: record(string(), unknown())
22497
22604
  }), object({
22498
22605
  ok: boolean(),
@@ -24944,10 +25051,24 @@ var FaceClusterSchema = object({
24944
25051
  size: number().int(),
24945
25052
  cohesion: number()
24946
25053
  });
25054
+ /**
25055
+ * One gallery media row: what the crop is, how big it is, and WHERE its bytes
25056
+ * are — never the bytes.
25057
+ *
25058
+ * `base64` was deleted here rather than deprecated. `MediaFile.base64` (the
25059
+ * track/event contract) is still populated because a deployed viewer requires
25060
+ * the field to parse a row at all; this method has no such reader. Its ONE
25061
+ * caller is the admin UI's detail modal, which was building
25062
+ * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
25063
+ * dialog already rendering its key FRAME from the `event-media` plane.
25064
+ *
25065
+ * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
25066
+ * media key directly, so this needed no new plane and no new access decision.
25067
+ */
24947
25068
  var MediaFileLiteSchema$1 = object({
24948
25069
  key: string(),
24949
25070
  kind: string(),
24950
- base64: string(),
25071
+ url: string(),
24951
25072
  sizeBytes: number(),
24952
25073
  timestamp: number()
24953
25074
  });
@@ -27199,10 +27320,24 @@ var PlateInfoSchema = object({
27199
27320
  */
27200
27321
  cropUrl: string().optional()
27201
27322
  });
27323
+ /**
27324
+ * One gallery media row: what the crop is, how big it is, and WHERE its bytes
27325
+ * are — never the bytes.
27326
+ *
27327
+ * `base64` was deleted here rather than deprecated. `MediaFile.base64` (the
27328
+ * track/event contract) is still populated because a deployed viewer requires
27329
+ * the field to parse a row at all; this method has no such reader. Its ONE
27330
+ * caller is the admin UI's detail modal, which was building
27331
+ * `data:image/jpeg;base64,…` from a row whose `key` sat right beside it, in a
27332
+ * dialog already rendering its key FRAME from the `event-media` plane.
27333
+ *
27334
+ * `url` is `/addon/<addonId>/event-media/<encoded key>`. The plane resolves a
27335
+ * media key directly, so this needed no new plane and no new access decision.
27336
+ */
27202
27337
  var MediaFileLiteSchema = object({
27203
27338
  key: string(),
27204
27339
  kind: string(),
27205
- base64: string(),
27340
+ url: string(),
27206
27341
  sizeBytes: number(),
27207
27342
  timestamp: number()
27208
27343
  });
@@ -33203,6 +33338,12 @@ Object.freeze({
33203
33338
  addonId: null,
33204
33339
  access: "view"
33205
33340
  },
33341
+ "pipelineAnalytics.getEventMediaFootprintByKind": {
33342
+ capName: "pipeline-analytics",
33343
+ capScope: "device",
33344
+ addonId: null,
33345
+ access: "view"
33346
+ },
33206
33347
  "pipelineAnalytics.getEventStoreFootprint": {
33207
33348
  capName: "pipeline-analytics",
33208
33349
  capScope: "device",
@@ -33299,6 +33440,12 @@ Object.freeze({
33299
33440
  addonId: null,
33300
33441
  access: "view"
33301
33442
  },
33443
+ "pipelineAnalytics.listEventMedia": {
33444
+ capName: "pipeline-analytics",
33445
+ capScope: "device",
33446
+ addonId: null,
33447
+ access: "view"
33448
+ },
33302
33449
  "pipelineAnalytics.listGroups": {
33303
33450
  capName: "pipeline-analytics",
33304
33451
  capScope: "device",
@@ -36862,6 +37009,11 @@ Object.freeze({
36862
37009
  form: "single",
36863
37010
  optional: false
36864
37011
  }],
37012
+ "pipelineAnalytics.getEventMediaFootprintByKind": [{
37013
+ name: "deviceId",
37014
+ form: "single",
37015
+ optional: true
37016
+ }],
36865
37017
  "pipelineAnalytics.getGroup": [{
36866
37018
  name: "deviceId",
36867
37019
  form: "single",
@@ -36922,6 +37074,11 @@ Object.freeze({
36922
37074
  form: "array",
36923
37075
  optional: false
36924
37076
  }],
37077
+ "pipelineAnalytics.listEventMedia": [{
37078
+ name: "deviceId",
37079
+ form: "single",
37080
+ optional: false
37081
+ }],
36925
37082
  "pipelineAnalytics.listGroups": [{
36926
37083
  name: "deviceIds",
36927
37084
  form: "array",
@@ -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_orchestrator_widgets-CXhvoqSz.mjs")).catch((e) => {
33
+ return l ||= d(() => import("./_virtual_mf-localSharedImportMap___mfe_internal__addon_pipeline_orchestrator_widgets-D6mD53Q1.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-pipeline-orchestrator",
3
- "version": "1.2.126",
3
+ "version": "1.2.128",
4
4
  "description": "Hub-side camera-to-agent load balancer — tracks runner capacity and dispatches attachCamera calls to the optimal pipeline-runner instance",
5
5
  "keywords": [
6
6
  "camstack",