@trillboards/edge-sdk 0.2.101 → 0.2.102
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.
- package/dist/EdgeAgent.js +1 -1
- package/dist/connectorCadence.d.ts +147 -40
- package/dist/connectorCadence.d.ts.map +1 -1
- package/dist/connectorCadence.js +170 -47
- package/dist/connectorCadence.js.map +1 -1
- package/dist/deployedCadence.d.ts +15 -4
- package/dist/deployedCadence.d.ts.map +1 -1
- package/dist/deployedCadence.js +21 -12
- package/dist/deployedCadence.js.map +1 -1
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.js +1 -1
- package/package.json +1 -1
package/dist/EdgeAgent.js
CHANGED
|
@@ -44,7 +44,7 @@ const edge_ads_1 = require("@trillboards/edge-ads");
|
|
|
44
44
|
const CommandRouter_1 = require("./CommandRouter");
|
|
45
45
|
const config_1 = require("./config");
|
|
46
46
|
const dataDir_1 = require("./dataDir");
|
|
47
|
-
const SDK_VERSION = '0.2.
|
|
47
|
+
const SDK_VERSION = '0.2.102';
|
|
48
48
|
class EdgeAgent extends events_1.EventEmitter {
|
|
49
49
|
constructor(config, deps) {
|
|
50
50
|
super();
|
|
@@ -90,18 +90,22 @@
|
|
|
90
90
|
* fire. A floor that ate the whole budget would starve them, and the connector
|
|
91
91
|
* would degrade into reporting empty aisles on time while missing the incident.
|
|
92
92
|
*
|
|
93
|
-
* Two clamps,
|
|
94
|
-
* - never faster than
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
93
|
+
* Two clamps, and NEITHER is a constant any more:
|
|
94
|
+
* - never faster than `analyzeFloorMsForFeedCount` — the LOOK cadence, because
|
|
95
|
+
* a frame nobody looked at cannot be analyzed. This clamp read
|
|
96
|
+
* `MIN_ANALYZE_INTERVAL_MS` (a flat 30 s) until 2026-08-26; see that
|
|
97
|
+
* constant's obituary above for the two claims that turned out not to hold
|
|
98
|
+
* and for why removing it cannot saturate the admission slot.
|
|
99
99
|
* - never slower than the DOOH default (300 s): this can only make a
|
|
100
100
|
* connector sample MORE often than it does today, never less.
|
|
101
101
|
*
|
|
102
|
-
* Result,
|
|
103
|
-
* 16 feeds 234 s, 21+ feeds pinned at 300 s.
|
|
104
|
-
*
|
|
102
|
+
* Result, on the 7.30 s BOOTSTRAP: 1 feed 14.6 s, 2 feeds 29.2 s, 4 feeds 59 s,
|
|
103
|
+
* 8 feeds 117 s, 16 feeds 234 s, 21+ feeds pinned at 300 s. The 4-feed row and
|
|
104
|
+
* everything slower are UNCHANGED by the 2026-08-26 removal — the flat floor only
|
|
105
|
+
* ever bound 1 and 2 feeds on the bootstrap. It bound far more on a box that had
|
|
106
|
+
* measured itself, which is the whole point: at the fleet's measured 2.75 s it
|
|
107
|
+
* bound every count up to 5, and at 0.50 s every count up to 30. A deployment of
|
|
108
|
+
* seven or fewer feeds per host resolves the 111-second threshold; past that the operator
|
|
105
109
|
* needs a second connector host, and `getSensingHealth()` already reports the
|
|
106
110
|
* honest effective interval so that shows up rather than hiding.
|
|
107
111
|
*
|
|
@@ -252,28 +256,121 @@ export declare const STILL_FLOOR_SLOT_SHARE = 0.5;
|
|
|
252
256
|
* magnitude, and is the only thing that would.
|
|
253
257
|
*/
|
|
254
258
|
/**
|
|
255
|
-
*
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
259
|
+
* ── THE 30 s ANALYZE FLOOR IS GONE (2026-08-26), AND WHY IT WAS NEVER A BOUND ──
|
|
260
|
+
*
|
|
261
|
+
* `MIN_ANALYZE_INTERVAL_MS = 30_000` used to live here and clamp
|
|
262
|
+
* `periodicIntervalMsForFeedCount` from below. It is deleted rather than
|
|
263
|
+
* lowered, because the two things it claimed to restate do not hold:
|
|
264
|
+
*
|
|
265
|
+
* 1. "`faceTriggeredIntervalMs` (`DEFAULT_FACE_MS`, 30 s) is the default for
|
|
266
|
+
* `MotionGate.minAnalyzeIntervalMs`." TRUE, and irrelevant to THIS
|
|
267
|
+
* number. `MotionGate.evaluate` resolves `heartbeatDue` FIRST and returns
|
|
268
|
+
* before `isRateLimited` is ever consulted (SceneChangeDetector.ts:616 vs
|
|
269
|
+
* :663), and `isRateLimited`'s own docblock says what it gates: "Is a
|
|
270
|
+
* MOTION-triggered analysis throttled right now?". The motion floor bounds
|
|
271
|
+
* the `motion` and `audio_onset` verdicts. It has never bounded the
|
|
272
|
+
* heartbeat. Restating it here made one datum live in two files and then
|
|
273
|
+
* let the copy govern a quantity the original does not.
|
|
274
|
+
*
|
|
275
|
+
* 2. "`vision_pacing_config.daily_limit_per_screen` is 2 880 for the
|
|
276
|
+
* connector fleet ... enforced server-side." FALSE for this connector,
|
|
277
|
+
* three ways over. `checkRateLimit()` has exactly one call site —
|
|
278
|
+
* `audienceVisionService.analyzeFrame`, the CLOUD path. The handler-top
|
|
279
|
+
* capped gate skips local frames by construction
|
|
280
|
+
* (`earner-misc.js`: `if (!hasPreAnalyzed && screenId)`). And
|
|
281
|
+
* `ingestPreAnalyzedFrame`, which is the path every `local-vlm` row is
|
|
282
|
+
* written by, says it in as many words: "we do NOT touch the rate limiter
|
|
283
|
+
* or Vertex client". The connector has been local-inference only since
|
|
284
|
+
* 0.2.41, so the sentence "a connector that analyzed faster than this would
|
|
285
|
+
* be throttled by the server anyway" described a path that no longer runs.
|
|
286
|
+
* (It could not have obeyed one either: there is no 429 handling anywhere
|
|
287
|
+
* in `AudienceAnalyzeClient`.)
|
|
288
|
+
*
|
|
289
|
+
* So the floor protected no resource that `required` does not already protect.
|
|
290
|
+
* What it cost is the product: at the fleet's MEASURED `local-vlm` p50 of
|
|
291
|
+
* 2 750 ms (`vision_call_log`, 2026-08-26) a one-feed connector can afford a
|
|
292
|
+
* 5 500 ms heartbeat and was pinned at 30 000 — 5.45x of hazard-persistence
|
|
293
|
+
* resolution thrown away against the 111-second constructive-notice bracket
|
|
294
|
+
* this whole file exists to resolve. On an M4 Max at 0.50 s/frame it was 30x,
|
|
295
|
+
* and every feed count up to 30 was pinned at the same 30 s.
|
|
296
|
+
*
|
|
297
|
+
* ── WHAT REPLACES IT: the look cadence, which is derived ────────────────────
|
|
298
|
+
*
|
|
299
|
+
* There is exactly one lower bound left that is not the slot budget, and it is
|
|
300
|
+
* a fact rather than a judgement: YOU CANNOT ANALYZE A FRAME YOU NEVER LOOKED
|
|
301
|
+
* AT. That is `lookIntervalMsForFeedCount`, already in this file, already
|
|
302
|
+
* derived per capture path and per feed count. See
|
|
303
|
+
* `analyzeFloorMsForFeedCount`.
|
|
304
|
+
*
|
|
305
|
+
* ── THE FAILURE MODE THIS MUST STILL AVOID, AND WHY IT DOES ────────────────
|
|
306
|
+
*
|
|
307
|
+
* `VlmAdmissionGate` holds ONE slot and DROPS rather than queues. On a box
|
|
308
|
+
* whose slot is saturated every feed is permanently heartbeat-due, so every
|
|
309
|
+
* look returns `analyze: true`, the motion gate stops gating anything, and the
|
|
310
|
+
* frames are dropped at admission having bought no measurement —
|
|
311
|
+
* `DetectorShadowProbe` measured that regime at 64 detector verdicts/second at
|
|
312
|
+
* 32 feeds.
|
|
313
|
+
*
|
|
314
|
+
* The derivation cannot enter it. `required` IS the cadence at which N feeds
|
|
315
|
+
* consume exactly `STILL_FLOOR_SLOT_SHARE` of the one slot, so flooring at
|
|
316
|
+
* `required` targets 50% utilisation by construction and can never saturate.
|
|
317
|
+
* Saturation comes only from a heartbeat set BELOW `required` — which is what a
|
|
318
|
+
* 5 s `sensing_profiles.capture_interval_ms` row asks for, and which the floor
|
|
319
|
+
* still refuses. What changed is only that the refusal is now computed from
|
|
320
|
+
* this machine instead of asserted at 30 s for every machine.
|
|
321
|
+
*
|
|
322
|
+
* Note also what the ceiling is NOT: analyses/hour for a whole connector is
|
|
323
|
+
* `feeds x 3600000 / required` = `3600 x SLOT_SHARE / perFrameSeconds`, which
|
|
324
|
+
* is INDEPENDENT of the feed count — 654/hour on the measured 2.75 s box,
|
|
325
|
+
* 3 600/hour on a 0.50 s one. Removing the clamp cannot produce unbounded load
|
|
326
|
+
* on any box; it can only let a box reach its own honest 50%.
|
|
275
327
|
*/
|
|
276
|
-
|
|
328
|
+
/**
|
|
329
|
+
* The floor between two MOTION-triggered analyses of ONE feed, in ms.
|
|
330
|
+
*
|
|
331
|
+
* This is the HALF of the deleted `MIN_ANALYZE_INTERVAL_MS` that was always
|
|
332
|
+
* real, separated out and named for what it is. That constant conflated two
|
|
333
|
+
* quantities — the motion floor and the still-scene heartbeat floor — and the
|
|
334
|
+
* conflation is what let a motion-path number govern a path it has never gated
|
|
335
|
+
* (`MotionGate.evaluate` resolves `heartbeatDue` first and returns before
|
|
336
|
+
* `isRateLimited` is consulted).
|
|
337
|
+
*
|
|
338
|
+
* `CloudSensingManager.DEFAULT_FACE_MS` is the SINGLE HOME of this value: it is
|
|
339
|
+
* what `faceTriggeredIntervalMs` defaults to and what the manager hands
|
|
340
|
+
* `MotionGate` as `minAnalyzeIntervalMs`. Restated here rather than imported so
|
|
341
|
+
* that `connectorCadence` stays a leaf with no manager dependency — the same
|
|
342
|
+
* treatment `LOOK_CAPTURE_CONCURRENCY` and `MEASURED_STREAM_FRAME_INTERVAL_MS`
|
|
343
|
+
* already get — and pinned against the real one by `connectorCadence.test.ts`
|
|
344
|
+
* so the two cannot drift.
|
|
345
|
+
*
|
|
346
|
+
* NOTHING IN THIS FILE'S DERIVATION MAY READ IT. It is exported for consumers
|
|
347
|
+
* that genuinely mean the motion path: the duration contract's `derivedFrom`,
|
|
348
|
+
* and `sense-e2e-soak.js`'s zero-hold rungs, whose six shapes all produce motion
|
|
349
|
+
* and are therefore analyzed on the motion path rather than by the heartbeat. A
|
|
350
|
+
* `periodicIntervalMsForFeedCount` that floored on this again would be the exact
|
|
351
|
+
* hack that came out on 2026-08-26, and the source scan in
|
|
352
|
+
* `connectorCadence.test.ts` fails the build if it does.
|
|
353
|
+
*/
|
|
354
|
+
export declare const MOTION_ANALYZE_FLOOR_MS = 30000;
|
|
355
|
+
/**
|
|
356
|
+
* The fastest a connector may ANALYZE one feed, in ms — DERIVED, not declared.
|
|
357
|
+
*
|
|
358
|
+
* A look is what puts a frame in front of the model, so the analyze cadence can
|
|
359
|
+
* never be finer than the look cadence; asking for one produces no extra
|
|
360
|
+
* analyses, just a `getSensingHealth()` that reports a cadence the connector is
|
|
361
|
+
* not running. `lookIntervalMsForFeedCount` already derives that per feed count
|
|
362
|
+
* and per capture path, so this is a rename of an existing quantity rather than
|
|
363
|
+
* a new bound — which is the point. There is no constant here to drift.
|
|
364
|
+
*
|
|
365
|
+
* `captureMode` defaults to `'per_grab'` because that is the SLOWER of the two
|
|
366
|
+
* paths (2 000 ms against a sub-millisecond ring read) and a floor should be
|
|
367
|
+
* conservative when the caller does not know which path a feed is on. A feed
|
|
368
|
+
* reaches per-grab by DEGRADING, so assuming it is the honest default.
|
|
369
|
+
*
|
|
370
|
+
* Returns `null` on an uncountable feed count, exactly as its two siblings do,
|
|
371
|
+
* so a caller can omit the key rather than derive a cadence from nothing.
|
|
372
|
+
*/
|
|
373
|
+
export declare function analyzeFloorMsForFeedCount(feedCount: unknown, captureMode?: CaptureMode): number | null;
|
|
277
374
|
/**
|
|
278
375
|
* Measured wall-clock cost of ONE LOOK on the PER-GRAB capture path, in ms — a
|
|
279
376
|
* real RTSP grab, not the decode-and-detect that follows it.
|
|
@@ -403,12 +500,12 @@ export declare const LOOK_CAPTURE_CONCURRENCY = 2;
|
|
|
403
500
|
* The look cadence this connector ran until 2026-08-18, kept ONLY as the slow
|
|
404
501
|
* clamp on `lookIntervalMsForFeedCount`.
|
|
405
502
|
*
|
|
406
|
-
* It is numerically equal to `
|
|
503
|
+
* It is numerically equal to `MOTION_ANALYZE_FLOOR_MS` and that is a
|
|
407
504
|
* coincidence of history, not a relationship: they were one constant, and the
|
|
408
|
-
* whole point of the split is that they are free to move independently. Using
|
|
409
|
-
*
|
|
410
|
-
* removes
|
|
411
|
-
*
|
|
505
|
+
* whole point of the split is that they are free to move independently. Using a
|
|
506
|
+
* motion-path number here would re-create exactly the conflation that change
|
|
507
|
+
* removes. The 2026-08-26 removal of the flat still-scene floor is the proof it
|
|
508
|
+
* was worth splitting: that number went away entirely and this one did not move.
|
|
412
509
|
*
|
|
413
510
|
* Its only job is a guarantee: deriving a look cadence can make a connector look
|
|
414
511
|
* MORE often than it used to, never less. A 32-feed host cannot sustain even
|
|
@@ -471,10 +568,14 @@ export declare const LEGACY_LOOK_INTERVAL_MS = 30000;
|
|
|
471
568
|
* failed: one 7.30 s slot is 493 analyses/hour for the whole connector and
|
|
472
569
|
* there is no cloud path left to absorb an overflow. Three things hold that,
|
|
473
570
|
* and all three are asserted rather than assumed:
|
|
474
|
-
* -
|
|
475
|
-
* `MotionGate.minAnalyzeIntervalMs`
|
|
476
|
-
*
|
|
477
|
-
*
|
|
571
|
+
* - the MOTION floor is a SEPARATE quantity (`MOTION_ANALYZE_FLOOR_MS`, 30 s,
|
|
572
|
+
* restating `DEFAULT_FACE_MS`) and is what `MotionGate.minAnalyzeIntervalMs`
|
|
573
|
+
* floors on. It is untouched by anything on the look side.
|
|
574
|
+
* - `periodicIntervalMsForFeedCount` floors on the SLOT BUDGET, and on the
|
|
575
|
+
* look cadence only where the budget is smaller still — which is no feed
|
|
576
|
+
* count on the bootstrap and none on any box measured to date. Since
|
|
577
|
+
* 2026-08-26 the look cadence IS the other term (`analyzeFloorMsForFeedCount`),
|
|
578
|
+
* and it is the honest one: a frame nobody looked at cannot be analyzed.
|
|
478
579
|
* - a still scene contributes exactly ZERO motion-triggered analyses, which
|
|
479
580
|
* is what makes the extra looks free at the model.
|
|
480
581
|
* The `sensing-catch-rate` guard's volume ratchet is the mechanical version:
|
|
@@ -509,7 +610,13 @@ export declare function periodicIntervalMsForFeedCount(feedCount: unknown,
|
|
|
509
610
|
* path: the equation was always per-machine, and until 2026-08-19 only its
|
|
510
611
|
* input was global.
|
|
511
612
|
*/
|
|
512
|
-
analyzeMs?: number | null
|
|
613
|
+
analyzeMs?: number | null,
|
|
614
|
+
/**
|
|
615
|
+
* How this connector's feeds are acquired. Only reaches the LOOK-derived
|
|
616
|
+
* floor (`analyzeFloorMsForFeedCount`), and defaults conservatively — see
|
|
617
|
+
* that function for why `'per_grab'` is the honest default.
|
|
618
|
+
*/
|
|
619
|
+
captureMode?: CaptureMode): number | null;
|
|
513
620
|
/**
|
|
514
621
|
* How a feed's frames are acquired. The look cadence a connector can SUSTAIN
|
|
515
622
|
* depends on this and on nothing else about the box.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectorCadence.d.ts","sourceRoot":"","sources":["../src/connectorCadence.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"connectorCadence.d.ts","sourceRoot":"","sources":["../src/connectorCadence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuHG;AAEH;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,OAAQ,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmGG;AAEH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,OAAO,EAClB,WAAW,GAAE,WAAwB,GACpC,MAAM,GAAG,IAAI,CAEf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iCAAiC,MAAM,CAAC;AAErD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqEG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAU,CAAC;AAExC;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,OAAO;AAClB;;;;;;;;;;GAUG;AACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;AACzB;;;;GAIG;AACH,WAAW,GAAE,WAAwB,GACpC,MAAM,GAAG,IAAI,CA+Cf;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,OAAO,EAClB,WAAW,GAAE,WAAwB,GACpC,MAAM,GAAG,IAAI,CA0Bf;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,cAAc,EAAE,OAAO,GAAG,MAAM,CAI1E;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,OAAO,EAClB,kBAAkB,EAAE,OAAO,GAC1B;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAOhD"}
|
package/dist/connectorCadence.js
CHANGED
|
@@ -91,18 +91,22 @@
|
|
|
91
91
|
* fire. A floor that ate the whole budget would starve them, and the connector
|
|
92
92
|
* would degrade into reporting empty aisles on time while missing the incident.
|
|
93
93
|
*
|
|
94
|
-
* Two clamps,
|
|
95
|
-
* - never faster than
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
94
|
+
* Two clamps, and NEITHER is a constant any more:
|
|
95
|
+
* - never faster than `analyzeFloorMsForFeedCount` — the LOOK cadence, because
|
|
96
|
+
* a frame nobody looked at cannot be analyzed. This clamp read
|
|
97
|
+
* `MIN_ANALYZE_INTERVAL_MS` (a flat 30 s) until 2026-08-26; see that
|
|
98
|
+
* constant's obituary above for the two claims that turned out not to hold
|
|
99
|
+
* and for why removing it cannot saturate the admission slot.
|
|
100
100
|
* - never slower than the DOOH default (300 s): this can only make a
|
|
101
101
|
* connector sample MORE often than it does today, never less.
|
|
102
102
|
*
|
|
103
|
-
* Result,
|
|
104
|
-
* 16 feeds 234 s, 21+ feeds pinned at 300 s.
|
|
105
|
-
*
|
|
103
|
+
* Result, on the 7.30 s BOOTSTRAP: 1 feed 14.6 s, 2 feeds 29.2 s, 4 feeds 59 s,
|
|
104
|
+
* 8 feeds 117 s, 16 feeds 234 s, 21+ feeds pinned at 300 s. The 4-feed row and
|
|
105
|
+
* everything slower are UNCHANGED by the 2026-08-26 removal — the flat floor only
|
|
106
|
+
* ever bound 1 and 2 feeds on the bootstrap. It bound far more on a box that had
|
|
107
|
+
* measured itself, which is the whole point: at the fleet's measured 2.75 s it
|
|
108
|
+
* bound every count up to 5, and at 0.50 s every count up to 30. A deployment of
|
|
109
|
+
* seven or fewer feeds per host resolves the 111-second threshold; past that the operator
|
|
106
110
|
* needs a second connector host, and `getSensingHealth()` already reports the
|
|
107
111
|
* honest effective interval so that shows up rather than hiding.
|
|
108
112
|
*
|
|
@@ -116,7 +120,8 @@
|
|
|
116
120
|
* what the operator wants; this says what the machine can deliver.
|
|
117
121
|
*/
|
|
118
122
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
119
|
-
exports.STREAM_LOOK_HEADROOM = exports.DOOH_PERIODIC_MS = exports.LOOK_INTERVAL_MS = exports.LEGACY_LOOK_INTERVAL_MS = exports.LOOK_CAPTURE_CONCURRENCY = exports.MEASURED_STREAM_FRAME_INTERVAL_MS = exports.LOOK_DETECT_CPU_SHARE = exports.MEASURED_LOOK_DETECT_MS = exports.MEASURED_STREAM_LOOK_MS = exports.MEASURED_LOOK_MS = exports.
|
|
123
|
+
exports.STREAM_LOOK_HEADROOM = exports.DOOH_PERIODIC_MS = exports.LOOK_INTERVAL_MS = exports.LEGACY_LOOK_INTERVAL_MS = exports.LOOK_CAPTURE_CONCURRENCY = exports.MEASURED_STREAM_FRAME_INTERVAL_MS = exports.LOOK_DETECT_CPU_SHARE = exports.MEASURED_LOOK_DETECT_MS = exports.MEASURED_STREAM_LOOK_MS = exports.MEASURED_LOOK_MS = exports.MOTION_ANALYZE_FLOOR_MS = exports.STILL_FLOOR_SLOT_SHARE = exports.MEASURED_LOCAL_ANALYZE_MS = void 0;
|
|
124
|
+
exports.analyzeFloorMsForFeedCount = analyzeFloorMsForFeedCount;
|
|
120
125
|
exports.periodicIntervalMsForFeedCount = periodicIntervalMsForFeedCount;
|
|
121
126
|
exports.lookIntervalMsForFeedCount = lookIntervalMsForFeedCount;
|
|
122
127
|
exports.streamFpsForLookIntervalMs = streamFpsForLookIntervalMs;
|
|
@@ -259,28 +264,123 @@ exports.STILL_FLOOR_SLOT_SHARE = 0.5;
|
|
|
259
264
|
* magnitude, and is the only thing that would.
|
|
260
265
|
*/
|
|
261
266
|
/**
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
267
|
+
* ── THE 30 s ANALYZE FLOOR IS GONE (2026-08-26), AND WHY IT WAS NEVER A BOUND ──
|
|
268
|
+
*
|
|
269
|
+
* `MIN_ANALYZE_INTERVAL_MS = 30_000` used to live here and clamp
|
|
270
|
+
* `periodicIntervalMsForFeedCount` from below. It is deleted rather than
|
|
271
|
+
* lowered, because the two things it claimed to restate do not hold:
|
|
272
|
+
*
|
|
273
|
+
* 1. "`faceTriggeredIntervalMs` (`DEFAULT_FACE_MS`, 30 s) is the default for
|
|
274
|
+
* `MotionGate.minAnalyzeIntervalMs`." TRUE, and irrelevant to THIS
|
|
275
|
+
* number. `MotionGate.evaluate` resolves `heartbeatDue` FIRST and returns
|
|
276
|
+
* before `isRateLimited` is ever consulted (SceneChangeDetector.ts:616 vs
|
|
277
|
+
* :663), and `isRateLimited`'s own docblock says what it gates: "Is a
|
|
278
|
+
* MOTION-triggered analysis throttled right now?". The motion floor bounds
|
|
279
|
+
* the `motion` and `audio_onset` verdicts. It has never bounded the
|
|
280
|
+
* heartbeat. Restating it here made one datum live in two files and then
|
|
281
|
+
* let the copy govern a quantity the original does not.
|
|
282
|
+
*
|
|
283
|
+
* 2. "`vision_pacing_config.daily_limit_per_screen` is 2 880 for the
|
|
284
|
+
* connector fleet ... enforced server-side." FALSE for this connector,
|
|
285
|
+
* three ways over. `checkRateLimit()` has exactly one call site —
|
|
286
|
+
* `audienceVisionService.analyzeFrame`, the CLOUD path. The handler-top
|
|
287
|
+
* capped gate skips local frames by construction
|
|
288
|
+
* (`earner-misc.js`: `if (!hasPreAnalyzed && screenId)`). And
|
|
289
|
+
* `ingestPreAnalyzedFrame`, which is the path every `local-vlm` row is
|
|
290
|
+
* written by, says it in as many words: "we do NOT touch the rate limiter
|
|
291
|
+
* or Vertex client". The connector has been local-inference only since
|
|
292
|
+
* 0.2.41, so the sentence "a connector that analyzed faster than this would
|
|
293
|
+
* be throttled by the server anyway" described a path that no longer runs.
|
|
294
|
+
* (It could not have obeyed one either: there is no 429 handling anywhere
|
|
295
|
+
* in `AudienceAnalyzeClient`.)
|
|
296
|
+
*
|
|
297
|
+
* So the floor protected no resource that `required` does not already protect.
|
|
298
|
+
* What it cost is the product: at the fleet's MEASURED `local-vlm` p50 of
|
|
299
|
+
* 2 750 ms (`vision_call_log`, 2026-08-26) a one-feed connector can afford a
|
|
300
|
+
* 5 500 ms heartbeat and was pinned at 30 000 — 5.45x of hazard-persistence
|
|
301
|
+
* resolution thrown away against the 111-second constructive-notice bracket
|
|
302
|
+
* this whole file exists to resolve. On an M4 Max at 0.50 s/frame it was 30x,
|
|
303
|
+
* and every feed count up to 30 was pinned at the same 30 s.
|
|
304
|
+
*
|
|
305
|
+
* ── WHAT REPLACES IT: the look cadence, which is derived ────────────────────
|
|
306
|
+
*
|
|
307
|
+
* There is exactly one lower bound left that is not the slot budget, and it is
|
|
308
|
+
* a fact rather than a judgement: YOU CANNOT ANALYZE A FRAME YOU NEVER LOOKED
|
|
309
|
+
* AT. That is `lookIntervalMsForFeedCount`, already in this file, already
|
|
310
|
+
* derived per capture path and per feed count. See
|
|
311
|
+
* `analyzeFloorMsForFeedCount`.
|
|
312
|
+
*
|
|
313
|
+
* ── THE FAILURE MODE THIS MUST STILL AVOID, AND WHY IT DOES ────────────────
|
|
314
|
+
*
|
|
315
|
+
* `VlmAdmissionGate` holds ONE slot and DROPS rather than queues. On a box
|
|
316
|
+
* whose slot is saturated every feed is permanently heartbeat-due, so every
|
|
317
|
+
* look returns `analyze: true`, the motion gate stops gating anything, and the
|
|
318
|
+
* frames are dropped at admission having bought no measurement —
|
|
319
|
+
* `DetectorShadowProbe` measured that regime at 64 detector verdicts/second at
|
|
320
|
+
* 32 feeds.
|
|
321
|
+
*
|
|
322
|
+
* The derivation cannot enter it. `required` IS the cadence at which N feeds
|
|
323
|
+
* consume exactly `STILL_FLOOR_SLOT_SHARE` of the one slot, so flooring at
|
|
324
|
+
* `required` targets 50% utilisation by construction and can never saturate.
|
|
325
|
+
* Saturation comes only from a heartbeat set BELOW `required` — which is what a
|
|
326
|
+
* 5 s `sensing_profiles.capture_interval_ms` row asks for, and which the floor
|
|
327
|
+
* still refuses. What changed is only that the refusal is now computed from
|
|
328
|
+
* this machine instead of asserted at 30 s for every machine.
|
|
329
|
+
*
|
|
330
|
+
* Note also what the ceiling is NOT: analyses/hour for a whole connector is
|
|
331
|
+
* `feeds x 3600000 / required` = `3600 x SLOT_SHARE / perFrameSeconds`, which
|
|
332
|
+
* is INDEPENDENT of the feed count — 654/hour on the measured 2.75 s box,
|
|
333
|
+
* 3 600/hour on a 0.50 s one. Removing the clamp cannot produce unbounded load
|
|
334
|
+
* on any box; it can only let a box reach its own honest 50%.
|
|
282
335
|
*/
|
|
283
|
-
|
|
336
|
+
/**
|
|
337
|
+
* The floor between two MOTION-triggered analyses of ONE feed, in ms.
|
|
338
|
+
*
|
|
339
|
+
* This is the HALF of the deleted `MIN_ANALYZE_INTERVAL_MS` that was always
|
|
340
|
+
* real, separated out and named for what it is. That constant conflated two
|
|
341
|
+
* quantities — the motion floor and the still-scene heartbeat floor — and the
|
|
342
|
+
* conflation is what let a motion-path number govern a path it has never gated
|
|
343
|
+
* (`MotionGate.evaluate` resolves `heartbeatDue` first and returns before
|
|
344
|
+
* `isRateLimited` is consulted).
|
|
345
|
+
*
|
|
346
|
+
* `CloudSensingManager.DEFAULT_FACE_MS` is the SINGLE HOME of this value: it is
|
|
347
|
+
* what `faceTriggeredIntervalMs` defaults to and what the manager hands
|
|
348
|
+
* `MotionGate` as `minAnalyzeIntervalMs`. Restated here rather than imported so
|
|
349
|
+
* that `connectorCadence` stays a leaf with no manager dependency — the same
|
|
350
|
+
* treatment `LOOK_CAPTURE_CONCURRENCY` and `MEASURED_STREAM_FRAME_INTERVAL_MS`
|
|
351
|
+
* already get — and pinned against the real one by `connectorCadence.test.ts`
|
|
352
|
+
* so the two cannot drift.
|
|
353
|
+
*
|
|
354
|
+
* NOTHING IN THIS FILE'S DERIVATION MAY READ IT. It is exported for consumers
|
|
355
|
+
* that genuinely mean the motion path: the duration contract's `derivedFrom`,
|
|
356
|
+
* and `sense-e2e-soak.js`'s zero-hold rungs, whose six shapes all produce motion
|
|
357
|
+
* and are therefore analyzed on the motion path rather than by the heartbeat. A
|
|
358
|
+
* `periodicIntervalMsForFeedCount` that floored on this again would be the exact
|
|
359
|
+
* hack that came out on 2026-08-26, and the source scan in
|
|
360
|
+
* `connectorCadence.test.ts` fails the build if it does.
|
|
361
|
+
*/
|
|
362
|
+
exports.MOTION_ANALYZE_FLOOR_MS = 30000;
|
|
363
|
+
/**
|
|
364
|
+
* The fastest a connector may ANALYZE one feed, in ms — DERIVED, not declared.
|
|
365
|
+
*
|
|
366
|
+
* A look is what puts a frame in front of the model, so the analyze cadence can
|
|
367
|
+
* never be finer than the look cadence; asking for one produces no extra
|
|
368
|
+
* analyses, just a `getSensingHealth()` that reports a cadence the connector is
|
|
369
|
+
* not running. `lookIntervalMsForFeedCount` already derives that per feed count
|
|
370
|
+
* and per capture path, so this is a rename of an existing quantity rather than
|
|
371
|
+
* a new bound — which is the point. There is no constant here to drift.
|
|
372
|
+
*
|
|
373
|
+
* `captureMode` defaults to `'per_grab'` because that is the SLOWER of the two
|
|
374
|
+
* paths (2 000 ms against a sub-millisecond ring read) and a floor should be
|
|
375
|
+
* conservative when the caller does not know which path a feed is on. A feed
|
|
376
|
+
* reaches per-grab by DEGRADING, so assuming it is the honest default.
|
|
377
|
+
*
|
|
378
|
+
* Returns `null` on an uncountable feed count, exactly as its two siblings do,
|
|
379
|
+
* so a caller can omit the key rather than derive a cadence from nothing.
|
|
380
|
+
*/
|
|
381
|
+
function analyzeFloorMsForFeedCount(feedCount, captureMode = 'per_grab') {
|
|
382
|
+
return lookIntervalMsForFeedCount(feedCount, captureMode);
|
|
383
|
+
}
|
|
284
384
|
/**
|
|
285
385
|
* Measured wall-clock cost of ONE LOOK on the PER-GRAB capture path, in ms — a
|
|
286
386
|
* real RTSP grab, not the decode-and-detect that follows it.
|
|
@@ -410,12 +510,12 @@ exports.LOOK_CAPTURE_CONCURRENCY = 2;
|
|
|
410
510
|
* The look cadence this connector ran until 2026-08-18, kept ONLY as the slow
|
|
411
511
|
* clamp on `lookIntervalMsForFeedCount`.
|
|
412
512
|
*
|
|
413
|
-
* It is numerically equal to `
|
|
513
|
+
* It is numerically equal to `MOTION_ANALYZE_FLOOR_MS` and that is a
|
|
414
514
|
* coincidence of history, not a relationship: they were one constant, and the
|
|
415
|
-
* whole point of the split is that they are free to move independently. Using
|
|
416
|
-
*
|
|
417
|
-
* removes
|
|
418
|
-
*
|
|
515
|
+
* whole point of the split is that they are free to move independently. Using a
|
|
516
|
+
* motion-path number here would re-create exactly the conflation that change
|
|
517
|
+
* removes. The 2026-08-26 removal of the flat still-scene floor is the proof it
|
|
518
|
+
* was worth splitting: that number went away entirely and this one did not move.
|
|
419
519
|
*
|
|
420
520
|
* Its only job is a guarantee: deriving a look cadence can make a connector look
|
|
421
521
|
* MORE often than it used to, never less. A 32-feed host cannot sustain even
|
|
@@ -478,10 +578,14 @@ exports.LEGACY_LOOK_INTERVAL_MS = 30000;
|
|
|
478
578
|
* failed: one 7.30 s slot is 493 analyses/hour for the whole connector and
|
|
479
579
|
* there is no cloud path left to absorb an overflow. Three things hold that,
|
|
480
580
|
* and all three are asserted rather than assumed:
|
|
481
|
-
* -
|
|
482
|
-
* `MotionGate.minAnalyzeIntervalMs`
|
|
483
|
-
*
|
|
484
|
-
*
|
|
581
|
+
* - the MOTION floor is a SEPARATE quantity (`MOTION_ANALYZE_FLOOR_MS`, 30 s,
|
|
582
|
+
* restating `DEFAULT_FACE_MS`) and is what `MotionGate.minAnalyzeIntervalMs`
|
|
583
|
+
* floors on. It is untouched by anything on the look side.
|
|
584
|
+
* - `periodicIntervalMsForFeedCount` floors on the SLOT BUDGET, and on the
|
|
585
|
+
* look cadence only where the budget is smaller still — which is no feed
|
|
586
|
+
* count on the bootstrap and none on any box measured to date. Since
|
|
587
|
+
* 2026-08-26 the look cadence IS the other term (`analyzeFloorMsForFeedCount`),
|
|
588
|
+
* and it is the honest one: a frame nobody looked at cannot be analyzed.
|
|
485
589
|
* - a still scene contributes exactly ZERO motion-triggered analyses, which
|
|
486
590
|
* is what makes the extra looks free at the model.
|
|
487
591
|
* The `sensing-catch-rate` guard's volume ratchet is the mechanical version:
|
|
@@ -516,7 +620,13 @@ function periodicIntervalMsForFeedCount(feedCount,
|
|
|
516
620
|
* path: the equation was always per-machine, and until 2026-08-19 only its
|
|
517
621
|
* input was global.
|
|
518
622
|
*/
|
|
519
|
-
analyzeMs
|
|
623
|
+
analyzeMs,
|
|
624
|
+
/**
|
|
625
|
+
* How this connector's feeds are acquired. Only reaches the LOOK-derived
|
|
626
|
+
* floor (`analyzeFloorMsForFeedCount`), and defaults conservatively — see
|
|
627
|
+
* that function for why `'per_grab'` is the honest default.
|
|
628
|
+
*/
|
|
629
|
+
captureMode = 'per_grab') {
|
|
520
630
|
const n = Number(feedCount);
|
|
521
631
|
if (!Number.isFinite(n) || n < 1)
|
|
522
632
|
return null;
|
|
@@ -545,12 +655,25 @@ analyzeMs) {
|
|
|
545
655
|
// and the fix is a second connector host, which `getSensingHealth()` already
|
|
546
656
|
// reports enough to see. Changing this constant would trade a measurable
|
|
547
657
|
// alert-path regression for a cadence number that reads tidier.
|
|
548
|
-
// Floored at the
|
|
549
|
-
//
|
|
550
|
-
//
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
|
|
658
|
+
// Floored at the LOOK cadence, which is the only lower bound left that is not
|
|
659
|
+
// the slot budget itself, and is a fact rather than a judgement: a frame
|
|
660
|
+
// nobody looked at cannot be analyzed.
|
|
661
|
+
//
|
|
662
|
+
// This read a flat `MIN_ANALYZE_INTERVAL_MS` (30 s) until 2026-08-26. The
|
|
663
|
+
// 2026-08-18 note that used to sit here warned that flooring on the look
|
|
664
|
+
// cadence would "let a faster look cadence pull the still-scene heartbeat down
|
|
665
|
+
// with it and double the analyses a 1-feed connector runs". That is exactly
|
|
666
|
+
// what this now does, deliberately, and the warning's arithmetic is what makes
|
|
667
|
+
// it safe rather than what makes it dangerous: the heartbeat lands at
|
|
668
|
+
// `required` — 50% of the one slot BY CONSTRUCTION — for every box on which
|
|
669
|
+
// `required` exceeds the look cadence, which is every box measured to date at
|
|
670
|
+
// every feed count. The look floor binds only where `required` is smaller
|
|
671
|
+
// still, i.e. on a machine fast enough that the CAPTURE path, not the model,
|
|
672
|
+
// is the scarce thing. Doubling a 1-feed connector's analyses is the product
|
|
673
|
+
// working: it is 5.45x of hazard-persistence resolution at the fleet's
|
|
674
|
+
// measured per-frame cost, against a 111-second bracket.
|
|
675
|
+
const lookFloor = analyzeFloorMsForFeedCount(n, captureMode) ?? 0;
|
|
676
|
+
return Math.min(exports.DOOH_PERIODIC_MS, Math.max(lookFloor, required));
|
|
554
677
|
}
|
|
555
678
|
/**
|
|
556
679
|
* The fastest look cadence a connector running `feedCount` feeds can actually
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectorCadence.js","sourceRoot":"","sources":["../src/connectorCadence.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"connectorCadence.js","sourceRoot":"","sources":["../src/connectorCadence.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
*
|
|
66
66
|
* It is a 32-bit `setTimeout` overflow guard. See its own docs below.
|
|
67
67
|
*/
|
|
68
|
+
import { type CaptureMode } from './connectorCadence';
|
|
68
69
|
/**
|
|
69
70
|
* Kept as a single-member type rather than deleted: it names the axis (WHICH
|
|
70
71
|
* run mode is being metered) that the callers still pass, and re-introducing a
|
|
@@ -110,12 +111,16 @@ export type CadenceReason =
|
|
|
110
111
|
/** Slowed to fit the single local inference slot. */
|
|
111
112
|
| 'local_inference_budget'
|
|
112
113
|
/**
|
|
113
|
-
* Faster than the connector may ANALYZE one feed
|
|
114
|
+
* Faster than the connector may ANALYZE one feed, because it is faster than
|
|
115
|
+
* the connector LOOKS at one — a frame nobody looked at cannot be analyzed.
|
|
114
116
|
*
|
|
115
117
|
* Named `look_interval` until 2026-08-18, when the look cadence and the
|
|
116
|
-
* analyze floor stopped being the same 30 000 ms constant
|
|
117
|
-
*
|
|
118
|
-
*
|
|
118
|
+
* analyze floor stopped being the same 30 000 ms constant, and pointed at a
|
|
119
|
+
* flat `MIN_ANALYZE_INTERVAL_MS = 30_000` until 2026-08-26, when that constant
|
|
120
|
+
* was deleted for restating a motion-gate floor that never bounded the
|
|
121
|
+
* heartbeat. It now means what its 2018-era name meant again: the capture path,
|
|
122
|
+
* not the model, is the scarce thing on this box. That is rare and it is
|
|
123
|
+
* REPORTED rather than assumed — `local_inference_budget` is the ordinary case.
|
|
119
124
|
*/
|
|
120
125
|
| 'analyze_floor'
|
|
121
126
|
/** SPED UP to `MAX_CADENCE_MS`: the request was slower than a capture loop can express. */
|
|
@@ -150,5 +155,11 @@ export declare function resolveDeployedCadence(input: {
|
|
|
150
155
|
* while it is still on the bootstrap constant. See `AnalyzeCostObserver`.
|
|
151
156
|
*/
|
|
152
157
|
analyzeMs?: number | null;
|
|
158
|
+
/**
|
|
159
|
+
* How this connector's feeds are acquired, when the caller knows. Reaches
|
|
160
|
+
* only the LOOK-derived floor; omitted falls back to the conservative
|
|
161
|
+
* `'per_grab'` — see `analyzeFloorMsForFeedCount`.
|
|
162
|
+
*/
|
|
163
|
+
captureMode?: CaptureMode;
|
|
153
164
|
}): DeployedCadence;
|
|
154
165
|
//# sourceMappingURL=deployedCadence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployedCadence.d.ts","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;
|
|
1
|
+
{"version":3,"file":"deployedCadence.d.ts","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,oBAAoB,CAAC;AAE5B;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,cAAc,QAAmB,CAAC;AAE/C,MAAM,MAAM,aAAa;AACvB,4EAA4E;AAC1E,YAAY;AACd;;;GAGG;GACD,iBAAiB;AACnB,uEAAuE;GACrE,kBAAkB;AACpB,qDAAqD;GACnD,wBAAwB;AAC1B;;;;;;;;;;;GAWG;GACD,eAAe;AACjB,2FAA2F;GACzF,iBAAiB,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,qEAAqE;IACrE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mFAAmF;IACnF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AASD;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,kBAAkB,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,GAAG,eAAe,CA+FlB"}
|
package/dist/deployedCadence.js
CHANGED
|
@@ -118,18 +118,21 @@ function resolveDeployedCadence(input) {
|
|
|
118
118
|
// cadence derived from nothing.
|
|
119
119
|
// `analyzeMs` is what THIS box measured for itself; undefined/null means it has
|
|
120
120
|
// not measured itself yet and the conservative bootstrap constant governs.
|
|
121
|
-
const floorMs = (0, connectorCadence_1.periodicIntervalMsForFeedCount)(input.feedCount, input.analyzeMs) ?? connectorCadence_1.DOOH_PERIODIC_MS;
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
// the two constants split: both were 30 000 ms, so the substitution changes no
|
|
125
|
-
// value today — but the look cadence is now 2 000 ms, and leaving this pointed
|
|
126
|
-
// at it would let a 5 s profile row land a 5 s heartbeat and multiply the
|
|
127
|
-
// still-scene analyses of every connector in the fleet.
|
|
121
|
+
const floorMs = (0, connectorCadence_1.periodicIntervalMsForFeedCount)(input.feedCount, input.analyzeMs, input.captureMode) ?? connectorCadence_1.DOOH_PERIODIC_MS;
|
|
122
|
+
// `periodicIntervalMsForFeedCount` already carries BOTH lower bounds — the
|
|
123
|
+
// slot budget and the look cadence — so there is nothing to re-floor here.
|
|
128
124
|
//
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
// the
|
|
132
|
-
|
|
125
|
+
// Until 2026-08-26 this line read `Math.max(floorMs, MIN_ANALYZE_INTERVAL_MS)`
|
|
126
|
+
// and applied a flat 30 s a second time. That was already a no-op in every
|
|
127
|
+
// reachable state (the derivation floored at the same constant internally, and
|
|
128
|
+
// its only other outcome is the 300 000 ms fallback), so deleting the constant
|
|
129
|
+
// changes no value on this line — but it does stop a second copy of the same
|
|
130
|
+
// assertion from silently becoming the binding one when the first stops being.
|
|
131
|
+
//
|
|
132
|
+
// The ceiling still wins over the floor: honouring a floor slower than a whole
|
|
133
|
+
// day would hand a capture loop a delay it cannot express (see MAX_CADENCE_MS)
|
|
134
|
+
// — the one clamp that must never be negotiable.
|
|
135
|
+
const effectiveFloor = Math.min(exports.MAX_CADENCE_MS, floorMs);
|
|
133
136
|
if (requestedMs === null) {
|
|
134
137
|
// The profile answered and asked for nothing. Fall back to the cadence this
|
|
135
138
|
// machine can afford, NOT to whatever the manager happened to be built with.
|
|
@@ -178,7 +181,13 @@ function resolveDeployedCadence(input) {
|
|
|
178
181
|
reason = 'cadence_ceiling';
|
|
179
182
|
}
|
|
180
183
|
else if (clamped) {
|
|
181
|
-
|
|
184
|
+
// WHICH of the two derived bounds actually bit? Both are per-box, so this is
|
|
185
|
+
// a real distinction and not a spelling one: `analyze_floor` means the
|
|
186
|
+
// CAPTURE path is the scarce thing (the box outruns its own cameras),
|
|
187
|
+
// `local_inference_budget` means the MODEL is. An operator who is told the
|
|
188
|
+
// wrong one buys the wrong hardware.
|
|
189
|
+
const lookFloorMs = (0, connectorCadence_1.analyzeFloorMsForFeedCount)(input.feedCount, input.captureMode);
|
|
190
|
+
if (lookFloorMs !== null && effectiveFloor === lookFloorMs)
|
|
182
191
|
reason = 'analyze_floor';
|
|
183
192
|
else
|
|
184
193
|
reason = 'local_inference_budget';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployedCadence.js","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;;;
|
|
1
|
+
{"version":3,"file":"deployedCadence.js","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;;;AAoGH,wDA8GC;AAhND,yDAK4B;AAU5B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACU,QAAA,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AA4C/C,8EAA8E;AAC9E,SAAS,gBAAgB,CAAC,KAAc;IACtC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,sBAAsB,CAAC,KAetC;IACC,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAExD,sEAAsE;IACtE,0EAA0E;IAC1E,wEAAwE;IACxE,gCAAgC;IAChC,gFAAgF;IAChF,2EAA2E;IAC3E,MAAM,OAAO,GAAG,IAAA,iDAA8B,EAC5C,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,WAAW,CACpD,IAAI,mCAAgB,CAAC;IAEtB,2EAA2E;IAC3E,2EAA2E;IAC3E,EAAE;IACF,+EAA+E;IAC/E,2EAA2E;IAC3E,+EAA+E;IAC/E,+EAA+E;IAC/E,6EAA6E;IAC7E,+EAA+E;IAC/E,EAAE;IACF,+EAA+E;IAC/E,+EAA+E;IAC/E,iDAAiD;IACjD,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,sBAAc,EAAE,OAAO,CAAC,CAAC;IAEzD,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,4EAA4E;QAC5E,6EAA6E;QAC7E,EAAE;QACF,oEAAoE;QACpE,yEAAyE;QACzE,sEAAsE;QACtE,uEAAuE;QACvE,2EAA2E;QAC3E,sEAAsE;QACtE,qBAAqB;QACrB,EAAE;QACF,qCAAqC;QACrC,wEAAwE;QACxE,qEAAqE;QACrE,2EAA2E;QAC3E,oEAAoE;QACpE,oEAAoE;QACpE,yEAAyE;QACzE,4EAA4E;QAC5E,mEAAmE;QACnE,kEAAkE;QAClE,EAAE;QACF,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,uEAAuE;QACvE,2EAA2E;QAC3E,qDAAqD;QACrD,EAAE;QACF,wEAAwE;QACxE,sCAAsC;QACtC,OAAO;YACL,UAAU,EAAE,cAAc;YAC1B,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,cAAc;YACvB,SAAS,EAAE,sBAAc;YACzB,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,iBAAiB;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,sBAAc,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,UAAU,KAAK,WAAW,CAAC;IAE3C,IAAI,MAAM,GAAkB,kBAAkB,CAAC;IAC/C,IAAI,UAAU,GAAG,WAAW,EAAE,CAAC;QAC7B,MAAM,GAAG,iBAAiB,CAAC;IAC7B,CAAC;SAAM,IAAI,OAAO,EAAE,CAAC;QACnB,6EAA6E;QAC7E,uEAAuE;QACvE,sEAAsE;QACtE,2EAA2E;QAC3E,qCAAqC;QACrC,MAAM,WAAW,GAAG,IAAA,6CAA0B,EAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;QACnF,IAAI,WAAW,KAAK,IAAI,IAAI,cAAc,KAAK,WAAW;YAAE,MAAM,GAAG,eAAe,CAAC;;YAChF,MAAM,GAAG,wBAAwB,CAAC;IACzC,CAAC;IAED,OAAO;QACL,UAAU;QACV,WAAW;QACX,OAAO,EAAE,cAAc;QACvB,SAAS,EAAE,sBAAc;QACzB,OAAO;QACP,MAAM;KACP,CAAC;AACJ,CAAC"}
|
package/dist/diagnose.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface DiagnosticReport {
|
|
|
31
31
|
skipped: number;
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
-
declare const SDK_VERSION = "0.2.
|
|
34
|
+
declare const SDK_VERSION = "0.2.102";
|
|
35
35
|
declare function checkNodeVersion(): CheckResult;
|
|
36
36
|
declare function checkPlatform(): CheckResult;
|
|
37
37
|
declare function checkMemory(): CheckResult;
|
package/dist/diagnose.js
CHANGED
|
@@ -63,7 +63,7 @@ const localNetworkAccess_1 = require("./localNetworkAccess");
|
|
|
63
63
|
// ---------------------------------------------------------------------------
|
|
64
64
|
// SDK version — mirrors package.json; kept in sync by CI
|
|
65
65
|
// ---------------------------------------------------------------------------
|
|
66
|
-
const SDK_VERSION = '0.2.
|
|
66
|
+
const SDK_VERSION = '0.2.102';
|
|
67
67
|
exports._SDK_VERSION = SDK_VERSION;
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
69
|
// ANSI helpers (no external deps)
|
package/package.json
CHANGED