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