@camstack/addon-ai 0.4.80 → 0.4.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/addon.js +125 -127
  2. package/dist/addon.mjs +125 -127
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7818,111 +7818,6 @@ var CameraSwitchGroupSchema = object({
7818
7818
  fetchedAt: number$1()
7819
7819
  });
7820
7820
  /**
7821
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7822
- * an addon declares its channels in.
7823
- *
7824
- * ## Two axes, deliberately separated
7825
- *
7826
- * - **DECLARATION** — which channels exist. Only the addon knows:
7827
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7828
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7829
- * and rots silently. So a channel is declared where it is consulted, and the
7830
- * `log-channels` capability enumerates the declarations.
7831
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7832
- * thing: the logging settings document on the `system` cap. Two authorities
7833
- * over the values is the exact defect
7834
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7835
- * remove; re-introducing it from the cure side would be grotesque.
7836
- *
7837
- * Nothing in this file reads a clock, an env var or a store. The registry is
7838
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7839
- * the hot path with a value somebody actually read, and by
7840
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7841
- * never reaches here, so it can neither disarm an armed channel nor arm a
7842
- * disarmed one (D49).
7843
- *
7844
- * ## The canonical call shape
7845
- *
7846
- * ```ts
7847
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7848
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7849
- * }
7850
- * ```
7851
- *
7852
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7853
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7854
- * object literal is never constructed because it lives inside the branch. It
7855
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7856
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7857
- * destination floor (measured at 1.93 ns/call when off).
7858
- *
7859
- * ## Why a channel emits at `info`
7860
- *
7861
- * `loki-logging.addon.ts` pins the destination default at `info` and
7862
- * `loki-destination.ts` drops everything below it, so a line emitted at
7863
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7864
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7865
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7866
- * emits at the channel's declared level, whose schema floor is `info`.
7867
- */
7868
- /**
7869
- * The level a channel writes at once armed.
7870
- *
7871
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7872
- * not leave the process for Loki, and the whole point of arming a channel is
7873
- * to read it later.
7874
- */
7875
- var LogChannelLevelSchema = _enum([
7876
- "info",
7877
- "warn",
7878
- "error"
7879
- ]);
7880
- /**
7881
- * What an addon declares about one channel. No value, no state — a
7882
- * declaration is inert.
7883
- */
7884
- var LogChannelDescriptorSchema = object({
7885
- /**
7886
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7887
- * the addon's short name so an operator reading a channel list can tell who
7888
- * owns it without a second lookup.
7889
- */
7890
- name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7891
- /** One sentence: what the operator will SEE after arming it. */
7892
- description: string().min(1),
7893
- /** The level its lines are emitted at. Never below `info`. */
7894
- defaultLevel: LogChannelLevelSchema,
7895
- /**
7896
- * Whether this channel can be narrowed to a camera.
7897
- *
7898
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7899
- * consulted with the numeric device id, AND every line the channel admits
7900
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7901
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7902
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7903
- * the body is the only way to filter.
7904
- *
7905
- * A channel whose lines carry the device only in `meta` (or not at all) is
7906
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7907
- * the operator narrows to one camera, sees nothing, and concludes the code
7908
- * path was never taken.
7909
- */
7910
- perDevice: boolean()
7911
- });
7912
- /**
7913
- * An armed window over one channel, as the document hands it to a mirror.
7914
- *
7915
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7916
- * expires by itself, which is the one failure a boolean cannot avoid.
7917
- */
7918
- var LogChannelWindowSchema = object({
7919
- channel: string().min(1),
7920
- /** Epoch ms the window closes at. */
7921
- armedUntilMs: number$1(),
7922
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7923
- deviceIds: array(number$1().int()).readonly().nullable()
7924
- });
7925
- /**
7926
7821
  * Ops-log — the durable, append-only operations audit shared by the
7927
7822
  * recordings and events management surfaces.
7928
7823
  *
@@ -8868,8 +8763,11 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8868
8763
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8869
8764
  *
8870
8765
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8871
- * The default location for a type uses `id === <type>:default` by
8872
- * convention (the bare type ref like `'backups'` resolves to it).
8766
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8767
+ * There is no default location any more (D383): `enabled` is the whole write
8768
+ * model, and a bare type ref resolves to the sole location of the type, or —
8769
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8770
+ * slug is `default`.
8873
8771
  *
8874
8772
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8875
8773
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8890,21 +8788,20 @@ var StorageLocationSchema = object({
8890
8788
  * flag at upsert time, not here (the schema is provider-agnostic).
8891
8789
  */
8892
8790
  nodeId: string().optional(),
8893
- isDefault: boolean().default(false),
8894
8791
  isSystem: boolean().default(false),
8895
8792
  /**
8896
- * Operator opt-in: whether consumers that BALANCE across several locations
8897
- * of a type may write here. Recordings reads it today; event media and
8898
- * backups are the next consumers, which is why the flag lives on the
8899
- * location rather than in any one addon's store — nothing has to be
8900
- * extended to add the next consumer.
8793
+ * THE write switch, and the only one (D383). `enabled: true` means every
8794
+ * consumer that chooses a write target for this type may write here, and all
8795
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8796
+ * still read, still played back, still age-swept, still drained, never
8797
+ * written.
8901
8798
  *
8902
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8903
- * flag existed reads back with no flag and keeps working exactly as before;
8904
- * that is the whole compat story, and it is why no migration ships with it.
8905
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8906
- * disk must not silently start writing to it); the default of a type is
8907
- * always stamped `true`.
8799
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8800
+ * stored" on an update and "born inert unless it is the first location of its
8801
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8802
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8803
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8804
+ * stops existing rather than being re-derived on every read.
8908
8805
  */
8909
8806
  enabled: boolean().optional(),
8910
8807
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8917,10 +8814,12 @@ var StorageLocationSchema = object({
8917
8814
  createdAt: number$1(),
8918
8815
  updatedAt: number$1()
8919
8816
  });
8817
+ object({ isDefault: boolean().optional() });
8920
8818
  /**
8921
8819
  * Reference accepted by consumer-facing `api.storage.*` calls.
8922
8820
  * Either:
8923
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8821
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8822
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8924
8823
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8925
8824
  *
8926
8825
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -9096,6 +8995,111 @@ var DecoderSessionConfigSchema = object({
9096
8995
  */
9097
8996
  debug: boolean().optional()
9098
8997
  });
8998
+ /**
8999
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
9000
+ * an addon declares its channels in.
9001
+ *
9002
+ * ## Two axes, deliberately separated
9003
+ *
9004
+ * - **DECLARATION** — which channels exist. Only the addon knows:
9005
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
9006
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
9007
+ * and rots silently. So a channel is declared where it is consulted, and the
9008
+ * `log-channels` capability enumerates the declarations.
9009
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
9010
+ * thing: the logging settings document on the `system` cap. Two authorities
9011
+ * over the values is the exact defect
9012
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
9013
+ * remove; re-introducing it from the cure side would be grotesque.
9014
+ *
9015
+ * Nothing in this file reads a clock, an env var or a store. The registry is
9016
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
9017
+ * the hot path with a value somebody actually read, and by
9018
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
9019
+ * never reaches here, so it can neither disarm an armed channel nor arm a
9020
+ * disarmed one (D49).
9021
+ *
9022
+ * ## The canonical call shape
9023
+ *
9024
+ * ```ts
9025
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
9026
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
9027
+ * }
9028
+ * ```
9029
+ *
9030
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
9031
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
9032
+ * object literal is never constructed because it lives inside the branch. It
9033
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
9034
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
9035
+ * destination floor (measured at 1.93 ns/call when off).
9036
+ *
9037
+ * ## Why a channel emits at `info`
9038
+ *
9039
+ * `loki-logging.addon.ts` pins the destination default at `info` and
9040
+ * `loki-destination.ts` drops everything below it, so a line emitted at
9041
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
9042
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
9043
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
9044
+ * emits at the channel's declared level, whose schema floor is `info`.
9045
+ */
9046
+ /**
9047
+ * The level a channel writes at once armed.
9048
+ *
9049
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
9050
+ * not leave the process for Loki, and the whole point of arming a channel is
9051
+ * to read it later.
9052
+ */
9053
+ var LogChannelLevelSchema = _enum([
9054
+ "info",
9055
+ "warn",
9056
+ "error"
9057
+ ]);
9058
+ /**
9059
+ * What an addon declares about one channel. No value, no state — a
9060
+ * declaration is inert.
9061
+ */
9062
+ var LogChannelDescriptorSchema = object({
9063
+ /**
9064
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
9065
+ * the addon's short name so an operator reading a channel list can tell who
9066
+ * owns it without a second lookup.
9067
+ */
9068
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
9069
+ /** One sentence: what the operator will SEE after arming it. */
9070
+ description: string().min(1),
9071
+ /** The level its lines are emitted at. Never below `info`. */
9072
+ defaultLevel: LogChannelLevelSchema,
9073
+ /**
9074
+ * Whether this channel can be narrowed to a camera.
9075
+ *
9076
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
9077
+ * consulted with the numeric device id, AND every line the channel admits
9078
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
9079
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
9080
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
9081
+ * the body is the only way to filter.
9082
+ *
9083
+ * A channel whose lines carry the device only in `meta` (or not at all) is
9084
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
9085
+ * the operator narrows to one camera, sees nothing, and concludes the code
9086
+ * path was never taken.
9087
+ */
9088
+ perDevice: boolean()
9089
+ });
9090
+ /**
9091
+ * An armed window over one channel, as the document hands it to a mirror.
9092
+ *
9093
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
9094
+ * expires by itself, which is the one failure a boolean cannot avoid.
9095
+ */
9096
+ var LogChannelWindowSchema = object({
9097
+ channel: string().min(1),
9098
+ /** Epoch ms the window closes at. */
9099
+ armedUntilMs: number$1(),
9100
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
9101
+ deviceIds: array(number$1().int()).readonly().nullable()
9102
+ });
9099
9103
  var MODEL_FORMATS = [
9100
9104
  "onnx",
9101
9105
  "coreml",
@@ -21854,7 +21858,7 @@ method(object({
21854
21858
  downloadId: string(),
21855
21859
  offset: number$1(),
21856
21860
  length: number$1()
21857
- }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21861
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21858
21862
  createdAt: true,
21859
21863
  updatedAt: true
21860
21864
  }), StorageLocationSchema, {
@@ -34703,12 +34707,6 @@ Object.freeze({
34703
34707
  addonId: null,
34704
34708
  access: "view"
34705
34709
  },
34706
- "storage.getDefaultLocation": {
34707
- capName: "storage",
34708
- capScope: "system",
34709
- addonId: null,
34710
- access: "view"
34711
- },
34712
34710
  "storage.list": {
34713
34711
  capName: "storage",
34714
34712
  capScope: "system",
package/dist/addon.mjs CHANGED
@@ -7845,111 +7845,6 @@ var CameraSwitchGroupSchema = object({
7845
7845
  fetchedAt: number$1()
7846
7846
  });
7847
7847
  /**
7848
- * Per-component log CHANNELS — the gate a hot path consults, and the registry
7849
- * an addon declares its channels in.
7850
- *
7851
- * ## Two axes, deliberately separated
7852
- *
7853
- * - **DECLARATION** — which channels exist. Only the addon knows:
7854
- * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
7855
- * baichuan/handshake. A hand-wired central list rots at the first addition,
7856
- * and rots silently. So a channel is declared where it is consulted, and the
7857
- * `log-channels` capability enumerates the declarations.
7858
- * - **VALUE** — at which level, for which scope, until when. That stays ONE
7859
- * thing: the logging settings document on the `system` cap. Two authorities
7860
- * over the values is the exact defect
7861
- * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
7862
- * remove; re-introducing it from the cure side would be grotesque.
7863
- *
7864
- * Nothing in this file reads a clock, an env var or a store. The registry is
7865
- * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
7866
- * the hot path with a value somebody actually read, and by
7867
- * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
7868
- * never reaches here, so it can neither disarm an armed channel nor arm a
7869
- * disarmed one (D49).
7870
- *
7871
- * ## The canonical call shape
7872
- *
7873
- * ```ts
7874
- * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
7875
- * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
7876
- * }
7877
- * ```
7878
- *
7879
- * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
7880
- * read. Disarmed, a call site costs one load and one branch, and the `extras`
7881
- * object literal is never constructed because it lives inside the branch. It
7882
- * is the same shape already proven in production at `stream-broker.ts:1650`,
7883
- * and the same discipline `LoggingGate.allowsDestination` uses for the
7884
- * destination floor (measured at 1.93 ns/call when off).
7885
- *
7886
- * ## Why a channel emits at `info`
7887
- *
7888
- * `loki-logging.addon.ts` pins the destination default at `info` and
7889
- * `loki-destination.ts` drops everything below it, so a line emitted at
7890
- * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
7891
- * minutes. A diagnostic that cannot be read an hour later is worse than no
7892
- * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
7893
- * emits at the channel's declared level, whose schema floor is `info`.
7894
- */
7895
- /**
7896
- * The level a channel writes at once armed.
7897
- *
7898
- * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
7899
- * not leave the process for Loki, and the whole point of arming a channel is
7900
- * to read it later.
7901
- */
7902
- var LogChannelLevelSchema = _enum([
7903
- "info",
7904
- "warn",
7905
- "error"
7906
- ]);
7907
- /**
7908
- * What an addon declares about one channel. No value, no state — a
7909
- * declaration is inert.
7910
- */
7911
- var LogChannelDescriptorSchema = object({
7912
- /**
7913
- * Dotted `area.thing`, unique across the workspace. `area` is conventionally
7914
- * the addon's short name so an operator reading a channel list can tell who
7915
- * owns it without a second lookup.
7916
- */
7917
- name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
7918
- /** One sentence: what the operator will SEE after arming it. */
7919
- description: string().min(1),
7920
- /** The level its lines are emitted at. Never below `info`. */
7921
- defaultLevel: LogChannelLevelSchema,
7922
- /**
7923
- * Whether this channel can be narrowed to a camera.
7924
- *
7925
- * `true` is a PROMISE with two halves, and both must hold: the gate is
7926
- * consulted with the numeric device id, AND every line the channel admits
7927
- * carries `tags: { deviceId }` with that same numeric id. The second half is
7928
- * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
7929
- * keeps `deviceId` out of the stream labels for cardinality, so the tag in
7930
- * the body is the only way to filter.
7931
- *
7932
- * A channel whose lines carry the device only in `meta` (or not at all) is
7933
- * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
7934
- * the operator narrows to one camera, sees nothing, and concludes the code
7935
- * path was never taken.
7936
- */
7937
- perDevice: boolean()
7938
- });
7939
- /**
7940
- * An armed window over one channel, as the document hands it to a mirror.
7941
- *
7942
- * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
7943
- * expires by itself, which is the one failure a boolean cannot avoid.
7944
- */
7945
- var LogChannelWindowSchema = object({
7946
- channel: string().min(1),
7947
- /** Epoch ms the window closes at. */
7948
- armedUntilMs: number$1(),
7949
- /** `null` = every camera. A non-empty list narrows to those numeric ids. */
7950
- deviceIds: array(number$1().int()).readonly().nullable()
7951
- });
7952
- /**
7953
7848
  * Ops-log — the durable, append-only operations audit shared by the
7954
7849
  * recordings and events management surfaces.
7955
7850
  *
@@ -8895,8 +8790,11 @@ var StorageLocationTypeSchema = string().regex(/^[a-z][a-zA-Z0-9-]*$/);
8895
8790
  * `STORAGE_LOCATION_CARDINALITY` map has been removed.
8896
8791
  *
8897
8792
  * `id` is a stable namespaced string of the form `<type>:<slug>`.
8898
- * The default location for a type uses `id === <type>:default` by
8899
- * convention (the bare type ref like `'backups'` resolves to it).
8793
+ * The seed names its first instance `<type>:default` — a NAME, not a flag.
8794
+ * There is no default location any more (D383): `enabled` is the whole write
8795
+ * model, and a bare type ref resolves to the sole location of the type, or —
8796
+ * transitionally, only while legacy NULL-stamped rows exist — to the row whose
8797
+ * slug is `default`.
8900
8798
  *
8901
8799
  * `isSystem` is a legacy persisted flag. Seed still creates the initial
8902
8800
  * `<type>:default` locations; the flag is no longer a lock, a badge, or a
@@ -8917,21 +8815,20 @@ var StorageLocationSchema = object({
8917
8815
  * flag at upsert time, not here (the schema is provider-agnostic).
8918
8816
  */
8919
8817
  nodeId: string().optional(),
8920
- isDefault: boolean().default(false),
8921
8818
  isSystem: boolean().default(false),
8922
8819
  /**
8923
- * Operator opt-in: whether consumers that BALANCE across several locations
8924
- * of a type may write here. Recordings reads it today; event media and
8925
- * backups are the next consumers, which is why the flag lives on the
8926
- * location rather than in any one addon's store — nothing has to be
8927
- * extended to add the next consumer.
8820
+ * THE write switch, and the only one (D383). `enabled: true` means every
8821
+ * consumer that chooses a write target for this type may write here, and all
8822
+ * enabled locations of a type are used TOGETHER; `false` means read-only —
8823
+ * still read, still played back, still age-swept, still drained, never
8824
+ * written.
8928
8825
  *
8929
- * OPTIONAL, and ABSENT MEANS ACTIVE. Every location persisted before the
8930
- * flag existed reads back with no flag and keeps working exactly as before;
8931
- * that is the whole compat story, and it is why no migration ships with it.
8932
- * A newly CREATED sibling is stamped `false` by the orchestrator (creating a
8933
- * disk must not silently start writing to it); the default of a type is
8934
- * always stamped `true`.
8826
+ * OPTIONAL only for the wire: an upsert that omits it means "leave what is
8827
+ * stored" on an update and "born inert unless it is the first location of its
8828
+ * type" on a create. On a PERSISTED row absence is legacy and it means
8829
+ * enabled — {@link isLocationEnabled} is the one place that says so, and the
8830
+ * orchestrator stamps every flagless row `true` once at hydrate so absence
8831
+ * stops existing rather than being re-derived on every read.
8935
8832
  */
8936
8833
  enabled: boolean().optional(),
8937
8834
  /** COMPUTED at read time by the orchestrator (statfs of the backing volume
@@ -8944,10 +8841,12 @@ var StorageLocationSchema = object({
8944
8841
  createdAt: number$1(),
8945
8842
  updatedAt: number$1()
8946
8843
  });
8844
+ object({ isDefault: boolean().optional() });
8947
8845
  /**
8948
8846
  * Reference accepted by consumer-facing `api.storage.*` calls.
8949
8847
  * Either:
8950
- * - a `StorageLocationType` (e.g. `'backups'`) → orchestrator resolves to the default of that type
8848
+ * - a `StorageLocationType` (e.g. `'backups'`) → the sole location of that type
8849
+ * (transitionally, the `<type>:default`-slugged row when several exist)
8951
8850
  * - a fully-qualified id (e.g. `'backups:nas-01'`) → addresses a specific instance
8952
8851
  *
8953
8852
  * The orchestrator's `resolveRef(ref)` handles both cases.
@@ -9123,6 +9022,111 @@ var DecoderSessionConfigSchema = object({
9123
9022
  */
9124
9023
  debug: boolean().optional()
9125
9024
  });
9025
+ /**
9026
+ * Per-component log CHANNELS — the gate a hot path consults, and the registry
9027
+ * an addon declares its channels in.
9028
+ *
9029
+ * ## Two axes, deliberately separated
9030
+ *
9031
+ * - **DECLARATION** — which channels exist. Only the addon knows:
9032
+ * `stream-broker` knows webrtc/ICE/RTP, `provider-reolink` knows
9033
+ * baichuan/handshake. A hand-wired central list rots at the first addition,
9034
+ * and rots silently. So a channel is declared where it is consulted, and the
9035
+ * `log-channels` capability enumerates the declarations.
9036
+ * - **VALUE** — at which level, for which scope, until when. That stays ONE
9037
+ * thing: the logging settings document on the `system` cap. Two authorities
9038
+ * over the values is the exact defect
9039
+ * `docs/design/plans/2026-08-26-logging-per-componente.md` was written to
9040
+ * remove; re-introducing it from the cure side would be grotesque.
9041
+ *
9042
+ * Nothing in this file reads a clock, an env var or a store. The registry is
9043
+ * a MIRROR: it is moved only by {@link LogChannelRegistry.apply}, called off
9044
+ * the hot path with a value somebody actually read, and by
9045
+ * {@link LogChannelRegistry.tick}, called on a timer. A store read that fails
9046
+ * never reaches here, so it can neither disarm an armed channel nor arm a
9047
+ * disarmed one (D49).
9048
+ *
9049
+ * ## The canonical call shape
9050
+ *
9051
+ * ```ts
9052
+ * if (CH_RTP.on && CH_RTP.wants(deviceId)) {
9053
+ * CH_RTP.log(logger, 'rtp subscriber added', { tags: { deviceId }, meta: { ssrc } })
9054
+ * }
9055
+ * ```
9056
+ *
9057
+ * `on` is a plain boolean FIELD — never a getter — and it is the FIRST thing
9058
+ * read. Disarmed, a call site costs one load and one branch, and the `extras`
9059
+ * object literal is never constructed because it lives inside the branch. It
9060
+ * is the same shape already proven in production at `stream-broker.ts:1650`,
9061
+ * and the same discipline `LoggingGate.allowsDestination` uses for the
9062
+ * destination floor (measured at 1.93 ns/call when off).
9063
+ *
9064
+ * ## Why a channel emits at `info`
9065
+ *
9066
+ * `loki-logging.addon.ts` pins the destination default at `info` and
9067
+ * `loki-destination.ts` drops everything below it, so a line emitted at
9068
+ * `debug` never reaches Loki and the hub's in-memory ring only holds ~35
9069
+ * minutes. A diagnostic that cannot be read an hour later is worse than no
9070
+ * diagnostic, because it looks done. {@link LogChannelGate.log} therefore
9071
+ * emits at the channel's declared level, whose schema floor is `info`.
9072
+ */
9073
+ /**
9074
+ * The level a channel writes at once armed.
9075
+ *
9076
+ * `debug` is absent ON PURPOSE and not by omission: below `info` the line does
9077
+ * not leave the process for Loki, and the whole point of arming a channel is
9078
+ * to read it later.
9079
+ */
9080
+ var LogChannelLevelSchema = _enum([
9081
+ "info",
9082
+ "warn",
9083
+ "error"
9084
+ ]);
9085
+ /**
9086
+ * What an addon declares about one channel. No value, no state — a
9087
+ * declaration is inert.
9088
+ */
9089
+ var LogChannelDescriptorSchema = object({
9090
+ /**
9091
+ * Dotted `area.thing`, unique across the workspace. `area` is conventionally
9092
+ * the addon's short name so an operator reading a channel list can tell who
9093
+ * owns it without a second lookup.
9094
+ */
9095
+ name: string().min(3).regex(/^[a-z0-9-]+(\.[a-z0-9-]+)+$/, "a channel name is dotted lower-kebab, e.g. area.thing"),
9096
+ /** One sentence: what the operator will SEE after arming it. */
9097
+ description: string().min(1),
9098
+ /** The level its lines are emitted at. Never below `info`. */
9099
+ defaultLevel: LogChannelLevelSchema,
9100
+ /**
9101
+ * Whether this channel can be narrowed to a camera.
9102
+ *
9103
+ * `true` is a PROMISE with two halves, and both must hold: the gate is
9104
+ * consulted with the numeric device id, AND every line the channel admits
9105
+ * carries `tags: { deviceId }` with that same numeric id. The second half is
9106
+ * what makes `| json | deviceId="617"` work in Loki — `loki-payload.ts`
9107
+ * keeps `deviceId` out of the stream labels for cardinality, so the tag in
9108
+ * the body is the only way to filter.
9109
+ *
9110
+ * A channel whose lines carry the device only in `meta` (or not at all) is
9111
+ * declared `false`. Declaring it `true` anyway would be a lie the UI repeats:
9112
+ * the operator narrows to one camera, sees nothing, and concludes the code
9113
+ * path was never taken.
9114
+ */
9115
+ perDevice: boolean()
9116
+ });
9117
+ /**
9118
+ * An armed window over one channel, as the document hands it to a mirror.
9119
+ *
9120
+ * A window is a DEADLINE, never a flag (ADR-0244): a channel somebody forgot
9121
+ * expires by itself, which is the one failure a boolean cannot avoid.
9122
+ */
9123
+ var LogChannelWindowSchema = object({
9124
+ channel: string().min(1),
9125
+ /** Epoch ms the window closes at. */
9126
+ armedUntilMs: number$1(),
9127
+ /** `null` = every camera. A non-empty list narrows to those numeric ids. */
9128
+ deviceIds: array(number$1().int()).readonly().nullable()
9129
+ });
9126
9130
  var MODEL_FORMATS = [
9127
9131
  "onnx",
9128
9132
  "coreml",
@@ -21881,7 +21885,7 @@ method(object({
21881
21885
  downloadId: string(),
21882
21886
  offset: number$1(),
21883
21887
  length: number$1()
21884
- }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(object({ type: StorageLocationTypeSchema }), StorageLocationSchema.nullable()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21888
+ }), _instanceof(Uint8Array)), method(object({ downloadId: string() }), _void(), { kind: "mutation" }), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
21885
21889
  createdAt: true,
21886
21890
  updatedAt: true
21887
21891
  }), StorageLocationSchema, {
@@ -34730,12 +34734,6 @@ Object.freeze({
34730
34734
  addonId: null,
34731
34735
  access: "view"
34732
34736
  },
34733
- "storage.getDefaultLocation": {
34734
- capName: "storage",
34735
- capScope: "system",
34736
- addonId: null,
34737
- access: "view"
34738
- },
34739
34737
  "storage.list": {
34740
34738
  capName: "storage",
34741
34739
  capScope: "system",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.4.80",
3
+ "version": "0.4.81",
4
4
  "description": "AI addon for CamStack — the `llm` collection provider (cloud, LAN, and camstack-managed local llama.cpp profiles) plus the per-node `llm-runtime` managed executor.",
5
5
  "keywords": [
6
6
  "camstack",