@camstack/system 1.2.90 → 1.2.92

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 (59) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.mjs +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +25 -1
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +25 -1
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/core-blocks/blocks-integration.d.ts +34 -1
  14. package/dist/builtins/core-blocks/core-block-store.d.ts +11 -0
  15. package/dist/builtins/core-blocks/core-blocks.addon.js +80 -22
  16. package/dist/builtins/core-blocks/core-blocks.addon.mjs +80 -22
  17. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  18. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  19. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  20. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  21. package/dist/builtins/hub-forwarder/index.js +1 -1
  22. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  23. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  24. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  25. package/dist/builtins/local-auth/auth-schema.d.ts +42 -0
  26. package/dist/builtins/local-auth/local-auth.addon.js +43 -1
  27. package/dist/builtins/local-auth/local-auth.addon.mjs +43 -1
  28. package/dist/builtins/local-network/local-network.addon.js +1 -1
  29. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  30. package/dist/builtins/loki-logging/index.js +1 -1
  31. package/dist/builtins/loki-logging/index.mjs +1 -1
  32. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  33. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  34. package/dist/builtins/platform-probe/index.js +1 -1
  35. package/dist/builtins/platform-probe/index.mjs +1 -1
  36. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  37. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  38. package/dist/builtins/snapshot/index.js +1 -1
  39. package/dist/builtins/snapshot/index.mjs +1 -1
  40. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  41. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  42. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +16 -5
  43. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +16 -5
  44. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +19 -2
  45. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +19 -2
  46. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +12 -0
  47. package/dist/builtins/system-config/system-config.addon.js +1 -1
  48. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  49. package/dist/builtins/winston-logging/index.js +1 -1
  50. package/dist/builtins/winston-logging/index.mjs +1 -1
  51. package/dist/{dist-CYqL8oZq.js → dist-BH2tZ9ZN.js} +525 -47
  52. package/dist/{dist-DMhxx5Kr.mjs → dist-DXDvrBi0.mjs} +525 -47
  53. package/dist/index.js +78 -6
  54. package/dist/index.mjs +78 -6
  55. package/dist/kernel/addon-health-monitor.d.ts +10 -1
  56. package/dist/logging/log-manager.d.ts +28 -0
  57. package/dist/{manifest-python-deps-C0zkl1Ae.js → manifest-python-deps-BZO_eMn-.js} +1 -7
  58. package/dist/{manifest-python-deps-fWzdEasI.mjs → manifest-python-deps-D3hP9W8D.mjs} +2 -8
  59. package/package.json +1 -1
@@ -728,12 +728,6 @@ var WELL_KNOWN_TAB_MAP = Object.fromEntries([
728
728
  icon: "shapes",
729
729
  order: 38
730
730
  },
731
- {
732
- id: "scenes",
733
- label: "Scenes",
734
- icon: "scan-eye",
735
- order: 36
736
- },
737
731
  {
738
732
  id: "analytics",
739
733
  label: "Analytics",
@@ -10105,6 +10099,17 @@ var LlmImageSchema = zod.z.object({
10105
10099
  bytes: zod.z.instanceof(Uint8Array),
10106
10100
  mimeType: zod.z.string()
10107
10101
  });
10102
+ /**
10103
+ * Retry policy. `enabled: false` is NOT the same as `maxAttempts: 1` in intent —
10104
+ * the flag is what a consumer table flips, the count is what the operator tunes.
10105
+ * A retry doubles the wall time of a call, so the two gates that run inside a
10106
+ * notification's budget keep it off (see `CONSUMER_RETRY_POLICY` in addon-ai).
10107
+ */
10108
+ var LlmRetryPolicySchema = zod.z.object({
10109
+ enabled: zod.z.boolean().default(false),
10110
+ /** Total attempts INCLUDING the first. 1 = no retry. */
10111
+ maxAttempts: zod.z.number().int().min(1).max(5).default(1)
10112
+ });
10108
10113
  var LlmGenerateBaseInputSchema = zod.z.object({
10109
10114
  /** Collection routing (the notification-output posture). */
10110
10115
  addonId: zod.z.string().optional(),
@@ -10119,7 +10124,28 @@ var LlmGenerateBaseInputSchema = zod.z.object({
10119
10124
  jsonSchema: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
10120
10125
  /** Per-call override of the profile default. */
10121
10126
  maxTokens: zod.z.number().int().positive().optional(),
10122
- temperature: zod.z.number().optional()
10127
+ temperature: zod.z.number().optional(),
10128
+ /** Per-call override of the profile default (nucleus sampling). */
10129
+ topP: zod.z.number().min(0).max(1).optional(),
10130
+ /** Per-call override of the profile default (top-k sampling). */
10131
+ topK: zod.z.number().int().positive().optional(),
10132
+ /** Per-call override of `profile.timeoutMs` — the total generation bound. */
10133
+ timeoutMs: zod.z.number().int().positive().optional(),
10134
+ /** Per-call override; beats both the consumer table and the profile. */
10135
+ retry: LlmRetryPolicySchema.optional(),
10136
+ /**
10137
+ * Caller-minted id that makes this generation CANCELLABLE.
10138
+ *
10139
+ * Without it a caller that stops waiting cannot stop the work: the gates race
10140
+ * the call against 8 s and free their own slot when the timer wins, while the
10141
+ * generation upstream keeps running to `profile.timeoutMs` — 60 s by default,
10142
+ * on a single-threaded local model. The per-camera bound then counts WAITS,
10143
+ * not generations, and the real load is unbounded.
10144
+ *
10145
+ * `AbortSignal` cannot cross a process boundary; an id can. Pass one here and
10146
+ * `llm.cancel({ requestId })` tears the socket down.
10147
+ */
10148
+ requestId: zod.z.string().optional()
10123
10149
  });
10124
10150
  /**
10125
10151
  * `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
@@ -10132,6 +10158,18 @@ var LlmGenerateBaseInputSchema = zod.z.object({
10132
10158
  * Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
10133
10159
  * watchdog — operator decision #3).
10134
10160
  */
10161
+ /**
10162
+ * A companion artifact that MUST land beside the main GGUF: the `mmproj`
10163
+ * projector of a vision model, or shards 2..N of a split GGUF. Carried on the
10164
+ * REF rather than looked up at install time, so what the operator approved in
10165
+ * the preview is exactly what the node downloads.
10166
+ */
10167
+ var ManagedModelExtraFileSchema = zod.z.object({
10168
+ url: zod.z.string(),
10169
+ filename: zod.z.string(),
10170
+ sizeBytes: zod.z.number(),
10171
+ sha256: zod.z.string().optional()
10172
+ });
10135
10173
  var ManagedModelRefSchema = zod.z.discriminatedUnion("kind", [
10136
10174
  zod.z.object({
10137
10175
  kind: zod.z.literal("catalog"),
@@ -10140,7 +10178,11 @@ var ManagedModelRefSchema = zod.z.discriminatedUnion("kind", [
10140
10178
  zod.z.object({
10141
10179
  kind: zod.z.literal("url"),
10142
10180
  url: zod.z.string(),
10143
- sha256: zod.z.string().optional()
10181
+ sha256: zod.z.string().optional(),
10182
+ /** Picker/status label; the file basename when absent. */
10183
+ label: zod.z.string().optional(),
10184
+ sizeBytes: zod.z.number().optional(),
10185
+ extraFiles: zod.z.array(ManagedModelExtraFileSchema).optional()
10144
10186
  }),
10145
10187
  zod.z.object({
10146
10188
  kind: zod.z.literal("path"),
@@ -10158,13 +10200,82 @@ var ManagedRuntimeConfigSchema = zod.z.object({
10158
10200
  gpuLayers: zod.z.number().int().default(0),
10159
10201
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
10160
10202
  threads: zod.z.number().int().optional(),
10161
- /** Concurrent slots. */
10203
+ /** Concurrent slots (`--parallel`). */
10162
10204
  parallel: zod.z.number().int().default(1),
10205
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
10206
+ batchSize: zod.z.number().int().positive().optional(),
10207
+ /** Physical batch / micro-batch (`-ub`). */
10208
+ ubatchSize: zod.z.number().int().positive().optional(),
10209
+ /**
10210
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
10211
+ * is a no-op elsewhere, so it is offered rather than assumed.
10212
+ */
10213
+ flashAttention: zod.z.boolean().default(false),
10214
+ /**
10215
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
10216
+ * inference. Costs the full model size in resident memory — which is exactly
10217
+ * what the RAM budget is counting.
10218
+ */
10219
+ mlock: zod.z.boolean().default(false),
10220
+ /**
10221
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
10222
+ * start, but avoids the page-fault stalls a network or spinning-disk model
10223
+ * store produces on every first token.
10224
+ */
10225
+ noMmap: zod.z.boolean().default(false),
10226
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
10227
+ * cheapest way to fit a longer context in the same RAM. */
10228
+ cacheTypeK: zod.z.enum([
10229
+ "f32",
10230
+ "f16",
10231
+ "q8_0",
10232
+ "q5_1",
10233
+ "q5_0",
10234
+ "q4_1",
10235
+ "q4_0"
10236
+ ]).optional(),
10237
+ cacheTypeV: zod.z.enum([
10238
+ "f32",
10239
+ "f16",
10240
+ "q8_0",
10241
+ "q5_1",
10242
+ "q5_0",
10243
+ "q4_1",
10244
+ "q4_0"
10245
+ ]).optional(),
10246
+ /**
10247
+ * Escape hatch for llama-server flags this schema does NOT model — `--jinja`
10248
+ * (which most vision chat templates need and some language-only models
10249
+ * dislike), `--cont-batching`, `--rope-scaling`, …
10250
+ *
10251
+ * It is NOT a second place to set the flags above. A token that collides
10252
+ * with a typed field is REJECTED at start, naming the field that owns it
10253
+ * (`assertNoOwnedFlags`), because two knobs writing the same argv is exactly
10254
+ * the "two switches that disagree" failure this repo has already shipped
10255
+ * twice (D62).
10256
+ */
10257
+ extraArgs: zod.z.array(zod.z.string()).default([]),
10163
10258
  /** Else lazy: first generate boots it. */
10164
10259
  autoStart: zod.z.boolean().default(false),
10165
10260
  /** 0 = never; frees RAM after quiet periods. */
10166
10261
  idleStopMinutes: zod.z.number().int().default(30)
10167
10262
  });
10263
+ /**
10264
+ * Where a multi-GB install currently is. A single 0..1 fraction cannot answer
10265
+ * "is it stuck?" for an install that is three files (shards + mmproj) followed
10266
+ * by a sha256 pass over 22 GB — during which the fraction sat at 1.0 and the
10267
+ * node looked hung. Phase + file + bytes is the smallest shape that does.
10268
+ */
10269
+ var LlmDownloadProgressSchema = zod.z.object({
10270
+ phase: zod.z.enum(["downloading", "verifying"]),
10271
+ /** The artifact currently moving, e.g. `mmproj-F16.gguf`. */
10272
+ file: zod.z.string(),
10273
+ fileIndex: zod.z.number().int(),
10274
+ fileCount: zod.z.number().int(),
10275
+ /** Across the WHOLE install, not the current file. */
10276
+ downloadedBytes: zod.z.number(),
10277
+ totalBytes: zod.z.number().optional()
10278
+ });
10168
10279
  var LlmRuntimeStatusSchema = zod.z.object({
10169
10280
  /** Status is ALWAYS node-qualified. */
10170
10281
  nodeId: zod.z.string(),
@@ -10181,6 +10292,8 @@ var LlmRuntimeStatusSchema = zod.z.object({
10181
10292
  modelPath: zod.z.string().optional(),
10182
10293
  modelId: zod.z.string().optional(),
10183
10294
  downloadProgress: zod.z.number().min(0).max(1).optional(),
10295
+ /** Detail behind `downloadProgress`; present for the same lifetime. */
10296
+ download: LlmDownloadProgressSchema.optional(),
10184
10297
  lastError: zod.z.string().optional(),
10185
10298
  crashesInWindow: zod.z.number(),
10186
10299
  /** Child RSS (sampled best-effort). */
@@ -10191,7 +10304,14 @@ var LlmNodeModelSchema = zod.z.object({
10191
10304
  file: zod.z.string(),
10192
10305
  sizeBytes: zod.z.number(),
10193
10306
  catalogId: zod.z.string().optional(),
10194
- installedAt: zod.z.number().optional()
10307
+ installedAt: zod.z.number().optional(),
10308
+ /**
10309
+ * Absolute path on the node. Present so a file that is on disk but matches
10310
+ * no catalog entry — a custom Hugging Face install, or a GGUF the operator
10311
+ * copied in by hand — is still SELECTABLE, as a `{kind:'path'}` ref. Without
10312
+ * it the picker could list such a file and do nothing with it.
10313
+ */
10314
+ path: zod.z.string().optional()
10195
10315
  });
10196
10316
  var LlmRuntimeDiskUsageSchema = zod.z.object({
10197
10317
  nodeId: zod.z.string(),
@@ -10262,10 +10382,47 @@ var LlmProfileSchema = zod.z.object({
10262
10382
  baseUrl: zod.z.string().optional(),
10263
10383
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
10264
10384
  apiKey: zod.z.string().optional(),
10385
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
10386
+ * degraded to text — that shipped once and produced a confident answer to a
10387
+ * question about a picture nobody sent. */
10265
10388
  supportsVision: zod.z.boolean(),
10266
10389
  temperature: zod.z.number().min(0).max(2).optional(),
10390
+ /** Nucleus sampling. Every wire we speak has it. */
10391
+ topP: zod.z.number().min(0).max(1).optional(),
10392
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
10393
+ * wire does, and the client drops it there (measured: the request body gets
10394
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
10395
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
10396
+ topK: zod.z.number().int().positive().optional(),
10267
10397
  maxTokens: zod.z.number().int().positive().optional(),
10398
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
10399
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
10400
+ * the model with, so it is the one field that changes a PROCESS. */
10401
+ contextLength: zod.z.number().int().positive().optional(),
10402
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
10403
+ * two system prompts fighting is worse than either alone). */
10404
+ systemPrompt: zod.z.string().optional(),
10405
+ /** Total generation bound — the only one a unary call has. */
10268
10406
  timeoutMs: zod.z.number().int().positive().default(6e4),
10407
+ /** The TCP handshake only — "is the port even open". NOT the wait for
10408
+ * response headers: on the LM Studio / llama-server wire those are written
10409
+ * once the model has finished loading, so they belong to the bound below. */
10410
+ connectTimeoutMs: zod.z.number().int().positive().default(1e4),
10411
+ /** Accepted, but no output yet — response headers included, because a cold
10412
+ * GPU load is exactly what happens before them. */
10413
+ firstTokenTimeoutMs: zod.z.number().int().positive().default(12e4),
10414
+ /** Output started then stopped. */
10415
+ idleTimeoutMs: zod.z.number().int().positive().default(6e4),
10416
+ /** Profile-level default. The per-consumer table and a per-call override
10417
+ * both beat it — see `resolveRetryPolicy`. */
10418
+ retry: LlmRetryPolicySchema.default({
10419
+ enabled: false,
10420
+ maxAttempts: 1
10421
+ }),
10422
+ /** Whether this profile may use tools. The tool-call plumbing rides the
10423
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
10424
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
10425
+ toolsEnabled: zod.z.boolean().default(false),
10269
10426
  extraHeaders: zod.z.record(zod.z.string(), zod.z.string()).optional(),
10270
10427
  /** kind === 'managed-local' only (spec §4). */
10271
10428
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -10315,6 +10472,36 @@ var ManagedModelCatalogEntrySchema = zod.z.object({
10315
10472
  /** Vision models: companion projector file. */
10316
10473
  mmprojUrl: zod.z.string().optional()
10317
10474
  });
10475
+ /**
10476
+ * The outcome of turning one operator-typed Hugging Face reference into a
10477
+ * download plan. A RESULT, never a throw: "this repo has 24 quantizations and
10478
+ * I will not pick for you" is a normal answer the UI has to render, not an
10479
+ * exception.
10480
+ *
10481
+ * `candidates` is the whole reason the refusal is usable — every string in it
10482
+ * is a tag that resolves when pasted back as `<org>/<repo>:<TAG>`.
10483
+ */
10484
+ var HfModelResolutionSchema = zod.z.discriminatedUnion("ok", [zod.z.object({
10485
+ ok: zod.z.literal(true),
10486
+ /** Ready to hand to `installModel` unchanged. */
10487
+ model: ManagedModelRefSchema,
10488
+ label: zod.z.string(),
10489
+ repo: zod.z.string(),
10490
+ quantization: zod.z.string(),
10491
+ purpose: zod.z.enum(["text", "vision"]),
10492
+ totalBytes: zod.z.number(),
10493
+ /** mmproj + shards, for the preview: an operator approving 23 GB should
10494
+ * see that 0.9 GB of it is a projector they did not name. */
10495
+ extraFilenames: zod.z.array(zod.z.string())
10496
+ }), zod.z.object({
10497
+ ok: zod.z.literal(false),
10498
+ code: zod.z.string(),
10499
+ message: zod.z.string(),
10500
+ candidates: zod.z.array(zod.z.string()).optional(),
10501
+ /** Set when the refusal was only the ceiling: re-calling with
10502
+ * `maxBytes: requiredBytes` is the operator's explicit override. */
10503
+ requiredBytes: zod.z.number().optional()
10504
+ })]);
10318
10505
  var LlmRuntimeNodeSchema = zod.z.object({
10319
10506
  nodeId: zod.z.string(),
10320
10507
  reachable: zod.z.boolean(),
@@ -10338,6 +10525,17 @@ var llmCapability = {
10338
10525
  methods: {
10339
10526
  generate: method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
10340
10527
  generateVision: method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
10528
+ /**
10529
+ * Stop a generation started with a `requestId`.
10530
+ *
10531
+ * Idempotent and always successful: cancelling an id that already finished,
10532
+ * never existed, or was cancelled a moment ago is a no-op. A caller that has
10533
+ * given up must never have to handle an error from giving up.
10534
+ */
10535
+ cancel: method(zod.z.object({
10536
+ addonId: zod.z.string().optional(),
10537
+ requestId: zod.z.string()
10538
+ }), zod.z.void(), { kind: "mutation" }),
10341
10539
  listProfileKinds: method(zod.z.object({}), zod.z.array(LlmProfileKindDescriptorSchema)),
10342
10540
  listProfiles: method(zod.z.object({}), zod.z.array(LlmProfileSchema)),
10343
10541
  upsertProfile: method(zod.z.object({ profile: LlmProfileSchema }), LlmProfileSchema, {
@@ -10371,6 +10569,25 @@ var llmCapability = {
10371
10569
  listModelCatalog: method(zod.z.object({}), zod.z.array(ManagedModelCatalogEntrySchema)),
10372
10570
  listRuntimeNodes: method(zod.z.object({}), zod.z.array(LlmRuntimeNodeSchema)),
10373
10571
  listNodeModels: method(zod.z.object({ nodeId: zod.z.string() }), zod.z.array(LlmNodeModelSchema)),
10572
+ /**
10573
+ * One typed Hugging Face reference → a pinned, verified `ManagedModelRef`.
10574
+ *
10575
+ * Runs on the HUB, not on the target node: resolution needs egress to
10576
+ * huggingface.co, and an agent that cannot reach it still installs fine
10577
+ * through the model-distributor relay. Nothing is downloaded here — this is
10578
+ * a tree read plus a HEAD, so the operator sees the size, the quantization
10579
+ * and the mmproj BEFORE approving a multi-GB pull.
10580
+ */
10581
+ resolveModelRef: method(zod.z.object({
10582
+ /** `https://huggingface.co/<org>/<repo>/resolve/main/<f>.gguf`,
10583
+ * `<org>/<repo>/<f>.gguf`, `<org>/<repo>` or `<org>/<repo>:<QUANT>`. */
10584
+ ref: zod.z.string(),
10585
+ /** Explicit ceiling override, in bytes. Absent = the built-in ceiling. */
10586
+ maxBytes: zod.z.number().positive().optional()
10587
+ }), HfModelResolutionSchema, {
10588
+ kind: "mutation",
10589
+ auth: "admin"
10590
+ }),
10374
10591
  installModel: method(zod.z.object({
10375
10592
  nodeId: zod.z.string(),
10376
10593
  model: ManagedModelRefSchema
@@ -12312,28 +12529,36 @@ var NcOccupancyConditionSchema = zod.z.object({
12312
12529
  /**
12313
12530
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
12314
12531
  *
12315
- * Operator-approved vocabulary (2026-08-12, option A — the same one the
12316
- * reference notifier uses, so an operator moving between them re-uses what
12317
- * they already know): a rule matches when, over a sampling window of
12318
- * `samplingSeconds`, at least `hitPercent`% of the audio samples in that
12319
- * window are HITS. A sample is a hit when it satisfies BOTH present filters:
12320
- *
12321
- * - `dbThreshold` its level is at or above this many dBFS (see
12322
- * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale);
12323
- * - `labels` the classifier put at least one of these labels on it.
12324
- *
12325
- * Both are OPTIONAL and independent, which is the point of the shape: a
12326
- * loudness rule ("something loud at 3am") needs no model to be right, and a
12327
- * label rule ("a dog barked") needs no threshold. **Fail-closed when NEITHER
12328
- * is given** a window in which every sample is trivially a hit would fire on
12329
- * silence, so the engine refuses such a condition rather than notifying on
12330
- * nothing (the schema cannot express "at least one of" without becoming a
12331
- * ZodEffects the cap path would have to special-case).
12332
- *
12333
- * `hitPercent` is over the samples the window actually HOLDS, and the window
12334
- * must be FULL before it can match a window that has been open for two
12335
- * seconds of its ten is 100% of nothing, and firing on it would make
12336
- * `samplingSeconds` decorative.
12532
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
12533
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
12534
+ * there is no second switch that can disagree with the first and every rule
12535
+ * authored before the decision migrates for free (`audioModeOf`):
12536
+ *
12537
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
12538
+ * classifier labels with one of them. No window, no percentage:
12539
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
12540
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
12541
+ * the analyzer's (`classificationMinScore`, per device) — a label only
12542
+ * reaches this condition if the classifier was already confident enough.
12543
+ * - **LEVEL mode `dbThreshold` present, no labels.** The sampling window IS
12544
+ * the condition: at least `hitPercent`% of the samples over
12545
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
12546
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
12547
+ * must be FULL before it can match a window open for two of its ten
12548
+ * seconds is 100% of nothing.
12549
+ *
12550
+ * **Why label mode has no window.** It had one, and it never fired: the
12551
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
12552
+ * of them per episode, even through continuous crying. The measured maximum
12553
+ * `hitPercent` over the whole live history was 40 — under the shipped default
12554
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
12555
+ * the wrong question to ask of a sparse classifier.
12556
+ *
12557
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
12558
+ * and the rule would fire on silence. The schema cannot express "exactly one
12559
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
12560
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
12561
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
12337
12562
  *
12338
12563
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
12339
12564
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -12341,13 +12566,13 @@ var NcOccupancyConditionSchema = zod.z.object({
12341
12566
  * an operator who typed `dog` mean the same thing.
12342
12567
  */
12343
12568
  var NcAudioConditionSchema = zod.z.object({
12344
- /** Audio macro labels; absent = any sound (level-only rule). */
12569
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
12345
12570
  labels: zod.z.array(zod.z.string().min(1)).min(1).optional(),
12346
- /** Level floor in dBFS (negative-going, `0` = full scale); absent = any level. */
12571
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
12347
12572
  dbThreshold: zod.z.number().min(-96).max(0).optional(),
12348
- /** Percentage of the window's samples that must be hits (1–100). */
12573
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
12349
12574
  hitPercent: zod.z.number().int().min(1).max(100).default(60),
12350
- /** Length of the sampling window in seconds. */
12575
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
12351
12576
  samplingSeconds: zod.z.number().int().min(1).max(300).default(10)
12352
12577
  });
12353
12578
  /**
@@ -12500,9 +12725,66 @@ var NcDeviceStateConditionSchema = zod.z.object({
12500
12725
  /** Any of these matches. */
12501
12726
  states: zod.z.array(zod.z.string().min(1)).min(1)
12502
12727
  });
12728
+ /**
12729
+ * "This rule applies only while scene `sceneId` is `matched` / `diverged`."
12730
+ *
12731
+ * A GATE, not a trigger. `occupancy` and `audio` each DISCRIMINATE their rule —
12732
+ * carrying one makes the rule fire on that subject and nothing else. Scene is
12733
+ * the other shape entirely, the `deviceState` shape: it narrows a rule that
12734
+ * already has a trigger ("tell me about a person at the front door, but only
12735
+ * while the bin is still out"). That is why it composes with every delivery
12736
+ * instead of owning one, and why no new `NcDelivery` member and no new subject
12737
+ * kind exist for it — see D159.
12738
+ *
12739
+ * ── Identity ───────────────────────────────────────────────────────────────
12740
+ * `sceneId` is `SceneMonitor.id`, a `randomUUID()` minted by `createScene` —
12741
+ * globally unique, so it needs no device to disambiguate it. `deviceId` is
12742
+ * carried as a HINT for the editor and for the log line, never as part of the
12743
+ * lookup key: a rule whose hint drifted must still gate correctly.
12744
+ *
12745
+ * ── Which boolean ──────────────────────────────────────────────────────────
12746
+ * `latched` ABSENT means "whatever the scene itself says" — `SceneMonitor.emit`
12747
+ * already declares which boolean drives notification rules, and a second knob
12748
+ * that could disagree with it is exactly the D62 failure. Set it only to
12749
+ * override one rule against the scene's own default.
12750
+ *
12751
+ * - LIVE reading (`emit`/`latched` resolve to live): passes iff
12752
+ * `verdict === requiredState`. `unknown` — no reference for this light, view
12753
+ * shifted, no snapshot — passes NEITHER. A scene that cannot judge is not
12754
+ * evidence, in either direction.
12755
+ * - LATCHED reading: passes iff `latched === (requiredState === 'diverged')`.
12756
+ * The latch is a durable fact about the past ("it has diverged since I armed
12757
+ * it"), so a camera that has gone dark does not clear it — that is the whole
12758
+ * reason the operator asked for a latch.
12759
+ *
12760
+ * The gate reads an in-memory mirror (`NcSceneStateCache`) refreshed OFF the
12761
+ * event path, never the cap: D49. A mirror that has never loaded, or a scene it
12762
+ * does not carry, reads absent and the rule does NOT fire — fail closed, and
12763
+ * said out loud in the log rather than dropped in silence.
12764
+ */
12765
+ var NcSceneConditionSchema = zod.z.object({
12766
+ /** `SceneMonitor.id` — the uuid the cap mints. The whole lookup key. */
12767
+ sceneId: zod.z.string().min(1),
12768
+ /** The camera the scene lives on. A hint for the editor and the log line. */
12769
+ deviceId: zod.z.number().int().optional(),
12770
+ /** The state the scene must be in for the rule to fire. */
12771
+ requiredState: zod.z.enum(["matched", "diverged"]),
12772
+ /**
12773
+ * Read the LATCH (`true`) or the LIVE verdict (`false`). Absent = follow the
12774
+ * scene's own `emit` field, which is the only place that decision belongs.
12775
+ */
12776
+ latched: zod.z.boolean().optional()
12777
+ });
12503
12778
  var NcConditionsSchema = zod.z.object({
12504
12779
  /** Gate on ANOTHER device's current state (the alarm armed, a switch on). */
12505
12780
  deviceState: NcDeviceStateConditionSchema.optional(),
12781
+ /**
12782
+ * Gate on a SCENE's state — "only while the bin is still out". Composes with
12783
+ * every trigger (detection, occupancy, audio, sensor, package, track-end);
12784
+ * unlike `occupancy`/`audio` it discriminates nothing. See
12785
+ * {@link NcSceneCondition} and D159.
12786
+ */
12787
+ scene: NcSceneConditionSchema.optional(),
12506
12788
  /** Device scope — absent = all devices. */
12507
12789
  devices: zod.z.array(zod.z.number()).optional(),
12508
12790
  /** Detector class names (any overlap with the record's class set). */
@@ -13140,6 +13422,7 @@ var NcConditionDescriptorSchema = zod.z.object({
13140
13422
  "occupancy",
13141
13423
  "audio",
13142
13424
  "deviceState",
13425
+ "scene",
13143
13426
  "systemEvent"
13144
13427
  ]),
13145
13428
  operator: zod.z.enum([
@@ -16365,6 +16648,17 @@ var maxSessionHoldMsField = {
16365
16648
  default: 12e4,
16366
16649
  step: 5e3
16367
16650
  };
16651
+ /**
16652
+ * Quiet period that closes an `audioMode: 'on-motion'` audio window. Floor of
16653
+ * 5s so a rearm can never degenerate into per-event stream churn; default 90s
16654
+ * comfortably outlives the gap between two PIR wakes on a battery camera.
16655
+ */
16656
+ var audioMotionWindowMsField = {
16657
+ min: 5e3,
16658
+ max: 6e5,
16659
+ default: 9e4,
16660
+ step: 5e3
16661
+ };
16368
16662
  var motionFpsField = {
16369
16663
  min: 1,
16370
16664
  max: 30,
@@ -16541,6 +16835,27 @@ var RunnerCameraConfigSchema = zod.z.object({
16541
16835
  * resolved `CameraDetectionConfig`.
16542
16836
  */
16543
16837
  maxSessionHoldMs: zod.z.number().min(maxSessionHoldMsField.min).max(maxSessionHoldMsField.max).optional(),
16838
+ /**
16839
+ * Orchestrator-side quiet period (ms) that closes an `audioMode:
16840
+ * 'on-motion'` audio window, measured from the LAST motion event.
16841
+ *
16842
+ * This exists because the falling edge cannot be relied on. Camera-native
16843
+ * providers emit motion as a RISING EDGE ONLY (Reolink's Baichuan push and
16844
+ * its email-push SMTP path both emit `detected: true` and never the
16845
+ * counterpart); only the frame-diff analyzer emits falls. So on an
16846
+ * onboard-only camera a window that closed only on `detected: false` never
16847
+ * closed at all, and `on-motion` silently behaved as `always-on` — on a
16848
+ * battery camera, the one failure mode the mode exists to prevent.
16849
+ *
16850
+ * Every motion event rearms this timer WITHOUT restarting the stream, so a
16851
+ * burst of re-fires costs nothing. A falling edge, when one does arrive,
16852
+ * still closes earlier via `motionCooldownMs` — whichever comes first wins.
16853
+ *
16854
+ * Not consumed by the runner: carried here so it shares the per-camera
16855
+ * device-settings surface with `motionCooldownMs`, exactly like
16856
+ * `maxSessionHoldMs`.
16857
+ */
16858
+ audioMotionWindowMs: zod.z.number().min(audioMotionWindowMsField.min).max(audioMotionWindowMsField.max).optional(),
16544
16859
  motionFps: zod.z.number().min(motionFpsField.min).max(motionFpsField.max).default(motionFpsField.default),
16545
16860
  detectionFps: zod.z.number().min(detectionFpsField.min).max(detectionFpsField.max).default(detectionFpsField.default),
16546
16861
  motionStreamId: zod.z.string(),
@@ -16636,7 +16951,7 @@ var RunnerCameraConfigSchema = zod.z.object({
16636
16951
  */
16637
16952
  inferenceDevices: zod.z.array(RunnerInferenceDeviceSchema).readonly().optional()
16638
16953
  });
16639
- motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
16954
+ motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
16640
16955
  /**
16641
16956
  * Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
16642
16957
  * load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
@@ -26642,10 +26957,22 @@ var recordingExportCapability = {
26642
26957
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
26643
26958
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
26644
26959
  *
26645
- * D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) the framework
26646
- * derives the device-detail contribution; the provider carries NO hand-written
26647
- * settings-contribution methods. `status.kind:'push'` the engine pushes on
26648
- * every hysteresis flip / availability change; consumers never poll.
26960
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
26961
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
26962
+ * for the thing: a scene is a standing question about the property ("is the bin
26963
+ * still out"), and the operator's question is "which of my scenes have tripped",
26964
+ * across every camera at once — not "what does camera 617 think". Buried one
26965
+ * camera deep it also could not be found. The surface is now a top-level admin
26966
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
26967
+ * picks the camera inside the create flow, the same shape Events and Faces have.
26968
+ *
26969
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
26970
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
26971
+ * directions, so a registration nobody declares fails exactly as loudly as a
26972
+ * declaration nobody registers. The editor is imported directly by the page.
26973
+ *
26974
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
26975
+ * availability change; consumers never poll.
26649
26976
  */
26650
26977
  /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
26651
26978
  * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
@@ -26656,6 +26983,33 @@ var recordingExportCapability = {
26656
26983
  * as `unknown`, never guessed. A day reference scored against an IR frame
26657
26984
  * collapses the cosine and would latch a false alarm every single night. */
26658
26985
  var SceneConditionSchema = zod.z.string();
26986
+ /**
26987
+ * What a scene does when the CURRENT light has no reference of its own.
26988
+ *
26989
+ * The lighting variants are not equally likely to exist. Almost every operator
26990
+ * captures daylight and then never stands outside at 22:00 to capture IR, and a
26991
+ * scene that is only ever going to be asked about a daytime question ("is the
26992
+ * bin still on the kerb at 08:00") does not need a night reference at all. The
26993
+ * night half must therefore be OPTIONAL, and optional means the scene keeps
26994
+ * working without it rather than degrading into a permanent complaint.
26995
+ *
26996
+ * - `skip` (default) — the check in that light is not made. Not a verdict, not
26997
+ * an alarm, not even an `unknown`: the live state simply stays whatever the
26998
+ * last covered light left it at, the latch is untouched, and the hysteresis
26999
+ * run is neither spent nor cleared. The scene resumes by itself at first
27000
+ * light. This is the only behaviour under which "I never captured IR" is a
27001
+ * configuration choice instead of a nightly fault.
27002
+ * - `judge-anyway` — score against the OTHER conditions' references. Available
27003
+ * for cameras whose IR frame is close enough to daylight (a floodlit
27004
+ * driveway, an always-white-light doorbell), and wrong for everything else:
27005
+ * cross-condition cosines are not comparable, so a day reference against a
27006
+ * true IR frame collapses and the scene reports a theft at 21:40.
27007
+ *
27008
+ * Never applies when the scene has NO comparable reference at all — that is
27009
+ * "not armed yet", it is reported as `no-reference-for-condition`, and silence
27010
+ * there would hide a scene the operator never finished setting up.
27011
+ */
27012
+ var SceneUncoveredPolicySchema = zod.z.enum(["skip", "judge-anyway"]);
26659
27013
  /** `matched` = the baseline is what we see; `diverged` = it demonstrably is not;
26660
27014
  * `unknown` = we cannot judge (no reference for this condition, encoder model
26661
27015
  * changed, view shifted, no snapshot). `unknown` is a real value, not a null,
@@ -26711,6 +27065,9 @@ var SceneCheckSchema = zod.z.discriminatedUnion("mode", [zod.z.object({
26711
27065
  hysteresisCount: zod.z.number().int().positive()
26712
27066
  })]);
26713
27067
  var SCENE_DEFAULT_ANCHOR_THRESHOLD = .85;
27068
+ /** Night is OPTIONAL. A scene with only a daylight reference sits the IR hours
27069
+ * out in silence rather than reporting a fault every night. */
27070
+ var SCENE_DEFAULT_UNCOVERED_POLICY = "skip";
26714
27071
  /**
26715
27072
  * Vision-model adjudication of a candidate flip. Field names deliberately
26716
27073
  * mirror `NcConfirmSchema` so an operator meets one vocabulary, not two.
@@ -26777,6 +27134,21 @@ var SceneMonitorSchema = zod.z.object({
26777
27134
  * automation can react to the bin coming back without the operator's own
26778
27135
  * alarm silently clearing itself. */
26779
27136
  autoRestore: zod.z.boolean().default(false),
27137
+ /** What to do when the current light has no reference of its own. See
27138
+ * {@link SceneUncoveredPolicySchema} — the default makes night OPTIONAL. */
27139
+ onUncoveredCondition: SceneUncoveredPolicySchema.default(SCENE_DEFAULT_UNCOVERED_POLICY),
27140
+ /**
27141
+ * The light whose checks are currently being SAT OUT under
27142
+ * `onUncoveredCondition: 'skip'` — `null` when the scene is checking normally.
27143
+ *
27144
+ * Engine-reported and advisory only: it moves no verdict, no latch and no
27145
+ * hysteresis. It exists so the card can say *"night (IR) — checks paused,
27146
+ * nothing captured in this light"* in the same calm voice as the coverage
27147
+ * line, because the alternative is a scene that silently stops answering
27148
+ * after sunset with nothing anywhere saying why. A skipped check must never
27149
+ * read as a broken one.
27150
+ */
27151
+ suspendedCondition: SceneConditionSchema.nullable().default(null),
26780
27152
  /** Named cause when `verdict === 'unknown'`. */
26781
27153
  unavailable: SceneUnavailableSchema.nullable(),
26782
27154
  /** Conditions that have at least one comparable reference — the coverage line
@@ -26794,12 +27166,6 @@ var sceneMonitorCapability = {
26794
27166
  kind: "wrapper",
26795
27167
  defaultActive: true,
26796
27168
  deviceTypes: [DeviceType.Camera],
26797
- deviceConfig: { ui: {
26798
- kind: "widget",
26799
- widgetId: "host/scene-monitor-editor",
26800
- tab: "scenes",
26801
- label: "Scenes"
26802
- } },
26803
27169
  methods: {
26804
27170
  listScenes: method(zod.z.object({ deviceId: zod.z.number() }), SceneMonitorStatusSchema),
26805
27171
  createScene: method(zod.z.object({
@@ -26836,6 +27202,7 @@ var sceneMonitorCapability = {
26836
27202
  minObservationSpacingSec: zod.z.number().int().min(0).max(3600).optional(),
26837
27203
  anchorThreshold: zod.z.number().min(0).max(1).optional(),
26838
27204
  autoRestore: zod.z.boolean().optional(),
27205
+ onUncoveredCondition: SceneUncoveredPolicySchema.optional(),
26839
27206
  /** `null` clears the vision-model adjudicator. */
26840
27207
  confirm: SceneConfirmSchema.nullable().optional()
26841
27208
  })
@@ -27658,6 +28025,61 @@ var NetworkAddressSchema = zod.z.object({
27658
28025
  family: zod.z.string(),
27659
28026
  internal: zod.z.boolean()
27660
28027
  });
28028
+ /**
28029
+ * Provenance of the site coordinates, and the whole reason this is not just two
28030
+ * numbers.
28031
+ *
28032
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
28033
+ * nothing overwrites it.
28034
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
28035
+ * default that is right to a few kilometres beats the coarse UTC clock split
28036
+ * the sun-times consumers otherwise fall back to.
28037
+ *
28038
+ * The UI shows which one it is. An operator who cannot tell a guess from their
28039
+ * own input will eventually trust the guess.
28040
+ */
28041
+ var SiteLocationSourceSchema = zod.z.enum(["operator-set", "derived-from-ip"]);
28042
+ /**
28043
+ * Where the installation physically is — a fact of the SITE, not of any addon.
28044
+ *
28045
+ * It used to live in `pipeline-analytics`' global settings, which made a
28046
+ * property of the building a property of one analytics addon. Anything that
28047
+ * needs sun-times (scene condition variants today; anything solar tomorrow)
28048
+ * reads it from here.
28049
+ */
28050
+ var SiteLocationSchema = zod.z.object({
28051
+ /** WGS84 decimal degrees. */
28052
+ latitude: zod.z.number().min(-90).max(90),
28053
+ longitude: zod.z.number().min(-180).max(180),
28054
+ source: SiteLocationSourceSchema,
28055
+ /** Epoch ms the value was last written. */
28056
+ updatedAt: zod.z.number(),
28057
+ /**
28058
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
28059
+ * only — never parsed, never matched on. Absent for an operator-typed value.
28060
+ */
28061
+ label: zod.z.string().optional()
28062
+ });
28063
+ /**
28064
+ * The read shape: the location plus the honest state of the one-shot derivation.
28065
+ *
28066
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
28067
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
28068
+ * failed; the hub will NOT try again on its own — the fallback is declared
28069
+ * (consumers degrade to their own last resort) and the operator either types the
28070
+ * coordinates or presses detect.
28071
+ */
28072
+ var SiteLocationStatusSchema = zod.z.object({
28073
+ location: SiteLocationSchema.nullable(),
28074
+ derivationAttemptedAt: zod.z.number().nullable(),
28075
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
28076
+ derivationError: zod.z.string().nullable()
28077
+ });
28078
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
28079
+ var SetSiteLocationInputSchema = zod.z.object({
28080
+ latitude: zod.z.number().min(-90).max(90),
28081
+ longitude: zod.z.number().min(-180).max(180)
28082
+ }).nullable();
27661
28083
  var systemCapability = {
27662
28084
  name: "system",
27663
28085
  scope: "system",
@@ -27675,6 +28097,32 @@ var systemCapability = {
27675
28097
  forceRetentionCleanup: method(zod.z.void(), zod.z.void(), {
27676
28098
  kind: "mutation",
27677
28099
  auth: "admin"
28100
+ }),
28101
+ /**
28102
+ * The site coordinates, deriving a default from the hub's public IP on the
28103
+ * FIRST read that finds nothing stored.
28104
+ *
28105
+ * The derivation is one-shot and bounded: one outbound request, a few
28106
+ * seconds, its outcome persisted either way. A hub with no internet pays it
28107
+ * once and never again, and neither boot nor any consumer is blocked on it —
28108
+ * the caller gets `location: null` and degrades exactly as it did before this
28109
+ * method existed.
28110
+ */
28111
+ getSiteLocation: method(zod.z.void(), SiteLocationStatusSchema),
28112
+ /** Operator input. Always lands as `source: 'operator-set'`. */
28113
+ setSiteLocation: method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
28114
+ kind: "mutation",
28115
+ auth: "admin"
28116
+ }),
28117
+ /**
28118
+ * Re-run the geo-IP derivation now. The ONLY way a spent or failed
28119
+ * derivation is retried — there is no timer, and no read path retries.
28120
+ * Overwrites an existing `derived-from-ip` value; refuses to clobber an
28121
+ * `operator-set` one.
28122
+ */
28123
+ detectSiteLocation: method(zod.z.void(), SiteLocationStatusSchema, {
28124
+ kind: "mutation",
28125
+ auth: "admin"
27678
28126
  })
27679
28127
  },
27680
28128
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
@@ -31410,6 +31858,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31410
31858
  addonId: null,
31411
31859
  access: "create"
31412
31860
  },
31861
+ "llm.cancel": {
31862
+ capName: "llm",
31863
+ capScope: "system",
31864
+ addonId: null,
31865
+ access: "create"
31866
+ },
31413
31867
  "llm.deleteModel": {
31414
31868
  capName: "llm",
31415
31869
  capScope: "system",
@@ -31494,6 +31948,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31494
31948
  addonId: null,
31495
31949
  access: "view"
31496
31950
  },
31951
+ "llm.resolveModelRef": {
31952
+ capName: "llm",
31953
+ capScope: "system",
31954
+ addonId: null,
31955
+ access: "create"
31956
+ },
31497
31957
  "llm.setDefault": {
31498
31958
  capName: "llm",
31499
31959
  capScope: "system",
@@ -34344,6 +34804,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34344
34804
  addonId: null,
34345
34805
  access: "create"
34346
34806
  },
34807
+ "system.detectSiteLocation": {
34808
+ capName: "system",
34809
+ capScope: "system",
34810
+ addonId: null,
34811
+ access: "create"
34812
+ },
34347
34813
  "system.featureFlags": {
34348
34814
  capName: "system",
34349
34815
  capScope: "system",
@@ -34362,6 +34828,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34362
34828
  addonId: null,
34363
34829
  access: "view"
34364
34830
  },
34831
+ "system.getSiteLocation": {
34832
+ capName: "system",
34833
+ capScope: "system",
34834
+ addonId: null,
34835
+ access: "view"
34836
+ },
34365
34837
  "system.health": {
34366
34838
  capName: "system",
34367
34839
  capScope: "system",
@@ -34386,6 +34858,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34386
34858
  addonId: null,
34387
34859
  access: "create"
34388
34860
  },
34861
+ "system.setSiteLocation": {
34862
+ capName: "system",
34863
+ capScope: "system",
34864
+ addonId: null,
34865
+ access: "create"
34866
+ },
34389
34867
  "terminalSession.adoptLegacyMonitor": {
34390
34868
  capName: "terminal-session",
34391
34869
  capScope: "system",