@camstack/addon-post-analysis 1.2.75 → 1.2.77

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.
@@ -5396,12 +5396,6 @@ Object.fromEntries([
5396
5396
  icon: "shapes",
5397
5397
  order: 38
5398
5398
  },
5399
- {
5400
- id: "scenes",
5401
- label: "Scenes",
5402
- icon: "scan-eye",
5403
- order: 36
5404
- },
5405
5399
  {
5406
5400
  id: "analytics",
5407
5401
  label: "Analytics",
@@ -12675,6 +12669,17 @@ var LlmImageSchema = object({
12675
12669
  bytes: _instanceof(Uint8Array),
12676
12670
  mimeType: string()
12677
12671
  });
12672
+ /**
12673
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
12674
+ * the flag is what a consumer table flips, the count is what the operator tunes.
12675
+ * A retry doubles the wall time of a call, so the two gates that run inside a
12676
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
12677
+ */
12678
+ var LlmRetryPolicySchema = object({
12679
+ enabled: boolean().default(false),
12680
+ /** Total attempts INCLUDING the first. 1 = no retry. */
12681
+ maxAttempts: number().int().min(1).max(5).default(1)
12682
+ });
12678
12683
  var LlmGenerateBaseInputSchema = object({
12679
12684
  /** Collection routing (the notification-output posture). */
12680
12685
  addonId: string().optional(),
@@ -12689,7 +12694,28 @@ var LlmGenerateBaseInputSchema = object({
12689
12694
  jsonSchema: record(string(), unknown()).optional(),
12690
12695
  /** Per-call override of the profile default. */
12691
12696
  maxTokens: number().int().positive().optional(),
12692
- temperature: number().optional()
12697
+ temperature: number().optional(),
12698
+ /** Per-call override of the profile default (nucleus sampling). */
12699
+ topP: number().min(0).max(1).optional(),
12700
+ /** Per-call override of the profile default (top-k sampling). */
12701
+ topK: number().int().positive().optional(),
12702
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
12703
+ timeoutMs: number().int().positive().optional(),
12704
+ /** Per-call override; beats both the consumer table and the profile. */
12705
+ retry: LlmRetryPolicySchema.optional(),
12706
+ /**
12707
+ * Caller-minted id that makes this generation CANCELLABLE.
12708
+ *
12709
+ * Without it a caller that stops waiting cannot stop the work: the gates race
12710
+ * the call against 8 s and free their own slot when the timer wins, while the
12711
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
12712
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
12713
+ * not generations, and the real load is unbounded.
12714
+ *
12715
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
12716
+ * `llm.cancel({ requestId })` tears the socket down.
12717
+ */
12718
+ requestId: string().optional()
12693
12719
  });
12694
12720
  /**
12695
12721
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -12728,8 +12754,49 @@ var ManagedRuntimeConfigSchema = object({
12728
12754
  gpuLayers: number().int().default(0),
12729
12755
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
12730
12756
  threads: number().int().optional(),
12731
- /** Concurrent slots. */
12757
+ /** Concurrent slots (`--parallel`). */
12732
12758
  parallel: number().int().default(1),
12759
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
12760
+ batchSize: number().int().positive().optional(),
12761
+ /** Physical batch / micro-batch (`-ub`). */
12762
+ ubatchSize: number().int().positive().optional(),
12763
+ /**
12764
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
12765
+ * is a no-op elsewhere, so it is offered rather than assumed.
12766
+ */
12767
+ flashAttention: boolean().default(false),
12768
+ /**
12769
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
12770
+ * inference. Costs the full model size in resident memory — which is exactly
12771
+ * what the RAM budget is counting.
12772
+ */
12773
+ mlock: boolean().default(false),
12774
+ /**
12775
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
12776
+ * start, but avoids the page-fault stalls a network or spinning-disk model
12777
+ * store produces on every first token.
12778
+ */
12779
+ noMmap: boolean().default(false),
12780
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
12781
+ * cheapest way to fit a longer context in the same RAM. */
12782
+ cacheTypeK: _enum([
12783
+ "f32",
12784
+ "f16",
12785
+ "q8_0",
12786
+ "q5_1",
12787
+ "q5_0",
12788
+ "q4_1",
12789
+ "q4_0"
12790
+ ]).optional(),
12791
+ cacheTypeV: _enum([
12792
+ "f32",
12793
+ "f16",
12794
+ "q8_0",
12795
+ "q5_1",
12796
+ "q5_0",
12797
+ "q4_1",
12798
+ "q4_0"
12799
+ ]).optional(),
12733
12800
  /** Else lazy: first generate boots it. */
12734
12801
  autoStart: boolean().default(false),
12735
12802
  /** 0 = never; frees RAM after quiet periods. */
@@ -12817,10 +12884,44 @@ var LlmProfileSchema = object({
12817
12884
  baseUrl: string().optional(),
12818
12885
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
12819
12886
  apiKey: string().optional(),
12887
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
12888
+ * degraded to text — that shipped once and produced a confident answer to a
12889
+ * question about a picture nobody sent. */
12820
12890
  supportsVision: boolean(),
12821
12891
  temperature: number().min(0).max(2).optional(),
12892
+ /** Nucleus sampling. Every wire we speak has it. */
12893
+ topP: number().min(0).max(1).optional(),
12894
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
12895
+ * wire does, and the client drops it there (measured: the request body gets
12896
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
12897
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
12898
+ topK: number().int().positive().optional(),
12822
12899
  maxTokens: number().int().positive().optional(),
12900
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
12901
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
12902
+ * the model with, so it is the one field that changes a PROCESS. */
12903
+ contextLength: number().int().positive().optional(),
12904
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
12905
+ * two system prompts fighting is worse than either alone). */
12906
+ systemPrompt: string().optional(),
12907
+ /** Total generation bound — the only one a unary call has. */
12823
12908
  timeoutMs: number().int().positive().default(6e4),
12909
+ /** Wait for response headers only. */
12910
+ connectTimeoutMs: number().int().positive().default(1e4),
12911
+ /** Accepted, but no output yet — a cold GPU load lives here. */
12912
+ firstTokenTimeoutMs: number().int().positive().default(12e4),
12913
+ /** Output started then stopped. */
12914
+ idleTimeoutMs: number().int().positive().default(6e4),
12915
+ /** Profile-level default. The per-consumer table and a per-call override
12916
+ * both beat it — see `resolveRetryPolicy`. */
12917
+ retry: LlmRetryPolicySchema.default({
12918
+ enabled: false,
12919
+ maxAttempts: 1
12920
+ }),
12921
+ /** Whether this profile may use tools. The tool-call plumbing rides the
12922
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
12923
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
12924
+ toolsEnabled: boolean().default(false),
12824
12925
  extraHeaders: record(string(), string()).optional(),
12825
12926
  /** kind === 'managed-local' only (spec §4). */
12826
12927
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -12882,7 +12983,10 @@ var ProfileRefInputSchema = object({
12882
12983
  addonId: string(),
12883
12984
  profileId: string()
12884
12985
  });
12885
- method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12986
+ method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({
12987
+ addonId: string().optional(),
12988
+ requestId: string()
12989
+ }), _void(), { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
12886
12990
  kind: "mutation",
12887
12991
  auth: "admin"
12888
12992
  }), method(ProfileRefInputSchema, _void(), {
@@ -14724,28 +14828,36 @@ var NcOccupancyConditionSchema = object({
14724
14828
  /**
14725
14829
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
14726
14830
  *
14727
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
14728
- * reference notifier uses, so an operator moving between them re-uses what
14729
- * they already know): a rule matches when, over a sampling window of
14730
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
14731
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
14732
- *
14733
- * - `dbThreshold` — its level is at or above this many dBFS (see
14734
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
14735
- * - `labels` — the classifier put at least one of these labels on it.
14736
- *
14737
- * Both are OPTIONAL and independent, which is the point of the shape: a
14738
- * loudness rule ("something loud at 3am") needs no model to be right, and a
14739
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
14740
- * is given** — a window in which every sample is trivially a hit would fire on
14741
- * silence, so the engine refuses such a condition rather than notifying on
14742
- * nothing (the schema cannot express "at least one of" without becoming a
14743
- * ZodEffects the cap path would have to special-case).
14744
- *
14745
- * `hitPercent` is over the samples the window actually HOLDS, and the window
14746
- * must be FULL before it can match — a window that has been open for two
14747
- * seconds of its ten is 100% of nothing, and firing on it would make
14748
- * `samplingSeconds` decorative.
14831
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
14832
+ * rule is in is not a stored field — it is WHICH FILTER the rule carries, so
14833
+ * there is no second switch that can disagree with the first and every rule
14834
+ * authored before the decision migrates for free (`audioModeOf`):
14835
+ *
14836
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
14837
+ * classifier labels with one of them. No window, no percentage:
14838
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
14839
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
14840
+ * the analyzer's (`classificationMinScore`, per device) — a label only
14841
+ * reaches this condition if the classifier was already confident enough.
14842
+ * - **LEVEL mode — `dbThreshold` present, no labels.** The sampling window IS
14843
+ * the condition: at least `hitPercent`% of the samples over
14844
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
14845
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
14846
+ * must be FULL before it can match — a window open for two of its ten
14847
+ * seconds is 100% of nothing.
14848
+ *
14849
+ * **Why label mode has no window.** It had one, and it never fired: the
14850
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
14851
+ * of them per episode, even through continuous crying. The measured maximum
14852
+ * `hitPercent` over the whole live history was 40 — under the shipped default
14853
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
14854
+ * the wrong question to ask of a sparse classifier.
14855
+ *
14856
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
14857
+ * and the rule would fire on silence. The schema cannot express "exactly one
14858
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
14859
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
14860
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
14749
14861
  *
14750
14862
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
14751
14863
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -14753,13 +14865,13 @@ var NcOccupancyConditionSchema = object({
14753
14865
  * an operator who typed `dog` mean the same thing.
14754
14866
  */
14755
14867
  var NcAudioConditionSchema = object({
14756
- /** Audio macro labels; absent = any sound (level-only rule). */
14868
+ /** LABEL MODE: audio macro labels. Present ⇒ fires on the first labelled frame. */
14757
14869
  labels: array(string().min(1)).min(1).optional(),
14758
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
14870
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
14759
14871
  dbThreshold: number().min(-96).max(0).optional(),
14760
- /** Percentage of the window's samples that must be hits (1–100). */
14872
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
14761
14873
  hitPercent: number().int().min(1).max(100).default(60),
14762
- /** Length of the sampling window in seconds. */
14874
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
14763
14875
  samplingSeconds: number().int().min(1).max(300).default(10)
14764
14876
  });
14765
14877
  /**
@@ -14897,13 +15009,81 @@ var NcRuleActionsSchema = object({
14897
15009
  */
14898
15010
  buttons: array(NcRuleNotificationButtonSchema).max(8).optional()
14899
15011
  });
15012
+ /**
15013
+ * "This rule applies only while `deviceId` is in one of `states`."
15014
+ *
15015
+ * The states are the DEVICE's own vocabulary — `AlarmState` for a panel,
15016
+ * `on`/`off` for a switch — not a normalised set, because normalising would
15017
+ * make the condition lie about devices whose states have no equivalent.
15018
+ *
15019
+ * An unreadable state does NOT match: see the engine's fail-closed gate. A
15020
+ * condition that fired on "I could not read it" would be worse than no gate.
15021
+ */
15022
+ var NcDeviceStateConditionSchema = object({
15023
+ deviceId: number().int(),
15024
+ /** Any of these matches. */
15025
+ states: array(string().min(1)).min(1)
15026
+ });
15027
+ /**
15028
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
15029
+ *
15030
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
15031
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
15032
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
15033
+ * already has a trigger ("tell me about a person at the front door, but only
15034
+ * while the bin is still out"). That is why it composes with every delivery
15035
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
15036
+ * kind exist for it — see D159.
15037
+ *
15038
+ * ── Identity ───────────────────────────────────────────────────────────────
15039
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
15040
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
15041
+ * carried as a HINT for the editor and for the log line, never as part of the
15042
+ * lookup key: a rule whose hint drifted must still gate correctly.
15043
+ *
15044
+ * ── Which boolean ──────────────────────────────────────────────────────────
15045
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
15046
+ * already declares which boolean drives notification rules, and a second knob
15047
+ * that could disagree with it is exactly the D62 failure. Set it only to
15048
+ * override one rule against the scene's own default.
15049
+ *
15050
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
15051
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
15052
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
15053
+ * evidence, in either direction.
15054
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
15055
+ * The latch is a durable fact about the past ("it has diverged since I armed
15056
+ * it"), so a camera that has gone dark does not clear it — that is the whole
15057
+ * reason the operator asked for a latch.
15058
+ *
15059
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
15060
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
15061
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
15062
+ * said out loud in the log rather than dropped in silence.
15063
+ */
15064
+ var NcSceneConditionSchema = object({
15065
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
15066
+ sceneId: string().min(1),
15067
+ /** The camera the scene lives on. A hint for the editor and the log line. */
15068
+ deviceId: number().int().optional(),
15069
+ /** The state the scene must be in for the rule to fire. */
15070
+ requiredState: _enum(["matched", "diverged"]),
15071
+ /**
15072
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
15073
+ * scene's own `emit` field, which is the only place that decision belongs.
15074
+ */
15075
+ latched: boolean().optional()
15076
+ });
14900
15077
  var NcConditionsSchema = object({
14901
15078
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
14902
- deviceState: object({
14903
- deviceId: number().int(),
14904
- /** Any of these matches. */
14905
- states: array(string().min(1)).min(1)
14906
- }).optional(),
15079
+ deviceState: NcDeviceStateConditionSchema.optional(),
15080
+ /**
15081
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
15082
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
15083
+ * unlike `occupancy`/`audio` it discriminates nothing. See
15084
+ * {@link NcSceneCondition} and D159.
15085
+ */
15086
+ scene: NcSceneConditionSchema.optional(),
14907
15087
  /** Device scope — absent = all devices. */
14908
15088
  devices: array(number()).optional(),
14909
15089
  /** Detector class names (any overlap with the record's class set). */
@@ -15541,6 +15721,7 @@ var NcConditionDescriptorSchema = object({
15541
15721
  "occupancy",
15542
15722
  "audio",
15543
15723
  "deviceState",
15724
+ "scene",
15544
15725
  "systemEvent"
15545
15726
  ]),
15546
15727
  operator: _enum([
@@ -15895,6 +16076,21 @@ var NC_CONDITION_CATALOG = [
15895
16076
  phase: "P2",
15896
16077
  description: "Only fire while another device is in one of the chosen states — the alarm armed, a switch on, a contact closed. A state that cannot be read does NOT fire."
15897
16078
  },
16079
+ {
16080
+ id: "scene",
16081
+ group: "scope",
16082
+ label: "Scene state",
16083
+ valueType: "scene",
16084
+ operator: "in",
16085
+ appliesTo: [
16086
+ "immediate",
16087
+ "track-end",
16088
+ "device-event",
16089
+ "package-event"
16090
+ ],
16091
+ phase: "P3",
16092
+ description: "Only fire while a scene is matched (the baseline is what we see) or diverged (it demonstrably is not) — “tell me about a person at the door, but only while the bin is still out”. A scene that cannot judge, or one this hub has not read yet, does NOT fire."
16093
+ },
15898
16094
  {
15899
16095
  id: "sensorKinds",
15900
16096
  group: "device",
@@ -27474,10 +27670,22 @@ method(object({
27474
27670
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
27475
27671
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
27476
27672
  *
27477
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
27478
- * derives the device-detail contribution; the provider carries NO hand-written
27479
- * settings-contribution methods. `status.kind:'push'` — the engine pushes on
27480
- * every hysteresis flip / availability change; consumers never poll.
27673
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
27674
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
27675
+ * for the thing: a scene is a standing question about the property ("is the bin
27676
+ * still out"), and the operator's question is "which of my scenes have tripped",
27677
+ * across every camera at once — not "what does camera 617 think". Buried one
27678
+ * camera deep it also could not be found. The surface is now a top-level admin
27679
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
27680
+ * picks the camera inside the create flow, the same shape Events and Faces have.
27681
+ *
27682
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
27683
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
27684
+ * directions, so a registration nobody declares fails exactly as loudly as a
27685
+ * declaration nobody registers. The editor is imported directly by the page.
27686
+ *
27687
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
27688
+ * availability change; consumers never poll.
27481
27689
  */
27482
27690
  /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
27483
27691
  * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
@@ -27632,12 +27840,6 @@ var sceneMonitorCapability = {
27632
27840
  kind: "wrapper",
27633
27841
  defaultActive: true,
27634
27842
  deviceTypes: [DeviceType.Camera],
27635
- deviceConfig: { ui: {
27636
- kind: "widget",
27637
- widgetId: "host/scene-monitor-editor",
27638
- tab: "scenes",
27639
- label: "Scenes"
27640
- } },
27641
27843
  methods: {
27642
27844
  listScenes: method(object({ deviceId: number() }), SceneMonitorStatusSchema),
27643
27845
  createScene: method(object({
@@ -28226,12 +28428,64 @@ var NetworkAddressSchema = object({
28226
28428
  family: string(),
28227
28429
  internal: boolean()
28228
28430
  });
28431
+ /**
28432
+ * Provenance of the site coordinates, and the whole reason this is not just two
28433
+ * numbers.
28434
+ *
28435
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
28436
+ * nothing overwrites it.
28437
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
28438
+ * default that is right to a few kilometres beats the coarse UTC clock split
28439
+ * the sun-times consumers otherwise fall back to.
28440
+ *
28441
+ * The UI shows which one it is. An operator who cannot tell a guess from their
28442
+ * own input will eventually trust the guess.
28443
+ */
28444
+ var SiteLocationSourceSchema = _enum(["operator-set", "derived-from-ip"]);
28445
+ /**
28446
+ * The read shape: the location plus the honest state of the one-shot derivation.
28447
+ *
28448
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
28449
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
28450
+ * failed; the hub will NOT try again on its own — the fallback is declared
28451
+ * (consumers degrade to their own last resort) and the operator either types the
28452
+ * coordinates or presses detect.
28453
+ */
28454
+ var SiteLocationStatusSchema = object({
28455
+ location: object({
28456
+ /** WGS84 decimal degrees. */
28457
+ latitude: number().min(-90).max(90),
28458
+ longitude: number().min(-180).max(180),
28459
+ source: SiteLocationSourceSchema,
28460
+ /** Epoch ms the value was last written. */
28461
+ updatedAt: number(),
28462
+ /**
28463
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
28464
+ * only — never parsed, never matched on. Absent for an operator-typed value.
28465
+ */
28466
+ label: string().optional()
28467
+ }).nullable(),
28468
+ derivationAttemptedAt: number().nullable(),
28469
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
28470
+ derivationError: string().nullable()
28471
+ });
28472
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
28473
+ var SetSiteLocationInputSchema = object({
28474
+ latitude: number().min(-90).max(90),
28475
+ longitude: number().min(-180).max(180)
28476
+ }).nullable();
28229
28477
  method(_void(), FeatureManifestSchema), method(_void(), HealthStatusSchema), method(_void(), FeatureManifestSchema), method(_void(), array(NetworkAddressSchema).readonly()), method(_void(), unknown().nullable(), { auth: "admin" }), method(record(string(), unknown()), _null(), {
28230
28478
  kind: "mutation",
28231
28479
  auth: "admin"
28232
28480
  }), method(_void(), _void(), {
28233
28481
  kind: "mutation",
28234
28482
  auth: "admin"
28483
+ }), method(_void(), SiteLocationStatusSchema), method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
28484
+ kind: "mutation",
28485
+ auth: "admin"
28486
+ }), method(_void(), SiteLocationStatusSchema, {
28487
+ kind: "mutation",
28488
+ auth: "admin"
28235
28489
  });
28236
28490
  /**
28237
28491
  * Tamper / case-open detection sensor. Drives Home Assistant
@@ -32420,6 +32674,12 @@ Object.freeze({
32420
32674
  addonId: null,
32421
32675
  access: "create"
32422
32676
  },
32677
+ "llm.cancel": {
32678
+ capName: "llm",
32679
+ capScope: "system",
32680
+ addonId: null,
32681
+ access: "create"
32682
+ },
32423
32683
  "llm.deleteModel": {
32424
32684
  capName: "llm",
32425
32685
  capScope: "system",
@@ -35354,6 +35614,12 @@ Object.freeze({
35354
35614
  addonId: null,
35355
35615
  access: "create"
35356
35616
  },
35617
+ "system.detectSiteLocation": {
35618
+ capName: "system",
35619
+ capScope: "system",
35620
+ addonId: null,
35621
+ access: "create"
35622
+ },
35357
35623
  "system.featureFlags": {
35358
35624
  capName: "system",
35359
35625
  capScope: "system",
@@ -35372,6 +35638,12 @@ Object.freeze({
35372
35638
  addonId: null,
35373
35639
  access: "view"
35374
35640
  },
35641
+ "system.getSiteLocation": {
35642
+ capName: "system",
35643
+ capScope: "system",
35644
+ addonId: null,
35645
+ access: "view"
35646
+ },
35375
35647
  "system.health": {
35376
35648
  capName: "system",
35377
35649
  capScope: "system",
@@ -35396,6 +35668,12 @@ Object.freeze({
35396
35668
  addonId: null,
35397
35669
  access: "create"
35398
35670
  },
35671
+ "system.setSiteLocation": {
35672
+ capName: "system",
35673
+ capScope: "system",
35674
+ addonId: null,
35675
+ access: "create"
35676
+ },
35399
35677
  "terminalSession.adoptLegacyMonitor": {
35400
35678
  capName: "terminal-session",
35401
35679
  capScope: "system",
@@ -37653,6 +37931,26 @@ Object.freeze({
37653
37931
  "network-access": "ingress",
37654
37932
  "smtp-provider": "email"
37655
37933
  });
37934
+ /**
37935
+ * Which mode this condition is in — the ONE place that question is answered.
37936
+ *
37937
+ * The mode is NOT a stored field, deliberately. It is which filter the rule
37938
+ * carries, so every rule authored before the modes existed migrates for free
37939
+ * and there is no second switch that can disagree with the first (the failure
37940
+ * this repo has shipped twice). `null` = neither filter, which the engine
37941
+ * refuses (see {@link audioIsFailClosed}).
37942
+ *
37943
+ * A LEGACY rule carrying BOTH resolves to `label`: it is the mode that fires,
37944
+ * and the alternative is silently keeping a window the operator can no longer
37945
+ * see in the editor. Nothing new can reach this branch — {@link patchAudio}
37946
+ * clears the other filter on every write.
37947
+ */
37948
+ function audioModeOf(value) {
37949
+ if (value === void 0) return null;
37950
+ if (value.labels !== void 0 && value.labels.length > 0) return "label";
37951
+ if (value.dbThreshold !== void 0) return "level";
37952
+ return null;
37953
+ }
37656
37954
  new Map(AUDIO_MACRO_LABELS.flatMap((macro) => macro.icon === void 0 ? [] : [[macro.id, macro.icon]]));
37657
37955
  new Set(["devices", "classes"]);
37658
37956
  var WEEKDAY_TO_DAY = {
@@ -38197,4 +38495,4 @@ function vectorDimFromBase64(encoded) {
38197
38495
  return Math.floor(Buffer.from(encoded, "base64").byteLength / 4);
38198
38496
  }
38199
38497
  //#endregion
38200
- export { isScheduleActive as $, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as A, EventCategory as At, alarmPanelCapability as B, NcSnoozeSchema as C, literal as Ct, OpsLogEntrySchema as D, record as Dt, NcTaxonomySchema as E, partialRecord as Et, TimelapseRuleInputSchema as F, customAction as G, audioMetricsCapability as H, TimelapseRulePatchSchema as I, embeddingEncoderCapability as J, defineCustomActions as K, TimelapseRuleSchema as L, SCENE_DIVERGED as M, SceneMonitorSchema as N, RECORDING_EXPORT_MAX_READ_BYTES as O, string as Ot, TIMELAPSE_DENSE_FLOOR_SEC as P, isDetectionMacroClass as Q, TrackSourceSchema as R, NcSnoozeInputSchema as S, discriminatedUnion as St, NcSystemEventKindSchema as T, object as Tt, buildEventKindDescriptor as U, assertTimelapseCadences as V, cosineSimilarity as W, faceGalleryCapability as X, encodeVectorBase64 as Y, hfModelUrl as Z, NcRuleInputSchema as _, nodePin as _t, DETECTION_PIPELINE_CAP_NAME as a, readTimelapseGeneratedAt as at, NcRuleTargetSchema as b, array as bt, EVENT_PAD_MS as c, vectorDimFromBase64 as ct, NC_ALARM_SYSTEM_EVENT_KINDS as d, errMsg as dt, kebabToCamel as et, NC_CONDITION_CATALOG as f, BaseAddon as ft, NcConditionDescriptorSchema as g, isDeviceScopedCap as gt, NC_TAXONOMY as h, hydrateSchema as ht, DETECTION_MACRO_CLASSES as i, readDeviceStateFrom as it, SCENE_DEFAULT_ANCHOR_THRESHOLD as j, RetrainStatusSchema as k, unknown as kt, LabelAttributionSchema as l, videoclipsCapability as lt, NC_SNOOZE_MAX_MINUTES as m, createEvent as mt, DEFAULT_EVENT_COLOR as n, pipelineAnalyticsCapability as nt, DeclaredDevices as o, sceneMonitorCapability as ot, NC_DEFAULT_SNOOZE_MINUTES as p, DeviceType as pt, deriveRecordingMode as q, DEFAULT_TIMELAPSE_PREVIEW_TEXT as r, plateGalleryCapability as rt, EVENT_KIND_BY_CAP as s, subKindsOf as st, BaseDevice as t, notificationRulesCapability as tt, MACRO_LABELS as u, zoneAnalyticsCapability as ut, NcRulePatchSchema as v, sleep as vt, NcSnoozeSuppressedSchema as w, number as wt, NcScheduleSchema as x, boolean as xt, NcRuleSchema as y, _enum as yt, addonWidgetsSourceCapability as z };
38498
+ export { isDetectionMacroClass as $, SCENE_CONFIRM_DEFAULT_TIMEOUT_MS as A, unknown as At, alarmPanelCapability as B, NcSnoozeSchema as C, discriminatedUnion as Ct, OpsLogEntrySchema as D, partialRecord as Dt, NcTaxonomySchema as E, object as Et, TimelapseRuleInputSchema as F, cosineSimilarity as G, audioMetricsCapability as H, TimelapseRulePatchSchema as I, deriveRecordingMode as J, customAction as K, TimelapseRuleSchema as L, SCENE_DIVERGED as M, SceneMonitorSchema as N, RECORDING_EXPORT_MAX_READ_BYTES as O, record as Ot, TIMELAPSE_DENSE_FLOOR_SEC as P, hfModelUrl as Q, TrackSourceSchema as R, NcSnoozeInputSchema as S, boolean as St, NcSystemEventKindSchema as T, number as Tt, audioModeOf as U, assertTimelapseCadences as V, buildEventKindDescriptor as W, encodeVectorBase64 as X, embeddingEncoderCapability as Y, faceGalleryCapability as Z, NcRuleInputSchema as _, isDeviceScopedCap as _t, DETECTION_PIPELINE_CAP_NAME as a, readDeviceStateFrom as at, NcRuleTargetSchema as b, _enum as bt, EVENT_PAD_MS as c, subKindsOf as ct, NC_ALARM_SYSTEM_EVENT_KINDS as d, zoneAnalyticsCapability as dt, isScheduleActive as et, NC_CONDITION_CATALOG as f, errMsg as ft, NcConditionDescriptorSchema as g, hydrateSchema as gt, NC_TAXONOMY as h, createEvent as ht, DETECTION_MACRO_CLASSES as i, plateGalleryCapability as it, SCENE_DEFAULT_ANCHOR_THRESHOLD as j, EventCategory as jt, RetrainStatusSchema as k, string as kt, LabelAttributionSchema as l, vectorDimFromBase64 as lt, NC_SNOOZE_MAX_MINUTES as m, DeviceType as mt, DEFAULT_EVENT_COLOR as n, notificationRulesCapability as nt, DeclaredDevices as o, readTimelapseGeneratedAt as ot, NC_DEFAULT_SNOOZE_MINUTES as p, BaseAddon as pt, defineCustomActions as q, DEFAULT_TIMELAPSE_PREVIEW_TEXT as r, pipelineAnalyticsCapability as rt, EVENT_KIND_BY_CAP as s, sceneMonitorCapability as st, BaseDevice as t, kebabToCamel as tt, MACRO_LABELS as u, videoclipsCapability as ut, NcRulePatchSchema as v, nodePin as vt, NcSnoozeSuppressedSchema as w, literal as wt, NcScheduleSchema as x, array as xt, NcRuleSchema as y, sleep as yt, addonWidgetsSourceCapability as z };
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_dist = require("../dist-DseTbokb.js");
5
+ const require_dist = require("../dist-Bs6oboqZ.js");
6
6
  let node_fs = require("node:fs");
7
7
  let node_fs$1 = require_dist.__toESM(node_fs, 1);
8
8
  node_fs = require_dist.__toESM(node_fs);
@@ -1,4 +1,4 @@
1
- import { J as embeddingEncoderCapability, Z as hfModelUrl, ft as BaseAddon } from "../dist-BA7ThdEJ.mjs";
1
+ import { Q as hfModelUrl, Y as embeddingEncoderCapability, pt as BaseAddon } from "../dist-CxeFHSSV.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import * as fs from "node:fs";
4
4
  import "node:fs";
@@ -1,6 +1,6 @@
1
1
  import { a as e, i as t, n, o as r, r as i, t as a } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react__loadShare__.js-C0AuF9av.mjs";
2
2
  import { t as o } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_tanstack_mf_1_react_mf_2_query__loadShare__.js-B3Wx5J80.mjs";
3
- import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BgpX7t50.mjs";
3
+ import { a as s, i as c, n as l, o as u, r as d, s as f, t as p } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DFckpC3K.mjs";
4
4
  import { n as m, r as h, t as g } from "./_virtual_mf___mfe_internal__addon_pipeline_analytics_widgets__loadShare__react_mf_1_jsx_mf_2_runtime__loadShare__.js-Bm-iyjmq.mjs";
5
5
  //#region ../../node_modules/lucide-react/dist/esm/shared/src/utils.js
6
6
  var _ = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), v = (e) => e.replace(/^([A-Z])|[\s-_]+(\w)/g, (e, t, n) => n ? n.toUpperCase() : t.toLowerCase()), y = (e) => {
@@ -18,7 +18,7 @@ var e = {
18
18
  },
19
19
  "@camstack/types": {
20
20
  name: "@camstack/types",
21
- version: "1.2.72",
21
+ version: "1.2.74",
22
22
  scope: ["default"],
23
23
  loaded: !1,
24
24
  from: "addon_pipeline_analytics_widgets",
@@ -33,7 +33,7 @@ var e = {
33
33
  },
34
34
  "@camstack/ui-library": {
35
35
  name: "@camstack/ui-library",
36
- version: "1.2.50",
36
+ version: "1.2.51",
37
37
  scope: ["default"],
38
38
  loaded: !1,
39
39
  from: "addon_pipeline_analytics_widgets",