@camstack/types 1.2.358 → 1.2.359

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.
@@ -80,6 +80,43 @@ declare const BulkRecordSchema: z.ZodObject<{
80
80
  id: z.ZodOptional<z.ZodString>;
81
81
  data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
82
82
  }, z.core.$strip>;
83
+ /**
84
+ * One write inside a {@link settingsStoreCapability.methods.transact} call.
85
+ * Only the three that a "replace this set, bump that revision" write needs.
86
+ */
87
+ declare const TransactOpSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
88
+ op: z.ZodLiteral<"deleteWhere">;
89
+ collection: z.ZodString;
90
+ filter: z.ZodObject<{
91
+ where: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
92
+ whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
93
+ whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
94
+ whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
95
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
96
+ }, z.core.$strip>;
97
+ }, z.core.$strip>, z.ZodObject<{
98
+ op: z.ZodLiteral<"insertMany">;
99
+ collection: z.ZodString;
100
+ records: z.ZodReadonly<z.ZodArray<z.ZodObject<{
101
+ id: z.ZodOptional<z.ZodString>;
102
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
103
+ }, z.core.$strip>>>;
104
+ }, z.core.$strip>, z.ZodObject<{
105
+ op: z.ZodLiteral<"update">;
106
+ collection: z.ZodString;
107
+ id: z.ZodString;
108
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
109
+ }, z.core.$strip>], "op">;
110
+ /**
111
+ * The precondition of a transaction: the row `key` of `collection` must hold
112
+ * `equals` in `column`, read INSIDE the transaction.
113
+ */
114
+ declare const TransactGuardSchema: z.ZodObject<{
115
+ collection: z.ZodString;
116
+ key: z.ZodString;
117
+ column: z.ZodString;
118
+ equals: z.ZodUnknown;
119
+ }, z.core.$strip>;
83
120
  /**
84
121
  * Column declaration for a structured (SQL-backed) collection.
85
122
  *
@@ -302,6 +339,60 @@ export declare const settingsStoreCapability: {
302
339
  }, z.core.$strip>, z.ZodObject<{
303
340
  inserted: z.ZodNumber;
304
341
  }, z.core.$strip>, "mutation">;
342
+ /**
343
+ * Several writes, ONE transaction, behind an optional precondition.
344
+ *
345
+ * The multi-collection twin of {@link insertMany}: a write that has to move
346
+ * a set of rows in one collection AND a revision in another cannot be
347
+ * expressed as separate calls without a window in which a reader (or a
348
+ * crash) sees half of it. The retrain annotation save replaces a frame's
349
+ * rows, its dismissed set and its revision this way.
350
+ *
351
+ * `guard`, when given, is read inside the transaction: if the row is
352
+ * missing or its column differs from `equals`, NOTHING is written and the
353
+ * answer is `{ applied: false, current }` with what was found — the
354
+ * compare-and-set a revisioned write needs, with no second reader able to
355
+ * slip between the check and the writes.
356
+ *
357
+ * **All or nothing**, like {@link insertMany}: a throw in any op rolls the
358
+ * whole call back. Ops run in the order given (delete before insert to
359
+ * replace a set). `namespace` applies to every collection named.
360
+ */
361
+ readonly transact: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
362
+ namespace: z.ZodOptional<z.ZodString>;
363
+ guard: z.ZodOptional<z.ZodObject<{
364
+ collection: z.ZodString;
365
+ key: z.ZodString;
366
+ column: z.ZodString;
367
+ equals: z.ZodUnknown;
368
+ }, z.core.$strip>>;
369
+ ops: z.ZodReadonly<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
370
+ op: z.ZodLiteral<"deleteWhere">;
371
+ collection: z.ZodString;
372
+ filter: z.ZodObject<{
373
+ where: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
374
+ whereIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
375
+ whereBetween: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodTuple<[z.ZodUnknown, z.ZodUnknown], null>>>;
376
+ whereNot: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
377
+ whereNotIn: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodUnknown>>>;
378
+ }, z.core.$strip>;
379
+ }, z.core.$strip>, z.ZodObject<{
380
+ op: z.ZodLiteral<"insertMany">;
381
+ collection: z.ZodString;
382
+ records: z.ZodReadonly<z.ZodArray<z.ZodObject<{
383
+ id: z.ZodOptional<z.ZodString>;
384
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
385
+ }, z.core.$strip>>>;
386
+ }, z.core.$strip>, z.ZodObject<{
387
+ op: z.ZodLiteral<"update">;
388
+ collection: z.ZodString;
389
+ id: z.ZodString;
390
+ data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
391
+ }, z.core.$strip>], "op">>>;
392
+ }, z.core.$strip>, z.ZodObject<{
393
+ applied: z.ZodBoolean;
394
+ current: z.ZodOptional<z.ZodUnknown>;
395
+ }, z.core.$strip>, "mutation">;
305
396
  /** Update an existing record by ID. */
306
397
  readonly update: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
307
398
  namespace: z.ZodOptional<z.ZodString>;
@@ -513,7 +604,7 @@ export type ISettingsStoreProvider = InferProvider<typeof settingsStoreCapabilit
513
604
  * Methods are accessed as `client.get.query(input)`, `client.set.mutate(input)`, etc.
514
605
  */
515
606
  export type SettingsStoreClient = import('../generated/addon-api.js').AddonApi['settingsStore'];
516
- export { AggregateFieldSchema, AggregateResultSchema, BulkRecordSchema, CollectionColumnSchema, CollectionIndexSchema, MutationFilterSchema, QueryFilterSchema, SettingsRecordSchema, };
607
+ export { AggregateFieldSchema, AggregateResultSchema, BulkRecordSchema, CollectionColumnSchema, CollectionIndexSchema, MutationFilterSchema, QueryFilterSchema, SettingsRecordSchema, TransactGuardSchema, TransactOpSchema, };
517
608
  export type BulkRecord = z.infer<typeof BulkRecordSchema>;
518
609
  export type AggregateField = z.infer<typeof AggregateFieldSchema>;
519
610
  export type AggregateResult = z.infer<typeof AggregateResultSchema>;
package/dist/client.d.ts CHANGED
@@ -14,4 +14,4 @@
14
14
  * value this entry does not export — a missing name here is `undefined` at
15
15
  * runtime, not a build error.
16
16
  */
17
- export { APPLE_SA_TO_MACRO, AUDIO_MACRO_LABELS, AlarmArmModeSchema, CLIP_BROKER_PACED_RATES, COCO_TO_MACRO, CamProfileSchema, DEFAULT_TIMELAPSE_PREVIEW_TEXT, DeviceType, EVENTFUL_CAP_NAMES, EVENT_KIND_BY_CAP, EVENT_TAXONOMY, MACRO_LABELS, NC_ALARM_DELAY_MAX_SEC, NC_ALARM_SENSORS_MAX, NC_ALARM_SYSTEM_EVENT_KINDS, NC_ALARM_TRIGGERED_MAX_SEC, NC_BATTERY_CONSUMABLE_SYSTEM_EVENT_KINDS, NC_CAMERA_SCOPE_CAP_NAME, NC_CONSUMABLE_LOW_PERCENT_DEFAULT, NC_CONSUMABLE_LOW_PERCENT_MAX, NC_GROUPING_DEFAULT_SEC, NC_GROUPING_MAX_SEC, NC_GROUPING_MIN_SEC, NC_RULE_EDITOR_SECTION_ORDER, NC_RULE_KIND_SPECS, NC_SENSOR_TRANSITION_UNSUPPORTED_REASON, NC_SYSTEM_EVENT_FAMILY_FILTERS, NC_SYSTEM_EVENT_KIND_GROUP_SPECS, NC_SYSTEM_EVENT_SUMMARY_FAMILIES, NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS, NC_TEMPLATE_VARS, NcAlarmConfigSchema, NcAlarmSensorsSchema, NcAlarmSettingsPatchSchema, NcHistoryEntrySchema, NcRuleLastRunsSchema, NodeDirectorySchema, RECORDING_EXPORT_MAX_READ_BYTES, SystemMirror, TIMELAPSE_DENSE_FLOOR_SEC, TimelapseRuleSchema, YAMNET_TO_MACRO, alarmDeviceBlocksArming, alarmDeviceSecureOnArm, alarmPatchLanded, conditionVisibleForKind, createSystemProxy, droppedConditionsForKind, isHeldBackTemplateVar, isSystemRuleKind, kindsForDiscriminator, knownValues, nodePickerItems, packagePickerItems, pickerForCondition, pickerForRuleClasses, resolveModeDelays, ruleDeviceCompatCapNames, ruleEditorSectionsForKind, ruleGroupingApplies, ruleKindOf, ruleKindSpec, ruleSeedForSection, sensorTransitionNeverFires, sensorTransitionOffered, stateLabel, systemEventDeliveryProblems, systemEventFilterAppliesToAnyKind, systemEventKindsOfFamily, systemFamilyOfKind, templateCarriesSummary, templateVarDeliveryApplies, templateVarKindApplies, templateVarSystemEventApplies, } from './index.js';
17
+ export { APPLE_SA_TO_MACRO, AUDIO_MACRO_LABELS, AlarmArmModeSchema, CLIP_BROKER_PACED_RATES, COCO_TO_MACRO, CamProfileSchema, DEFAULT_TIMELAPSE_PREVIEW_TEXT, DeviceType, EVENTFUL_CAP_NAMES, EVENT_KIND_BY_CAP, EVENT_TAXONOMY, MACRO_LABELS, NC_ALARM_DELAY_MAX_SEC, NC_ALARM_SENSORS_MAX, NC_ALARM_SYSTEM_EVENT_KINDS, NC_ALARM_TRIGGERED_MAX_SEC, NC_BATTERY_CONSUMABLE_SYSTEM_EVENT_KINDS, NC_CAMERA_SCOPE_CAP_NAME, NC_CONSUMABLE_LOW_PERCENT_DEFAULT, NC_CONSUMABLE_LOW_PERCENT_MAX, NC_GROUPING_DEFAULT_SEC, NC_GROUPING_MAX_SEC, NC_GROUPING_MIN_SEC, NC_RULE_EDITOR_SECTION_ORDER, NC_RULE_KIND_SPECS, NC_SENSOR_TRANSITION_UNSUPPORTED_REASON, NC_SYSTEM_EVENT_FAMILY_FILTERS, NC_SYSTEM_EVENT_KIND_GROUP_SPECS, NC_SYSTEM_EVENT_SUMMARY_FAMILIES, NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS, NC_TEMPLATE_VARS, NcAlarmConfigSchema, NcAlarmSensorsSchema, NcAlarmSettingsPatchSchema, NcHistoryEntrySchema, NcRuleLastRunsSchema, NodeDirectorySchema, RECORDING_EXPORT_MAX_READ_BYTES, RETRAIN_REVIEW_ADJUSTED_IOU, RETRAIN_REVIEW_MATCH_IOU, RETRAIN_REVIEW_STATUSES, RETRAIN_REVISION_CONFLICT, SystemMirror, TIMELAPSE_DENSE_FLOOR_SEC, TimelapseRuleSchema, YAMNET_TO_MACRO, alarmDeviceBlocksArming, alarmDeviceSecureOnArm, alarmPatchLanded, classifyRetrainReview, conditionVisibleForKind, createSystemProxy, droppedConditionsForKind, isHeldBackTemplateVar, isSystemRuleKind, kindsForDiscriminator, knownValues, nodePickerItems, packagePickerItems, pickerForCondition, pickerForRuleClasses, parseRetrainRevisionConflict, resolveModeDelays, retrainBoxIou, retrainRevisionConflictMessage, ruleDeviceCompatCapNames, ruleEditorSectionsForKind, ruleGroupingApplies, ruleKindOf, ruleKindSpec, ruleSeedForSection, sensorTransitionNeverFires, sensorTransitionOffered, stateLabel, summarizeReview, systemEventDeliveryProblems, systemEventFilterAppliesToAnyKind, systemEventKindsOfFamily, systemFamilyOfKind, templateCarriesSummary, templateVarDeliveryApplies, templateVarKindApplies, templateVarSystemEventApplies, } from './index.js';
package/dist/client.js CHANGED
@@ -100,6 +100,202 @@ var PeerBytesTicketSchema = zod.z.object({
100
100
  declaredBytes: zod.z.number().int().nonnegative().nullable()
101
101
  });
102
102
  //#endregion
103
+ //#region src/retrain-review.ts
104
+ /** Every status, in the order a summary lists them. */
105
+ var RETRAIN_REVIEW_STATUSES = [
106
+ "unchanged",
107
+ "adjusted",
108
+ "reclassified",
109
+ "added",
110
+ "removed",
111
+ "model-error"
112
+ ];
113
+ /**
114
+ * Two boxes overlapping at least this much are the same subject when a legacy
115
+ * row (no origin) is matched against a proposal. Deliberately loose: a legacy
116
+ * row is judged on evidence that is no longer exact.
117
+ */
118
+ var RETRAIN_REVIEW_MATCH_IOU = .5;
119
+ /**
120
+ * A kept box overlapping its origin at least this much is `unchanged`; below it
121
+ * the rectangle was moved or resized on purpose (`adjusted`). Not 1.0: the
122
+ * canvas rounds, and a box dragged by one pixel and dragged back is not an edit.
123
+ */
124
+ var RETRAIN_REVIEW_ADJUSTED_IOU = .95;
125
+ /** Two boxes this close on every edge are the same rectangle (an exact match). */
126
+ var EXACT_EDGE_EPSILON = 1e-6;
127
+ /** Intersection over union of two normalised rectangles. */
128
+ function retrainBoxIou(a, b) {
129
+ const inter = Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
130
+ const union = a.w * a.h + b.w * b.h - inter;
131
+ return union > 0 ? inter / union : 0;
132
+ }
133
+ function sameRect(a, b) {
134
+ return Math.abs(a.x - b.x) <= EXACT_EDGE_EPSILON && Math.abs(a.y - b.y) <= EXACT_EDGE_EPSILON && Math.abs(a.w - b.w) <= EXACT_EDGE_EPSILON && Math.abs(a.h - b.h) <= EXACT_EDGE_EPSILON;
135
+ }
136
+ function labelOf(value) {
137
+ return value?.trim() ?? "";
138
+ }
139
+ /** Kept box versus its baseline: class/label first, then geometry. */
140
+ function compareKept(saved, baseline, adjustedIou) {
141
+ const iou = retrainBoxIou(saved.bbox, baseline.bbox);
142
+ if (saved.macroClass !== baseline.macroClass || labelOf(saved.label) !== labelOf(baseline.label)) return {
143
+ status: "reclassified",
144
+ iou
145
+ };
146
+ return {
147
+ status: iou < adjustedIou ? "adjusted" : "unchanged",
148
+ iou
149
+ };
150
+ }
151
+ /**
152
+ * Pair legacy rows with proposals: exact rectangles first, then by descending
153
+ * overlap, one proposal per row. Greedy on a sorted list so the result does not
154
+ * depend on the order the rows were listed in.
155
+ */
156
+ function pairLegacy(saved, legacyIndexes, origins, matchIou) {
157
+ const candidates = [];
158
+ for (const savedIndex of legacyIndexes) {
159
+ const box = saved[savedIndex];
160
+ if (box === void 0) continue;
161
+ origins.forEach((origin, originIndex) => {
162
+ const exact = sameRect(box.bbox, origin.bbox);
163
+ const iou = exact ? 1 : retrainBoxIou(box.bbox, origin.bbox);
164
+ if (exact || iou >= matchIou) candidates.push({
165
+ savedIndex,
166
+ originIndex,
167
+ iou,
168
+ exact
169
+ });
170
+ });
171
+ }
172
+ candidates.sort((a, b) => Number(b.exact) - Number(a.exact) || b.iou - a.iou);
173
+ const pairs = /* @__PURE__ */ new Map();
174
+ const takenOrigins = /* @__PURE__ */ new Set();
175
+ for (const c of candidates) {
176
+ if (pairs.has(c.savedIndex) || takenOrigins.has(c.originIndex)) continue;
177
+ pairs.set(c.savedIndex, c.originIndex);
178
+ takenOrigins.add(c.originIndex);
179
+ }
180
+ return pairs;
181
+ }
182
+ /**
183
+ * Classify what the operator did on ONE frame.
184
+ *
185
+ * @param saved the frame's saved boxes.
186
+ * @param origins the proposals known now (the pipeline's boxes, re-derived) —
187
+ * consulted ONLY for rows that carry no origin snapshot.
188
+ * @param dismissed the proposals the operator deleted, with their snapshots.
189
+ */
190
+ function classifyRetrainReview(saved, origins, dismissed, opts = {}) {
191
+ const matchIou = opts.matchIou ?? .5;
192
+ const adjustedIou = opts.adjustedIou ?? .95;
193
+ const claimedRefs = /* @__PURE__ */ new Set();
194
+ const boxes = [];
195
+ const legacyIndexes = [];
196
+ saved.forEach((box, index) => {
197
+ if (box.origin === null) legacyIndexes.push(index);
198
+ else claimedRefs.add(box.origin.ref);
199
+ });
200
+ for (const entry of dismissed) claimedRefs.add(entry.ref);
201
+ const legacyPairs = pairLegacy(saved, legacyIndexes, origins, matchIou);
202
+ for (const originIndex of legacyPairs.values()) {
203
+ const origin = origins[originIndex];
204
+ if (origin !== void 0) claimedRefs.add(origin.ref);
205
+ }
206
+ saved.forEach((box, index) => {
207
+ const exactBaseline = box.origin;
208
+ const pairedOrigin = exactBaseline === null ? origins[legacyPairs.get(index) ?? -1] : void 0;
209
+ const baseline = exactBaseline ?? pairedOrigin ?? null;
210
+ const approximate = exactBaseline === null;
211
+ if (box.kind === "model_error") {
212
+ boxes.push({
213
+ ref: box.id,
214
+ savedId: box.id,
215
+ status: "model-error",
216
+ approximate: false,
217
+ ...baseline !== null ? {
218
+ origin: baseline,
219
+ iou: retrainBoxIou(box.bbox, baseline.bbox)
220
+ } : {}
221
+ });
222
+ return;
223
+ }
224
+ if (baseline === null || baseline.source === "operator") {
225
+ boxes.push({
226
+ ref: box.id,
227
+ savedId: box.id,
228
+ status: "added",
229
+ approximate: approximate && baseline === null,
230
+ ...baseline !== null ? { origin: baseline } : {}
231
+ });
232
+ return;
233
+ }
234
+ const verdict = compareKept(box, baseline, adjustedIou);
235
+ boxes.push({
236
+ ref: box.id,
237
+ savedId: box.id,
238
+ status: verdict.status,
239
+ approximate,
240
+ origin: baseline,
241
+ iou: verdict.iou
242
+ });
243
+ });
244
+ for (const entry of dismissed) boxes.push({
245
+ ref: entry.ref,
246
+ status: "removed",
247
+ approximate: false,
248
+ origin: entry.snapshot
249
+ });
250
+ if (opts.inferRemoved === true) for (const origin of origins) {
251
+ if (claimedRefs.has(origin.ref)) continue;
252
+ boxes.push({
253
+ ref: origin.ref,
254
+ status: "removed",
255
+ approximate: true,
256
+ origin
257
+ });
258
+ }
259
+ return {
260
+ boxes,
261
+ approximate: boxes.some((b) => b.approximate)
262
+ };
263
+ }
264
+ /** Counts per status, plus the totals a list row shows. */
265
+ function summarizeReview(review) {
266
+ const counts = {
267
+ unchanged: 0,
268
+ adjusted: 0,
269
+ reclassified: 0,
270
+ added: 0,
271
+ removed: 0,
272
+ "model-error": 0
273
+ };
274
+ for (const box of review.boxes) counts[box.status] += 1;
275
+ return {
276
+ counts,
277
+ total: review.boxes.length,
278
+ changed: review.boxes.length - counts.unchanged,
279
+ approximate: review.approximate
280
+ };
281
+ }
282
+ /** The prefix of the error a stale `expectedRevision` save is refused with. */
283
+ var RETRAIN_REVISION_CONFLICT = "retrain-revision-conflict";
284
+ /** The message a refused save carries — `parseRetrainRevisionConflict` reads it back. */
285
+ function retrainRevisionConflictMessage(frameId, conflict) {
286
+ return `${RETRAIN_REVISION_CONFLICT}: frame ${frameId} moved on — it is at revision ${String(conflict.currentRevision)}, the save expected ${String(conflict.expectedRevision)} (current=${String(conflict.currentRevision)} expected=${String(conflict.expectedRevision)})`;
287
+ }
288
+ /** The conflict an error message names, or `null` when it is some other error. */
289
+ function parseRetrainRevisionConflict(message) {
290
+ if (!message.includes("retrain-revision-conflict")) return null;
291
+ const match = /current=(\d+) expected=(\d+)/.exec(message);
292
+ if (match?.[1] === void 0 || match[2] === void 0) return null;
293
+ return {
294
+ currentRevision: Number(match[1]),
295
+ expectedRevision: Number(match[2])
296
+ };
297
+ }
298
+ //#endregion
103
299
  //#region src/capabilities/schemas/streaming-shared.ts
104
300
  /** Shared Zod schemas used across streaming capabilities. */
105
301
  var CamProfileSchema = zod.z.enum([
@@ -5770,6 +5966,7 @@ function createDeviceProxy(api, binding, opts) {
5770
5966
  proposeRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "proposeRetrainAnnotations", "mutation", input),
5771
5967
  listRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainAnnotations", "query", input),
5772
5968
  saveRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "saveRetrainAnnotations", "mutation", input),
5969
+ backfillRetrainOrigins: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "backfillRetrainOrigins", "mutation", input),
5773
5970
  completeRetrainTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "completeRetrainTrack", "mutation", input),
5774
5971
  restageRetrainTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "restageRetrainTrack", "mutation", input),
5775
5972
  getRetrainExportUrl: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getRetrainExportUrl", "query", input),
@@ -7577,6 +7774,7 @@ function createSystemProxy(api) {
7577
7774
  upsert: (input) => dispatch("settingsStore", "upsert", "mutation", input),
7578
7775
  insert: (input) => dispatch("settingsStore", "insert", "mutation", input),
7579
7776
  insertMany: (input) => dispatch("settingsStore", "insertMany", "mutation", input),
7777
+ transact: (input) => dispatch("settingsStore", "transact", "mutation", input),
7580
7778
  update: (input) => dispatch("settingsStore", "update", "mutation", input),
7581
7779
  delete: (input) => dispatch("settingsStore", "delete", "mutation", input),
7582
7780
  deleteWhere: (input) => dispatch("settingsStore", "deleteWhere", "mutation", input),
@@ -10642,6 +10840,10 @@ exports.NcHistoryEntrySchema = NcHistoryEntrySchema;
10642
10840
  exports.NcRuleLastRunsSchema = NcRuleLastRunsSchema;
10643
10841
  exports.NodeDirectorySchema = NodeDirectorySchema;
10644
10842
  exports.RECORDING_EXPORT_MAX_READ_BYTES = RECORDING_EXPORT_MAX_READ_BYTES;
10843
+ exports.RETRAIN_REVIEW_ADJUSTED_IOU = RETRAIN_REVIEW_ADJUSTED_IOU;
10844
+ exports.RETRAIN_REVIEW_MATCH_IOU = RETRAIN_REVIEW_MATCH_IOU;
10845
+ exports.RETRAIN_REVIEW_STATUSES = RETRAIN_REVIEW_STATUSES;
10846
+ exports.RETRAIN_REVISION_CONFLICT = RETRAIN_REVISION_CONFLICT;
10645
10847
  exports.SystemMirror = SystemMirror;
10646
10848
  exports.TIMELAPSE_DENSE_FLOOR_SEC = TIMELAPSE_DENSE_FLOOR_SEC;
10647
10849
  exports.TimelapseRuleSchema = TimelapseRuleSchema;
@@ -10649,6 +10851,7 @@ exports.YAMNET_TO_MACRO = YAMNET_TO_MACRO;
10649
10851
  exports.alarmDeviceBlocksArming = alarmDeviceBlocksArming;
10650
10852
  exports.alarmDeviceSecureOnArm = alarmDeviceSecureOnArm;
10651
10853
  exports.alarmPatchLanded = alarmPatchLanded;
10854
+ exports.classifyRetrainReview = classifyRetrainReview;
10652
10855
  exports.conditionVisibleForKind = conditionVisibleForKind;
10653
10856
  exports.createSystemProxy = createSystemProxy;
10654
10857
  exports.droppedConditionsForKind = droppedConditionsForKind;
@@ -10658,9 +10861,12 @@ exports.kindsForDiscriminator = kindsForDiscriminator;
10658
10861
  exports.knownValues = knownValues;
10659
10862
  exports.nodePickerItems = nodePickerItems;
10660
10863
  exports.packagePickerItems = packagePickerItems;
10864
+ exports.parseRetrainRevisionConflict = parseRetrainRevisionConflict;
10661
10865
  exports.pickerForCondition = pickerForCondition;
10662
10866
  exports.pickerForRuleClasses = pickerForRuleClasses;
10663
10867
  exports.resolveModeDelays = resolveModeDelays;
10868
+ exports.retrainBoxIou = retrainBoxIou;
10869
+ exports.retrainRevisionConflictMessage = retrainRevisionConflictMessage;
10664
10870
  exports.ruleDeviceCompatCapNames = ruleDeviceCompatCapNames;
10665
10871
  exports.ruleEditorSectionsForKind = ruleEditorSectionsForKind;
10666
10872
  exports.ruleGroupingApplies = ruleGroupingApplies;
@@ -10670,6 +10876,7 @@ exports.ruleSeedForSection = ruleSeedForSection;
10670
10876
  exports.sensorTransitionNeverFires = sensorTransitionNeverFires;
10671
10877
  exports.sensorTransitionOffered = sensorTransitionOffered;
10672
10878
  exports.stateLabel = stateLabel;
10879
+ exports.summarizeReview = summarizeReview;
10673
10880
  exports.systemEventDeliveryProblems = systemEventDeliveryProblems;
10674
10881
  exports.systemEventFilterAppliesToAnyKind = systemEventFilterAppliesToAnyKind;
10675
10882
  exports.systemEventKindsOfFamily = systemEventKindsOfFamily;
package/dist/client.mjs CHANGED
@@ -99,6 +99,202 @@ var PeerBytesTicketSchema = z.object({
99
99
  declaredBytes: z.number().int().nonnegative().nullable()
100
100
  });
101
101
  //#endregion
102
+ //#region src/retrain-review.ts
103
+ /** Every status, in the order a summary lists them. */
104
+ var RETRAIN_REVIEW_STATUSES = [
105
+ "unchanged",
106
+ "adjusted",
107
+ "reclassified",
108
+ "added",
109
+ "removed",
110
+ "model-error"
111
+ ];
112
+ /**
113
+ * Two boxes overlapping at least this much are the same subject when a legacy
114
+ * row (no origin) is matched against a proposal. Deliberately loose: a legacy
115
+ * row is judged on evidence that is no longer exact.
116
+ */
117
+ var RETRAIN_REVIEW_MATCH_IOU = .5;
118
+ /**
119
+ * A kept box overlapping its origin at least this much is `unchanged`; below it
120
+ * the rectangle was moved or resized on purpose (`adjusted`). Not 1.0: the
121
+ * canvas rounds, and a box dragged by one pixel and dragged back is not an edit.
122
+ */
123
+ var RETRAIN_REVIEW_ADJUSTED_IOU = .95;
124
+ /** Two boxes this close on every edge are the same rectangle (an exact match). */
125
+ var EXACT_EDGE_EPSILON = 1e-6;
126
+ /** Intersection over union of two normalised rectangles. */
127
+ function retrainBoxIou(a, b) {
128
+ const inter = Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
129
+ const union = a.w * a.h + b.w * b.h - inter;
130
+ return union > 0 ? inter / union : 0;
131
+ }
132
+ function sameRect(a, b) {
133
+ return Math.abs(a.x - b.x) <= EXACT_EDGE_EPSILON && Math.abs(a.y - b.y) <= EXACT_EDGE_EPSILON && Math.abs(a.w - b.w) <= EXACT_EDGE_EPSILON && Math.abs(a.h - b.h) <= EXACT_EDGE_EPSILON;
134
+ }
135
+ function labelOf(value) {
136
+ return value?.trim() ?? "";
137
+ }
138
+ /** Kept box versus its baseline: class/label first, then geometry. */
139
+ function compareKept(saved, baseline, adjustedIou) {
140
+ const iou = retrainBoxIou(saved.bbox, baseline.bbox);
141
+ if (saved.macroClass !== baseline.macroClass || labelOf(saved.label) !== labelOf(baseline.label)) return {
142
+ status: "reclassified",
143
+ iou
144
+ };
145
+ return {
146
+ status: iou < adjustedIou ? "adjusted" : "unchanged",
147
+ iou
148
+ };
149
+ }
150
+ /**
151
+ * Pair legacy rows with proposals: exact rectangles first, then by descending
152
+ * overlap, one proposal per row. Greedy on a sorted list so the result does not
153
+ * depend on the order the rows were listed in.
154
+ */
155
+ function pairLegacy(saved, legacyIndexes, origins, matchIou) {
156
+ const candidates = [];
157
+ for (const savedIndex of legacyIndexes) {
158
+ const box = saved[savedIndex];
159
+ if (box === void 0) continue;
160
+ origins.forEach((origin, originIndex) => {
161
+ const exact = sameRect(box.bbox, origin.bbox);
162
+ const iou = exact ? 1 : retrainBoxIou(box.bbox, origin.bbox);
163
+ if (exact || iou >= matchIou) candidates.push({
164
+ savedIndex,
165
+ originIndex,
166
+ iou,
167
+ exact
168
+ });
169
+ });
170
+ }
171
+ candidates.sort((a, b) => Number(b.exact) - Number(a.exact) || b.iou - a.iou);
172
+ const pairs = /* @__PURE__ */ new Map();
173
+ const takenOrigins = /* @__PURE__ */ new Set();
174
+ for (const c of candidates) {
175
+ if (pairs.has(c.savedIndex) || takenOrigins.has(c.originIndex)) continue;
176
+ pairs.set(c.savedIndex, c.originIndex);
177
+ takenOrigins.add(c.originIndex);
178
+ }
179
+ return pairs;
180
+ }
181
+ /**
182
+ * Classify what the operator did on ONE frame.
183
+ *
184
+ * @param saved the frame's saved boxes.
185
+ * @param origins the proposals known now (the pipeline's boxes, re-derived) —
186
+ * consulted ONLY for rows that carry no origin snapshot.
187
+ * @param dismissed the proposals the operator deleted, with their snapshots.
188
+ */
189
+ function classifyRetrainReview(saved, origins, dismissed, opts = {}) {
190
+ const matchIou = opts.matchIou ?? .5;
191
+ const adjustedIou = opts.adjustedIou ?? .95;
192
+ const claimedRefs = /* @__PURE__ */ new Set();
193
+ const boxes = [];
194
+ const legacyIndexes = [];
195
+ saved.forEach((box, index) => {
196
+ if (box.origin === null) legacyIndexes.push(index);
197
+ else claimedRefs.add(box.origin.ref);
198
+ });
199
+ for (const entry of dismissed) claimedRefs.add(entry.ref);
200
+ const legacyPairs = pairLegacy(saved, legacyIndexes, origins, matchIou);
201
+ for (const originIndex of legacyPairs.values()) {
202
+ const origin = origins[originIndex];
203
+ if (origin !== void 0) claimedRefs.add(origin.ref);
204
+ }
205
+ saved.forEach((box, index) => {
206
+ const exactBaseline = box.origin;
207
+ const pairedOrigin = exactBaseline === null ? origins[legacyPairs.get(index) ?? -1] : void 0;
208
+ const baseline = exactBaseline ?? pairedOrigin ?? null;
209
+ const approximate = exactBaseline === null;
210
+ if (box.kind === "model_error") {
211
+ boxes.push({
212
+ ref: box.id,
213
+ savedId: box.id,
214
+ status: "model-error",
215
+ approximate: false,
216
+ ...baseline !== null ? {
217
+ origin: baseline,
218
+ iou: retrainBoxIou(box.bbox, baseline.bbox)
219
+ } : {}
220
+ });
221
+ return;
222
+ }
223
+ if (baseline === null || baseline.source === "operator") {
224
+ boxes.push({
225
+ ref: box.id,
226
+ savedId: box.id,
227
+ status: "added",
228
+ approximate: approximate && baseline === null,
229
+ ...baseline !== null ? { origin: baseline } : {}
230
+ });
231
+ return;
232
+ }
233
+ const verdict = compareKept(box, baseline, adjustedIou);
234
+ boxes.push({
235
+ ref: box.id,
236
+ savedId: box.id,
237
+ status: verdict.status,
238
+ approximate,
239
+ origin: baseline,
240
+ iou: verdict.iou
241
+ });
242
+ });
243
+ for (const entry of dismissed) boxes.push({
244
+ ref: entry.ref,
245
+ status: "removed",
246
+ approximate: false,
247
+ origin: entry.snapshot
248
+ });
249
+ if (opts.inferRemoved === true) for (const origin of origins) {
250
+ if (claimedRefs.has(origin.ref)) continue;
251
+ boxes.push({
252
+ ref: origin.ref,
253
+ status: "removed",
254
+ approximate: true,
255
+ origin
256
+ });
257
+ }
258
+ return {
259
+ boxes,
260
+ approximate: boxes.some((b) => b.approximate)
261
+ };
262
+ }
263
+ /** Counts per status, plus the totals a list row shows. */
264
+ function summarizeReview(review) {
265
+ const counts = {
266
+ unchanged: 0,
267
+ adjusted: 0,
268
+ reclassified: 0,
269
+ added: 0,
270
+ removed: 0,
271
+ "model-error": 0
272
+ };
273
+ for (const box of review.boxes) counts[box.status] += 1;
274
+ return {
275
+ counts,
276
+ total: review.boxes.length,
277
+ changed: review.boxes.length - counts.unchanged,
278
+ approximate: review.approximate
279
+ };
280
+ }
281
+ /** The prefix of the error a stale `expectedRevision` save is refused with. */
282
+ var RETRAIN_REVISION_CONFLICT = "retrain-revision-conflict";
283
+ /** The message a refused save carries — `parseRetrainRevisionConflict` reads it back. */
284
+ function retrainRevisionConflictMessage(frameId, conflict) {
285
+ return `${RETRAIN_REVISION_CONFLICT}: frame ${frameId} moved on — it is at revision ${String(conflict.currentRevision)}, the save expected ${String(conflict.expectedRevision)} (current=${String(conflict.currentRevision)} expected=${String(conflict.expectedRevision)})`;
286
+ }
287
+ /** The conflict an error message names, or `null` when it is some other error. */
288
+ function parseRetrainRevisionConflict(message) {
289
+ if (!message.includes("retrain-revision-conflict")) return null;
290
+ const match = /current=(\d+) expected=(\d+)/.exec(message);
291
+ if (match?.[1] === void 0 || match[2] === void 0) return null;
292
+ return {
293
+ currentRevision: Number(match[1]),
294
+ expectedRevision: Number(match[2])
295
+ };
296
+ }
297
+ //#endregion
102
298
  //#region src/capabilities/schemas/streaming-shared.ts
103
299
  /** Shared Zod schemas used across streaming capabilities. */
104
300
  var CamProfileSchema = z.enum([
@@ -5769,6 +5965,7 @@ function createDeviceProxy(api, binding, opts) {
5769
5965
  proposeRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "proposeRetrainAnnotations", "mutation", input),
5770
5966
  listRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRetrainAnnotations", "query", input),
5771
5967
  saveRetrainAnnotations: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "saveRetrainAnnotations", "mutation", input),
5968
+ backfillRetrainOrigins: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "backfillRetrainOrigins", "mutation", input),
5772
5969
  completeRetrainTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "completeRetrainTrack", "mutation", input),
5773
5970
  restageRetrainTrack: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "restageRetrainTrack", "mutation", input),
5774
5971
  getRetrainExportUrl: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "getRetrainExportUrl", "query", input),
@@ -7576,6 +7773,7 @@ function createSystemProxy(api) {
7576
7773
  upsert: (input) => dispatch("settingsStore", "upsert", "mutation", input),
7577
7774
  insert: (input) => dispatch("settingsStore", "insert", "mutation", input),
7578
7775
  insertMany: (input) => dispatch("settingsStore", "insertMany", "mutation", input),
7776
+ transact: (input) => dispatch("settingsStore", "transact", "mutation", input),
7579
7777
  update: (input) => dispatch("settingsStore", "update", "mutation", input),
7580
7778
  delete: (input) => dispatch("settingsStore", "delete", "mutation", input),
7581
7779
  deleteWhere: (input) => dispatch("settingsStore", "deleteWhere", "mutation", input),
@@ -10603,4 +10801,4 @@ var TimelapseRuleSchema = TimelapseRuleInputSchema.extend({
10603
10801
  updatedAt: z.number()
10604
10802
  });
10605
10803
  //#endregion
10606
- export { APPLE_SA_TO_MACRO, AUDIO_MACRO_LABELS, AlarmArmModeSchema, CLIP_BROKER_PACED_RATES, COCO_TO_MACRO, CamProfileSchema, DEFAULT_TIMELAPSE_PREVIEW_TEXT, DeviceType, EVENTFUL_CAP_NAMES, EVENT_KIND_BY_CAP, EVENT_TAXONOMY, MACRO_LABELS, NC_ALARM_DELAY_MAX_SEC, NC_ALARM_SENSORS_MAX, NC_ALARM_SYSTEM_EVENT_KINDS, NC_ALARM_TRIGGERED_MAX_SEC, NC_BATTERY_CONSUMABLE_SYSTEM_EVENT_KINDS, NC_CAMERA_SCOPE_CAP_NAME, NC_CONSUMABLE_LOW_PERCENT_DEFAULT, NC_CONSUMABLE_LOW_PERCENT_MAX, NC_GROUPING_DEFAULT_SEC, NC_GROUPING_MAX_SEC, NC_GROUPING_MIN_SEC, NC_RULE_EDITOR_SECTION_ORDER, NC_RULE_KIND_SPECS, NC_SENSOR_TRANSITION_UNSUPPORTED_REASON, NC_SYSTEM_EVENT_FAMILY_FILTERS, NC_SYSTEM_EVENT_KIND_GROUP_SPECS, NC_SYSTEM_EVENT_SUMMARY_FAMILIES, NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS, NC_TEMPLATE_VARS, NcAlarmConfigSchema, NcAlarmSensorsSchema, NcAlarmSettingsPatchSchema, NcHistoryEntrySchema, NcRuleLastRunsSchema, NodeDirectorySchema, RECORDING_EXPORT_MAX_READ_BYTES, SystemMirror, TIMELAPSE_DENSE_FLOOR_SEC, TimelapseRuleSchema, YAMNET_TO_MACRO, alarmDeviceBlocksArming, alarmDeviceSecureOnArm, alarmPatchLanded, conditionVisibleForKind, createSystemProxy, droppedConditionsForKind, isHeldBackTemplateVar, isSystemRuleKind, kindsForDiscriminator, knownValues, nodePickerItems, packagePickerItems, pickerForCondition, pickerForRuleClasses, resolveModeDelays, ruleDeviceCompatCapNames, ruleEditorSectionsForKind, ruleGroupingApplies, ruleKindOf, ruleKindSpec, ruleSeedForSection, sensorTransitionNeverFires, sensorTransitionOffered, stateLabel, systemEventDeliveryProblems, systemEventFilterAppliesToAnyKind, systemEventKindsOfFamily, systemFamilyOfKind, templateCarriesSummary, templateVarDeliveryApplies, templateVarKindApplies, templateVarSystemEventApplies };
10804
+ export { APPLE_SA_TO_MACRO, AUDIO_MACRO_LABELS, AlarmArmModeSchema, CLIP_BROKER_PACED_RATES, COCO_TO_MACRO, CamProfileSchema, DEFAULT_TIMELAPSE_PREVIEW_TEXT, DeviceType, EVENTFUL_CAP_NAMES, EVENT_KIND_BY_CAP, EVENT_TAXONOMY, MACRO_LABELS, NC_ALARM_DELAY_MAX_SEC, NC_ALARM_SENSORS_MAX, NC_ALARM_SYSTEM_EVENT_KINDS, NC_ALARM_TRIGGERED_MAX_SEC, NC_BATTERY_CONSUMABLE_SYSTEM_EVENT_KINDS, NC_CAMERA_SCOPE_CAP_NAME, NC_CONSUMABLE_LOW_PERCENT_DEFAULT, NC_CONSUMABLE_LOW_PERCENT_MAX, NC_GROUPING_DEFAULT_SEC, NC_GROUPING_MAX_SEC, NC_GROUPING_MIN_SEC, NC_RULE_EDITOR_SECTION_ORDER, NC_RULE_KIND_SPECS, NC_SENSOR_TRANSITION_UNSUPPORTED_REASON, NC_SYSTEM_EVENT_FAMILY_FILTERS, NC_SYSTEM_EVENT_KIND_GROUP_SPECS, NC_SYSTEM_EVENT_SUMMARY_FAMILIES, NC_TEMPLATE_HELD_BACK_SYSTEM_EVENT_KINDS, NC_TEMPLATE_VARS, NcAlarmConfigSchema, NcAlarmSensorsSchema, NcAlarmSettingsPatchSchema, NcHistoryEntrySchema, NcRuleLastRunsSchema, NodeDirectorySchema, RECORDING_EXPORT_MAX_READ_BYTES, RETRAIN_REVIEW_ADJUSTED_IOU, RETRAIN_REVIEW_MATCH_IOU, RETRAIN_REVIEW_STATUSES, RETRAIN_REVISION_CONFLICT, SystemMirror, TIMELAPSE_DENSE_FLOOR_SEC, TimelapseRuleSchema, YAMNET_TO_MACRO, alarmDeviceBlocksArming, alarmDeviceSecureOnArm, alarmPatchLanded, classifyRetrainReview, conditionVisibleForKind, createSystemProxy, droppedConditionsForKind, isHeldBackTemplateVar, isSystemRuleKind, kindsForDiscriminator, knownValues, nodePickerItems, packagePickerItems, parseRetrainRevisionConflict, pickerForCondition, pickerForRuleClasses, resolveModeDelays, retrainBoxIou, retrainRevisionConflictMessage, ruleDeviceCompatCapNames, ruleEditorSectionsForKind, ruleGroupingApplies, ruleKindOf, ruleKindSpec, ruleSeedForSection, sensorTransitionNeverFires, sensorTransitionOffered, stateLabel, summarizeReview, systemEventDeliveryProblems, systemEventFilterAppliesToAnyKind, systemEventKindsOfFamily, systemFamilyOfKind, templateCarriesSummary, templateVarDeliveryApplies, templateVarKindApplies, templateVarSystemEventApplies };
@@ -1885,6 +1885,13 @@ export type AppRouter = TrpcCoreRouter<{
1885
1885
  output: z.infer<typeof dataStoreProviderCapability.methods.insertMany.output>;
1886
1886
  meta: object;
1887
1887
  }>;
1888
+ transact: TRPCMutationProcedure<{
1889
+ input: {
1890
+ [x: string]: unknown;
1891
+ } & z.input<typeof dataStoreProviderCapability.methods.transact.input>;
1892
+ output: z.infer<typeof dataStoreProviderCapability.methods.transact.output>;
1893
+ meta: object;
1894
+ }>;
1888
1895
  update: TRPCMutationProcedure<{
1889
1896
  input: {
1890
1897
  [x: string]: unknown;
@@ -6136,6 +6143,13 @@ export type AppRouter = TrpcCoreRouter<{
6136
6143
  output: z.infer<typeof pipelineAnalyticsCapability.methods.saveRetrainAnnotations.output>;
6137
6144
  meta: object;
6138
6145
  }>;
6146
+ backfillRetrainOrigins: TRPCMutationProcedure<{
6147
+ input: {
6148
+ [x: string]: unknown;
6149
+ } & z.input<typeof pipelineAnalyticsCapability.methods.backfillRetrainOrigins.input>;
6150
+ output: z.infer<typeof pipelineAnalyticsCapability.methods.backfillRetrainOrigins.output>;
6151
+ meta: object;
6152
+ }>;
6139
6153
  completeRetrainTrack: TRPCMutationProcedure<{
6140
6154
  input: {
6141
6155
  [x: string]: unknown;
@@ -8153,6 +8167,13 @@ export type AppRouter = TrpcCoreRouter<{
8153
8167
  output: z.infer<typeof settingsStoreCapability.methods.insertMany.output>;
8154
8168
  meta: object;
8155
8169
  }>;
8170
+ transact: TRPCMutationProcedure<{
8171
+ input: {
8172
+ [x: string]: unknown;
8173
+ } & z.input<typeof settingsStoreCapability.methods.transact.input>;
8174
+ output: z.infer<typeof settingsStoreCapability.methods.transact.output>;
8175
+ meta: object;
8176
+ }>;
8156
8177
  update: TRPCMutationProcedure<{
8157
8178
  input: {
8158
8179
  [x: string]: unknown;
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 1300 method paths across 170 capabilities.
9
+ * Coverage: 1303 method paths across 170 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {
@@ -6,7 +6,7 @@
6
6
  * system-scope cap that takes a deviceId was previously never device-filtered
7
7
  * — see the generator header).
8
8
  *
9
- * Coverage: 519 methods carry a device reference, of which
9
+ * Coverage: 520 methods carry a device reference, of which
10
10
  * 165 are on SYSTEM-scope caps.
11
11
  *
12
12
  * Top-level fields, number arrays, and one-level arrays of objects carrying