@camstack/types 1.2.72 → 1.2.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_event_category = require("./event-category-D3gG7oil.js");
3
- const require_sleep = require("./sleep-CKWYtvXl.js");
3
+ const require_sleep = require("./sleep-B-mRsuDp.js");
4
4
  const require_canonical_hash = require("./canonical-hash-DNV8S5ET.js");
5
5
  const require_enums = require("./enums.js");
6
6
  const require_err_msg = require("./err-msg-COpsHMw2.js");
@@ -9438,6 +9438,35 @@ var LlmImageSchema = zod.z.object({
9438
9438
  bytes: zod.z.instanceof(Uint8Array),
9439
9439
  mimeType: zod.z.string()
9440
9440
  });
9441
+ /**
9442
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
9443
+ * the flag is what a consumer table flips, the count is what the operator tunes.
9444
+ * A retry doubles the wall time of a call, so the two gates that run inside a
9445
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
9446
+ */
9447
+ var LlmRetryPolicySchema = zod.z.object({
9448
+ enabled: zod.z.boolean().default(false),
9449
+ /** Total attempts INCLUDING the first. 1 = no retry. */
9450
+ maxAttempts: zod.z.number().int().min(1).max(5).default(1)
9451
+ });
9452
+ /**
9453
+ * The four bounds, as ONE vocabulary.
9454
+ *
9455
+ * These were three magic constants inside the test chat (`connect` 10 s,
9456
+ * `first token` 120 s, `idle` 60 s) plus a separate `timeoutMs` on the profile.
9457
+ * They are the same four questions on every call path, so they are profile
9458
+ * fields with defaults — and each still names a DIFFERENT failure:
9459
+ * - `connect` — the endpoint never accepted the request (port closed).
9460
+ * - `firstToken` — accepted, nothing produced yet (cold model loading).
9461
+ * - `idle` — tokens started then stopped (a stall, model proven up).
9462
+ * - `total` — the whole generation, the only bound a unary call has.
9463
+ */
9464
+ var LlmTimeoutDefaults = {
9465
+ totalMs: 6e4,
9466
+ connectMs: 1e4,
9467
+ firstTokenMs: 12e4,
9468
+ idleMs: 6e4
9469
+ };
9441
9470
  var LlmGenerateBaseInputSchema = zod.z.object({
9442
9471
  /** Collection routing (the notification-output posture). */
9443
9472
  addonId: zod.z.string().optional(),
@@ -9452,7 +9481,28 @@ var LlmGenerateBaseInputSchema = zod.z.object({
9452
9481
  jsonSchema: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
9453
9482
  /** Per-call override of the profile default. */
9454
9483
  maxTokens: zod.z.number().int().positive().optional(),
9455
- temperature: zod.z.number().optional()
9484
+ temperature: zod.z.number().optional(),
9485
+ /** Per-call override of the profile default (nucleus sampling). */
9486
+ topP: zod.z.number().min(0).max(1).optional(),
9487
+ /** Per-call override of the profile default (top-k sampling). */
9488
+ topK: zod.z.number().int().positive().optional(),
9489
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
9490
+ timeoutMs: zod.z.number().int().positive().optional(),
9491
+ /** Per-call override; beats both the consumer table and the profile. */
9492
+ retry: LlmRetryPolicySchema.optional(),
9493
+ /**
9494
+ * Caller-minted id that makes this generation CANCELLABLE.
9495
+ *
9496
+ * Without it a caller that stops waiting cannot stop the work: the gates race
9497
+ * the call against 8 s and free their own slot when the timer wins, while the
9498
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
9499
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
9500
+ * not generations, and the real load is unbounded.
9501
+ *
9502
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
9503
+ * `llm.cancel({ requestId })` tears the socket down.
9504
+ */
9505
+ requestId: zod.z.string().optional()
9456
9506
  });
9457
9507
  //#endregion
9458
9508
  //#region src/capabilities/llm-runtime.cap.ts
@@ -9493,8 +9543,49 @@ var ManagedRuntimeConfigSchema = zod.z.object({
9493
9543
  gpuLayers: zod.z.number().int().default(0),
9494
9544
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
9495
9545
  threads: zod.z.number().int().optional(),
9496
- /** Concurrent slots. */
9546
+ /** Concurrent slots (`--parallel`). */
9497
9547
  parallel: zod.z.number().int().default(1),
9548
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
9549
+ batchSize: zod.z.number().int().positive().optional(),
9550
+ /** Physical batch / micro-batch (`-ub`). */
9551
+ ubatchSize: zod.z.number().int().positive().optional(),
9552
+ /**
9553
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
9554
+ * is a no-op elsewhere, so it is offered rather than assumed.
9555
+ */
9556
+ flashAttention: zod.z.boolean().default(false),
9557
+ /**
9558
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
9559
+ * inference. Costs the full model size in resident memory — which is exactly
9560
+ * what the RAM budget is counting.
9561
+ */
9562
+ mlock: zod.z.boolean().default(false),
9563
+ /**
9564
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
9565
+ * start, but avoids the page-fault stalls a network or spinning-disk model
9566
+ * store produces on every first token.
9567
+ */
9568
+ noMmap: zod.z.boolean().default(false),
9569
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
9570
+ * cheapest way to fit a longer context in the same RAM. */
9571
+ cacheTypeK: zod.z.enum([
9572
+ "f32",
9573
+ "f16",
9574
+ "q8_0",
9575
+ "q5_1",
9576
+ "q5_0",
9577
+ "q4_1",
9578
+ "q4_0"
9579
+ ]).optional(),
9580
+ cacheTypeV: zod.z.enum([
9581
+ "f32",
9582
+ "f16",
9583
+ "q8_0",
9584
+ "q5_1",
9585
+ "q5_0",
9586
+ "q4_1",
9587
+ "q4_0"
9588
+ ]).optional(),
9498
9589
  /** Else lazy: first generate boots it. */
9499
9590
  autoStart: zod.z.boolean().default(false),
9500
9591
  /** 0 = never; frees RAM after quiet periods. */
@@ -9600,10 +9691,44 @@ var LlmProfileSchema = zod.z.object({
9600
9691
  baseUrl: zod.z.string().optional(),
9601
9692
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
9602
9693
  apiKey: zod.z.string().optional(),
9694
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
9695
+ * degraded to text — that shipped once and produced a confident answer to a
9696
+ * question about a picture nobody sent. */
9603
9697
  supportsVision: zod.z.boolean(),
9604
9698
  temperature: zod.z.number().min(0).max(2).optional(),
9699
+ /** Nucleus sampling. Every wire we speak has it. */
9700
+ topP: zod.z.number().min(0).max(1).optional(),
9701
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
9702
+ * wire does, and the client drops it there (measured: the request body gets
9703
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
9704
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
9705
+ topK: zod.z.number().int().positive().optional(),
9605
9706
  maxTokens: zod.z.number().int().positive().optional(),
9707
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
9708
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
9709
+ * the model with, so it is the one field that changes a PROCESS. */
9710
+ contextLength: zod.z.number().int().positive().optional(),
9711
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
9712
+ * two system prompts fighting is worse than either alone). */
9713
+ systemPrompt: zod.z.string().optional(),
9714
+ /** Total generation bound — the only one a unary call has. */
9606
9715
  timeoutMs: zod.z.number().int().positive().default(6e4),
9716
+ /** Wait for response headers only. */
9717
+ connectTimeoutMs: zod.z.number().int().positive().default(1e4),
9718
+ /** Accepted, but no output yet — a cold GPU load lives here. */
9719
+ firstTokenTimeoutMs: zod.z.number().int().positive().default(12e4),
9720
+ /** Output started then stopped. */
9721
+ idleTimeoutMs: zod.z.number().int().positive().default(6e4),
9722
+ /** Profile-level default. The per-consumer table and a per-call override
9723
+ * both beat it — see `resolveRetryPolicy`. */
9724
+ retry: LlmRetryPolicySchema.default({
9725
+ enabled: false,
9726
+ maxAttempts: 1
9727
+ }),
9728
+ /** Whether this profile may use tools. The tool-call plumbing rides the
9729
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
9730
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
9731
+ toolsEnabled: zod.z.boolean().default(false),
9607
9732
  extraHeaders: zod.z.record(zod.z.string(), zod.z.string()).optional(),
9608
9733
  /** kind === 'managed-local' only (spec §4). */
9609
9734
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -9676,6 +9801,17 @@ var llmCapability = {
9676
9801
  methods: {
9677
9802
  generate: require_sleep.method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
9678
9803
  generateVision: require_sleep.method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
9804
+ /**
9805
+ * Stop a generation started with a `requestId`.
9806
+ *
9807
+ * Idempotent and always successful: cancelling an id that already finished,
9808
+ * never existed, or was cancelled a moment ago is a no-op. A caller that has
9809
+ * given up must never have to handle an error from giving up.
9810
+ */
9811
+ cancel: require_sleep.method(zod.z.object({
9812
+ addonId: zod.z.string().optional(),
9813
+ requestId: zod.z.string()
9814
+ }), zod.z.void(), { kind: "mutation" }),
9679
9815
  listProfileKinds: require_sleep.method(zod.z.object({}), zod.z.array(LlmProfileKindDescriptorSchema)),
9680
9816
  listProfiles: require_sleep.method(zod.z.object({}), zod.z.array(LlmProfileSchema)),
9681
9817
  upsertProfile: require_sleep.method(zod.z.object({ profile: LlmProfileSchema }), LlmProfileSchema, {
@@ -12125,28 +12261,36 @@ var NC_AUDIO_DBFS_FLOOR = -96;
12125
12261
  /**
12126
12262
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
12127
12263
  *
12128
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
12129
- * reference notifier uses, so an operator moving between them re-uses what
12130
- * they already know): a rule matches when, over a sampling window of
12131
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
12132
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
12133
- *
12134
- * - `dbThreshold` its level is at or above this many dBFS (see
12135
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
12136
- * - `labels` the classifier put at least one of these labels on it.
12137
- *
12138
- * Both are OPTIONAL and independent, which is the point of the shape: a
12139
- * loudness rule ("something loud at 3am") needs no model to be right, and a
12140
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
12141
- * is given** a window in which every sample is trivially a hit would fire on
12142
- * silence, so the engine refuses such a condition rather than notifying on
12143
- * nothing (the schema cannot express "at least one of" without becoming a
12144
- * ZodEffects the cap path would have to special-case).
12145
- *
12146
- * `hitPercent` is over the samples the window actually HOLDS, and the window
12147
- * must be FULL before it can match a window that has been open for two
12148
- * seconds of its ten is 100% of nothing, and firing on it would make
12149
- * `samplingSeconds` decorative.
12264
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
12265
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
12266
+ * there is no second switch that can disagree with the first and every rule
12267
+ * authored before the decision migrates for free (`audioModeOf`):
12268
+ *
12269
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
12270
+ * classifier labels with one of them. No window, no percentage:
12271
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
12272
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
12273
+ * the analyzer's (`classificationMinScore`, per device) — a label only
12274
+ * reaches this condition if the classifier was already confident enough.
12275
+ * - **LEVEL mode `dbThreshold` present, no labels.** The sampling window IS
12276
+ * the condition: at least `hitPercent`% of the samples over
12277
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
12278
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
12279
+ * must be FULL before it can match a window open for two of its ten
12280
+ * seconds is 100% of nothing.
12281
+ *
12282
+ * **Why label mode has no window.** It had one, and it never fired: the
12283
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
12284
+ * of them per episode, even through continuous crying. The measured maximum
12285
+ * `hitPercent` over the whole live history was 40 — under the shipped default
12286
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
12287
+ * the wrong question to ask of a sparse classifier.
12288
+ *
12289
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
12290
+ * and the rule would fire on silence. The schema cannot express "exactly one
12291
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
12292
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
12293
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
12150
12294
  *
12151
12295
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
12152
12296
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -12154,13 +12298,13 @@ var NC_AUDIO_DBFS_FLOOR = -96;
12154
12298
  * an operator who typed `dog` mean the same thing.
12155
12299
  */
12156
12300
  var NcAudioConditionSchema = zod.z.object({
12157
- /** Audio macro labels; absent = any sound (level-only rule). */
12301
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
12158
12302
  labels: zod.z.array(zod.z.string().min(1)).min(1).optional(),
12159
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
12303
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
12160
12304
  dbThreshold: zod.z.number().min(-96).max(0).optional(),
12161
- /** Percentage of the window's samples that must be hits (1–100). */
12305
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
12162
12306
  hitPercent: zod.z.number().int().min(1).max(100).default(60),
12163
- /** Length of the sampling window in seconds. */
12307
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
12164
12308
  samplingSeconds: zod.z.number().int().min(1).max(300).default(10)
12165
12309
  });
12166
12310
  /**
@@ -12313,9 +12457,66 @@ var NcDeviceStateConditionSchema = zod.z.object({
12313
12457
  /** Any of these matches. */
12314
12458
  states: zod.z.array(zod.z.string().min(1)).min(1)
12315
12459
  });
12460
+ /**
12461
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
12462
+ *
12463
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
12464
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
12465
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
12466
+ * already has a trigger ("tell me about a person at the front door, but only
12467
+ * while the bin is still out"). That is why it composes with every delivery
12468
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
12469
+ * kind exist for it — see D159.
12470
+ *
12471
+ * ── Identity ───────────────────────────────────────────────────────────────
12472
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
12473
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
12474
+ * carried as a HINT for the editor and for the log line, never as part of the
12475
+ * lookup key: a rule whose hint drifted must still gate correctly.
12476
+ *
12477
+ * ── Which boolean ──────────────────────────────────────────────────────────
12478
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
12479
+ * already declares which boolean drives notification rules, and a second knob
12480
+ * that could disagree with it is exactly the D62 failure. Set it only to
12481
+ * override one rule against the scene's own default.
12482
+ *
12483
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
12484
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
12485
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
12486
+ * evidence, in either direction.
12487
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
12488
+ * The latch is a durable fact about the past ("it has diverged since I armed
12489
+ * it"), so a camera that has gone dark does not clear it — that is the whole
12490
+ * reason the operator asked for a latch.
12491
+ *
12492
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
12493
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
12494
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
12495
+ * said out loud in the log rather than dropped in silence.
12496
+ */
12497
+ var NcSceneConditionSchema = zod.z.object({
12498
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
12499
+ sceneId: zod.z.string().min(1),
12500
+ /** The camera the scene lives on. A hint for the editor and the log line. */
12501
+ deviceId: zod.z.number().int().optional(),
12502
+ /** The state the scene must be in for the rule to fire. */
12503
+ requiredState: zod.z.enum(["matched", "diverged"]),
12504
+ /**
12505
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
12506
+ * scene's own `emit` field, which is the only place that decision belongs.
12507
+ */
12508
+ latched: zod.z.boolean().optional()
12509
+ });
12316
12510
  var NcConditionsSchema = zod.z.object({
12317
12511
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
12318
12512
  deviceState: NcDeviceStateConditionSchema.optional(),
12513
+ /**
12514
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
12515
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
12516
+ * unlike `occupancy`/`audio` it discriminates nothing. See
12517
+ * {@link NcSceneCondition} and D159.
12518
+ */
12519
+ scene: NcSceneConditionSchema.optional(),
12319
12520
  /** Device scope — absent = all devices. */
12320
12521
  devices: zod.z.array(zod.z.number()).optional(),
12321
12522
  /** Detector class names (any overlap with the record's class set). */
@@ -12954,6 +13155,7 @@ var NcConditionDescriptorSchema = zod.z.object({
12954
13155
  "occupancy",
12955
13156
  "audio",
12956
13157
  "deviceState",
13158
+ "scene",
12957
13159
  "systemEvent"
12958
13160
  ]),
12959
13161
  operator: zod.z.enum([
@@ -13308,6 +13510,21 @@ var NC_CONDITION_CATALOG = [
13308
13510
  phase: "P2",
13309
13511
  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."
13310
13512
  },
13513
+ {
13514
+ id: "scene",
13515
+ group: "scope",
13516
+ label: "Scene state",
13517
+ valueType: "scene",
13518
+ operator: "in",
13519
+ appliesTo: [
13520
+ "immediate",
13521
+ "track-end",
13522
+ "device-event",
13523
+ "package-event"
13524
+ ],
13525
+ phase: "P3",
13526
+ 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."
13527
+ },
13311
13528
  {
13312
13529
  id: "sensorKinds",
13313
13530
  group: "device",
@@ -27373,10 +27590,22 @@ var recordingExportCapability = {
27373
27590
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
27374
27591
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
27375
27592
  *
27376
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
27377
- * derives the device-detail contribution; the provider carries NO hand-written
27378
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
27379
- * every hysteresis flip / availability change; consumers never poll.
27593
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
27594
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
27595
+ * for the thing: a scene is a standing question about the property ("is the bin
27596
+ * still out"), and the operator's question is "which of my scenes have tripped",
27597
+ * across every camera at once — not "what does camera 617 think". Buried one
27598
+ * camera deep it also could not be found. The surface is now a top-level admin
27599
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
27600
+ * picks the camera inside the create flow, the same shape Events and Faces have.
27601
+ *
27602
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
27603
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
27604
+ * directions, so a registration nobody declares fails exactly as loudly as a
27605
+ * declaration nobody registers. The editor is imported directly by the page.
27606
+ *
27607
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
27608
+ * availability change; consumers never poll.
27380
27609
  */
27381
27610
  /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
27382
27611
  * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
@@ -27552,12 +27781,6 @@ var sceneMonitorCapability = {
27552
27781
  kind: "wrapper",
27553
27782
  defaultActive: true,
27554
27783
  deviceTypes: [require_sleep.DeviceType.Camera],
27555
- deviceConfig: { ui: {
27556
- kind: "widget",
27557
- widgetId: "host/scene-monitor-editor",
27558
- tab: "scenes",
27559
- label: "Scenes"
27560
- } },
27561
27784
  methods: {
27562
27785
  listScenes: require_sleep.method(zod.z.object({ deviceId: zod.z.number() }), SceneMonitorStatusSchema),
27563
27786
  createScene: require_sleep.method(zod.z.object({
@@ -28436,6 +28659,61 @@ var NetworkAddressSchema = zod.z.object({
28436
28659
  family: zod.z.string(),
28437
28660
  internal: zod.z.boolean()
28438
28661
  });
28662
+ /**
28663
+ * Provenance of the site coordinates, and the whole reason this is not just two
28664
+ * numbers.
28665
+ *
28666
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
28667
+ * nothing overwrites it.
28668
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
28669
+ * default that is right to a few kilometres beats the coarse UTC clock split
28670
+ * the sun-times consumers otherwise fall back to.
28671
+ *
28672
+ * The UI shows which one it is. An operator who cannot tell a guess from their
28673
+ * own input will eventually trust the guess.
28674
+ */
28675
+ var SiteLocationSourceSchema = zod.z.enum(["operator-set", "derived-from-ip"]);
28676
+ /**
28677
+ * Where the installation physically is — a fact of the SITE, not of any addon.
28678
+ *
28679
+ * It used to live in `pipeline-analytics`' global settings, which made a
28680
+ * property of the building a property of one analytics addon. Anything that
28681
+ * needs sun-times (scene condition variants today; anything solar tomorrow)
28682
+ * reads it from here.
28683
+ */
28684
+ var SiteLocationSchema = zod.z.object({
28685
+ /** WGS84 decimal degrees. */
28686
+ latitude: zod.z.number().min(-90).max(90),
28687
+ longitude: zod.z.number().min(-180).max(180),
28688
+ source: SiteLocationSourceSchema,
28689
+ /** Epoch ms the value was last written. */
28690
+ updatedAt: zod.z.number(),
28691
+ /**
28692
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
28693
+ * only — never parsed, never matched on. Absent for an operator-typed value.
28694
+ */
28695
+ label: zod.z.string().optional()
28696
+ });
28697
+ /**
28698
+ * The read shape: the location plus the honest state of the one-shot derivation.
28699
+ *
28700
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
28701
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
28702
+ * failed; the hub will NOT try again on its own — the fallback is declared
28703
+ * (consumers degrade to their own last resort) and the operator either types the
28704
+ * coordinates or presses detect.
28705
+ */
28706
+ var SiteLocationStatusSchema = zod.z.object({
28707
+ location: SiteLocationSchema.nullable(),
28708
+ derivationAttemptedAt: zod.z.number().nullable(),
28709
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
28710
+ derivationError: zod.z.string().nullable()
28711
+ });
28712
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
28713
+ var SetSiteLocationInputSchema = zod.z.object({
28714
+ latitude: zod.z.number().min(-90).max(90),
28715
+ longitude: zod.z.number().min(-180).max(180)
28716
+ }).nullable();
28439
28717
  var systemCapability = {
28440
28718
  name: "system",
28441
28719
  scope: "system",
@@ -28453,6 +28731,32 @@ var systemCapability = {
28453
28731
  forceRetentionCleanup: require_sleep.method(zod.z.void(), zod.z.void(), {
28454
28732
  kind: "mutation",
28455
28733
  auth: "admin"
28734
+ }),
28735
+ /**
28736
+ * The site coordinates, deriving a default from the hub's public IP on the
28737
+ * FIRST read that finds nothing stored.
28738
+ *
28739
+ * The derivation is one-shot and bounded: one outbound request, a few
28740
+ * seconds, its outcome persisted either way. A hub with no internet pays it
28741
+ * once and never again, and neither boot nor any consumer is blocked on it —
28742
+ * the caller gets `location: null` and degrades exactly as it did before this
28743
+ * method existed.
28744
+ */
28745
+ getSiteLocation: require_sleep.method(zod.z.void(), SiteLocationStatusSchema),
28746
+ /** Operator input. Always lands as `source: 'operator-set'`. */
28747
+ setSiteLocation: require_sleep.method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
28748
+ kind: "mutation",
28749
+ auth: "admin"
28750
+ }),
28751
+ /**
28752
+ * Re-run the geo-IP derivation now. The ONLY way a spent or failed
28753
+ * derivation is retried — there is no timer, and no read path retries.
28754
+ * Overwrites an existing `derived-from-ip` value; refuses to clobber an
28755
+ * `operator-set` one.
28756
+ */
28757
+ detectSiteLocation: require_sleep.method(zod.z.void(), SiteLocationStatusSchema, {
28758
+ kind: "mutation",
28759
+ auth: "admin"
28456
28760
  })
28457
28761
  },
28458
28762
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
@@ -35366,6 +35670,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
35366
35670
  addonId: null,
35367
35671
  access: "create"
35368
35672
  },
35673
+ "llm.cancel": {
35674
+ capName: "llm",
35675
+ capScope: "system",
35676
+ addonId: null,
35677
+ access: "create"
35678
+ },
35369
35679
  "llm.deleteModel": {
35370
35680
  capName: "llm",
35371
35681
  capScope: "system",
@@ -38300,6 +38610,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
38300
38610
  addonId: null,
38301
38611
  access: "create"
38302
38612
  },
38613
+ "system.detectSiteLocation": {
38614
+ capName: "system",
38615
+ capScope: "system",
38616
+ addonId: null,
38617
+ access: "create"
38618
+ },
38303
38619
  "system.featureFlags": {
38304
38620
  capName: "system",
38305
38621
  capScope: "system",
@@ -38318,6 +38634,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
38318
38634
  addonId: null,
38319
38635
  access: "view"
38320
38636
  },
38637
+ "system.getSiteLocation": {
38638
+ capName: "system",
38639
+ capScope: "system",
38640
+ addonId: null,
38641
+ access: "view"
38642
+ },
38321
38643
  "system.health": {
38322
38644
  capName: "system",
38323
38645
  capScope: "system",
@@ -38342,6 +38664,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
38342
38664
  addonId: null,
38343
38665
  access: "create"
38344
38666
  },
38667
+ "system.setSiteLocation": {
38668
+ capName: "system",
38669
+ capScope: "system",
38670
+ addonId: null,
38671
+ access: "create"
38672
+ },
38345
38673
  "terminalSession.adoptLegacyMonitor": {
38346
38674
  capName: "terminal-session",
38347
38675
  capScope: "system",
@@ -41662,6 +41990,7 @@ function createSystemProxy(api) {
41662
41990
  llm: {
41663
41991
  generate: (input) => dispatch("llm", "generate", "mutation", input),
41664
41992
  generateVision: (input) => dispatch("llm", "generateVision", "mutation", input),
41993
+ cancel: (input) => dispatch("llm", "cancel", "mutation", input),
41665
41994
  listProfileKinds: (input) => dispatch("llm", "listProfileKinds", "query", input),
41666
41995
  listProfiles: (input) => dispatch("llm", "listProfiles", "query", input),
41667
41996
  upsertProfile: (input) => dispatch("llm", "upsertProfile", "mutation", input),
@@ -41958,7 +42287,10 @@ function createSystemProxy(api) {
41958
42287
  networkAddresses: (input) => dispatch("system", "networkAddresses", "query", input),
41959
42288
  getRetentionConfig: (input) => dispatch("system", "getRetentionConfig", "query", input),
41960
42289
  setRetentionConfig: (input) => dispatch("system", "setRetentionConfig", "mutation", input),
41961
- forceRetentionCleanup: (input) => dispatch("system", "forceRetentionCleanup", "mutation", input)
42290
+ forceRetentionCleanup: (input) => dispatch("system", "forceRetentionCleanup", "mutation", input),
42291
+ getSiteLocation: (input) => dispatch("system", "getSiteLocation", "query", input),
42292
+ setSiteLocation: (input) => dispatch("system", "setSiteLocation", "mutation", input),
42293
+ detectSiteLocation: (input) => dispatch("system", "detectSiteLocation", "mutation", input)
41962
42294
  },
41963
42295
  terminalSession: {
41964
42296
  listProfiles: (input) => dispatch("terminalSession", "listProfiles", "query", input),
@@ -42058,8 +42390,16 @@ var NC_AUDIO_DB_STEP = 3;
42058
42390
  * an absent `dbThreshold` means "any level" and stays absent until asked for,
42059
42391
  * and a stepper seeded at the -96 floor would need thirty taps to reach a
42060
42392
  * threshold that can actually fire.
42393
+ *
42394
+ * **It was -30 and -30 is a trap.** dBFS is negative-going, so a HIGHER number
42395
+ * is a NARROWER filter, and the sounds this hub is actually asked about are not
42396
+ * loud: measured on the live installation, real crying sits between -55 and
42397
+ * -26 dBFS. A floor at -30 therefore rejects most of the very sound the
42398
+ * operator turned the filter on for, and the rule looks broken rather than
42399
+ * mis-seeded. The offered floor is the QUIET end of that measured band; the
42400
+ * editor says which way the number runs rather than leaving it to be inferred.
42061
42401
  */
42062
- var NC_AUDIO_DB_OFFERED = -30;
42402
+ var NC_AUDIO_DB_OFFERED = -55;
42063
42403
  var NC_AUDIO_HIT_PERCENT_MIN = 1;
42064
42404
  var NC_AUDIO_HIT_PERCENT_MAX = 100;
42065
42405
  var NC_AUDIO_SAMPLING_MIN_SEC = 1;
@@ -42074,6 +42414,26 @@ function audioOrDefaults(value) {
42074
42414
  return value ?? NC_AUDIO_DEFAULTS;
42075
42415
  }
42076
42416
  /**
42417
+ * Which mode this condition is in — the ONE place that question is answered.
42418
+ *
42419
+ * The mode is NOT a stored field, deliberately. It is which filter the rule
42420
+ * carries, so every rule authored before the modes existed migrates for free
42421
+ * and there is no second switch that can disagree with the first (the failure
42422
+ * this repo has shipped twice). `null` = neither filter, which the engine
42423
+ * refuses (see {@link audioIsFailClosed}).
42424
+ *
42425
+ * A LEGACY rule carrying BOTH resolves to `label`: it is the mode that fires,
42426
+ * and the alternative is silently keeping a window the operator can no longer
42427
+ * see in the editor. Nothing new can reach this branch — {@link patchAudio}
42428
+ * clears the other filter on every write.
42429
+ */
42430
+ function audioModeOf(value) {
42431
+ if (value === void 0) return null;
42432
+ if (value.labels !== void 0 && value.labels.length > 0) return "label";
42433
+ if (value.dbThreshold !== void 0) return "level";
42434
+ return null;
42435
+ }
42436
+ /**
42077
42437
  * True when the condition names NEITHER a level floor NOR any label. The engine
42078
42438
  * refuses such a rule (every sample is a hit, so the window fires on silence) —
42079
42439
  * an editor warns instead of letting the operator ship a rule that never
@@ -42098,13 +42458,29 @@ function has(patch, key) {
42098
42458
  * The result is always in-bounds for `NcAudioConditionSchema`, and a cleared
42099
42459
  * filter is OMITTED rather than written as an explicit `undefined` (which
42100
42460
  * survives a merge patch as a deliberate erase and reads as authored).
42461
+ *
42462
+ * **The two modes are exclusive here, structurally.** Writing labels drops any
42463
+ * stored `dbThreshold`; writing a `dbThreshold` drops the labels. The schema
42464
+ * cannot say "exactly one of" without becoming a ZodEffects the cap path would
42465
+ * have to special-case, so the exclusivity lives in the ONE function every
42466
+ * editor writes through — which also means a legacy both-filters rule is
42467
+ * normalized the first time it is touched, onto the mode {@link audioModeOf}
42468
+ * already reads it as.
42469
+ *
42470
+ * Switching to `level` with nothing stored seeds {@link NC_AUDIO_DB_OFFERED}:
42471
+ * a level rule with no floor is fail-closed, and handing the operator a mode
42472
+ * that cannot fire is the trap this whole change exists to remove.
42101
42473
  */
42102
42474
  function patchAudio(current, patch) {
42103
42475
  const base = audioOrDefaults(current);
42104
- const labels = has(patch, "labels") ? patch.labels : base.labels;
42105
- const dbThreshold = has(patch, "dbThreshold") ? patch.dbThreshold : base.dbThreshold;
42476
+ const wantedLabels = has(patch, "labels") ? patch.labels : base.labels;
42477
+ const wantedDb = has(patch, "dbThreshold") ? patch.dbThreshold : base.dbThreshold;
42478
+ const hasLabels = wantedLabels !== void 0 && wantedLabels.length > 0;
42479
+ const mode = patch.mode ?? (has(patch, "labels") && hasLabels ? "label" : has(patch, "dbThreshold") && wantedDb !== void 0 ? "level" : hasLabels ? "label" : wantedDb !== void 0 ? "level" : null);
42480
+ const labels = mode === "label" && hasLabels ? wantedLabels : void 0;
42481
+ const dbThreshold = mode !== "level" ? void 0 : wantedDb ?? (patch.mode === "level" ? -55 : void 0);
42106
42482
  return {
42107
- ...labels !== void 0 && labels.length > 0 ? { labels: [...labels] } : {},
42483
+ ...labels !== void 0 ? { labels: [...labels] } : {},
42108
42484
  ...dbThreshold !== void 0 ? { dbThreshold: clampInt(dbThreshold, NC_AUDIO_DB_MIN, 0) } : {},
42109
42485
  hitPercent: clampInt(patch.hitPercent ?? base.hitPercent, 1, 100),
42110
42486
  samplingSeconds: clampInt(patch.samplingSeconds ?? base.samplingSeconds, 1, 300)
@@ -45294,10 +45670,12 @@ exports.LlmNodeModelSchema = LlmNodeModelSchema;
45294
45670
  exports.LlmProfileKindDescriptorSchema = LlmProfileKindDescriptorSchema;
45295
45671
  exports.LlmProfileKindSchema = LlmProfileKindSchema;
45296
45672
  exports.LlmProfileSchema = LlmProfileSchema;
45673
+ exports.LlmRetryPolicySchema = LlmRetryPolicySchema;
45297
45674
  exports.LlmRuntimeCompleteInputSchema = LlmRuntimeCompleteInputSchema;
45298
45675
  exports.LlmRuntimeDiskUsageSchema = LlmRuntimeDiskUsageSchema;
45299
45676
  exports.LlmRuntimeNodeSchema = LlmRuntimeNodeSchema;
45300
45677
  exports.LlmRuntimeStatusSchema = LlmRuntimeStatusSchema;
45678
+ exports.LlmTimeoutDefaults = LlmTimeoutDefaults;
45301
45679
  exports.LlmUsageRollupSchema = LlmUsageRollupSchema;
45302
45680
  exports.LlmUsageSchema = LlmUsageSchema;
45303
45681
  exports.LocateSegmentResultSchema = LocateSegmentResultSchema;
@@ -45441,6 +45819,7 @@ exports.NcRuleNotificationButtonSchema = NcRuleNotificationButtonSchema;
45441
45819
  exports.NcRulePatchSchema = NcRulePatchSchema;
45442
45820
  exports.NcRuleSchema = NcRuleSchema;
45443
45821
  exports.NcRuleTargetSchema = NcRuleTargetSchema;
45822
+ exports.NcSceneConditionSchema = NcSceneConditionSchema;
45444
45823
  exports.NcScheduleSchema = NcScheduleSchema;
45445
45824
  exports.NcScheduleWindowSchema = NcScheduleWindowSchema;
45446
45825
  exports.NcSnoozeInputSchema = NcSnoozeInputSchema;
@@ -45652,11 +46031,15 @@ exports.ServerRollbackInfoSchema = ServerRollbackInfoSchema;
45652
46031
  exports.ServerUpdateActionResultSchema = ServerUpdateActionResultSchema;
45653
46032
  exports.ServerUpdateCheckResultSchema = ServerUpdateCheckResultSchema;
45654
46033
  exports.ServerUpdateStateSchema = ServerUpdateStateSchema;
46034
+ exports.SetSiteLocationInputSchema = SetSiteLocationInputSchema;
45655
46035
  exports.SettingsPatchSchema = SettingsPatchSchema;
45656
46036
  exports.SettingsRecordSchema = SettingsRecordSchema;
45657
46037
  exports.SettingsSchemaWithValuesSchema = SettingsSchemaWithValuesSchema;
45658
46038
  exports.SettingsUpdateResultSchema = SettingsUpdateResultSchema;
45659
46039
  exports.ShmRingStatsSchema = ShmRingStatsSchema;
46040
+ exports.SiteLocationSchema = SiteLocationSchema;
46041
+ exports.SiteLocationSourceSchema = SiteLocationSourceSchema;
46042
+ exports.SiteLocationStatusSchema = SiteLocationStatusSchema;
45660
46043
  exports.SmokeStatusSchema = SmokeStatusSchema;
45661
46044
  exports.SmtpStatusSchema = SmtpStatusSchema;
45662
46045
  exports.SnapshotImageSchema = SnapshotImageSchema;
@@ -45832,6 +46215,7 @@ exports.audioCodecCapability = audioCodecCapability;
45832
46215
  exports.audioIsFailClosed = audioIsFailClosed;
45833
46216
  exports.audioLabelChoices = audioLabelChoices;
45834
46217
  exports.audioMetricsCapability = audioMetricsCapability;
46218
+ exports.audioModeOf = audioModeOf;
45835
46219
  exports.audioOrDefaults = audioOrDefaults;
45836
46220
  exports.audioPlanFromEncodeProfile = require_canonical_hash.audioPlanFromEncodeProfile;
45837
46221
  exports.authProviderCapability = authProviderCapability;