@trillboards/edge-sdk 0.2.39 → 0.2.41

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 (82) hide show
  1. package/README.md +1 -1
  2. package/deploy/docker/{Dockerfile.cloud-sense → Dockerfile.connector} +52 -39
  3. package/deploy/docker/README.md +73 -1
  4. package/deploy/docker/connector-entrypoint.sh +46 -80
  5. package/deploy/macos/README.md +1 -1
  6. package/deploy/macos/test-install-pair.sh +9 -9
  7. package/deploy/systemd/install.sh +16 -1
  8. package/deploy/systemd/setup-litertlm.sh +201 -0
  9. package/deploy/systemd/trillboards-edge.service +8 -4
  10. package/deploy/systemd/trillboards-litertlm.service +46 -0
  11. package/deploy/windows/register-task.ps1 +6 -3
  12. package/deploy/windows/run-connector.ps1 +1 -1
  13. package/deploy/windows/trillboards-edge-task.vbs +5 -1
  14. package/dist/CloudSensingManager.d.ts +45 -19
  15. package/dist/CloudSensingManager.d.ts.map +1 -1
  16. package/dist/CloudSensingManager.js +89 -46
  17. package/dist/CloudSensingManager.js.map +1 -1
  18. package/dist/EdgeAgent.js +1 -1
  19. package/dist/SensingProfileClient.d.ts +9 -0
  20. package/dist/SensingProfileClient.d.ts.map +1 -1
  21. package/dist/SensingProfileClient.js +4 -0
  22. package/dist/SensingProfileClient.js.map +1 -1
  23. package/dist/SetupSessionClient.d.ts +0 -1
  24. package/dist/SetupSessionClient.d.ts.map +1 -1
  25. package/dist/autoSense.d.ts +42 -31
  26. package/dist/autoSense.d.ts.map +1 -1
  27. package/dist/autoSense.js +127 -99
  28. package/dist/autoSense.js.map +1 -1
  29. package/dist/cli.d.ts.map +1 -1
  30. package/dist/cli.js +25 -84
  31. package/dist/cli.js.map +1 -1
  32. package/dist/combinedSense.d.ts +1 -3
  33. package/dist/combinedSense.d.ts.map +1 -1
  34. package/dist/combinedSense.js +0 -1
  35. package/dist/combinedSense.js.map +1 -1
  36. package/dist/config.d.ts +17 -64
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/config.js +0 -10
  39. package/dist/config.js.map +1 -1
  40. package/dist/deployedCadence.d.ts +37 -75
  41. package/dist/deployedCadence.d.ts.map +1 -1
  42. package/dist/deployedCadence.js +45 -105
  43. package/dist/deployedCadence.js.map +1 -1
  44. package/dist/deployedCadenceSource.d.ts +0 -2
  45. package/dist/deployedCadenceSource.d.ts.map +1 -1
  46. package/dist/deployedCadenceSource.js +0 -1
  47. package/dist/deployedCadenceSource.js.map +1 -1
  48. package/dist/diagnose.d.ts +1 -1
  49. package/dist/diagnose.js +1 -1
  50. package/dist/index.d.ts +0 -3
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +1 -8
  53. package/dist/index.js.map +1 -1
  54. package/dist/localInferenceSchema.d.ts +19 -0
  55. package/dist/localInferenceSchema.d.ts.map +1 -1
  56. package/dist/localInferenceSchema.js +23 -0
  57. package/dist/localInferenceSchema.js.map +1 -1
  58. package/dist/localSensing.d.ts +48 -107
  59. package/dist/localSensing.d.ts.map +1 -1
  60. package/dist/localSensing.js +80 -171
  61. package/dist/localSensing.js.map +1 -1
  62. package/dist/sensingCatchRate.d.ts +246 -0
  63. package/dist/sensingCatchRate.d.ts.map +1 -0
  64. package/dist/sensingCatchRate.js +514 -0
  65. package/dist/sensingCatchRate.js.map +1 -0
  66. package/dist/sensingRunMode.d.ts +14 -11
  67. package/dist/sensingRunMode.d.ts.map +1 -1
  68. package/dist/sensingRunMode.js +9 -92
  69. package/dist/sensingRunMode.js.map +1 -1
  70. package/dist/setupMode.d.ts +0 -1
  71. package/dist/setupMode.d.ts.map +1 -1
  72. package/dist/setupMode.js +5 -3
  73. package/dist/setupMode.js.map +1 -1
  74. package/dist/startupDiscovery.d.ts +43 -0
  75. package/dist/startupDiscovery.d.ts.map +1 -0
  76. package/dist/startupDiscovery.js +72 -0
  77. package/dist/startupDiscovery.js.map +1 -0
  78. package/package.json +1 -1
  79. package/dist/CloudEscalationPolicy.d.ts +0 -143
  80. package/dist/CloudEscalationPolicy.d.ts.map +0 -1
  81. package/dist/CloudEscalationPolicy.js +0 -137
  82. package/dist/CloudEscalationPolicy.js.map +0 -1
@@ -0,0 +1,514 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SAMPLER = exports.TEMPORAL_SHAPES = exports.SHAPE_GROUPS = void 0;
4
+ exports.shapeById = shapeById;
5
+ exports.classifyShape = classifyShape;
6
+ exports.lookCoverage = lookCoverage;
7
+ exports.expectedCatchRate = expectedCatchRate;
8
+ exports.replay = replay;
9
+ exports.reportByShape = reportByShape;
10
+ exports.reportByGroup = reportByGroup;
11
+ exports.requiredLookCadenceS = requiredLookCadenceS;
12
+ /**
13
+ * sensingCatchRate — CATCH RATE as a measured, per-temporal-shape number.
14
+ *
15
+ * ── The question this answers ───────────────────────────────────────────────
16
+ *
17
+ * The product sells ~50 distinct promises ("text me when the line passes 3",
18
+ * "alert me if a vehicle pulls in after closing", "tell me when the yard is
19
+ * full"). Every one of them is a claim that we will NOTICE something. Nothing
20
+ * in the tree measured whether we do.
21
+ *
22
+ * A single blended "catch rate" is worse than none, because the spread is the
23
+ * whole story: at the shipped 30 s look cadence a 2.2 s door-open is caught
24
+ * ~7% of the time and a full car park is caught ~100% of the time. Averaging
25
+ * those produces a number that describes no promise we actually sell. So the
26
+ * unit of measurement here is the TEMPORAL SHAPE, and every rate is reported
27
+ * with its false-trigger rate beside it — a detector that fires constantly has
28
+ * a wonderful catch rate.
29
+ *
30
+ * ── The mechanism, which is OURS and not a model of one ─────────────────────
31
+ *
32
+ * `CloudSensingManager` runs one cadence loop per feed. Each tick is a LOOK:
33
+ * pull a frame, run `MotionGate.evaluate`. Looking is cheap (3.6 ms median —
34
+ * see connectorCadence.ts) and its interval is `faceTriggeredIntervalMs`,
35
+ * `LOOK_INTERVAL_MS` = 30 s. Only when the gate says `analyze` does the frame
36
+ * contend for the single `VlmAdmissionGate` slot, and an analysis costs
37
+ * `MEASURED_LOCAL_ANALYZE_MS` = 7.30 s.
38
+ *
39
+ * The consequence is the entire finding, and it is arithmetic:
40
+ *
41
+ * an event is catchable only if a LOOK lands inside it.
42
+ *
43
+ * A motion gate cannot see motion it did not look at. For an event of duration
44
+ * `d` against a look cadence `T` whose phase is uncorrelated with the event,
45
+ * the probability that at least one look lands inside is `min(d/T, 1)`, so
46
+ *
47
+ * catch rate = E[ min(d/T, 1) ] x P(admitted before the buffer TTL)
48
+ * x P(the model reads the frame right)
49
+ *
50
+ * The first factor dominates and it is bounded by the LOOK cadence — not by
51
+ * the analysis budget everyone reaches for first. That distinction is load
52
+ * bearing: looking 12x more often costs 2.3% of one core across 16 feeds,
53
+ * while analyzing 12x more often is impossible on one 7.30 s slot. The two
54
+ * cadences are separable and today they are not separated.
55
+ *
56
+ * ── Why the closed form is not enough, and the replay exists ────────────────
57
+ *
58
+ * `expectedCatchRate` below IS that closed form and it is used to VALIDATE the
59
+ * replay, never to replace it. The closed form assumes an uncorrelated phase,
60
+ * an always-free admission slot, and a gate that never misses motion it looked
61
+ * at. The replay drops all three: it advances real look ticks against real
62
+ * event intervals, contends for a shared slot at the measured analysis cost,
63
+ * and honours the candidate buffer's TTL. Where they disagree, the disagreement
64
+ * IS the measurement — a shape whose replayed rate sits far below its closed
65
+ * form is starved by the slot, not by the cadence, and that is a different fix.
66
+ *
67
+ * ── What is deliberately NOT scored here ────────────────────────────────────
68
+ *
69
+ * Per-field content accuracy. `scoreFields` in localVlmBench.ts already does
70
+ * that, it is unit-tested, and it deliberately never scores `confidence`.
71
+ * The content layer enters this file only as `fieldAccuracy` — one number per
72
+ * shape, supplied by a scored replay — because a second scorer would drift
73
+ * from the first and the two would disagree about the same frame.
74
+ */
75
+ const connectorCadence_1 = require("./connectorCadence");
76
+ exports.SHAPE_GROUPS = ['Instant', 'Brief', 'Sustained', 'State'];
77
+ /**
78
+ * Seven shapes. Each `exampleField` is a real `condition_field` present in
79
+ * `sensing_routes` in production — this table is a classification of what we
80
+ * already sell, not a wish list.
81
+ *
82
+ * The duration bands are cut where the FAILURE MODE changes, not on round
83
+ * numbers. Below ~3 s a 30 s look cadence misses the event outright. Between
84
+ * 3 s and 30 s the look lands but the single admission slot (7.30 s) may still
85
+ * lose it. Past 30 s at least one look is guaranteed and the question stops
86
+ * being "did we see it" and becomes "did we fire on the right edge".
87
+ */
88
+ exports.TEMPORAL_SHAPES = Object.freeze([
89
+ {
90
+ id: 'instant',
91
+ group: 'Instant',
92
+ description: 'A punctual act that is over before the next look. A door opens; a package is put down.',
93
+ minDurationS: 0,
94
+ maxDurationS: 3,
95
+ rulePolicy: 'once_per_episode',
96
+ holdSeconds: 0,
97
+ exampleField: 'package_detected',
98
+ },
99
+ {
100
+ id: 'arrival',
101
+ group: 'Instant',
102
+ description: 'An entity crossing a boundary. One alert per NEW entity, so a crowd arriving one-by-one costs one alert.',
103
+ minDurationS: 0,
104
+ maxDurationS: 3,
105
+ rulePolicy: 'per_new_entity',
106
+ holdSeconds: 0,
107
+ exampleField: 'person_entered',
108
+ },
109
+ {
110
+ id: 'interaction',
111
+ group: 'Brief',
112
+ description: 'A bounded interaction between people, or a person and a thing. A purchase; someone waiting at a counter.',
113
+ minDurationS: 3,
114
+ maxDurationS: 30,
115
+ rulePolicy: 'once_per_episode',
116
+ holdSeconds: 0,
117
+ exampleField: 'customer_count',
118
+ },
119
+ {
120
+ id: 'excursion',
121
+ group: 'Brief',
122
+ description: 'A count crossing a line and coming back. The queue passes three, then clears.',
123
+ minDurationS: 3,
124
+ maxDurationS: 30,
125
+ rulePolicy: 'on_escalation',
126
+ holdSeconds: 0,
127
+ exampleField: 'person_count',
128
+ },
129
+ {
130
+ id: 'dwell',
131
+ group: 'Sustained',
132
+ description: 'Something that only matters once it has HELD. Loitering; a hazard left on the floor.',
133
+ minDurationS: 30,
134
+ maxDurationS: Infinity,
135
+ rulePolicy: 'once_per_episode',
136
+ holdSeconds: 111,
137
+ exampleField: 'loitering_duration_seconds',
138
+ },
139
+ {
140
+ id: 'condition',
141
+ group: 'State',
142
+ description: 'A level that is simply true for a long time. The yard is full; a vehicle is in the bay after closing.',
143
+ minDurationS: 30,
144
+ maxDurationS: Infinity,
145
+ rulePolicy: 'once_per_episode',
146
+ holdSeconds: 0,
147
+ exampleField: 'vehicle_count',
148
+ },
149
+ {
150
+ id: 'absence',
151
+ group: 'State',
152
+ description: 'Nothing where something should be. No one on the till; the fire lane is meant to be clear and is not.',
153
+ minDurationS: 30,
154
+ maxDurationS: Infinity,
155
+ rulePolicy: 'nag_until_resolved',
156
+ holdSeconds: 0,
157
+ exampleField: 'fire_lane_violation',
158
+ },
159
+ ]);
160
+ const SHAPE_BY_ID = new Map(exports.TEMPORAL_SHAPES.map((s) => [s.id, s]));
161
+ /** Look a shape up by id. `null` — never a throw — for an unknown id. */
162
+ function shapeById(id) {
163
+ return SHAPE_BY_ID.get(id) || null;
164
+ }
165
+ /**
166
+ * The shape of an annotated event.
167
+ *
168
+ * Duration alone does NOT determine the shape: `instant` and `arrival` share a
169
+ * band and are separated by the rule policy, as do `interaction`/`excursion`
170
+ * and `condition`/`absence`. So the corpus must carry the policy, and this
171
+ * function refuses to guess when it is absent rather than silently picking the
172
+ * first band match — a misclassified event lands in the wrong shape's rate and
173
+ * is invisible.
174
+ */
175
+ function classifyShape(durationS, rulePolicy) {
176
+ if (!Number.isFinite(durationS) || durationS < 0)
177
+ return null;
178
+ for (const shape of exports.TEMPORAL_SHAPES) {
179
+ if (durationS >= shape.minDurationS && durationS < shape.maxDurationS
180
+ && shape.rulePolicy === rulePolicy)
181
+ return shape;
182
+ }
183
+ return null;
184
+ }
185
+ // ---------------------------------------------------------------------------
186
+ // The closed form — used to VALIDATE the replay, never to replace it
187
+ // ---------------------------------------------------------------------------
188
+ /**
189
+ * P(at least one look lands inside an event of `durationS`) for a periodic
190
+ * look cadence of `cadenceS` whose phase is uncorrelated with the event.
191
+ *
192
+ * `min(d/T, 1)`. An event at least as long as the cadence is caught with
193
+ * certainty; a shorter one is caught in proportion to how much of the cadence
194
+ * it occupies. This is the ceiling every other factor multiplies down from.
195
+ */
196
+ function lookCoverage(durationS, cadenceS) {
197
+ if (!Number.isFinite(durationS) || durationS <= 0)
198
+ return 0;
199
+ if (!Number.isFinite(cadenceS) || cadenceS <= 0)
200
+ return 1;
201
+ return Math.min(durationS / cadenceS, 1);
202
+ }
203
+ /**
204
+ * The closed-form catch rate over a whole duration sample: `E[min(d/T, 1)]`.
205
+ *
206
+ * Note this is driven by the MEAN of the truncated ratio, not by the median
207
+ * duration. A right-skewed duration sample — which every real activity corpus
208
+ * is — has a mean well above its median, so quoting `median/T` understates the
209
+ * rate. That is exactly the arithmetic behind the published baseline: 59,093
210
+ * activities with a 2.4 s median catch at 18.2%, not at 2.4/30 = 8.0%.
211
+ */
212
+ function expectedCatchRate(durationsS, cadenceS) {
213
+ const clean = (durationsS || []).filter((d) => typeof d === 'number' && Number.isFinite(d) && d >= 0);
214
+ if (clean.length === 0)
215
+ return null;
216
+ let acc = 0;
217
+ for (const d of clean)
218
+ acc += lookCoverage(d, cadenceS);
219
+ return acc / clean.length;
220
+ }
221
+ exports.DEFAULT_SAMPLER = Object.freeze({
222
+ lookCadenceS: connectorCadence_1.LOOK_INTERVAL_MS / 1000,
223
+ heartbeatS: 300,
224
+ maxInFlight: 1,
225
+ analyzeS: connectorCadence_1.MEASURED_LOCAL_ANALYZE_MS / 1000,
226
+ bufferTtlS: 90,
227
+ bufferMaxFrames: 32,
228
+ fieldAccuracy: 1,
229
+ falsePositiveRate: 0,
230
+ });
231
+ /**
232
+ * A tiny deterministic PRNG. Seeded and explicit, because a replay whose
233
+ * numbers move between runs cannot be a regression gate — and because
234
+ * `Math.random()` in an eval is how a flaky CI check gets born.
235
+ */
236
+ function mulberry32(seed) {
237
+ let a = seed >>> 0;
238
+ return () => {
239
+ a = (a + 0x6D2B79F5) >>> 0;
240
+ let t = Math.imul(a ^ (a >>> 15), 1 | a);
241
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
242
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
243
+ };
244
+ }
245
+ /**
246
+ * Replay an annotated corpus through the shipped scheduling law.
247
+ *
248
+ * The loop is the real one, in the real order:
249
+ * look tick per feed (staggered) -> motion gate -> admission slot ->
250
+ * candidate buffer on denial -> pump when the slot frees.
251
+ *
252
+ * `durationS` is the replay length; events must fall inside it. `seed` fixes
253
+ * the per-feed look PHASE, which is the only stochastic input — and it is the
254
+ * one that matters, because a phase locked to the events would manufacture a
255
+ * catch rate of either 0% or 100%.
256
+ */
257
+ function replay(events, config = {}, { durationS, seed = 1 }) {
258
+ const cfg = { ...exports.DEFAULT_SAMPLER, ...config };
259
+ const rand = mulberry32(seed);
260
+ const feeds = Array.from(new Set(events.map((e) => e.feed))).sort();
261
+ // Per-feed look phase. `feedStaggerMs` in CloudSensingManager spreads feeds
262
+ // across one cadence so N cameras never grab simultaneously; the random
263
+ // component is the event-uncorrelated part.
264
+ const phase = new Map();
265
+ feeds.forEach((f, i) => {
266
+ const stagger = feeds.length > 0 ? (i * cfg.lookCadenceS) / feeds.length : 0;
267
+ phase.set(f, (stagger + rand() * cfg.lookCadenceS) % cfg.lookCadenceS);
268
+ });
269
+ const byFeed = new Map();
270
+ for (const e of events) {
271
+ if (!byFeed.has(e.feed))
272
+ byFeed.set(e.feed, []);
273
+ byFeed.get(e.feed).push(e);
274
+ }
275
+ // Slot occupancy: the times at which each in-flight analysis frees up.
276
+ const slotFreeAt = new Array(Math.max(1, cfg.maxInFlight)).fill(0);
277
+ const buffer = [];
278
+ const analyzedEvents = new Map(); // eventId -> analysis land time
279
+ let idleAnalyses = 0;
280
+ let analyses = 0;
281
+ let looks = 0;
282
+ // `heartbeatDue` in MotionGate.evaluate is measured from `lastAnalyzedAtMs`,
283
+ // which `markAnalyzed()` sets only AFTER an inference actually lands — never
284
+ // from the last look. A starved feed therefore keeps qualifying for the
285
+ // heartbeat, which is the behaviour that lets the buffer rescue it.
286
+ const lastAnalyzedAt = new Map();
287
+ // Which events have already presented their CHANGE to the gate. The detector
288
+ // is a frame-to-frame differ: a person who arrives and then stands still
289
+ // produces motion on the look that catches the arrival and `stationary`
290
+ // afterwards. Firing `motion` on every look inside a long event would invent
291
+ // analysis demand the real gate never generates, and would then charge the
292
+ // shortfall to slot starvation.
293
+ const changePresented = new Set();
294
+ const everLooked = new Set();
295
+ const slotStarved = new Set();
296
+ /** Events whose frame was HELD in the candidate buffer at least once. */
297
+ const everBuffered = new Set();
298
+ /** Take the earliest-free slot if one is free at `t`. */
299
+ const acquire = (t) => {
300
+ let best = -1;
301
+ for (let i = 0; i < slotFreeAt.length; i += 1) {
302
+ if (slotFreeAt[i] <= t && (best === -1 || slotFreeAt[i] < slotFreeAt[best]))
303
+ best = i;
304
+ }
305
+ return best;
306
+ };
307
+ /** Run the analysis, booking the slot and recording what it found. */
308
+ const runAnalysis = (slot, t, frame) => {
309
+ slotFreeAt[slot] = t + cfg.analyzeS;
310
+ analyses += 1;
311
+ // `markAnalyzed()` — the single commit point. Only a landed inference
312
+ // resets the heartbeat clock.
313
+ lastAnalyzedAt.set(frame.feed, t);
314
+ if (frame.eventId) {
315
+ if (!analyzedEvents.has(frame.eventId))
316
+ analyzedEvents.set(frame.eventId, t);
317
+ }
318
+ else {
319
+ idleAnalyses += 1;
320
+ }
321
+ };
322
+ /** Drain the buffer into any free slot. Called after every tick. */
323
+ const pump = (t) => {
324
+ // Expire first — the real pump does `expire()` before it serves.
325
+ for (let i = buffer.length - 1; i >= 0; i -= 1) {
326
+ if (t - buffer[i].capturedAtS > cfg.bufferTtlS)
327
+ buffer.splice(i, 1);
328
+ }
329
+ let slot = acquire(t);
330
+ while (slot !== -1 && buffer.length > 0) {
331
+ const frame = buffer.shift();
332
+ runAnalysis(slot, t, frame);
333
+ slot = acquire(t);
334
+ }
335
+ };
336
+ // Every look tick across every feed, in time order.
337
+ const ticks = [];
338
+ for (const feed of feeds) {
339
+ const p = phase.get(feed);
340
+ for (let t = p; t < durationS; t += cfg.lookCadenceS)
341
+ ticks.push({ t, feed });
342
+ }
343
+ ticks.sort((a, b) => a.t - b.t || a.feed.localeCompare(b.feed));
344
+ for (const { t, feed } of ticks) {
345
+ pump(t);
346
+ looks += 1;
347
+ const active = (byFeed.get(feed) || []).find((e) => t >= e.startS && t < e.endS);
348
+ if (active)
349
+ everLooked.add(active.id);
350
+ // MotionGate decision precedence, in the shipped order:
351
+ // heartbeatDue -> no_motion -> calibrating -> stationary -> motion.
352
+ const hbLast = lastAnalyzedAt.get(feed);
353
+ const heartbeatDue = hbLast === undefined || (t - hbLast) >= cfg.heartbeatS;
354
+ // `motion` only on the look that catches the CHANGE. A still-scene event
355
+ // never presents one at all and is reachable ONLY by the heartbeat — the
356
+ // leaking-cooler case connectorCadence.ts is written about.
357
+ const motion = Boolean(active && active.producesMotion && !changePresented.has(active.id));
358
+ if (!motion && !heartbeatDue)
359
+ continue; // no_motion / stationary -> dropped
360
+ if (motion && active)
361
+ changePresented.add(active.id);
362
+ const frame = {
363
+ eventId: active ? active.id : null,
364
+ feed,
365
+ capturedAtS: t,
366
+ };
367
+ const slot = acquire(t);
368
+ if (slot !== -1) {
369
+ runAnalysis(slot, t, frame);
370
+ continue;
371
+ }
372
+ // Denied. `isChangeCandidate` holds a real change candidate; a bare
373
+ // heartbeat on a still scene is NOT a candidate and is dropped here.
374
+ if (active)
375
+ slotStarved.add(active.id);
376
+ if (motion && buffer.length < cfg.bufferMaxFrames) {
377
+ buffer.push(frame);
378
+ if (active)
379
+ everBuffered.add(active.id);
380
+ }
381
+ }
382
+ pump(durationS);
383
+ // Content layer. One draw per caught event, from the same seeded stream, so
384
+ // the whole result is reproducible from `seed` alone.
385
+ const outcomes = events.map((e) => {
386
+ const looked = everLooked.has(e.id);
387
+ const landedAt = analyzedEvents.get(e.id);
388
+ const analyzed = landedAt !== undefined;
389
+ let lostTo = null;
390
+ let caught = false;
391
+ if (!looked)
392
+ lostTo = 'never_looked';
393
+ else if (!analyzed) {
394
+ // Order matters: a frame that was HELD and then aged out is a buffer
395
+ // failure; one that was denied and never held is a slot failure; one
396
+ // that was never denied at all was dropped by the GATE — it was looked
397
+ // at and the gate declined to analyse it, which is the still-scene case
398
+ // and is fixed by the heartbeat, not by anything downstream.
399
+ if (everBuffered.has(e.id))
400
+ lostTo = 'buffer_expired';
401
+ else if (slotStarved.has(e.id))
402
+ lostTo = 'slot_starved';
403
+ else
404
+ lostTo = 'gate_dropped';
405
+ }
406
+ else if (rand() < cfg.fieldAccuracy)
407
+ caught = true;
408
+ else
409
+ lostTo = 'model_wrong';
410
+ return {
411
+ id: e.id,
412
+ shapeId: e.shapeId,
413
+ eventClass: e.eventClass,
414
+ looked,
415
+ analyzed,
416
+ caught,
417
+ lostTo,
418
+ latencyS: analyzed ? landedAt - e.startS : null,
419
+ };
420
+ });
421
+ let falseTriggers = 0;
422
+ for (let i = 0; i < idleAnalyses; i += 1) {
423
+ if (rand() < cfg.falsePositiveRate)
424
+ falseTriggers += 1;
425
+ }
426
+ return { outcomes, idleAnalyses, falseTriggers, analyses, looks };
427
+ }
428
+ function medianOf(values) {
429
+ const clean = values.filter((v) => typeof v === 'number' && Number.isFinite(v));
430
+ if (clean.length === 0)
431
+ return null;
432
+ const s = [...clean].sort((a, b) => a - b);
433
+ const mid = Math.floor(s.length / 2);
434
+ return s.length % 2 === 1 ? s[mid] : (s[mid - 1] + s[mid]) / 2;
435
+ }
436
+ /**
437
+ * Group a replay into one row per shape.
438
+ *
439
+ * The false-trigger rate is charged to every shape that was present, because
440
+ * an idle analysis is not attributable to one shape — it happened when NOTHING
441
+ * was in progress. Reporting it per shape anyway is deliberate: an operator
442
+ * reads one row, and a row that hides the noise is the row that sells a
443
+ * detector nobody can live with.
444
+ */
445
+ function reportByShape(result) {
446
+ const byShape = new Map();
447
+ for (const o of result.outcomes) {
448
+ if (!byShape.has(o.shapeId))
449
+ byShape.set(o.shapeId, []);
450
+ byShape.get(o.shapeId).push(o);
451
+ }
452
+ const reports = [];
453
+ for (const shape of exports.TEMPORAL_SHAPES) {
454
+ const outs = byShape.get(shape.id);
455
+ if (!outs || outs.length === 0)
456
+ continue;
457
+ const caught = outs.filter((o) => o.caught).length;
458
+ const totalFires = caught + result.falseTriggers;
459
+ reports.push({
460
+ shapeId: shape.id,
461
+ group: shape.group,
462
+ events: outs.length,
463
+ lookRate: outs.filter((o) => o.looked).length / outs.length,
464
+ analyzeRate: outs.filter((o) => o.analyzed).length / outs.length,
465
+ catchRate: caught / outs.length,
466
+ falseTriggerRate: totalFires > 0 ? result.falseTriggers / totalFires : 0,
467
+ medianLatencyS: medianOf(outs.filter((o) => o.caught).map((o) => o.latencyS)),
468
+ lost: {
469
+ never_looked: outs.filter((o) => o.lostTo === 'never_looked').length,
470
+ gate_dropped: outs.filter((o) => o.lostTo === 'gate_dropped').length,
471
+ slot_starved: outs.filter((o) => o.lostTo === 'slot_starved').length,
472
+ buffer_expired: outs.filter((o) => o.lostTo === 'buffer_expired').length,
473
+ model_wrong: outs.filter((o) => o.lostTo === 'model_wrong').length,
474
+ },
475
+ });
476
+ }
477
+ return reports;
478
+ }
479
+ /** Roll the shape rows up to the four reporting groups. */
480
+ function reportByGroup(reports) {
481
+ const out = [];
482
+ for (const group of exports.SHAPE_GROUPS) {
483
+ const rows = reports.filter((r) => r.group === group);
484
+ if (rows.length === 0)
485
+ continue;
486
+ const events = rows.reduce((a, r) => a + r.events, 0);
487
+ if (events === 0)
488
+ continue;
489
+ out.push({
490
+ group,
491
+ events,
492
+ catchRate: rows.reduce((a, r) => a + r.catchRate * r.events, 0) / events,
493
+ falseTriggerRate: rows.reduce((a, r) => a + r.falseTriggerRate * r.events, 0) / events,
494
+ lookRate: rows.reduce((a, r) => a + r.lookRate * r.events, 0) / events,
495
+ });
496
+ }
497
+ return out;
498
+ }
499
+ /**
500
+ * The look cadence a shape needs to reach `target`.
501
+ *
502
+ * `min(d/T,1) >= target => T <= d/target`. Reported so a red gate carries
503
+ * the fix rather than only the failure: a 2.2 s event at a 90% target needs a
504
+ * 2.44 s look cadence, which is a real, affordable number (looking costs
505
+ * 3.6 ms) and is 12x finer than the 30 s we ship.
506
+ */
507
+ function requiredLookCadenceS(medianDurationS, target) {
508
+ if (!Number.isFinite(medianDurationS) || medianDurationS <= 0)
509
+ return null;
510
+ if (!Number.isFinite(target) || target <= 0 || target > 1)
511
+ return null;
512
+ return medianDurationS / target;
513
+ }
514
+ //# sourceMappingURL=sensingCatchRate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensingCatchRate.js","sourceRoot":"","sources":["../src/sensingCatchRate.ts"],"names":[],"mappings":"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"}
@@ -21,13 +21,23 @@ export interface SensingManagerOpts {
21
21
  * can hold either a real manager or a test fake.
22
22
  */
23
23
  export interface SensingManager {
24
- start(): Promise<void>;
24
+ /**
25
+ * `healthOnly` runs the cameras WITHOUT inference: open, prove video, report
26
+ * camera health, analyze nothing. `auto-sense` runs this while it waits for
27
+ * the on-device model so a connector that cannot sense yet is still visibly
28
+ * ALIVE in the portal — see `AutoSenseSupervisor` and the `healthOnlyMode`
29
+ * docs on `CloudSensingManager`.
30
+ */
31
+ start(opts?: {
32
+ healthOnly?: boolean;
33
+ }): Promise<void>;
25
34
  stop(): Promise<void>;
26
35
  /**
27
36
  * How many frames the ON-DEVICE runtime has produced a result for so far this
28
- * run (`start()` resets the count). NOT the total analysis count: a frame the
29
- * local provider threw on and the cloud answered — `cloudFallbackOnError`, or
30
- * an escalation — is an analysis but proves nothing about the local runtime.
37
+ * run (`start()` resets the count). Every analysis is now an on-device one —
38
+ * the connector has no cloud path — but the counter stays distinct from the
39
+ * total because it is the supervisor's proof-of-life and must never be
40
+ * advanceable by anything except a completed local inference.
31
41
  *
32
42
  * OPTIONAL, and structurally satisfied by `CloudSensingManager` already — no
33
43
  * manager code changes to supply it. The auto-sense supervisor reads it for
@@ -116,13 +126,6 @@ export declare function formatSensingHealth(health: ReturnType<CloudSensingManag
116
126
  export declare function createTransportFailureReporter(io: SensingIo, now?: () => number): {
117
127
  report(name: string, error: Error): boolean;
118
128
  };
119
- /**
120
- * Build (but do NOT start) the thin cloud-sensing manager: RTSP frame → POST
121
- * /v2/earner/audience-analyze (cloud VLM does the inference). Returns `null`
122
- * after logging NO_FEEDS_MESSAGE when no feeds are configured — the caller
123
- * decides how to surface that (the CLI + the supervisor both exit 1).
124
- */
125
- export declare function buildCloudSensingManager(config: EdgeAgentConfig, io?: SensingIo, opts?: SensingManagerOpts): CloudSensingManager | null;
126
129
  /**
127
130
  * Build (but do NOT start) the on-device LOCAL-sensing manager: RTSP frame →
128
131
  * LOCAL VLM (LiteRT-LM serving Gemma) → POST the STRUCTURED result as
@@ -1 +1 @@
1
- {"version":3,"file":"sensingRunMode.d.ts","sourceRoot":"","sources":["../src/sensingRunMode.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAInB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAOhD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC/C,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,KAAK,KACT,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC7B;AA2CD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;;;;;;;;;;;OAcG;IACH,gBAAgB,CAAC,IAAI;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC;KACxC,CAAC;CACH;AAED,iFAAiF;AACjF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,qEAAqE;AACrE,eAAO,MAAM,SAAS,EAAE,SAIvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAEgD,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAgB,CAAC;AAE5D;;;;;;;;;GASG;AACH,eAAO,MAAM,qCAAqC,QAAiB,CAAC;AAuNpE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,CAAC,GAC1D,MAAM,CA6CR;AAED;;;;;;;;;;GAUG;AACH;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,SAAS,EACb,GAAG,GAAE,MAAM,MAAiB,GAC3B;IAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAA;CAAE,CA+BjD;AAoED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,EAAE,GAAE,SAAqB,EACzB,IAAI,CAAC,EAAE,kBAAkB,GACxB,mBAAmB,GAAG,IAAI,CAiE5B;AAqCD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,EAAE,GAAE,SAAqB,EACzB,IAAI,CAAC,EAAE,kBAAkB,GACxB,mBAAmB,GAAG,IAAI,CAyF5B"}
1
+ {"version":3,"file":"sensingRunMode.d.ts","sourceRoot":"","sources":["../src/sensingRunMode.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EAGnB,KAAK,YAAY,EAElB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAOhD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC/C,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,CAClB,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,KAAK,KACT,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC7B;AA0CD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,IAAI;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB;;;;;;WAMG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC;KACxC,CAAC;CACH;AAED,iFAAiF;AACjF,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,qEAAqE;AACrE,eAAO,MAAM,SAAS,EAAE,SAIvB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAEgD,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,QAAgB,CAAC;AAE5D;;;;;;;;;GASG;AACH,eAAO,MAAM,qCAAqC,QAAiB,CAAC;AAmNpE;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,CAAC,GAC1D,MAAM,CAsCR;AAED;;;;;;;;;;GAUG;AACH;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,EAAE,EAAE,SAAS,EACb,GAAG,GAAE,MAAM,MAAiB,GAC3B;IAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAA;CAAE,CA+BjD;AAuGD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,EAAE,GAAE,SAAqB,EACzB,IAAI,CAAC,EAAE,kBAAkB,GACxB,mBAAmB,GAAG,IAAI,CAoF5B"}