@camstack/addon-ai 0.4.4 → 0.4.6

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 +124 -12
  2. package/dist/addon.mjs +124 -12
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -11021,6 +11021,8 @@ var QueryFilterSchema = object({
11021
11021
  where: record(string(), unknown()).optional(),
11022
11022
  whereIn: record(string(), array(unknown())).optional(),
11023
11023
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11024
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
11025
+ whereNot: record(string(), unknown()).optional(),
11024
11026
  orderBy: object({
11025
11027
  field: string(),
11026
11028
  direction: _enum(["asc", "desc"])
@@ -11040,7 +11042,8 @@ var QueryFilterSchema = object({
11040
11042
  var MutationFilterSchema = object({
11041
11043
  where: record(string(), unknown()).optional(),
11042
11044
  whereIn: record(string(), array(unknown())).optional(),
11043
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
11045
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11046
+ whereNot: record(string(), unknown()).optional()
11044
11047
  });
11045
11048
  /** A single stored record: `{ id, data }`. */
11046
11049
  var SettingsRecordSchema = object({
@@ -16997,7 +17000,10 @@ var RecentTracksQueryInput = object({
16997
17000
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
16998
17001
  cursor: string().optional(),
16999
17002
  /** See {@link TrackProjectionSchema}. Default `full`. */
17000
- projection: TrackProjectionSchema.optional()
17003
+ projection: TrackProjectionSchema.optional(),
17004
+ /** Include stationary-promoted rows (parked objects). Default false: the
17005
+ * feed lists passages; parking records live on the stationary registry. */
17006
+ includeStationary: boolean().optional()
17001
17007
  });
17002
17008
  var RecentTracksPageSchema = object({
17003
17009
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -17215,7 +17221,11 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
17215
17221
  zone: TrackZoneFilterSchema.optional(),
17216
17222
  /** See {@link TrackProjectionSchema}. Default `full` (backward
17217
17223
  * compatible — omitting the field keeps today's exact behaviour). */
17218
- projection: TrackProjectionSchema.optional()
17224
+ projection: TrackProjectionSchema.optional(),
17225
+ /** Include stationary-promoted rows (parked objects handed to the
17226
+ * stationary registry). Default false: the timeline lists passages,
17227
+ * not parking records (operator decision, 2026-08-15). */
17228
+ includeStationary: boolean().optional()
17219
17229
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number$1() }), _void(), {
17220
17230
  kind: "mutation",
17221
17231
  auth: "admin"
@@ -17379,11 +17389,16 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
17379
17389
  auth: "admin"
17380
17390
  }), method(object({
17381
17391
  eventId: string(),
17382
- kind: MediaFileKindEnum.optional()
17392
+ kind: MediaFileKindEnum.optional(),
17393
+ deviceId: number$1()
17394
+ }), array(MediaFileSchema).readonly()), method(object({
17395
+ trackId: string(),
17396
+ kinds: array(MediaFileKindEnum).optional(),
17397
+ deviceId: number$1()
17383
17398
  }), array(MediaFileSchema).readonly()), method(object({
17384
17399
  trackId: string(),
17385
- kinds: array(MediaFileKindEnum).optional()
17386
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17400
+ deviceId: number$1()
17401
+ }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17387
17402
  kind: "mutation",
17388
17403
  auth: "admin"
17389
17404
  }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
@@ -19332,7 +19347,31 @@ DeviceType.Camera, method(object({
19332
19347
  lastCapturedAt: number$1().nullable(),
19333
19348
  cacheAgeMs: number$1().nullable(),
19334
19349
  etag: string().nullable()
19335
- }))), systemMethod(object({
19350
+ }))), systemMethod(object({ deviceId: number$1() }), object({
19351
+ /** The battery slice as read, or null when the device has none. */
19352
+ battery: object({
19353
+ sleeping: boolean(),
19354
+ lastUpdated: number$1(),
19355
+ lastContactAt: number$1().optional()
19356
+ }).nullable(),
19357
+ /** The resolved snapshot state (what the overlay decision used). */
19358
+ state: object({
19359
+ isBattery: boolean(),
19360
+ reason: _enum([
19361
+ "disabled",
19362
+ "sleeping",
19363
+ "unreachable",
19364
+ "waking"
19365
+ ]).nullable()
19366
+ }),
19367
+ /** The cached frame behind the next paint. */
19368
+ frame: object({
19369
+ capturedAt: number$1().nullable(),
19370
+ ageMs: number$1().nullable()
19371
+ }),
19372
+ /** A wake window is currently open (the Waking overlay's source). */
19373
+ waking: boolean()
19374
+ })), systemMethod(object({
19336
19375
  /** The tiles a surface is actually rendering. One entry per (device,
19337
19376
  * width) the caller will paint — the width is snapped to the server's
19338
19377
  * ladder and becomes part of the link's SIGNED identity. */
@@ -19362,7 +19401,16 @@ targets: array(object({
19362
19401
  /** A sleeping battery camera: the frame is deliberately stale and will
19363
19402
  * NOT refresh in the background. A surface should say so rather than
19364
19403
  * present it as current. */
19365
- sleeping: boolean()
19404
+ sleeping: boolean(),
19405
+ /** Current device state rendered over the cached frame. State images
19406
+ * remain authoritative even when their photographic background is
19407
+ * old; null means the link must carry a current camera frame. */
19408
+ stateReason: _enum([
19409
+ "disabled",
19410
+ "sleeping",
19411
+ "unreachable",
19412
+ "waking"
19413
+ ]).nullable()
19366
19414
  })));
19367
19415
  /**
19368
19416
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -20884,6 +20932,25 @@ var BatteryStatusSchema = object({
20884
20932
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
20885
20933
  lastUpdated: number$1(),
20886
20934
  /**
20935
+ * Ms epoch of the last time the device PROVED it was reachable — a
20936
+ * completed firmware round-trip, an observed wake, or an inbound push
20937
+ * (firmware event, email). `0`/absent = never since this slice was born.
20938
+ *
20939
+ * This is the ONLY input that separates "asleep" from "gone", and it is
20940
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
20941
+ * for the radio, because a poll that confirms reachability is the same
20942
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
20943
+ * single derivation every consumer must use; no surface computes its own.
20944
+ *
20945
+ * It is deliberately NOT a clock in the
20946
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
20947
+ * observation itself, and it is the only thing a 30-hour silence is
20948
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
20949
+ * Reolink provider) so a value that means "recently" cannot cost a
20950
+ * SQLite commit per round-trip.
20951
+ */
20952
+ lastContactAt: number$1().optional(),
20953
+ /**
20887
20954
  * True when the source is a BINARY low-battery indicator (HA
20888
20955
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
20889
20956
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -25161,6 +25228,16 @@ var CamStreamDescriptorSchema = object({
25161
25228
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
25162
25229
  metadata: record(string(), unknown()).optional()
25163
25230
  });
25231
+ object({
25232
+ /** The descriptors as last built from a real camera response. Never a guess:
25233
+ * a failed or refused build writes NOTHING, so a restored catalog is always
25234
+ * one the camera itself once produced. */
25235
+ descriptors: array(CamStreamDescriptorSchema),
25236
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
25237
+ * path decide whether the camera's own awake window is worth spending on a
25238
+ * re-read. */
25239
+ lastFetchedAt: number$1()
25240
+ });
25164
25241
  DeviceType.Camera, method(object({ deviceId: number$1().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
25165
25242
  /** One of the camera's stream profiles. */
25166
25243
  var StreamProfileSchema = _enum([
@@ -30509,6 +30586,12 @@ Object.freeze({
30509
30586
  addonId: null,
30510
30587
  access: "view"
30511
30588
  },
30589
+ "snapshot.getDebugState": {
30590
+ capName: "snapshot",
30591
+ capScope: "device",
30592
+ addonId: null,
30593
+ access: "view"
30594
+ },
30512
30595
  "snapshot.getSnapshot": {
30513
30596
  capName: "snapshot",
30514
30597
  capScope: "device",
@@ -32591,6 +32674,11 @@ Object.freeze({
32591
32674
  form: "single",
32592
32675
  optional: false
32593
32676
  }],
32677
+ "pipelineAnalytics.getEventMedia": [{
32678
+ name: "deviceId",
32679
+ form: "single",
32680
+ optional: false
32681
+ }],
32594
32682
  "pipelineAnalytics.getKeyEvents": [{
32595
32683
  name: "deviceId",
32596
32684
  form: "single",
@@ -32621,6 +32709,11 @@ Object.freeze({
32621
32709
  form: "single",
32622
32710
  optional: false
32623
32711
  }],
32712
+ "pipelineAnalytics.getTrackMedia": [{
32713
+ name: "deviceId",
32714
+ form: "single",
32715
+ optional: false
32716
+ }],
32624
32717
  "pipelineAnalytics.getTrainingExportSummary": [{
32625
32718
  name: "deviceIds",
32626
32719
  form: "array",
@@ -32656,6 +32749,11 @@ Object.freeze({
32656
32749
  form: "array",
32657
32750
  optional: true
32658
32751
  }],
32752
+ "pipelineAnalytics.listTrackMedia": [{
32753
+ name: "deviceId",
32754
+ form: "single",
32755
+ optional: false
32756
+ }],
32659
32757
  "pipelineAnalytics.listTracks": [{
32660
32758
  name: "deviceId",
32661
32759
  form: "single",
@@ -33091,11 +33189,22 @@ Object.freeze({
33091
33189
  form: "single",
33092
33190
  optional: false
33093
33191
  }],
33192
+ "snapshot.getDebugState": [{
33193
+ name: "deviceId",
33194
+ form: "single",
33195
+ optional: false
33196
+ }],
33094
33197
  "snapshot.getSnapshot": [{
33095
33198
  name: "deviceId",
33096
33199
  form: "single",
33097
33200
  optional: false
33098
33201
  }],
33202
+ "snapshot.getSnapshotLinks": [{
33203
+ name: "targets",
33204
+ form: "object-array",
33205
+ optional: false,
33206
+ itemField: "deviceId"
33207
+ }],
33099
33208
  "snapshot.getSnapshotOverview": [{
33100
33209
  name: "deviceIds",
33101
33210
  form: "array",
@@ -72400,7 +72509,9 @@ var TestChatImageRefSchema = discriminatedUnion("kind", [object({
72400
72509
  deviceId: number$1().int().positive()
72401
72510
  }), object({
72402
72511
  kind: literal("track"),
72403
- trackId: string().min(1)
72512
+ trackId: string().min(1),
72513
+ /** Owning device — `getTrackMedia` requires it as the read's scope. */
72514
+ deviceId: number$1().int().positive()
72404
72515
  })]);
72405
72516
  /**
72406
72517
  * `.strict()`, and it is load-bearing.
@@ -72626,7 +72737,7 @@ async function resolveImage(deps, ref) {
72626
72737
  }
72627
72738
  let rows;
72628
72739
  try {
72629
- rows = await deps.getTrackMedia(ref.trackId, TRACK_MEDIA_PREFERENCE);
72740
+ rows = await deps.getTrackMedia(ref.trackId, TRACK_MEDIA_PREFERENCE, ref.deviceId);
72630
72741
  } catch (cause) {
72631
72742
  const message = cause instanceof Error ? cause.message : String(cause);
72632
72743
  deps.logger.warn("ai test chat: track media read failed — turn dropped", { meta: {
@@ -73221,9 +73332,10 @@ var AiAddon = class extends BaseAddon {
73221
73332
  });
73222
73333
  },
73223
73334
  getSnapshot: (deviceId) => api.snapshot.getSnapshot.query({ deviceId }),
73224
- getTrackMedia: (trackId, kinds) => api.pipelineAnalytics.getTrackMedia.query({
73335
+ getTrackMedia: (trackId, kinds, deviceId) => api.pipelineAnalytics.getTrackMedia.query({
73225
73336
  trackId,
73226
- kinds: [...kinds]
73337
+ kinds: [...kinds],
73338
+ deviceId
73227
73339
  }),
73228
73340
  recordUsage: (row) => usage.record(row),
73229
73341
  logger,
package/dist/addon.mjs CHANGED
@@ -11047,6 +11047,8 @@ var QueryFilterSchema = object({
11047
11047
  where: record(string(), unknown()).optional(),
11048
11048
  whereIn: record(string(), array(unknown())).optional(),
11049
11049
  whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11050
+ /** NULL-safe exclusion: matches rows whose field is NULL OR != the value. */
11051
+ whereNot: record(string(), unknown()).optional(),
11050
11052
  orderBy: object({
11051
11053
  field: string(),
11052
11054
  direction: _enum(["asc", "desc"])
@@ -11066,7 +11068,8 @@ var QueryFilterSchema = object({
11066
11068
  var MutationFilterSchema = object({
11067
11069
  where: record(string(), unknown()).optional(),
11068
11070
  whereIn: record(string(), array(unknown())).optional(),
11069
- whereBetween: record(string(), tuple([unknown(), unknown()])).optional()
11071
+ whereBetween: record(string(), tuple([unknown(), unknown()])).optional(),
11072
+ whereNot: record(string(), unknown()).optional()
11070
11073
  });
11071
11074
  /** A single stored record: `{ id, data }`. */
11072
11075
  var SettingsRecordSchema = object({
@@ -17023,7 +17026,10 @@ var RecentTracksQueryInput = object({
17023
17026
  * Encodes the (lastSeen, trackId) sort position — treat as opaque. */
17024
17027
  cursor: string().optional(),
17025
17028
  /** See {@link TrackProjectionSchema}. Default `full`. */
17026
- projection: TrackProjectionSchema.optional()
17029
+ projection: TrackProjectionSchema.optional(),
17030
+ /** Include stationary-promoted rows (parked objects). Default false: the
17031
+ * feed lists passages; parking records live on the stationary registry. */
17032
+ includeStationary: boolean().optional()
17027
17033
  });
17028
17034
  var RecentTracksPageSchema = object({
17029
17035
  /** Merged page, ordered by (`lastSeen` DESC, `trackId` DESC). */
@@ -17241,7 +17247,11 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
17241
17247
  zone: TrackZoneFilterSchema.optional(),
17242
17248
  /** See {@link TrackProjectionSchema}. Default `full` (backward
17243
17249
  * compatible — omitting the field keeps today's exact behaviour). */
17244
- projection: TrackProjectionSchema.optional()
17250
+ projection: TrackProjectionSchema.optional(),
17251
+ /** Include stationary-promoted rows (parked objects handed to the
17252
+ * stationary registry). Default false: the timeline lists passages,
17253
+ * not parking records (operator decision, 2026-08-15). */
17254
+ includeStationary: boolean().optional()
17245
17255
  }), array(TrackSchema).readonly()), method(RecentTracksQueryInput, RecentTracksPageSchema), method(object({ deviceId: number$1() }), _void(), {
17246
17256
  kind: "mutation",
17247
17257
  auth: "admin"
@@ -17405,11 +17415,16 @@ DeviceType.Camera, method(object({ deviceId: number$1() }), array(TrackSchema).r
17405
17415
  auth: "admin"
17406
17416
  }), method(object({
17407
17417
  eventId: string(),
17408
- kind: MediaFileKindEnum.optional()
17418
+ kind: MediaFileKindEnum.optional(),
17419
+ deviceId: number$1()
17420
+ }), array(MediaFileSchema).readonly()), method(object({
17421
+ trackId: string(),
17422
+ kinds: array(MediaFileKindEnum).optional(),
17423
+ deviceId: number$1()
17409
17424
  }), array(MediaFileSchema).readonly()), method(object({
17410
17425
  trackId: string(),
17411
- kinds: array(MediaFileKindEnum).optional()
17412
- }), array(MediaFileSchema).readonly()), method(object({ trackId: string() }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17426
+ deviceId: number$1()
17427
+ }), array(MediaFileInfoSchema).readonly()), method(SearchObjectEventsInput, array(ScoredObjectEventSchema).readonly()), method(object({}), WipeObjectEmbeddingsResultSchema, {
17413
17428
  kind: "mutation",
17414
17429
  auth: "admin"
17415
17430
  }), method(RebuildObjectEmbeddingsInput, RebuildObjectEmbeddingsResultSchema, {
@@ -19358,7 +19373,31 @@ DeviceType.Camera, method(object({
19358
19373
  lastCapturedAt: number$1().nullable(),
19359
19374
  cacheAgeMs: number$1().nullable(),
19360
19375
  etag: string().nullable()
19361
- }))), systemMethod(object({
19376
+ }))), systemMethod(object({ deviceId: number$1() }), object({
19377
+ /** The battery slice as read, or null when the device has none. */
19378
+ battery: object({
19379
+ sleeping: boolean(),
19380
+ lastUpdated: number$1(),
19381
+ lastContactAt: number$1().optional()
19382
+ }).nullable(),
19383
+ /** The resolved snapshot state (what the overlay decision used). */
19384
+ state: object({
19385
+ isBattery: boolean(),
19386
+ reason: _enum([
19387
+ "disabled",
19388
+ "sleeping",
19389
+ "unreachable",
19390
+ "waking"
19391
+ ]).nullable()
19392
+ }),
19393
+ /** The cached frame behind the next paint. */
19394
+ frame: object({
19395
+ capturedAt: number$1().nullable(),
19396
+ ageMs: number$1().nullable()
19397
+ }),
19398
+ /** A wake window is currently open (the Waking overlay's source). */
19399
+ waking: boolean()
19400
+ })), systemMethod(object({
19362
19401
  /** The tiles a surface is actually rendering. One entry per (device,
19363
19402
  * width) the caller will paint — the width is snapped to the server's
19364
19403
  * ladder and becomes part of the link's SIGNED identity. */
@@ -19388,7 +19427,16 @@ targets: array(object({
19388
19427
  /** A sleeping battery camera: the frame is deliberately stale and will
19389
19428
  * NOT refresh in the background. A surface should say so rather than
19390
19429
  * present it as current. */
19391
- sleeping: boolean()
19430
+ sleeping: boolean(),
19431
+ /** Current device state rendered over the cached frame. State images
19432
+ * remain authoritative even when their photographic background is
19433
+ * old; null means the link must carry a current camera frame. */
19434
+ stateReason: _enum([
19435
+ "disabled",
19436
+ "sleeping",
19437
+ "unreachable",
19438
+ "waking"
19439
+ ]).nullable()
19392
19440
  })));
19393
19441
  /**
19394
19442
  * `sso-bridge` — internal hub-only cap that lets SSO-style auth
@@ -20910,6 +20958,25 @@ var BatteryStatusSchema = object({
20910
20958
  /** Ms epoch of the last observation. Lets consumers reason about freshness. */
20911
20959
  lastUpdated: number$1(),
20912
20960
  /**
20961
+ * Ms epoch of the last time the device PROVED it was reachable — a
20962
+ * completed firmware round-trip, an observed wake, or an inbound push
20963
+ * (firmware event, email). `0`/absent = never since this slice was born.
20964
+ *
20965
+ * This is the ONLY input that separates "asleep" from "gone", and it is
20966
+ * fed exclusively by PASSIVE signals: nothing may write it by reaching
20967
+ * for the radio, because a poll that confirms reachability is the same
20968
+ * poll that drains the battery. See {@link deriveBatteryPresence} — the
20969
+ * single derivation every consumer must use; no surface computes its own.
20970
+ *
20971
+ * It is deliberately NOT a clock in the
20972
+ * `scripts/check-runtime-state-durability.ts` sense: it is the
20973
+ * observation itself, and it is the only thing a 30-hour silence is
20974
+ * visible in. Writers quantise it (see `CONTACT_WRITE_QUANTUM_MS` in the
20975
+ * Reolink provider) so a value that means "recently" cannot cost a
20976
+ * SQLite commit per round-trip.
20977
+ */
20978
+ lastContactAt: number$1().optional(),
20979
+ /**
20913
20980
  * True when the source is a BINARY low-battery indicator (HA
20914
20981
  * `binary_sensor` device_class=battery / `LOW_BAT`) that has no real
20915
20982
  * charge level — `percentage` is then a coarse stand-in (100 = normal,
@@ -25187,6 +25254,16 @@ var CamStreamDescriptorSchema = object({
25187
25254
  /** Transport-specific opaque metadata (e.g. rfc4571 SDP). */
25188
25255
  metadata: record(string(), unknown()).optional()
25189
25256
  });
25257
+ object({
25258
+ /** The descriptors as last built from a real camera response. Never a guess:
25259
+ * a failed or refused build writes NOTHING, so a restored catalog is always
25260
+ * one the camera itself once produced. */
25261
+ descriptors: array(CamStreamDescriptorSchema),
25262
+ /** Ms epoch of the build that produced {@link descriptors}. Lets the wake
25263
+ * path decide whether the camera's own awake window is worth spending on a
25264
+ * re-read. */
25265
+ lastFetchedAt: number$1()
25266
+ });
25190
25267
  DeviceType.Camera, method(object({ deviceId: number$1().int().nonnegative() }), array(CamStreamDescriptorSchema).readonly());
25191
25268
  /** One of the camera's stream profiles. */
25192
25269
  var StreamProfileSchema = _enum([
@@ -30535,6 +30612,12 @@ Object.freeze({
30535
30612
  addonId: null,
30536
30613
  access: "view"
30537
30614
  },
30615
+ "snapshot.getDebugState": {
30616
+ capName: "snapshot",
30617
+ capScope: "device",
30618
+ addonId: null,
30619
+ access: "view"
30620
+ },
30538
30621
  "snapshot.getSnapshot": {
30539
30622
  capName: "snapshot",
30540
30623
  capScope: "device",
@@ -32617,6 +32700,11 @@ Object.freeze({
32617
32700
  form: "single",
32618
32701
  optional: false
32619
32702
  }],
32703
+ "pipelineAnalytics.getEventMedia": [{
32704
+ name: "deviceId",
32705
+ form: "single",
32706
+ optional: false
32707
+ }],
32620
32708
  "pipelineAnalytics.getKeyEvents": [{
32621
32709
  name: "deviceId",
32622
32710
  form: "single",
@@ -32647,6 +32735,11 @@ Object.freeze({
32647
32735
  form: "single",
32648
32736
  optional: false
32649
32737
  }],
32738
+ "pipelineAnalytics.getTrackMedia": [{
32739
+ name: "deviceId",
32740
+ form: "single",
32741
+ optional: false
32742
+ }],
32650
32743
  "pipelineAnalytics.getTrainingExportSummary": [{
32651
32744
  name: "deviceIds",
32652
32745
  form: "array",
@@ -32682,6 +32775,11 @@ Object.freeze({
32682
32775
  form: "array",
32683
32776
  optional: true
32684
32777
  }],
32778
+ "pipelineAnalytics.listTrackMedia": [{
32779
+ name: "deviceId",
32780
+ form: "single",
32781
+ optional: false
32782
+ }],
32685
32783
  "pipelineAnalytics.listTracks": [{
32686
32784
  name: "deviceId",
32687
32785
  form: "single",
@@ -33117,11 +33215,22 @@ Object.freeze({
33117
33215
  form: "single",
33118
33216
  optional: false
33119
33217
  }],
33218
+ "snapshot.getDebugState": [{
33219
+ name: "deviceId",
33220
+ form: "single",
33221
+ optional: false
33222
+ }],
33120
33223
  "snapshot.getSnapshot": [{
33121
33224
  name: "deviceId",
33122
33225
  form: "single",
33123
33226
  optional: false
33124
33227
  }],
33228
+ "snapshot.getSnapshotLinks": [{
33229
+ name: "targets",
33230
+ form: "object-array",
33231
+ optional: false,
33232
+ itemField: "deviceId"
33233
+ }],
33125
33234
  "snapshot.getSnapshotOverview": [{
33126
33235
  name: "deviceIds",
33127
33236
  form: "array",
@@ -72426,7 +72535,9 @@ var TestChatImageRefSchema = discriminatedUnion("kind", [object({
72426
72535
  deviceId: number$1().int().positive()
72427
72536
  }), object({
72428
72537
  kind: literal("track"),
72429
- trackId: string().min(1)
72538
+ trackId: string().min(1),
72539
+ /** Owning device — `getTrackMedia` requires it as the read's scope. */
72540
+ deviceId: number$1().int().positive()
72430
72541
  })]);
72431
72542
  /**
72432
72543
  * `.strict()`, and it is load-bearing.
@@ -72652,7 +72763,7 @@ async function resolveImage(deps, ref) {
72652
72763
  }
72653
72764
  let rows;
72654
72765
  try {
72655
- rows = await deps.getTrackMedia(ref.trackId, TRACK_MEDIA_PREFERENCE);
72766
+ rows = await deps.getTrackMedia(ref.trackId, TRACK_MEDIA_PREFERENCE, ref.deviceId);
72656
72767
  } catch (cause) {
72657
72768
  const message = cause instanceof Error ? cause.message : String(cause);
72658
72769
  deps.logger.warn("ai test chat: track media read failed — turn dropped", { meta: {
@@ -73247,9 +73358,10 @@ var AiAddon = class extends BaseAddon {
73247
73358
  });
73248
73359
  },
73249
73360
  getSnapshot: (deviceId) => api.snapshot.getSnapshot.query({ deviceId }),
73250
- getTrackMedia: (trackId, kinds) => api.pipelineAnalytics.getTrackMedia.query({
73361
+ getTrackMedia: (trackId, kinds, deviceId) => api.pipelineAnalytics.getTrackMedia.query({
73251
73362
  trackId,
73252
- kinds: [...kinds]
73363
+ kinds: [...kinds],
73364
+ deviceId
73253
73365
  }),
73254
73366
  recordUsage: (row) => usage.record(row),
73255
73367
  logger,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-ai",
3
- "version": "0.4.4",
3
+ "version": "0.4.6",
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",