@camstack/addon-ai 0.4.31 → 0.4.33

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/addon.js CHANGED
@@ -13500,6 +13500,114 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13500
13500
  limit: number$1().optional(),
13501
13501
  tags: record(string(), string()).optional()
13502
13502
  }), array(LogEntrySchema).readonly());
13503
+ /**
13504
+ * `failure-contribution` — the capability an addon reports its OWN losses
13505
+ * through, per camera, with the denominator attached. It stores nothing.
13506
+ *
13507
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
13508
+ *
13509
+ * `load-contribution` answers *what did this camera COST*. This answers *what
13510
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
13511
+ * copied: the contributor reports what it already knows, hub-main adds only
13512
+ * `addonId`, nothing needs global knowledge, and there is no central list for
13513
+ * somebody to forget to edit.
13514
+ *
13515
+ * They are not merged, because their invariants are opposites:
13516
+ *
13517
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
13518
+ * claim a camera cost nothing, which is a measurement nobody made;
13519
+ * - a `failure-contribution` zero is the **most valuable value on the
13520
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
13521
+ * and it is exactly what an absent entry cannot say.
13522
+ *
13523
+ * Putting a loss counter on a cost entry would also break the reconciliation
13524
+ * that gives `load-contribution` its point: contributions are subtracted from
13525
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
13526
+ * has no process.
13527
+ *
13528
+ * ## Why not a log line, since the counters already exist
13529
+ *
13530
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
13531
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
13532
+ * ends in a log line, and a log line is the thing the operator asked to stop
13533
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
13534
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
13535
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
13536
+ * media blackout were both diagnosed. The counters stay; this is where they can
13537
+ * be READ.
13538
+ *
13539
+ * ## The rate is served with its denominator or not at all
13540
+ *
13541
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
13542
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
13543
+ * than yesterday" and was **flat across twelve hours** once divided by the
13544
+ * successes on the same path. A surface that publishes only the numerator
13545
+ * reproduces that mistake on every read.
13546
+ *
13547
+ * ## Shape
13548
+ *
13549
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
13550
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
13551
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
13552
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
13553
+ * a forked runner's entries reach hub-main over transport that already exists.
13554
+ * No new UDS message, no second registry (D3). The operator reads the assembled
13555
+ * result through `system.getFailureContributions`.
13556
+ */
13557
+ var FailureReasonCountSchema = object({
13558
+ /**
13559
+ * Why the attempt did not land, in the contributor's own vocabulary —
13560
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
13561
+ * strings that already appear in this repo's logs and, where one exists, the
13562
+ * same string the per-track `previewMissReason` records (D276): a second
13563
+ * vocabulary for the same loss would make the row and the counter
13564
+ * un-joinable.
13565
+ */
13566
+ reason: string(),
13567
+ count: number$1().int().nonnegative()
13568
+ });
13569
+ var FailureContributionSchema = object({
13570
+ /**
13571
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
13572
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
13573
+ * `unit` free: the families are owned by different addons and a shared enum
13574
+ * is a central list that rots invisibly.
13575
+ */
13576
+ family: string(),
13577
+ /**
13578
+ * The NUMERIC device id — the same value every log line carries as
13579
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
13580
+ * cannot name the camera must not emit the entry, because a fleet total
13581
+ * cannot answer the only question anybody asks of this surface.
13582
+ */
13583
+ deviceId: number$1().int().positive(),
13584
+ /**
13585
+ * A second dimension inside the family: the model / step id for an inference
13586
+ * timeout, so "which camera AND which model" is one read. Absent when the
13587
+ * family has a single variant.
13588
+ */
13589
+ variant: string().optional(),
13590
+ /**
13591
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
13592
+ * differencing two reads must drop the interval when it changes, because the
13593
+ * counter restarted from zero in a respawned runner. Same discipline as
13594
+ * `LoadContribution.startedAtMs`.
13595
+ */
13596
+ sinceMs: number$1(),
13597
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
13598
+ atMs: number$1(),
13599
+ /**
13600
+ * THE DENOMINATOR — every attempt on this path for this camera in the
13601
+ * window. A failure count published without it is the mistake this schema
13602
+ * exists to make impossible.
13603
+ */
13604
+ attempts: number$1().int().nonnegative(),
13605
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
13606
+ succeeded: number$1().int().nonnegative(),
13607
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
13608
+ reasons: array(FailureReasonCountSchema).readonly()
13609
+ });
13610
+ method(_void(), array(FailureContributionSchema).readonly());
13503
13611
  var LoadContributionSchema = object({
13504
13612
  role: _enum([
13505
13613
  "decode",
@@ -18012,6 +18120,20 @@ var TrackSchema = object({
18012
18120
  * `=== true` and render nothing otherwise — never infer "no rider".
18013
18121
  */
18014
18122
  hasRider: boolean().optional(),
18123
+ /**
18124
+ * WHY this track ended without a NATIVE best-shot tile
18125
+ * ([D276](../decisions/adr-0276-a-stand-in-tile-is-provisional-and-a-close-says-why.md)) —
18126
+ * a composed token line (`no-key-frame capture=keyframe:native-missx4`,
18127
+ * `derive-returned-null tile=standin`, …) written at close and CLEARED by
18128
+ * the late-keyFrame upgrade when a native tile lands after all. The
18129
+ * operator-facing answer to "perché manca l'immagine?" on a track whose
18130
+ * tile is a face/plate stand-in, a raster crop, or an icon.
18131
+ *
18132
+ * **Absent ≠ "missed silently"**: a row written before the column, a hub
18133
+ * that predates the field, and every track whose tile landed native all
18134
+ * omit it. Render nothing when absent.
18135
+ */
18136
+ previewMissReason: string().optional(),
18015
18137
  ...TrackFlagFields,
18016
18138
  ...TrackRetrainFields
18017
18139
  });
@@ -27238,6 +27360,13 @@ var LoggingSettingsPatchSchema = object({
27238
27360
  * anyone but its owner.
27239
27361
  */
27240
27362
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string() });
27363
+ /**
27364
+ * One per-camera failure counter, plus WHO reported it.
27365
+ *
27366
+ * Same rule as {@link ReportedLoadContributionSchema}: `addonId` is stamped by
27367
+ * the hub as it enumerates providers, never by the contributor.
27368
+ */
27369
+ var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: string() });
27241
27370
  var GetLoggingSettingsInputSchema = object({
27242
27371
  scopeNodeId: string().optional(),
27243
27372
  /**
@@ -27296,7 +27425,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
27296
27425
  }), method(_void(), SiteLocationStatusSchema, {
27297
27426
  kind: "mutation",
27298
27427
  auth: "admin"
27299
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27428
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(_void(), array(ReportedFailureContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27300
27429
  kind: "mutation",
27301
27430
  auth: "admin"
27302
27431
  });
@@ -29878,6 +30007,12 @@ Object.freeze({
29878
30007
  addonId: null,
29879
30008
  access: "create"
29880
30009
  },
30010
+ "failureContribution.list": {
30011
+ capName: "failure-contribution",
30012
+ capScope: "system",
30013
+ addonId: null,
30014
+ access: "view"
30015
+ },
29881
30016
  "fanControl.setDirection": {
29882
30017
  capName: "fan-control",
29883
30018
  capScope: "device",
@@ -33184,6 +33319,12 @@ Object.freeze({
33184
33319
  addonId: null,
33185
33320
  access: "create"
33186
33321
  },
33322
+ "system.getFailureContributions": {
33323
+ capName: "system",
33324
+ capScope: "system",
33325
+ addonId: null,
33326
+ access: "view"
33327
+ },
33187
33328
  "system.getLoadContributions": {
33188
33329
  capName: "system",
33189
33330
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -13527,6 +13527,114 @@ method(LogEntrySchema, _void(), { kind: "mutation" }), method(object({
13527
13527
  limit: number$1().optional(),
13528
13528
  tags: record(string(), string()).optional()
13529
13529
  }), array(LogEntrySchema).readonly());
13530
+ /**
13531
+ * `failure-contribution` — the capability an addon reports its OWN losses
13532
+ * through, per camera, with the denominator attached. It stores nothing.
13533
+ *
13534
+ * ## The twin of `load-contribution`, and why it is a twin and not a field
13535
+ *
13536
+ * `load-contribution` answers *what did this camera COST*. This answers *what
13537
+ * did this camera LOSE*. The reporting discipline is identical and deliberately
13538
+ * copied: the contributor reports what it already knows, hub-main adds only
13539
+ * `addonId`, nothing needs global knowledge, and there is no central list for
13540
+ * somebody to forget to edit.
13541
+ *
13542
+ * They are not merged, because their invariants are opposites:
13543
+ *
13544
+ * - a `load-contribution` measurement is **absent, never zero** — a zero would
13545
+ * claim a camera cost nothing, which is a measurement nobody made;
13546
+ * - a `failure-contribution` zero is the **most valuable value on the
13547
+ * surface** — `attempts: 400, succeeded: 400` is the proof a fix landed,
13548
+ * and it is exactly what an absent entry cannot say.
13549
+ *
13550
+ * Putting a loss counter on a cost entry would also break the reconciliation
13551
+ * that gives `load-contribution` its point: contributions are subtracted from
13552
+ * `metrics.node-processes-snapshot` to find processes nobody claims. A failure
13553
+ * has no process.
13554
+ *
13555
+ * ## Why not a log line, since the counters already exist
13556
+ *
13557
+ * Several of these paths already counted themselves — `CaptureScheduler`'s
13558
+ * per-device window, `KeyFrameCaptureLog`, `bumpCropMetric`. Every one of them
13559
+ * ends in a log line, and a log line is the thing the operator asked to stop
13560
+ * needing: *"possiamo armare questi errori intanto? Così al prossimo giro
13561
+ * ricontrolliamo tutti questi punti"*. Reading them meant grepping Loki and
13562
+ * hand-correlating timestamps, which is how a 22% thumbnail gap and a 3-hour
13563
+ * media blackout were both diagnosed. The counters stay; this is where they can
13564
+ * be READ.
13565
+ *
13566
+ * ## The rate is served with its denominator or not at all
13567
+ *
13568
+ * Every entry carries `attempts` and `succeeded`. A miss count alone is
13569
+ * unreadable: on 2026-08-28 the enrichment-crop miss count read as "35x worse
13570
+ * than yesterday" and was **flat across twelve hours** once divided by the
13571
+ * successes on the same path. A surface that publishes only the numerator
13572
+ * reproduces that mistake on every read.
13573
+ *
13574
+ * ## Shape
13575
+ *
13576
+ * Copied from `load-contribution.cap.ts` (`mode: 'collection'`,
13577
+ * `internal: true`, `mount: { kind: 'skip' }`): no tRPC route of its own and no
13578
+ * generated hooks, while `addons.listCapabilityProviders` still enumerates it
13579
+ * and the hub's `CapabilityRegistry` still holds an RPC proxy per provider — so
13580
+ * a forked runner's entries reach hub-main over transport that already exists.
13581
+ * No new UDS message, no second registry (D3). The operator reads the assembled
13582
+ * result through `system.getFailureContributions`.
13583
+ */
13584
+ var FailureReasonCountSchema = object({
13585
+ /**
13586
+ * Why the attempt did not land, in the contributor's own vocabulary —
13587
+ * `worker-lease-gone`, `queue-overflow`, `timeout`, `empty-read`. The same
13588
+ * strings that already appear in this repo's logs and, where one exists, the
13589
+ * same string the per-track `previewMissReason` records (D276): a second
13590
+ * vocabulary for the same loss would make the row and the counter
13591
+ * un-joinable.
13592
+ */
13593
+ reason: string(),
13594
+ count: number$1().int().nonnegative()
13595
+ });
13596
+ var FailureContributionSchema = object({
13597
+ /**
13598
+ * The failing path — `enrichment-crop`, `inference`, `plate-ocr`,
13599
+ * `person-over-vehicle`. Free text, for the reason `load-contribution` keeps
13600
+ * `unit` free: the families are owned by different addons and a shared enum
13601
+ * is a central list that rots invisibly.
13602
+ */
13603
+ family: string(),
13604
+ /**
13605
+ * The NUMERIC device id — the same value every log line carries as
13606
+ * `tags.deviceId`. Never nullable and never absent: a contributor that
13607
+ * cannot name the camera must not emit the entry, because a fleet total
13608
+ * cannot answer the only question anybody asks of this surface.
13609
+ */
13610
+ deviceId: number$1().int().positive(),
13611
+ /**
13612
+ * A second dimension inside the family: the model / step id for an inference
13613
+ * timeout, so "which camera AND which model" is one read. Absent when the
13614
+ * family has a single variant.
13615
+ */
13616
+ variant: string().optional(),
13617
+ /**
13618
+ * Epoch ms this counter started — the INCARNATION MARKER. A consumer
13619
+ * differencing two reads must drop the interval when it changes, because the
13620
+ * counter restarted from zero in a respawned runner. Same discipline as
13621
+ * `LoadContribution.startedAtMs`.
13622
+ */
13623
+ sinceMs: number$1(),
13624
+ /** Epoch ms it was read. `atMs - sinceMs` is the interval this covers. */
13625
+ atMs: number$1(),
13626
+ /**
13627
+ * THE DENOMINATOR — every attempt on this path for this camera in the
13628
+ * window. A failure count published without it is the mistake this schema
13629
+ * exists to make impossible.
13630
+ */
13631
+ attempts: number$1().int().nonnegative(),
13632
+ /** Attempts that landed. `attempts - succeeded` is the loss. */
13633
+ succeeded: number$1().int().nonnegative(),
13634
+ /** The loss, partitioned. Sums to `attempts - succeeded`. */
13635
+ reasons: array(FailureReasonCountSchema).readonly()
13636
+ });
13637
+ method(_void(), array(FailureContributionSchema).readonly());
13530
13638
  var LoadContributionSchema = object({
13531
13639
  role: _enum([
13532
13640
  "decode",
@@ -18039,6 +18147,20 @@ var TrackSchema = object({
18039
18147
  * `=== true` and render nothing otherwise — never infer "no rider".
18040
18148
  */
18041
18149
  hasRider: boolean().optional(),
18150
+ /**
18151
+ * WHY this track ended without a NATIVE best-shot tile
18152
+ * ([D276](../decisions/adr-0276-a-stand-in-tile-is-provisional-and-a-close-says-why.md)) —
18153
+ * a composed token line (`no-key-frame capture=keyframe:native-missx4`,
18154
+ * `derive-returned-null tile=standin`, …) written at close and CLEARED by
18155
+ * the late-keyFrame upgrade when a native tile lands after all. The
18156
+ * operator-facing answer to "perché manca l'immagine?" on a track whose
18157
+ * tile is a face/plate stand-in, a raster crop, or an icon.
18158
+ *
18159
+ * **Absent ≠ "missed silently"**: a row written before the column, a hub
18160
+ * that predates the field, and every track whose tile landed native all
18161
+ * omit it. Render nothing when absent.
18162
+ */
18163
+ previewMissReason: string().optional(),
18042
18164
  ...TrackFlagFields,
18043
18165
  ...TrackRetrainFields
18044
18166
  });
@@ -27265,6 +27387,13 @@ var LoggingSettingsPatchSchema = object({
27265
27387
  * anyone but its owner.
27266
27388
  */
27267
27389
  var ReportedLoadContributionSchema = LoadContributionSchema.extend({ addonId: string() });
27390
+ /**
27391
+ * One per-camera failure counter, plus WHO reported it.
27392
+ *
27393
+ * Same rule as {@link ReportedLoadContributionSchema}: `addonId` is stamped by
27394
+ * the hub as it enumerates providers, never by the contributor.
27395
+ */
27396
+ var ReportedFailureContributionSchema = FailureContributionSchema.extend({ addonId: string() });
27268
27397
  var GetLoggingSettingsInputSchema = object({
27269
27398
  scopeNodeId: string().optional(),
27270
27399
  /**
@@ -27323,7 +27452,7 @@ method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), met
27323
27452
  }), method(_void(), SiteLocationStatusSchema, {
27324
27453
  kind: "mutation",
27325
27454
  auth: "admin"
27326
- }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27455
+ }), method(_void(), RequestCensusStatusSchema, { auth: "admin" }), method(_void(), array(ReportedLoadContributionSchema).readonly(), { auth: "admin" }), method(_void(), array(ReportedFailureContributionSchema).readonly(), { auth: "admin" }), method(GetLoggingSettingsInputSchema, LoggingSettingsStateSchema, { auth: "admin" }), method(SetLoggingSettingsInputSchema, LoggingSettingsStateSchema, {
27327
27456
  kind: "mutation",
27328
27457
  auth: "admin"
27329
27458
  });
@@ -29905,6 +30034,12 @@ Object.freeze({
29905
30034
  addonId: null,
29906
30035
  access: "create"
29907
30036
  },
30037
+ "failureContribution.list": {
30038
+ capName: "failure-contribution",
30039
+ capScope: "system",
30040
+ addonId: null,
30041
+ access: "view"
30042
+ },
29908
30043
  "fanControl.setDirection": {
29909
30044
  capName: "fan-control",
29910
30045
  capScope: "device",
@@ -33211,6 +33346,12 @@ Object.freeze({
33211
33346
  addonId: null,
33212
33347
  access: "create"
33213
33348
  },
33349
+ "system.getFailureContributions": {
33350
+ capName: "system",
33351
+ capScope: "system",
33352
+ addonId: null,
33353
+ access: "view"
33354
+ },
33214
33355
  "system.getLoadContributions": {
33215
33356
  capName: "system",
33216
33357
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.4.31",
3
+ "version": "0.4.33",
4
4
  "description": "AI addon for CamStack — the `llm` collection provider (cloud, LAN, and camstack-managed local llama.cpp profiles) plus the per-node `llm-runtime` managed executor.",
5
5
  "keywords": [
6
6
  "camstack",