@camstack/system 1.2.90 → 1.2.91

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 (52) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +25 -1
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +25 -1
  9. package/dist/builtins/console-logging/index.js +1 -1
  10. package/dist/builtins/console-logging/index.mjs +1 -1
  11. package/dist/builtins/core-blocks/core-block-store.d.ts +11 -0
  12. package/dist/builtins/core-blocks/core-blocks.addon.js +12 -1
  13. package/dist/builtins/core-blocks/core-blocks.addon.mjs +12 -1
  14. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  15. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  17. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  18. package/dist/builtins/hub-forwarder/index.js +1 -1
  19. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  20. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  21. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  22. package/dist/builtins/local-auth/auth-schema.d.ts +42 -0
  23. package/dist/builtins/local-auth/local-auth.addon.js +43 -1
  24. package/dist/builtins/local-auth/local-auth.addon.mjs +43 -1
  25. package/dist/builtins/local-network/local-network.addon.js +1 -1
  26. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  27. package/dist/builtins/loki-logging/index.js +1 -1
  28. package/dist/builtins/loki-logging/index.mjs +1 -1
  29. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  30. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  31. package/dist/builtins/platform-probe/index.js +1 -1
  32. package/dist/builtins/platform-probe/index.mjs +1 -1
  33. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  34. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  35. package/dist/builtins/snapshot/index.js +1 -1
  36. package/dist/builtins/snapshot/index.mjs +1 -1
  37. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  38. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +16 -5
  40. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +16 -5
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +19 -2
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +19 -2
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +12 -0
  44. package/dist/builtins/system-config/system-config.addon.js +1 -1
  45. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  46. package/dist/builtins/winston-logging/index.js +1 -1
  47. package/dist/builtins/winston-logging/index.mjs +1 -1
  48. package/dist/{dist-CYqL8oZq.js → dist-9Gfk4KOk.js} +275 -44
  49. package/dist/{dist-DMhxx5Kr.mjs → dist-DhrTd8LL.mjs} +275 -44
  50. package/dist/index.js +38 -1
  51. package/dist/index.mjs +38 -1
  52. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-9Gfk4KOk.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs = require("node:fs");
9
9
  let node_module = require("node:module");
@@ -1144,10 +1144,21 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
1144
1144
  }
1145
1145
  }
1146
1146
  this.getDb().exec(`CREATE TABLE IF NOT EXISTS "${table}" (${colDefs.join(", ")})`);
1147
- if (schema.indexes) for (const idx of schema.indexes) {
1148
- const unique = idx.unique ? "UNIQUE " : "";
1149
- const cols = idx.columns.map((c) => `"${c}"`).join(", ");
1150
- this.getDb().exec(`CREATE ${unique}INDEX IF NOT EXISTS "${idx.name}" ON "${table}" (${cols})`);
1147
+ if (schema.indexes) {
1148
+ const pkName = schema.columns.find((c) => c.primaryKey === true)?.name ?? "id";
1149
+ const nonPkColumns = schema.columns.filter((c) => c.name !== pkName).map((c) => c.name);
1150
+ const kvBlobColumn = nonPkColumns.length === 1 && nonPkColumns[0] === "data" ? "data" : null;
1151
+ const isRealColumn = (field) => field === pkName || nonPkColumns.includes(field);
1152
+ const indexExpr = (field) => {
1153
+ if (isRealColumn(field)) return `"${field}"`;
1154
+ return kvBlobColumn === null ? null : `json_extract("${kvBlobColumn}", '$.${field}')`;
1155
+ };
1156
+ for (const idx of schema.indexes) {
1157
+ const exprs = idx.columns.map((c) => indexExpr(c));
1158
+ if (exprs.some((e) => e === null)) continue;
1159
+ const unique = idx.unique ? "UNIQUE " : "";
1160
+ this.getDb().exec(`CREATE ${unique}INDEX IF NOT EXISTS "${idx.name}" ON "${table}" (${exprs.join(", ")})`);
1161
+ }
1151
1162
  }
1152
1163
  this.structuredTables.set(table, fingerprint);
1153
1164
  }
@@ -1,4 +1,4 @@
1
- import { A as decodeVectorBase64, Dt as asJsonObject, Rt as parseJsonUnknown, _t as BaseAddon, f as RUNTIME_DEFAULTS, gt as errMsg, ht as vectorStoreCapability, k as dataStoreProviderCapability, mt as vectorDimFromBase64, w as bareAddonId } from "../../dist-DMhxx5Kr.mjs";
1
+ import { A as decodeVectorBase64, Dt as asJsonObject, Rt as parseJsonUnknown, _t as BaseAddon, f as RUNTIME_DEFAULTS, gt as errMsg, ht as vectorStoreCapability, k as dataStoreProviderCapability, mt as vectorDimFromBase64, w as bareAddonId } from "../../dist-DhrTd8LL.mjs";
2
2
  import { createRequire } from "node:module";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { statSync } from "node:fs";
@@ -1138,10 +1138,21 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
1138
1138
  }
1139
1139
  }
1140
1140
  this.getDb().exec(`CREATE TABLE IF NOT EXISTS "${table}" (${colDefs.join(", ")})`);
1141
- if (schema.indexes) for (const idx of schema.indexes) {
1142
- const unique = idx.unique ? "UNIQUE " : "";
1143
- const cols = idx.columns.map((c) => `"${c}"`).join(", ");
1144
- this.getDb().exec(`CREATE ${unique}INDEX IF NOT EXISTS "${idx.name}" ON "${table}" (${cols})`);
1141
+ if (schema.indexes) {
1142
+ const pkName = schema.columns.find((c) => c.primaryKey === true)?.name ?? "id";
1143
+ const nonPkColumns = schema.columns.filter((c) => c.name !== pkName).map((c) => c.name);
1144
+ const kvBlobColumn = nonPkColumns.length === 1 && nonPkColumns[0] === "data" ? "data" : null;
1145
+ const isRealColumn = (field) => field === pkName || nonPkColumns.includes(field);
1146
+ const indexExpr = (field) => {
1147
+ if (isRealColumn(field)) return `"${field}"`;
1148
+ return kvBlobColumn === null ? null : `json_extract("${kvBlobColumn}", '$.${field}')`;
1149
+ };
1150
+ for (const idx of schema.indexes) {
1151
+ const exprs = idx.columns.map((c) => indexExpr(c));
1152
+ if (exprs.some((e) => e === null)) continue;
1153
+ const unique = idx.unique ? "UNIQUE " : "";
1154
+ this.getDb().exec(`CREATE ${unique}INDEX IF NOT EXISTS "${idx.name}" ON "${table}" (${exprs.join(", ")})`);
1155
+ }
1145
1156
  }
1146
1157
  this.structuredTables.set(table, fingerprint);
1147
1158
  }
@@ -3,13 +3,18 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-9Gfk4KOk.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
10
10
  let node_path = require("node:path");
11
11
  node_path = require_chunk.__toESM(node_path);
12
12
  //#region src/builtins/storage-orchestrator/location-store.ts
13
+ /**
14
+ * @durable class=config owner=storage-orchestrator
15
+ * write="an operator creates or edits a location (basePath, minFreePercent, enabled, node pin); plus the boot reconcile — declared defaults seeded on a fresh install, and in-memory-only locations backfilled when the store is late-wired"
16
+ * retention="none — a row goes only when the operator deletes the location, or when the declaration pass prunes one whose declaring addon is gone. Bounded by the storage targets a human configures."
17
+ */
13
18
  var STORAGE_LOCATIONS = "storage_locations";
14
19
  /**
15
20
  * Reserved key under which the cluster `nodeId` (SP1) is persisted INSIDE
@@ -799,11 +804,23 @@ var StorageOrchestratorService = class {
799
804
  * 2. backfill — in-memory locations the DB doesn't have yet (the
800
805
  * pre-store seed defaults on a fresh install) are persisted, so the
801
806
  * store becomes the durable source of truth from here on.
807
+ *
808
+ * **The re-entry guard latches on a SUCCESSFUL hydrate, not on the attempt** —
809
+ * `loadAll()` is awaited BEFORE `this.locationStore` is assigned, and the
810
+ * order is load-bearing. Latching first meant one transient read error
811
+ * answered `hasStore()` true forever, so the addon's `ensureStoreWired` gate
812
+ * skipped every retry; the service then ran the whole process lifetime on
813
+ * pre-store seed defaults while `upsert` mirrored them onto rows it had never
814
+ * read, silently replacing the operator's persisted `minFreePercent`. Failing
815
+ * back to store-less is the safe direction: an in-memory-only service writes
816
+ * nothing over what is on disk, and the next `capability:provider-registered`
817
+ * event tries again. Held by
818
+ * `__tests__/storage-orchestrator-persistence.spec.ts`.
802
819
  */
803
820
  async attachStore(store) {
804
821
  if (this.locationStore) return;
805
- this.locationStore = store;
806
822
  const rows = await store.loadAll();
823
+ this.locationStore = store;
807
824
  const dbIds = new Set(rows.map((r) => r.id));
808
825
  for (const loc of rows) {
809
826
  const upgraded = !loc.isSystem && loc.id === `${loc.type}:default` ? {
@@ -1,8 +1,13 @@
1
- import { Lt as parseJsonObject, _t as BaseAddon, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as storageCapability, st as settingsStoreCapability, ut as storageMigrationCapability } from "../../dist-DMhxx5Kr.mjs";
1
+ import { Lt as parseJsonObject, _t as BaseAddon, g as StorageMigrationJobSchema, h as StorageLocationTypeSchema, lt as storageCapability, st as settingsStoreCapability, ut as storageMigrationCapability } from "../../dist-DhrTd8LL.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import * as fs from "node:fs/promises";
4
4
  import * as path$1 from "node:path";
5
5
  //#region src/builtins/storage-orchestrator/location-store.ts
6
+ /**
7
+ * @durable class=config owner=storage-orchestrator
8
+ * write="an operator creates or edits a location (basePath, minFreePercent, enabled, node pin); plus the boot reconcile — declared defaults seeded on a fresh install, and in-memory-only locations backfilled when the store is late-wired"
9
+ * retention="none — a row goes only when the operator deletes the location, or when the declaration pass prunes one whose declaring addon is gone. Bounded by the storage targets a human configures."
10
+ */
6
11
  var STORAGE_LOCATIONS = "storage_locations";
7
12
  /**
8
13
  * Reserved key under which the cluster `nodeId` (SP1) is persisted INSIDE
@@ -792,11 +797,23 @@ var StorageOrchestratorService = class {
792
797
  * 2. backfill — in-memory locations the DB doesn't have yet (the
793
798
  * pre-store seed defaults on a fresh install) are persisted, so the
794
799
  * store becomes the durable source of truth from here on.
800
+ *
801
+ * **The re-entry guard latches on a SUCCESSFUL hydrate, not on the attempt** —
802
+ * `loadAll()` is awaited BEFORE `this.locationStore` is assigned, and the
803
+ * order is load-bearing. Latching first meant one transient read error
804
+ * answered `hasStore()` true forever, so the addon's `ensureStoreWired` gate
805
+ * skipped every retry; the service then ran the whole process lifetime on
806
+ * pre-store seed defaults while `upsert` mirrored them onto rows it had never
807
+ * read, silently replacing the operator's persisted `minFreePercent`. Failing
808
+ * back to store-less is the safe direction: an in-memory-only service writes
809
+ * nothing over what is on disk, and the next `capability:provider-registered`
810
+ * event tries again. Held by
811
+ * `__tests__/storage-orchestrator-persistence.spec.ts`.
795
812
  */
796
813
  async attachStore(store) {
797
814
  if (this.locationStore) return;
798
- this.locationStore = store;
799
815
  const rows = await store.loadAll();
816
+ this.locationStore = store;
800
817
  const dbIds = new Set(rows.map((r) => r.id));
801
818
  for (const loc of rows) {
802
819
  const upgraded = !loc.isSystem && loc.id === `${loc.type}:default` ? {
@@ -139,6 +139,18 @@ export declare class StorageOrchestratorService {
139
139
  * 2. backfill — in-memory locations the DB doesn't have yet (the
140
140
  * pre-store seed defaults on a fresh install) are persisted, so the
141
141
  * store becomes the durable source of truth from here on.
142
+ *
143
+ * **The re-entry guard latches on a SUCCESSFUL hydrate, not on the attempt** —
144
+ * `loadAll()` is awaited BEFORE `this.locationStore` is assigned, and the
145
+ * order is load-bearing. Latching first meant one transient read error
146
+ * answered `hasStore()` true forever, so the addon's `ensureStoreWired` gate
147
+ * skipped every retry; the service then ran the whole process lifetime on
148
+ * pre-store seed defaults while `upsert` mirrored them onto rows it had never
149
+ * read, silently replacing the operator's persisted `minFreePercent`. Failing
150
+ * back to store-less is the safe direction: an in-memory-only service writes
151
+ * nothing over what is on disk, and the next `capability:provider-registered`
152
+ * event tries again. Held by
153
+ * `__tests__/storage-orchestrator-persistence.spec.ts`.
142
154
  */
143
155
  attachStore(store: ILocationStore): Promise<void>;
144
156
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-9Gfk4KOk.js");
7
7
  //#region src/builtins/system-config/system-config.addon.ts
8
8
  /**
9
9
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -1,4 +1,4 @@
1
- import { Pt as hydrateSchema, _t as BaseAddon, gt as errMsg } from "../../dist-DMhxx5Kr.mjs";
1
+ import { Pt as hydrateSchema, _t as BaseAddon, gt as errMsg } from "../../dist-DhrTd8LL.mjs";
2
2
  //#region src/builtins/system-config/system-config.addon.ts
3
3
  /**
4
4
  * Built-in `system-config` addon — Phase 4 of the settings redesign.
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-CYqL8oZq.js");
6
+ const require_dist = require("../../dist-9Gfk4KOk.js");
7
7
  const require_formatter = require("../../formatter-DqAKDlvN.js");
8
8
  let node_path = require("node:path");
9
9
  node_path = require_chunk.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { J as logDestinationCapability, _t as BaseAddon } from "../../dist-DMhxx5Kr.mjs";
1
+ import { J as logDestinationCapability, _t as BaseAddon } from "../../dist-DhrTd8LL.mjs";
2
2
  import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
3
3
  import * as path$1 from "node:path";
4
4
  import path from "node:path";
@@ -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
@@ -10158,8 +10184,49 @@ var ManagedRuntimeConfigSchema = zod.z.object({
10158
10184
  gpuLayers: zod.z.number().int().default(0),
10159
10185
  /** Default: cpus-2, clamped ≥1 (resolved node-side). */
10160
10186
  threads: zod.z.number().int().optional(),
10161
- /** Concurrent slots. */
10187
+ /** Concurrent slots (`--parallel`). */
10162
10188
  parallel: zod.z.number().int().default(1),
10189
+ /** Logical batch size (`-b`). Larger = faster prompt ingest, more RAM. */
10190
+ batchSize: zod.z.number().int().positive().optional(),
10191
+ /** Physical batch / micro-batch (`-ub`). */
10192
+ ubatchSize: zod.z.number().int().positive().optional(),
10193
+ /**
10194
+ * `--flash-attn`. Cuts KV-cache memory on the backends that implement it and
10195
+ * is a no-op elsewhere, so it is offered rather than assumed.
10196
+ */
10197
+ flashAttention: zod.z.boolean().default(false),
10198
+ /**
10199
+ * `--mlock`. Pins the weights in RAM so the OS cannot page them out mid
10200
+ * inference. Costs the full model size in resident memory — which is exactly
10201
+ * what the RAM budget is counting.
10202
+ */
10203
+ mlock: zod.z.boolean().default(false),
10204
+ /**
10205
+ * `--no-mmap`. Reads the whole GGUF up front instead of mapping it. Slower to
10206
+ * start, but avoids the page-fault stalls a network or spinning-disk model
10207
+ * store produces on every first token.
10208
+ */
10209
+ noMmap: zod.z.boolean().default(false),
10210
+ /** `--cache-type-k` / `--cache-type-v` — quantising the KV cache is the
10211
+ * cheapest way to fit a longer context in the same RAM. */
10212
+ cacheTypeK: zod.z.enum([
10213
+ "f32",
10214
+ "f16",
10215
+ "q8_0",
10216
+ "q5_1",
10217
+ "q5_0",
10218
+ "q4_1",
10219
+ "q4_0"
10220
+ ]).optional(),
10221
+ cacheTypeV: zod.z.enum([
10222
+ "f32",
10223
+ "f16",
10224
+ "q8_0",
10225
+ "q5_1",
10226
+ "q5_0",
10227
+ "q4_1",
10228
+ "q4_0"
10229
+ ]).optional(),
10163
10230
  /** Else lazy: first generate boots it. */
10164
10231
  autoStart: zod.z.boolean().default(false),
10165
10232
  /** 0 = never; frees RAM after quiet periods. */
@@ -10262,10 +10329,44 @@ var LlmProfileSchema = zod.z.object({
10262
10329
  baseUrl: zod.z.string().optional(),
10263
10330
  /** ConfigUISchema type:'password' — never round-trips (spec §5). */
10264
10331
  apiKey: zod.z.string().optional(),
10332
+ /** Vision on/off. A vision call against a `false` profile is REFUSED, never
10333
+ * degraded to text — that shipped once and produced a confident answer to a
10334
+ * question about a picture nobody sent. */
10265
10335
  supportsVision: zod.z.boolean(),
10266
10336
  temperature: zod.z.number().min(0).max(2).optional(),
10337
+ /** Nucleus sampling. Every wire we speak has it. */
10338
+ topP: zod.z.number().min(0).max(1).optional(),
10339
+ /** Top-k sampling. Carried only by the wires that have it — NEITHER OpenAI
10340
+ * wire does, and the client drops it there (measured: the request body gets
10341
+ * `top_p` and no `top_k`). The profile editor hides the field wherever it
10342
+ * would change nothing; `KINDS_WITH_TOP_K` is the single owner of that list. */
10343
+ topK: zod.z.number().int().positive().optional(),
10267
10344
  maxTokens: zod.z.number().int().positive().optional(),
10345
+ /** Prompt context window. Advisory for cloud kinds (they enforce their own);
10346
+ * for `managed-local` it is the llama.cpp `--ctx-size` the runtime starts
10347
+ * the model with, so it is the one field that changes a PROCESS. */
10348
+ contextLength: zod.z.number().int().positive().optional(),
10349
+ /** Default system prompt. A caller's `system` REPLACES it (never appends —
10350
+ * two system prompts fighting is worse than either alone). */
10351
+ systemPrompt: zod.z.string().optional(),
10352
+ /** Total generation bound — the only one a unary call has. */
10268
10353
  timeoutMs: zod.z.number().int().positive().default(6e4),
10354
+ /** Wait for response headers only. */
10355
+ connectTimeoutMs: zod.z.number().int().positive().default(1e4),
10356
+ /** Accepted, but no output yet — a cold GPU load lives here. */
10357
+ firstTokenTimeoutMs: zod.z.number().int().positive().default(12e4),
10358
+ /** Output started then stopped. */
10359
+ idleTimeoutMs: zod.z.number().int().positive().default(6e4),
10360
+ /** Profile-level default. The per-consumer table and a per-call override
10361
+ * both beat it — see `resolveRetryPolicy`. */
10362
+ retry: LlmRetryPolicySchema.default({
10363
+ enabled: false,
10364
+ maxAttempts: 1
10365
+ }),
10366
+ /** Whether this profile may use tools. The tool-call plumbing rides the
10367
+ * library; the REGISTRY of callable tools is ours and is empty in v1, so a
10368
+ * `true` here buys the wiring, not behaviour, until tools are registered. */
10369
+ toolsEnabled: zod.z.boolean().default(false),
10269
10370
  extraHeaders: zod.z.record(zod.z.string(), zod.z.string()).optional(),
10270
10371
  /** kind === 'managed-local' only (spec §4). */
10271
10372
  runtime: ManagedRuntimeConfigSchema.optional()
@@ -10338,6 +10439,17 @@ var llmCapability = {
10338
10439
  methods: {
10339
10440
  generate: method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
10340
10441
  generateVision: method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }),
10442
+ /**
10443
+ * Stop a generation started with a `requestId`.
10444
+ *
10445
+ * Idempotent and always successful: cancelling an id that already finished,
10446
+ * never existed, or was cancelled a moment ago is a no-op. A caller that has
10447
+ * given up must never have to handle an error from giving up.
10448
+ */
10449
+ cancel: method(zod.z.object({
10450
+ addonId: zod.z.string().optional(),
10451
+ requestId: zod.z.string()
10452
+ }), zod.z.void(), { kind: "mutation" }),
10341
10453
  listProfileKinds: method(zod.z.object({}), zod.z.array(LlmProfileKindDescriptorSchema)),
10342
10454
  listProfiles: method(zod.z.object({}), zod.z.array(LlmProfileSchema)),
10343
10455
  upsertProfile: method(zod.z.object({ profile: LlmProfileSchema }), LlmProfileSchema, {
@@ -12312,28 +12424,36 @@ var NcOccupancyConditionSchema = zod.z.object({
12312
12424
  /**
12313
12425
  * Audio condition (IMMEDIATE trigger) — a rule on SOUND, not on a picture.
12314
12426
  *
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.
12427
+ * **TWO EXCLUSIVE MODES** (operator decision 2026-08-14, D157). Which one a
12428
+ * rule is in is not a stored field it is WHICH FILTER the rule carries, so
12429
+ * there is no second switch that can disagree with the first and every rule
12430
+ * authored before the decision migrates for free (`audioModeOf`):
12431
+ *
12432
+ * - **LABEL mode — `labels` present.** The rule fires on the FIRST frame the
12433
+ * classifier labels with one of them. No window, no percentage:
12434
+ * `hitPercent` and `samplingSeconds` are ignored, and the rule's own
12435
+ * `throttle` cooldown is the only brake. The per-label confidence floor is
12436
+ * the analyzer's (`classificationMinScore`, per device) — a label only
12437
+ * reaches this condition if the classifier was already confident enough.
12438
+ * - **LEVEL mode `dbThreshold` present, no labels.** The sampling window IS
12439
+ * the condition: at least `hitPercent`% of the samples over
12440
+ * `samplingSeconds` must be at or above `dbThreshold` dBFS (see
12441
+ * {@link NC_AUDIO_DBFS_FLOOR}: negative-going, `0` = full scale). The window
12442
+ * must be FULL before it can match a window open for two of its ten
12443
+ * seconds is 100% of nothing.
12444
+ *
12445
+ * **Why label mode has no window.** It had one, and it never fired: the
12446
+ * analyzer emits ~1 audio frame per second but YAMNet only LABELS one to three
12447
+ * of them per episode, even through continuous crying. The measured maximum
12448
+ * `hitPercent` over the whole live history was 40 — under the shipped default
12449
+ * of 60, so a label rule could not fire at all, ever. A percentage of frames is
12450
+ * the wrong question to ask of a sparse classifier.
12451
+ *
12452
+ * **Fail-closed when NEITHER is given** — every sample would be a trivial hit
12453
+ * and the rule would fire on silence. The schema cannot express "exactly one
12454
+ * of" without becoming a ZodEffects the cap path would have to special-case, so
12455
+ * the exclusivity is enforced where every editor writes (`patchAudio`) and a
12456
+ * legacy rule carrying both resolves to LABEL (the mode that fires).
12337
12457
  *
12338
12458
  * Labels are the audio macro classes (`AUDIO_MACRO_LABELS` / the NC taxonomy's
12339
12459
  * `audio-*` ids). Both spellings are accepted — the matcher normalizes the
@@ -12341,13 +12461,13 @@ var NcOccupancyConditionSchema = zod.z.object({
12341
12461
  * an operator who typed `dog` mean the same thing.
12342
12462
  */
12343
12463
  var NcAudioConditionSchema = zod.z.object({
12344
- /** Audio macro labels; absent = any sound (level-only rule). */
12464
+ /** LABEL MODE: audio macro labels. Present fires on the first labelled frame. */
12345
12465
  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. */
12466
+ /** LEVEL MODE: floor in dBFS (negative-going, `0` = full scale). */
12347
12467
  dbThreshold: zod.z.number().min(-96).max(0).optional(),
12348
- /** Percentage of the window's samples that must be hits (1–100). */
12468
+ /** LEVEL MODE ONLY: percentage of the window's samples that must be hits (1–100). */
12349
12469
  hitPercent: zod.z.number().int().min(1).max(100).default(60),
12350
- /** Length of the sampling window in seconds. */
12470
+ /** LEVEL MODE ONLY: length of the sampling window in seconds. */
12351
12471
  samplingSeconds: zod.z.number().int().min(1).max(300).default(10)
12352
12472
  });
12353
12473
  /**
@@ -26642,10 +26762,22 @@ var recordingExportCapability = {
26642
26762
  * thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
26643
26763
  * vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
26644
26764
  *
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.
26765
+ * **No `deviceConfig`, deliberately.** This shipped as the D14 widget archetype,
26766
+ * which put a "Scenes" tab on one camera's detail page. That is the wrong shape
26767
+ * for the thing: a scene is a standing question about the property ("is the bin
26768
+ * still out"), and the operator's question is "which of my scenes have tripped",
26769
+ * across every camera at once — not "what does camera 617 think". Buried one
26770
+ * camera deep it also could not be found. The surface is now a top-level admin
26771
+ * page (`/scenes`, `pages/Scenes.tsx`) that lists every scene on every camera and
26772
+ * picks the camera inside the create flow, the same shape Events and Faces have.
26773
+ *
26774
+ * The consequence to keep in mind: `host/scene-monitor-editor` is gone from
26775
+ * `HOST_WIDGETS` too. `scripts/check-host-widget-resolves.ts` asserts BOTH
26776
+ * directions, so a registration nobody declares fails exactly as loudly as a
26777
+ * declaration nobody registers. The editor is imported directly by the page.
26778
+ *
26779
+ * `status.kind:'push'` — the engine pushes on every hysteresis flip /
26780
+ * availability change; consumers never poll.
26649
26781
  */
26650
26782
  /** Extensible condition tag. Seeded 'day' | 'ir' (the two variants the operator
26651
26783
  * captures) plus 'night' | 'dawn' | 'dusk' from the resolver's sun-times band.
@@ -26794,12 +26926,6 @@ var sceneMonitorCapability = {
26794
26926
  kind: "wrapper",
26795
26927
  defaultActive: true,
26796
26928
  deviceTypes: [DeviceType.Camera],
26797
- deviceConfig: { ui: {
26798
- kind: "widget",
26799
- widgetId: "host/scene-monitor-editor",
26800
- tab: "scenes",
26801
- label: "Scenes"
26802
- } },
26803
26929
  methods: {
26804
26930
  listScenes: method(zod.z.object({ deviceId: zod.z.number() }), SceneMonitorStatusSchema),
26805
26931
  createScene: method(zod.z.object({
@@ -27658,6 +27784,61 @@ var NetworkAddressSchema = zod.z.object({
27658
27784
  family: zod.z.string(),
27659
27785
  internal: zod.z.boolean()
27660
27786
  });
27787
+ /**
27788
+ * Provenance of the site coordinates, and the whole reason this is not just two
27789
+ * numbers.
27790
+ *
27791
+ * - `operator-set` — a human typed it, or accepted a detection. Authoritative;
27792
+ * nothing overwrites it.
27793
+ * - `derived-from-ip` — the hub geolocated its own public IP once, because a
27794
+ * default that is right to a few kilometres beats the coarse UTC clock split
27795
+ * the sun-times consumers otherwise fall back to.
27796
+ *
27797
+ * The UI shows which one it is. An operator who cannot tell a guess from their
27798
+ * own input will eventually trust the guess.
27799
+ */
27800
+ var SiteLocationSourceSchema = zod.z.enum(["operator-set", "derived-from-ip"]);
27801
+ /**
27802
+ * Where the installation physically is — a fact of the SITE, not of any addon.
27803
+ *
27804
+ * It used to live in `pipeline-analytics`' global settings, which made a
27805
+ * property of the building a property of one analytics addon. Anything that
27806
+ * needs sun-times (scene condition variants today; anything solar tomorrow)
27807
+ * reads it from here.
27808
+ */
27809
+ var SiteLocationSchema = zod.z.object({
27810
+ /** WGS84 decimal degrees. */
27811
+ latitude: zod.z.number().min(-90).max(90),
27812
+ longitude: zod.z.number().min(-180).max(180),
27813
+ source: SiteLocationSourceSchema,
27814
+ /** Epoch ms the value was last written. */
27815
+ updatedAt: zod.z.number(),
27816
+ /**
27817
+ * Human-readable place the geo-IP service reported ("Napoli, IT"). Display
27818
+ * only — never parsed, never matched on. Absent for an operator-typed value.
27819
+ */
27820
+ label: zod.z.string().optional()
27821
+ });
27822
+ /**
27823
+ * The read shape: the location plus the honest state of the one-shot derivation.
27824
+ *
27825
+ * `derivationAttemptedAt` is what makes the "one call, ever" contract
27826
+ * inspectable. When it is set and `location` is null, the geo-IP lookup ran and
27827
+ * failed; the hub will NOT try again on its own — the fallback is declared
27828
+ * (consumers degrade to their own last resort) and the operator either types the
27829
+ * coordinates or presses detect.
27830
+ */
27831
+ var SiteLocationStatusSchema = zod.z.object({
27832
+ location: SiteLocationSchema.nullable(),
27833
+ derivationAttemptedAt: zod.z.number().nullable(),
27834
+ /** Why the last derivation failed, for the UI to show instead of a shrug. */
27835
+ derivationError: zod.z.string().nullable()
27836
+ });
27837
+ /** `null` clears the location and re-arms nothing — the derivation stays spent. */
27838
+ var SetSiteLocationInputSchema = zod.z.object({
27839
+ latitude: zod.z.number().min(-90).max(90),
27840
+ longitude: zod.z.number().min(-180).max(180)
27841
+ }).nullable();
27661
27842
  var systemCapability = {
27662
27843
  name: "system",
27663
27844
  scope: "system",
@@ -27675,6 +27856,32 @@ var systemCapability = {
27675
27856
  forceRetentionCleanup: method(zod.z.void(), zod.z.void(), {
27676
27857
  kind: "mutation",
27677
27858
  auth: "admin"
27859
+ }),
27860
+ /**
27861
+ * The site coordinates, deriving a default from the hub's public IP on the
27862
+ * FIRST read that finds nothing stored.
27863
+ *
27864
+ * The derivation is one-shot and bounded: one outbound request, a few
27865
+ * seconds, its outcome persisted either way. A hub with no internet pays it
27866
+ * once and never again, and neither boot nor any consumer is blocked on it —
27867
+ * the caller gets `location: null` and degrades exactly as it did before this
27868
+ * method existed.
27869
+ */
27870
+ getSiteLocation: method(zod.z.void(), SiteLocationStatusSchema),
27871
+ /** Operator input. Always lands as `source: 'operator-set'`. */
27872
+ setSiteLocation: method(SetSiteLocationInputSchema, SiteLocationStatusSchema, {
27873
+ kind: "mutation",
27874
+ auth: "admin"
27875
+ }),
27876
+ /**
27877
+ * Re-run the geo-IP derivation now. The ONLY way a spent or failed
27878
+ * derivation is retried — there is no timer, and no read path retries.
27879
+ * Overwrites an existing `derived-from-ip` value; refuses to clobber an
27880
+ * `operator-set` one.
27881
+ */
27882
+ detectSiteLocation: method(zod.z.void(), SiteLocationStatusSchema, {
27883
+ kind: "mutation",
27884
+ auth: "admin"
27678
27885
  })
27679
27886
  },
27680
27887
  /** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
@@ -31410,6 +31617,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
31410
31617
  addonId: null,
31411
31618
  access: "create"
31412
31619
  },
31620
+ "llm.cancel": {
31621
+ capName: "llm",
31622
+ capScope: "system",
31623
+ addonId: null,
31624
+ access: "create"
31625
+ },
31413
31626
  "llm.deleteModel": {
31414
31627
  capName: "llm",
31415
31628
  capScope: "system",
@@ -34344,6 +34557,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34344
34557
  addonId: null,
34345
34558
  access: "create"
34346
34559
  },
34560
+ "system.detectSiteLocation": {
34561
+ capName: "system",
34562
+ capScope: "system",
34563
+ addonId: null,
34564
+ access: "create"
34565
+ },
34347
34566
  "system.featureFlags": {
34348
34567
  capName: "system",
34349
34568
  capScope: "system",
@@ -34362,6 +34581,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34362
34581
  addonId: null,
34363
34582
  access: "view"
34364
34583
  },
34584
+ "system.getSiteLocation": {
34585
+ capName: "system",
34586
+ capScope: "system",
34587
+ addonId: null,
34588
+ access: "view"
34589
+ },
34365
34590
  "system.health": {
34366
34591
  capName: "system",
34367
34592
  capScope: "system",
@@ -34386,6 +34611,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
34386
34611
  addonId: null,
34387
34612
  access: "create"
34388
34613
  },
34614
+ "system.setSiteLocation": {
34615
+ capName: "system",
34616
+ capScope: "system",
34617
+ addonId: null,
34618
+ access: "create"
34619
+ },
34389
34620
  "terminalSession.adoptLegacyMonitor": {
34390
34621
  capName: "terminal-session",
34391
34622
  capScope: "system",