@camstack/addon-provider-homematic 1.2.131 → 1.2.133

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 (3) hide show
  1. package/dist/addon.js +413 -136
  2. package/dist/addon.mjs +413 -136
  3. package/package.json +1 -1
package/dist/addon.mjs CHANGED
@@ -5387,7 +5387,7 @@ var ZodIssueCode = {
5387
5387
  var ZodFirstPartyTypeKind;
5388
5388
  ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
5389
5389
  //#endregion
5390
- //#region ../types/dist/sleep-r3RWkGVx.mjs
5390
+ //#region ../types/dist/sleep-CI0n5D44.mjs
5391
5391
  /**
5392
5392
  * The audio chunk plane's byte format, and the ONE expansion from a coded
5393
5393
  * window to float samples (D455).
@@ -9393,6 +9393,16 @@ var CompositionSchema = object({
9393
9393
  seen.add(feature.cap);
9394
9394
  });
9395
9395
  });
9396
+ var NodeColorIdSchema = _enum([
9397
+ "node-a",
9398
+ "node-b",
9399
+ "node-c",
9400
+ "node-d",
9401
+ "node-e",
9402
+ "node-f",
9403
+ "node-g",
9404
+ "node-h"
9405
+ ]);
9396
9406
  new Set([
9397
9407
  "track",
9398
9408
  "face",
@@ -24459,24 +24469,69 @@ var RetrainFrameListSchema = object({
24459
24469
  /** What the page pre-selects — the native key frame when one survives. */
24460
24470
  autoPickMediaId: number().int().optional()
24461
24471
  });
24472
+ /** Exclusive partition of a corpus copy's annotation progress (design §4.2). */
24473
+ var RetrainFrameWorkStateSchema = _enum([
24474
+ "unannotated",
24475
+ "annotated",
24476
+ "complete"
24477
+ ]);
24478
+ /**
24479
+ * Filters for paginated corpus list, KPI, and matching export (design §4.4).
24480
+ * Every narrowing field is ANDed; omitted fields impose no constraint.
24481
+ */
24482
+ var RetrainCorpusFilterSchema = object({
24483
+ workState: RetrainFrameWorkStateSchema.optional(),
24484
+ deviceIds: array(number()).max(500).optional(),
24485
+ /** When set, only frames in this named collection. */
24486
+ collectionId: string().optional(),
24487
+ /** Frames that carry at least one saved annotation in one of these classes. */
24488
+ classes: array(RetrainMacroClassSchema).optional(),
24489
+ page: number().int().positive().default(1),
24490
+ pageSize: number().int().positive().max(100).default(50)
24491
+ });
24492
+ var RetrainCorpusPageSchema = object({
24493
+ frames: array(RetrainFrameSchema.extend({ workState: RetrainFrameWorkStateSchema })).readonly(),
24494
+ total: number().int().nonnegative(),
24495
+ page: number().int().positive(),
24496
+ pageSize: number().int().positive()
24497
+ });
24498
+ var RetrainCorpusKpiCameraRowSchema = object({
24499
+ deviceId: number(),
24500
+ unannotated: number().int().nonnegative(),
24501
+ annotated: number().int().nonnegative(),
24502
+ complete: number().int().nonnegative(),
24503
+ /** This camera's `complete` count divided by corpus `total`, in `[0,1]`.
24504
+ * `null` when the corpus is empty (no ratio). */
24505
+ pctOfCorpus: number().min(0).max(1).nullable()
24506
+ });
24507
+ var RetrainCorpusKpiSchema = object({
24508
+ total: number().int().nonnegative(),
24509
+ unannotated: number().int().nonnegative(),
24510
+ annotated: number().int().nonnegative(),
24511
+ complete: number().int().nonnegative(),
24512
+ cameras: array(RetrainCorpusKpiCameraRowSchema).readonly()
24513
+ });
24462
24514
  /**
24463
24515
  * Which frames an annotated export carries. Every field narrows; an empty
24464
- * filter is every annotated copy. A dataset VERSION is a frozen `frameIds`
24465
- * list owned by whoever trains (the training orchestrator), so `frameIds` is
24466
- * the primitive a version export uses; `batchId` re-selects what an earlier
24467
- * export carried (`exportedInBatch`).
24516
+ * filter is every annotated copy. `frameIds` is the primitive a job or
24517
+ * download uses; `batchId` re-selects what an earlier export carried
24518
+ * (`exportedInBatch`). Frozen dataset versions were removed — live selection
24519
+ * resolves to `frameIds` at plan/start.
24468
24520
  */
24469
24521
  var RetrainExportFilterSchema = object({
24470
24522
  deviceIds: array(number()).optional(),
24471
24523
  frameIds: array(string()).max(2e4).optional(),
24472
24524
  /** Only frames in at least one of these named collections. */
24473
24525
  collectionIds: array(string()).max(200).optional(),
24474
- /** Exactly the frames of this dataset version (and its completeOnly). An
24475
- * unknown id selects nothing. */
24476
- datasetVersionId: string().optional(),
24526
+ /** Single collection (Frames filter bar). ANDed with `collectionIds` when both set. */
24527
+ collectionId: string().optional(),
24477
24528
  batchId: string().optional(),
24478
24529
  /** Only frames the operator marked complete (all subjects boxed). */
24479
- completeOnly: boolean().optional()
24530
+ completeOnly: boolean().optional(),
24531
+ /** Exclusive work-state partition (`workStateOf`); same as the corpus list. */
24532
+ workState: RetrainFrameWorkStateSchema.optional(),
24533
+ /** Frames with at least one saved annotation in one of these classes. */
24534
+ classes: array(RetrainMacroClassSchema).optional()
24480
24535
  });
24481
24536
  /**
24482
24537
  * `frames`: the full frame copies, boxes in `annotations.json`.
@@ -24676,8 +24731,7 @@ var RetrainDatasetStatsSchema = object({
24676
24731
  completeFrames: number().int()
24677
24732
  })).readonly()
24678
24733
  });
24679
- /** An immutable dataset version, without its frame list. */
24680
- var RetrainDatasetVersionSchema = object({
24734
+ object({
24681
24735
  id: string(),
24682
24736
  name: string(),
24683
24737
  createdAt: number(),
@@ -24685,22 +24739,33 @@ var RetrainDatasetVersionSchema = object({
24685
24739
  layout: RetrainExportLayoutSchema,
24686
24740
  completeOnly: boolean(),
24687
24741
  frameCount: number().int(),
24688
- /** sha256 of what the frames taught when the version was frozen. */
24689
24742
  annotationHash: string(),
24690
- /** The export manifest schema the version was cut under. */
24691
24743
  manifestSchema: string(),
24692
24744
  stats: RetrainDatasetStatsSchema
24693
24745
  });
24694
- var CreateRetrainDatasetVersionResultSchema = discriminatedUnion("kind", [object({
24695
- kind: literal("created"),
24696
- version: RetrainDatasetVersionSchema
24746
+ /**
24747
+ * Live training corpus criteria — same shape as `TrainingCorpusSelection` on
24748
+ * the training cap. Resolved here so plan/start and the annotated export share
24749
+ * one definition of "the frames" (`buildRetrainExportPlan`).
24750
+ */
24751
+ var RetrainTrainingCorpusSelectionSchema = object({
24752
+ collectionIds: array(string()).min(1),
24753
+ workState: _enum([
24754
+ "complete",
24755
+ "annotated_or_complete",
24756
+ "any_annotated"
24757
+ ]).default("complete")
24758
+ });
24759
+ var RetrainTrainingCorpusResolveResultSchema = discriminatedUnion("kind", [object({
24760
+ kind: literal("resolved"),
24761
+ frameIds: array(string()).max(2e4).readonly(),
24762
+ stats: RetrainDatasetStatsSchema,
24763
+ annotationHash: string(),
24764
+ layout: RetrainExportLayoutSchema
24697
24765
  }), object({
24698
24766
  kind: literal("refused"),
24699
- reason: _enum([
24700
- "empty-name",
24701
- "duplicate-name",
24702
- "no-frames"
24703
- ])
24767
+ reason: _enum(["unknown-collection", "no-frames"]),
24768
+ detail: string()
24704
24769
  })]);
24705
24770
  /** The outcome of a lifecycle move owned by the retrain page. */
24706
24771
  var RetrainTransitionResultSchema = object({
@@ -25407,6 +25472,12 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
25407
25472
  }), array(RetrainTrackSchema).readonly(), {
25408
25473
  kind: "query",
25409
25474
  auth: "admin"
25475
+ }), method(RetrainCorpusFilterSchema, RetrainCorpusPageSchema, {
25476
+ kind: "query",
25477
+ auth: "admin"
25478
+ }), method(object({}), RetrainCorpusKpiSchema, {
25479
+ kind: "query",
25480
+ auth: "admin"
25410
25481
  }), method(object({ trackId: string() }), RetrainFrameListSchema, {
25411
25482
  kind: "query",
25412
25483
  auth: "admin"
@@ -25502,36 +25573,6 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
25502
25573
  kind: "mutation",
25503
25574
  auth: "admin",
25504
25575
  timeoutMs: 12e4
25505
- }), method(object({
25506
- selection: RetrainDatasetSelectionSchema,
25507
- completeOnly: boolean().optional()
25508
- }), RetrainDatasetStatsSchema, {
25509
- kind: "query",
25510
- auth: "admin"
25511
- }), method(object({
25512
- name: string().max(120),
25513
- selection: RetrainDatasetSelectionSchema,
25514
- layout: RetrainExportLayoutSchema,
25515
- completeOnly: boolean()
25516
- }), CreateRetrainDatasetVersionResultSchema, {
25517
- kind: "mutation",
25518
- auth: "admin",
25519
- timeoutMs: 12e4
25520
- }), method(object({}), array(RetrainDatasetVersionSchema).readonly(), {
25521
- kind: "query",
25522
- auth: "admin"
25523
- }), method(object({ id: string() }), object({ deleted: boolean() }), {
25524
- kind: "mutation",
25525
- auth: "admin"
25526
- }), method(object({ id: string() }), object({
25527
- found: boolean(),
25528
- annotationHash: string(),
25529
- matches: boolean(),
25530
- missingFrames: number().int()
25531
- }), {
25532
- kind: "query",
25533
- auth: "admin",
25534
- timeoutMs: 12e4
25535
25576
  }), method(object({}), array(RetrainAssistProfileSchema).readonly(), {
25536
25577
  kind: "query",
25537
25578
  auth: "admin"
@@ -25558,6 +25599,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
25558
25599
  }), method(object({}), array(RetrainCollectionSchema).readonly(), {
25559
25600
  kind: "query",
25560
25601
  auth: "admin"
25602
+ }), method(RetrainTrainingCorpusSelectionSchema, RetrainTrainingCorpusResolveResultSchema, {
25603
+ kind: "query",
25604
+ auth: "admin",
25605
+ timeoutMs: 6e4
25561
25606
  }), method(object({
25562
25607
  id: string(),
25563
25608
  name: string().max(120)
@@ -26174,11 +26219,14 @@ var HardwareClassIdSchema = _enum([
26174
26219
  "intel-n-series",
26175
26220
  "apple-m-base",
26176
26221
  "apple-m-pro",
26222
+ "amd-phoenix",
26177
26223
  "unknown"
26178
26224
  ]);
26179
26225
  var AcceleratorKindSchema = _enum([
26180
26226
  "intel-npu",
26181
26227
  "intel-igpu",
26228
+ "amd-igpu",
26229
+ "amd-npu",
26182
26230
  "x86-cpu",
26183
26231
  "arm-cpu",
26184
26232
  "apple-ane",
@@ -28108,6 +28156,8 @@ var AgentPipelineSettingsSchema = object({
28108
28156
  * address is wrong (multi-homed host, NAT, custom interface).
28109
28157
  */
28110
28158
  reachableHost: string().optional(),
28159
+ /** Hub-persisted UI colour (`--color-node-*` theme tokens). */
28160
+ color: NodeColorIdSchema.optional(),
28111
28161
  /**
28112
28162
  * Multi-device inference opt-in (Phase 4). Per-node map deviceKey → {enabled,
28113
28163
  * weight, steps}. Absent / all-disabled ⇒ the node's single default
@@ -28657,8 +28707,9 @@ method(object({
28657
28707
  auth: "admin"
28658
28708
  }), method(object({
28659
28709
  agentNodeId: string(),
28660
- reachableHost: string().nullable()
28661
- }), object({ success: literal(true) }), {
28710
+ reachableHost: string().nullable().optional(),
28711
+ color: NodeColorIdSchema.nullable().optional()
28712
+ }).refine((v) => v.reachableHost !== void 0 || v.color !== void 0, { message: "reachableHost or color required" }), object({ success: literal(true) }), {
28662
28713
  kind: "mutation",
28663
28714
  auth: "admin"
28664
28715
  }), method(object({
@@ -36796,7 +36847,13 @@ var TopologyNodeSchema = object({
36796
36847
  })).readonly(),
36797
36848
  processes: array(TopologyProcessSchema).readonly(),
36798
36849
  categories: array(TopologyCategorySchema).readonly(),
36799
- rootPackage: TopologyRootPackageSchema.nullable()
36850
+ rootPackage: TopologyRootPackageSchema.nullable(),
36851
+ /**
36852
+ * How an online AGENT reaches the hub (mesh mTLS Release A): `tls` once it has
36853
+ * completed a mutual-TLS handshake, `clear` otherwise. Absent for the hub,
36854
+ * for offline rows and when the hub cannot tell.
36855
+ */
36856
+ meshTransport: _enum(["tls", "clear"]).optional()
36800
36857
  });
36801
36858
  var CapUsageEdgeSchema = object({
36802
36859
  callerAddonId: string(),
@@ -37766,7 +37823,15 @@ var GpuInfoSchema = object({
37766
37823
  name: string(),
37767
37824
  memoryMB: number().optional()
37768
37825
  });
37769
- var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
37826
+ var NpuInfoSchema = object({
37827
+ type: _enum([
37828
+ "apple-ane",
37829
+ "intel-npu",
37830
+ "amd-xdna"
37831
+ ]),
37832
+ name: string().optional(),
37833
+ generation: _enum(["xdna1", "xdna2"]).optional()
37834
+ });
37770
37835
  var CoralInfoSchema = object({
37771
37836
  type: literal("coral-edgetpu"),
37772
37837
  bus: string().optional()
@@ -42509,9 +42574,11 @@ var zoneRulesCapability = {
42509
42574
  * once is what makes a second provider (RunPod, a local NVIDIA node) a new
42510
42575
  * manifest entry and nothing else.
42511
42576
  *
42512
- * Collection, routed by the `addonId` in every single-row input (the `llm` /
42513
- * `notification-output` posture); `describe` concat-fans across providers.
42514
- * Every method is `admin`: these spend money.
42577
+ * Collection of provider **types** (addons). Each addon owns zero or more
42578
+ * **entries** (credentials + defaults). Concat-fans: `describe`,
42579
+ * `describeTypes`, `listEntries`, `listJobs`. Single-row work is keyed by
42580
+ * `entryId`; callers also pass the owning `addonId` as the collection
42581
+ * selector (broker posture). Every method is `admin`: these spend money.
42515
42582
  *
42516
42583
  * Every cloud object a provider creates is tagged with the orchestrator's
42517
42584
  * `jobKey`, so a retried submit finds the job it already started instead of
@@ -42535,8 +42602,8 @@ var TrainingRegionOptionSchema = object({
42535
42602
  priceMultiplier: number().positive()
42536
42603
  });
42537
42604
  /**
42538
- * The provider's per-account settings, as the orchestrator reads them. They
42539
- * live in the PROVIDER addon's settings (operator decision 2026-10-08: they
42605
+ * The provider's per-entry settings, as the orchestrator reads them. They
42606
+ * live on the PROVIDER addon's entry (operator decision 2026-10-08: they
42540
42607
  * depend on the user's account), never hard-coded in the orchestrator.
42541
42608
  */
42542
42609
  var TrainingProviderDefaultsSchema = object({
@@ -42547,9 +42614,32 @@ var TrainingProviderDefaultsSchema = object({
42547
42614
  /** Hours a FAILED job's cloud dataset is kept for debugging; 0 = delete at once. */
42548
42615
  failedDatasetGraceHours: number().nonnegative()
42549
42616
  });
42617
+ /** One creatable provider type, for the Add-entry dialog. */
42618
+ var TrainingProviderTypeSchema = object({
42619
+ typeId: string(),
42620
+ label: string(),
42621
+ /** Path under the addon package (e.g. `assets/training-modal/icon.svg`). */
42622
+ iconAsset: string(),
42623
+ /** Owning `training-provider` addon — collection selector for createEntry. */
42624
+ addonId: string()
42625
+ });
42626
+ /** One configured account / workspace under a provider type. */
42627
+ var TrainingProviderEntrySchema = object({
42628
+ entryId: string(),
42629
+ typeId: string(),
42630
+ name: string(),
42631
+ addonId: string(),
42632
+ /** Credentials are present — never the values. */
42633
+ configured: boolean()
42634
+ });
42550
42635
  var TrainingProviderDescriptorSchema = object({
42551
42636
  addonId: string(),
42552
- /** `modal`, `runpod`, `fake`, … */
42637
+ entryId: string(),
42638
+ typeId: string(),
42639
+ /** Operator-chosen entry name. */
42640
+ name: string(),
42641
+ iconAsset: string(),
42642
+ /** `modal`, `runpod`, `fake`, … — same as typeId for current providers. */
42553
42643
  kind: string(),
42554
42644
  label: string(),
42555
42645
  gpuTypes: array(TrainingGpuOptionSchema).readonly(),
@@ -42613,20 +42703,30 @@ var TrainerSourceRefSchema = object({
42613
42703
  /** A commit sha (never a branch) — the job must be reproducible. */
42614
42704
  revision: string().regex(/^[0-9a-f]{7,64}$/)
42615
42705
  });
42706
+ /** Cloud job identity — route with the owning addon's `addonId` selector. */
42616
42707
  var JobRef = {
42617
- addonId: string(),
42708
+ entryId: string().min(1),
42618
42709
  jobKey: string().uuid()
42619
42710
  };
42620
42711
  var TrainingLogLineSchema = object({
42621
42712
  ts: number(),
42622
42713
  text: string()
42623
42714
  });
42624
- method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({ addonId: string() }), TrainingCredentialCheckSchema, {
42715
+ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderTypeSchema).readonly(), { auth: "admin" }), method(object({}), array(TrainingProviderEntrySchema).readonly(), { auth: "admin" }), method(object({
42716
+ typeId: string().min(1),
42717
+ name: string().min(1)
42718
+ }), object({ entryId: string() }), {
42719
+ kind: "mutation",
42720
+ auth: "admin"
42721
+ }), method(object({ entryId: string().min(1) }), object({ ok: literal(true) }), {
42722
+ kind: "mutation",
42723
+ auth: "admin"
42724
+ }), method(object({ entryId: string().min(1) }), TrainingCredentialCheckSchema, {
42625
42725
  kind: "mutation",
42626
42726
  auth: "admin",
42627
42727
  timeoutMs: 3e4
42628
42728
  }), method(object({
42629
- addonId: string(),
42729
+ entryId: string().min(1),
42630
42730
  gpuType: string(),
42631
42731
  region: string(),
42632
42732
  datasetBytes: number().int().nonnegative(),
@@ -42692,7 +42792,7 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
42692
42792
  kind: "mutation",
42693
42793
  auth: "admin",
42694
42794
  timeoutMs: 5 * 6e4
42695
- }), method(object({ addonId: string() }), array(ProviderJobStatusSchema).readonly(), {
42795
+ }), method(object({ entryId: string().min(1).optional() }), array(ProviderJobStatusSchema).readonly(), {
42696
42796
  auth: "admin",
42697
42797
  timeoutMs: 2 * 6e4
42698
42798
  });
@@ -42702,20 +42802,31 @@ method(object({}), array(TrainingProviderDescriptorSchema).readonly(), { auth: "
42702
42802
  *
42703
42803
  * It owns what every provider must agree on, written once:
42704
42804
  *
42705
- * - **Dataset versions.** Taken over from pipeline-analytics (D796): the rows
42706
- * live here (D808); pipeline-analytics still CUTS a version (it owns the corpus and
42707
- * the export plan) and reaches this store through the five row methods
42708
- * below, so the Retrain page and the export did not change.
42709
42805
  * - **Jobs.** A persisted state machine, a reconcile loop against the
42710
42806
  * provider, a cost guard, a daily orphan sweep and the retention rules.
42711
42807
  * - **The return flow.** Verified artefacts → Model Studio
42712
42808
  * (`custom-model-registry.importModelBundle`) → canary on cameras →
42713
42809
  * rollback. Nothing is ever registered automatically.
42714
42810
  *
42811
+ * Plan/start takes a live {@link TrainingCorpusSelection} (collections +
42812
+ * work-state rule); frame ids are resolved via pipeline-analytics at that
42813
+ * moment and snapshotted on the job. No frozen dataset version.
42814
+ *
42715
42815
  * Every method is `admin`.
42716
42816
  */
42717
- /** A version WITH its frame list — what the export and a job read. */
42718
- var TrainingDatasetVersionRowSchema = RetrainDatasetVersionSchema.omit({ frameCount: true }).extend({ frameIds: array(string()).max(2e4).readonly() });
42817
+ /**
42818
+ * Live corpus criteria for plan/start (design §5.2). Default is complete-only.
42819
+ * Frame ids are resolved at plan/start — never a frozen version id.
42820
+ */
42821
+ var TrainingCorpusSelectionSchema = object({
42822
+ collectionIds: array(string()).min(1),
42823
+ /** Default complete-only for v1 */
42824
+ workState: _enum([
42825
+ "complete",
42826
+ "annotated_or_complete",
42827
+ "any_annotated"
42828
+ ]).default("complete")
42829
+ });
42719
42830
  /** A base model a detector job can fine-tune, and the baseline it is judged against. */
42720
42831
  var TrainingBaseModelSchema = object({
42721
42832
  modelId: string(),
@@ -42733,9 +42844,13 @@ var TrainingPresetSchema = _enum([
42733
42844
  "thorough"
42734
42845
  ]);
42735
42846
  var TrainingJobRequestSchema = object({
42736
- datasetVersionId: string().min(1),
42737
- /** The `training-provider` addon that runs it. */
42738
- addonId: string().min(1),
42847
+ /** Live collection + work-state criteria; resolved at plan/start. */
42848
+ selection: TrainingCorpusSelectionSchema,
42849
+ /**
42850
+ * The provider **entry** that runs it (credentials + defaults).
42851
+ * Resolve to an owning addon via `training-provider.describe` / `listEntries`.
42852
+ */
42853
+ entryId: string().min(1),
42739
42854
  gpuType: string().min(1),
42740
42855
  region: string(),
42741
42856
  baseModelId: string().min(1),
@@ -42794,6 +42909,7 @@ var TrainingJobPlanSchema = object({
42794
42909
  * result lives after the operator acted on it.
42795
42910
  */
42796
42911
  var TrainingJobStateSchema = _enum([
42912
+ "queued",
42797
42913
  "uploading",
42798
42914
  "submitted",
42799
42915
  "starting",
@@ -42847,6 +42963,11 @@ var TrainingJobSchema = object({
42847
42963
  createdAt: number(),
42848
42964
  updatedAt: number(),
42849
42965
  state: TrainingJobStateSchema,
42966
+ /**
42967
+ * Shared by every job enqueued together via `startJobBatch`.
42968
+ * `null` for a lone `startJob`. The engine advances queued siblings by this id.
42969
+ */
42970
+ batchId: string().nullable().default(null),
42850
42971
  stage: TrainingStageSchema.nullable(),
42851
42972
  failure: object({
42852
42973
  reason: TrainingFailureReasonSchema,
@@ -42858,6 +42979,12 @@ var TrainingJobSchema = object({
42858
42979
  maxRuntimeSec: number().int().positive(),
42859
42980
  trainer: TrainerSourceRefSchema,
42860
42981
  datasetLayout: RetrainExportLayoutSchema,
42982
+ /** Frame count snapshotted when the job started (audit; equals `frameIds.length`). */
42983
+ frameCount: number().int().nonnegative(),
42984
+ /** Resolved frame ids at start — the archive ships exactly these. */
42985
+ frameIds: array(string()).max(2e4).readonly(),
42986
+ /** Hash of what those frames taught at start (trainer `dataset.annotationHash`). */
42987
+ annotationHash: string(),
42861
42988
  dataset: object({
42862
42989
  bytes: number().int(),
42863
42990
  sha256: string(),
@@ -42899,13 +43026,7 @@ var TrainingJobSchema = object({
42899
43026
  canary: TrainingCanarySchema.nullable()
42900
43027
  });
42901
43028
  var IdInput = object({ id: string().min(1) });
42902
- method(TrainingDatasetVersionRowSchema, object({ stored: boolean() }), {
42903
- kind: "mutation",
42904
- auth: "admin"
42905
- }), method(IdInput, TrainingDatasetVersionRowSchema.nullable(), { auth: "admin" }), method(object({}), array(RetrainDatasetVersionSchema).readonly(), { auth: "admin" }), method(object({ name: string() }), object({ id: string().nullable() }), { auth: "admin" }), method(IdInput, object({ removed: boolean() }), {
42906
- kind: "mutation",
42907
- auth: "admin"
42908
- }), method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" }), method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
43029
+ method(object({}), array(TrainingBaseModelSchema).readonly(), { auth: "admin" }), method(TrainingJobRequestSchema, TrainingJobPlanSchema, {
42909
43030
  auth: "admin",
42910
43031
  timeoutMs: 6e4
42911
43032
  }), method(TrainingJobRequestSchema.extend({
@@ -42924,6 +43045,24 @@ method(TrainingDatasetVersionRowSchema, object({ stored: boolean() }), {
42924
43045
  kind: "mutation",
42925
43046
  auth: "admin",
42926
43047
  timeoutMs: 12e4
43048
+ }), method(TrainingJobRequestSchema.omit({ baseModelId: true }).extend({
43049
+ baseModelIds: array(string().min(1)).min(1),
43050
+ name: string().min(1).max(120),
43051
+ maxCostUsd: number().positive(),
43052
+ maxRuntimeSec: number().int().positive(),
43053
+ idempotencyKey: string().min(8).max(80)
43054
+ }), discriminatedUnion("kind", [object({
43055
+ kind: literal("started"),
43056
+ batchId: string(),
43057
+ jobs: array(TrainingJobSchema).min(1).readonly()
43058
+ }), object({
43059
+ kind: literal("refused"),
43060
+ reason: TrainingRefusalReasonSchema,
43061
+ detail: string()
43062
+ })]), {
43063
+ kind: "mutation",
43064
+ auth: "admin",
43065
+ timeoutMs: 12e4
42927
43066
  }), method(object({}), array(TrainingJobSchema).readonly(), { auth: "admin" }), method(IdInput, TrainingJobSchema.nullable(), { auth: "admin" }), method(IdInput, TrainingJobSchema, {
42928
43067
  kind: "mutation",
42929
43068
  auth: "admin",
@@ -48692,12 +48831,6 @@ Object.freeze({
48692
48831
  addonId: null,
48693
48832
  access: "create"
48694
48833
  },
48695
- "pipelineAnalytics.createRetrainDatasetVersion": {
48696
- capName: "pipeline-analytics",
48697
- capScope: "device",
48698
- addonId: null,
48699
- access: "create"
48700
- },
48701
48834
  "pipelineAnalytics.deleteDeviceEvents": {
48702
48835
  capName: "pipeline-analytics",
48703
48836
  capScope: "device",
@@ -48710,12 +48843,6 @@ Object.freeze({
48710
48843
  addonId: null,
48711
48844
  access: "delete"
48712
48845
  },
48713
- "pipelineAnalytics.deleteRetrainDatasetVersion": {
48714
- capName: "pipeline-analytics",
48715
- capScope: "device",
48716
- addonId: null,
48717
- access: "delete"
48718
- },
48719
48846
  "pipelineAnalytics.deleteTracks": {
48720
48847
  capName: "pipeline-analytics",
48721
48848
  capScope: "device",
@@ -48818,6 +48945,12 @@ Object.freeze({
48818
48945
  addonId: null,
48819
48946
  access: "view"
48820
48947
  },
48948
+ "pipelineAnalytics.getRetrainCorpusKpi": {
48949
+ capName: "pipeline-analytics",
48950
+ capScope: "device",
48951
+ addonId: null,
48952
+ access: "view"
48953
+ },
48821
48954
  "pipelineAnalytics.getRetrainExportUrl": {
48822
48955
  capName: "pipeline-analytics",
48823
48956
  capScope: "device",
@@ -48938,7 +49071,7 @@ Object.freeze({
48938
49071
  addonId: null,
48939
49072
  access: "view"
48940
49073
  },
48941
- "pipelineAnalytics.listRetrainDatasetVersions": {
49074
+ "pipelineAnalytics.listRetrainCorpusPage": {
48942
49075
  capName: "pipeline-analytics",
48943
49076
  capScope: "device",
48944
49077
  addonId: null,
@@ -48992,12 +49125,6 @@ Object.freeze({
48992
49125
  addonId: null,
48993
49126
  access: "create"
48994
49127
  },
48995
- "pipelineAnalytics.previewRetrainDataset": {
48996
- capName: "pipeline-analytics",
48997
- capScope: "device",
48998
- addonId: null,
48999
- access: "view"
49000
- },
49001
49128
  "pipelineAnalytics.proposeRetrainAnnotations": {
49002
49129
  capName: "pipeline-analytics",
49003
49130
  capScope: "device",
@@ -49076,6 +49203,12 @@ Object.freeze({
49076
49203
  addonId: null,
49077
49204
  access: "create"
49078
49205
  },
49206
+ "pipelineAnalytics.resolveRetrainTrainingCorpus": {
49207
+ capName: "pipeline-analytics",
49208
+ capScope: "device",
49209
+ addonId: null,
49210
+ access: "view"
49211
+ },
49079
49212
  "pipelineAnalytics.restageRetrainTrack": {
49080
49213
  capName: "pipeline-analytics",
49081
49214
  capScope: "device",
@@ -49136,12 +49269,6 @@ Object.freeze({
49136
49269
  addonId: null,
49137
49270
  access: "create"
49138
49271
  },
49139
- "pipelineAnalytics.verifyRetrainDatasetVersion": {
49140
- capName: "pipeline-analytics",
49141
- capScope: "device",
49142
- addonId: null,
49143
- access: "view"
49144
- },
49145
49272
  "pipelineAnalytics.wipeAllAnalytics": {
49146
49273
  capName: "pipeline-analytics",
49147
49274
  capScope: "device",
@@ -51536,18 +51663,6 @@ Object.freeze({
51536
51663
  addonId: null,
51537
51664
  access: "create"
51538
51665
  },
51539
- "training.findDatasetVersionIdByName": {
51540
- capName: "training",
51541
- capScope: "system",
51542
- addonId: null,
51543
- access: "view"
51544
- },
51545
- "training.getDatasetVersion": {
51546
- capName: "training",
51547
- capScope: "system",
51548
- addonId: null,
51549
- access: "view"
51550
- },
51551
51666
  "training.getJob": {
51552
51667
  capName: "training",
51553
51668
  capScope: "system",
@@ -51566,12 +51681,6 @@ Object.freeze({
51566
51681
  addonId: null,
51567
51682
  access: "view"
51568
51683
  },
51569
- "training.listDatasetVersions": {
51570
- capName: "training",
51571
- capScope: "system",
51572
- addonId: null,
51573
- access: "view"
51574
- },
51575
51684
  "training.listJobs": {
51576
51685
  capName: "training",
51577
51686
  capScope: "system",
@@ -51584,54 +51693,66 @@ Object.freeze({
51584
51693
  addonId: null,
51585
51694
  access: "view"
51586
51695
  },
51587
- "training.putDatasetVersion": {
51696
+ "training.registerResult": {
51588
51697
  capName: "training",
51589
51698
  capScope: "system",
51590
51699
  addonId: null,
51591
51700
  access: "create"
51592
51701
  },
51593
- "training.registerResult": {
51702
+ "training.rollbackCanary": {
51594
51703
  capName: "training",
51595
51704
  capScope: "system",
51596
51705
  addonId: null,
51597
51706
  access: "create"
51598
51707
  },
51599
- "training.removeDatasetVersion": {
51708
+ "training.startJob": {
51600
51709
  capName: "training",
51601
51710
  capScope: "system",
51602
51711
  addonId: null,
51603
- access: "delete"
51712
+ access: "create"
51604
51713
  },
51605
- "training.rollbackCanary": {
51714
+ "training.startJobBatch": {
51606
51715
  capName: "training",
51607
51716
  capScope: "system",
51608
51717
  addonId: null,
51609
51718
  access: "create"
51610
51719
  },
51611
- "training.startJob": {
51612
- capName: "training",
51720
+ "trainingProvider.cancelJob": {
51721
+ capName: "training-provider",
51613
51722
  capScope: "system",
51614
51723
  addonId: null,
51615
51724
  access: "create"
51616
51725
  },
51617
- "trainingProvider.cancelJob": {
51726
+ "trainingProvider.cleanupJob": {
51618
51727
  capName: "training-provider",
51619
51728
  capScope: "system",
51620
51729
  addonId: null,
51621
51730
  access: "create"
51622
51731
  },
51623
- "trainingProvider.cleanupJob": {
51732
+ "trainingProvider.createEntry": {
51624
51733
  capName: "training-provider",
51625
51734
  capScope: "system",
51626
51735
  addonId: null,
51627
51736
  access: "create"
51628
51737
  },
51738
+ "trainingProvider.deleteEntry": {
51739
+ capName: "training-provider",
51740
+ capScope: "system",
51741
+ addonId: null,
51742
+ access: "delete"
51743
+ },
51629
51744
  "trainingProvider.describe": {
51630
51745
  capName: "training-provider",
51631
51746
  capScope: "system",
51632
51747
  addonId: null,
51633
51748
  access: "view"
51634
51749
  },
51750
+ "trainingProvider.describeTypes": {
51751
+ capName: "training-provider",
51752
+ capScope: "system",
51753
+ addonId: null,
51754
+ access: "view"
51755
+ },
51635
51756
  "trainingProvider.estimateJob": {
51636
51757
  capName: "training-provider",
51637
51758
  capScope: "system",
@@ -51644,6 +51765,12 @@ Object.freeze({
51644
51765
  addonId: null,
51645
51766
  access: "view"
51646
51767
  },
51768
+ "trainingProvider.listEntries": {
51769
+ capName: "training-provider",
51770
+ capScope: "system",
51771
+ addonId: null,
51772
+ access: "view"
51773
+ },
51647
51774
  "trainingProvider.listJobEvents": {
51648
51775
  capName: "training-provider",
51649
51776
  capScope: "system",
@@ -55100,6 +55227,11 @@ Object.freeze({
55100
55227
  form: "array",
55101
55228
  optional: false
55102
55229
  }],
55230
+ "pipelineAnalytics.listRetrainCorpusPage": [{
55231
+ name: "deviceIds",
55232
+ form: "array",
55233
+ optional: true
55234
+ }],
55103
55235
  "pipelineAnalytics.listRetrainStaging": [{
55104
55236
  name: "deviceIds",
55105
55237
  form: "array",
@@ -56694,6 +56826,8 @@ var STATE_SUMMARY_VARS = [
56694
56826
  [...RULE_TEXT],
56695
56827
  [...RULE_TEXT],
56696
56828
  [...RULE_TEXT],
56829
+ [...RULE_TEXT],
56830
+ [...RULE_TEXT],
56697
56831
  ([...RULE_TEXT], [...UPDATES]),
56698
56832
  ([...RULE_TEXT], [...PACKAGE_UPDATES]),
56699
56833
  ([...RULE_TEXT], [...PACKAGE_UPDATES]),
@@ -80252,6 +80386,19 @@ function sameConnection(a, b) {
80252
80386
  return a.host === b.host && a.enableHmipRf === b.enableHmipRf && a.enableBidcosRf === b.enableBidcosRf && a.enableGroups === b.enableGroups && a.enableBidcosWired === b.enableBidcosWired && a.callbackHost === b.callbackHost && a.callbackPort === b.callbackPort && a.username === b.username && a.password === b.password && a.tls === b.tls;
80253
80387
  }
80254
80388
  /**
80389
+ * Delays (ms) between background restore retries after a failed
80390
+ * `facade.start()` (e.g. CCU `ECONNREFUSED`). Six attempts ≈ 2+5+10+20+30+60 s.
80391
+ * Exported for specs that drive the schedule with fake timers.
80392
+ */
80393
+ var RESTORE_RETRY_DELAYS_MS = [
80394
+ 2e3,
80395
+ 5e3,
80396
+ 1e4,
80397
+ 2e4,
80398
+ 3e4,
80399
+ 6e4
80400
+ ];
80401
+ /**
80255
80402
  * Manages N live CCU connections ("brokers"), each backed by an
80256
80403
  * {@link HmIntegrationManager}. Replaces the old single-CCU
80257
80404
  * `homematic-gateway.ts` singleton.
@@ -80297,6 +80444,10 @@ var HmBrokerRegistry = class {
80297
80444
  * typically in tests) facade creation is skipped.
80298
80445
  */
80299
80446
  #useDefaultFactory;
80447
+ /** Pending restore-retry timers — cleared on {@link shutdown}. */
80448
+ #retryTimers = /* @__PURE__ */ new Map();
80449
+ /** True after {@link shutdown} — blocks further restore retries. */
80450
+ #shuttingDown = false;
80300
80451
  constructor(logger, deps = {}) {
80301
80452
  this.#logger = logger;
80302
80453
  this.#onBrokerConnected = deps.onBrokerConnected ?? (() => void 0);
@@ -80321,10 +80472,13 @@ var HmBrokerRegistry = class {
80321
80472
  * Restore previously-persisted broker entries on addon boot.
80322
80473
  *
80323
80474
  * Seeds the id counter from existing ids so future allocations never
80324
- * collide. Starts each manager best-effort: a failed start is logged as a
80325
- * warning and the manager is still tracked (it can be retried later).
80475
+ * collide. Starts each manager best-effort: a failed start is rolled back
80476
+ * out of `#managers` (so a later attempt can rebuild) and scheduled for a
80477
+ * bounded background retry — a transient CCU `ECONNREFUSED` must not leave
80478
+ * the broker dead until an operator restarts the addon.
80326
80479
  */
80327
80480
  async restore(entries) {
80481
+ this.#shuttingDown = false;
80328
80482
  for (const entry of entries) this.#seedCounter(entry.id);
80329
80483
  for (const entry of entries) try {
80330
80484
  await this.#startManager(entry);
@@ -80334,6 +80488,7 @@ var HmBrokerRegistry = class {
80334
80488
  tags: { brokerId: entry.id },
80335
80489
  meta: { error: msg }
80336
80490
  });
80491
+ this.#scheduleRestoreRetry(entry, 0);
80337
80492
  }
80338
80493
  }
80339
80494
  /**
@@ -80341,6 +80496,9 @@ var HmBrokerRegistry = class {
80341
80496
  * shutdown.
80342
80497
  */
80343
80498
  async shutdown() {
80499
+ this.#shuttingDown = true;
80500
+ for (const timer of this.#retryTimers.values()) clearTimeout(timer);
80501
+ this.#retryTimers.clear();
80344
80502
  const ids = Array.from(this.#managers.keys());
80345
80503
  await Promise.all(ids.map(async (id) => {
80346
80504
  const mgr = this.#managers.get(id);
@@ -80532,6 +80690,10 @@ var HmBrokerRegistry = class {
80532
80690
  * Build (via the factory), register, and start the manager for one entry.
80533
80691
  * If a manager for the same id already exists (e.g. a duplicate restore
80534
80692
  * call), skip construction and only re-index the integration FK.
80693
+ *
80694
+ * On `start()` failure the half-built manager is abandoned so a retry can
80695
+ * construct a fresh one — leaving it in `#managers` used to make every
80696
+ * subsequent `#startManager` a no-op.
80535
80697
  */
80536
80698
  async #startManager(entry) {
80537
80699
  if (this.#managers.has(entry.id)) {
@@ -80548,13 +80710,92 @@ var HmBrokerRegistry = class {
80548
80710
  });
80549
80711
  this.#managers.set(entry.id, mgr);
80550
80712
  if (entry.integrationId !== void 0) this.#integrationToBroker.set(entry.integrationId, entry.id);
80551
- await mgr.start();
80713
+ try {
80714
+ await mgr.start();
80715
+ } catch (err) {
80716
+ await this.#abandonFailedStart(entry.id, mgr);
80717
+ throw err;
80718
+ }
80719
+ const pending = this.#retryTimers.get(entry.id);
80720
+ if (pending !== void 0) {
80721
+ clearTimeout(pending);
80722
+ this.#retryTimers.delete(entry.id);
80723
+ }
80552
80724
  this.#logger.info("HmBrokerRegistry: manager started", {
80553
80725
  tags: { brokerId: entry.id },
80554
80726
  meta: { name: entry.name }
80555
80727
  });
80556
80728
  }
80557
80729
  /**
80730
+ * Drop a manager that never reached `connected` so a later attempt can
80731
+ * rebuild it. Best-effort `stop()`; never throws.
80732
+ */
80733
+ async #abandonFailedStart(id, mgr) {
80734
+ this.#managers.delete(id);
80735
+ this.#facades.delete(id);
80736
+ homematicFacades.set(id, null);
80737
+ try {
80738
+ await mgr.stop();
80739
+ } catch (err) {
80740
+ const msg = err instanceof Error ? err.message : String(err);
80741
+ this.#logger.debug("HmBrokerRegistry: abandon stop failed", {
80742
+ tags: { brokerId: id },
80743
+ meta: { error: msg }
80744
+ });
80745
+ }
80746
+ }
80747
+ /**
80748
+ * Schedule attempt `attemptIndex` of {@link RESTORE_RETRY_DELAYS_MS} for
80749
+ * `entry`. No-ops when the registry is shutting down or the schedule is
80750
+ * exhausted (warns once at the end).
80751
+ */
80752
+ #scheduleRestoreRetry(entry, attemptIndex) {
80753
+ if (this.#shuttingDown) return;
80754
+ const delay = RESTORE_RETRY_DELAYS_MS[attemptIndex];
80755
+ if (delay === void 0) {
80756
+ this.#logger.warn("HmBrokerRegistry: restore retries exhausted", {
80757
+ tags: { brokerId: entry.id },
80758
+ meta: { attempts: RESTORE_RETRY_DELAYS_MS.length }
80759
+ });
80760
+ return;
80761
+ }
80762
+ const existing = this.#retryTimers.get(entry.id);
80763
+ if (existing !== void 0) clearTimeout(existing);
80764
+ const timer = setTimeout(() => {
80765
+ this.#retryTimers.delete(entry.id);
80766
+ this.#runRestoreRetry(entry, attemptIndex);
80767
+ }, delay);
80768
+ this.#retryTimers.set(entry.id, timer);
80769
+ this.#logger.info("HmBrokerRegistry: scheduling restore retry", {
80770
+ tags: { brokerId: entry.id },
80771
+ meta: {
80772
+ attempt: attemptIndex + 1,
80773
+ delayMs: delay
80774
+ }
80775
+ });
80776
+ }
80777
+ async #runRestoreRetry(entry, attemptIndex) {
80778
+ if (this.#shuttingDown) return;
80779
+ if (this.#managers.has(entry.id)) return;
80780
+ try {
80781
+ await this.#startManager(entry);
80782
+ this.#logger.info("HmBrokerRegistry: restore retry succeeded", {
80783
+ tags: { brokerId: entry.id },
80784
+ meta: { attempt: attemptIndex + 1 }
80785
+ });
80786
+ } catch (err) {
80787
+ const msg = err instanceof Error ? err.message : String(err);
80788
+ this.#logger.warn("HmBrokerRegistry: restore retry failed", {
80789
+ tags: { brokerId: entry.id },
80790
+ meta: {
80791
+ attempt: attemptIndex + 1,
80792
+ error: msg
80793
+ }
80794
+ });
80795
+ this.#scheduleRestoreRetry(entry, attemptIndex + 1);
80796
+ }
80797
+ }
80798
+ /**
80558
80799
  * Allocate the next monotonic id in the `hm_NNN` sequence and advance
80559
80800
  * the counter.
80560
80801
  */
@@ -83011,6 +83252,19 @@ var HmContainerDevice = class HmContainerDevice extends BaseDevice {
83011
83252
  }
83012
83253
  }
83013
83254
  /**
83255
+ * Stamp `feature-probe.lastProbedAt` only when the CCU device is on the
83256
+ * facade. An empty accessory-spec list + a stamped probe is how the kernel
83257
+ * orphan-prunes linked children (`device-cap-proxy` safety gate): a restore
83258
+ * that races a CCU `ECONNREFUSED` used to delete every accessory and fence
83259
+ * their identities for 5 minutes. Leaving `lastProbedAt === 0` until the
83260
+ * device is present keeps the gate closed; `reprobe()` on broker connect
83261
+ * completes the probe and spawns/reconciles children for real.
83262
+ */
83263
+ async onProbe() {
83264
+ if (this.resolveHmDevice() === null) return;
83265
+ await super.onProbe();
83266
+ }
83267
+ /**
83014
83268
  * Derive the accessory children LIVE from the facade descriptor. Each child
83015
83269
  * is a {@link HmEntitySpec} (the shared per-entity decomposition) wrapped into
83016
83270
  * an `AccessoryChildSpec`: the `stableIdSuffix` IS the spec `key`, the broker
@@ -83150,6 +83404,7 @@ var HomematicProviderAddon = class extends BaseDeviceProvider {
83150
83404
  this.registry.setOnBrokerConnected((brokerId) => {
83151
83405
  this.ctx.logger.info("Homematic: broker connected", { meta: { brokerId } });
83152
83406
  this.setBrokerDevicesOnline(brokerId, true);
83407
+ this.reprobeBrokerContainers(brokerId);
83153
83408
  });
83154
83409
  this.registry.setOnBrokerDisconnected((brokerId) => {
83155
83410
  this.setBrokerDevicesOnline(brokerId, false);
@@ -83544,6 +83799,28 @@ var HomematicProviderAddon = class extends BaseDeviceProvider {
83544
83799
  count: n
83545
83800
  } });
83546
83801
  }
83802
+ /**
83803
+ * Reprobe every adopted {@link HmContainerDevice} for `brokerId` so the
83804
+ * kernel re-runs accessory reconcile against the live facade. Best-effort:
83805
+ * one container's failure must not block the rest (same discipline as HA's
83806
+ * post-connect reconcile).
83807
+ */
83808
+ async reprobeBrokerContainers(brokerId) {
83809
+ const reg = this.ctx.kernel.deviceRegistry;
83810
+ if (!reg) return;
83811
+ const containers = reg.getAllForAddon(this.addonId).filter((d) => d instanceof HmContainerDevice && d.config.get("brokerId") === brokerId);
83812
+ for (const container of containers) try {
83813
+ await container.reprobe();
83814
+ } catch (err) {
83815
+ this.ctx.logger.warn("Homematic: container reprobe failed", {
83816
+ tags: { deviceId: container.id },
83817
+ meta: {
83818
+ brokerId,
83819
+ error: errMsg(err)
83820
+ }
83821
+ });
83822
+ }
83823
+ }
83547
83824
  /** Return the live registry or throw if the addon is not initialised. */
83548
83825
  requireRegistry() {
83549
83826
  if (!this.registry) throw new Error("Homematic provider not initialised");