@trillboards/edge-sdk 0.2.61 → 0.2.64
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/deploy/litertlm/test_trillboards_litertlm_shim.py +132 -0
- package/deploy/litertlm/trillboards-litertlm-shim.pth +1 -0
- package/deploy/litertlm/trillboards_litertlm_shim.py +214 -0
- package/deploy/macos/bench-local-vlm.sh +8 -4
- package/deploy/macos/build-macos-pkg.sh +4 -0
- package/deploy/macos/scripts/setup-litertlm.sh +99 -2
- package/deploy/systemd/setup-litertlm.sh +99 -2
- package/deploy/windows/build-windows.ps1 +4 -0
- package/deploy/windows/register-litertlm-task.ps1 +22 -0
- package/deploy/windows/setup-litertlm.ps1 +131 -3
- package/deploy/windows/trillboards-edge.cmd +19 -0
- package/dist/CloudSensingManager.d.ts +65 -0
- package/dist/CloudSensingManager.d.ts.map +1 -1
- package/dist/CloudSensingManager.js +65 -30
- package/dist/CloudSensingManager.js.map +1 -1
- package/dist/DurableVlmCandidateSpool.d.ts +21 -0
- package/dist/DurableVlmCandidateSpool.d.ts.map +1 -1
- package/dist/DurableVlmCandidateSpool.js +126 -1
- package/dist/DurableVlmCandidateSpool.js.map +1 -1
- package/dist/EdgeAgent.js +1 -1
- package/dist/LiteRtVlmProvider.d.ts +74 -7
- package/dist/LiteRtVlmProvider.d.ts.map +1 -1
- package/dist/LiteRtVlmProvider.js.map +1 -1
- package/dist/MosaicSource.d.ts +18 -0
- package/dist/MosaicSource.d.ts.map +1 -1
- package/dist/MosaicSource.js +23 -0
- package/dist/MosaicSource.js.map +1 -1
- package/dist/VlmCandidateBuffer.d.ts +45 -6
- package/dist/VlmCandidateBuffer.d.ts.map +1 -1
- package/dist/VlmCandidateBuffer.js +76 -7
- package/dist/VlmCandidateBuffer.js.map +1 -1
- package/dist/analyzeCostObserver.d.ts +96 -0
- package/dist/analyzeCostObserver.d.ts.map +1 -0
- package/dist/analyzeCostObserver.js +130 -0
- package/dist/analyzeCostObserver.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/connectorCadence.d.ts +286 -57
- package/dist/connectorCadence.d.ts.map +1 -1
- package/dist/connectorCadence.js +313 -61
- package/dist/connectorCadence.js.map +1 -1
- package/dist/deployedCadence.d.ts +8 -2
- package/dist/deployedCadence.d.ts.map +1 -1
- package/dist/deployedCadence.js +6 -3
- package/dist/deployedCadence.js.map +1 -1
- package/dist/deployedCadenceSource.d.ts +10 -0
- package/dist/deployedCadenceSource.d.ts.map +1 -1
- package/dist/deployedCadenceSource.js +11 -0
- package/dist/deployedCadenceSource.js.map +1 -1
- package/dist/diagnose.d.ts +1 -1
- package/dist/diagnose.js +1 -1
- package/dist/localInferenceSchema.d.ts +8 -3
- package/dist/localInferenceSchema.d.ts.map +1 -1
- package/dist/localInferenceSchema.js +8 -3
- package/dist/localInferenceSchema.js.map +1 -1
- package/dist/localSensing.d.ts +22 -1
- package/dist/localSensing.d.ts.map +1 -1
- package/dist/localSensing.js +101 -7
- package/dist/localSensing.js.map +1 -1
- package/dist/localVlmBench.d.ts +82 -0
- package/dist/localVlmBench.d.ts.map +1 -1
- package/dist/localVlmBench.js +88 -0
- package/dist/localVlmBench.js.map +1 -1
- package/dist/sensingCatchRate.d.ts.map +1 -1
- package/dist/sensingCatchRate.js +75 -10
- package/dist/sensingCatchRate.js.map +1 -1
- package/dist/sensingRunMode.d.ts.map +1 -1
- package/dist/sensingRunMode.js +38 -2
- package/dist/sensingRunMode.js.map +1 -1
- package/package.json +1 -1
package/dist/connectorCadence.js
CHANGED
|
@@ -45,11 +45,13 @@
|
|
|
45
45
|
* of 493 analyses/hour for the WHOLE connector, however many
|
|
46
46
|
* cameras it runs.
|
|
47
47
|
* STALE — see the block under MEASURED_LOCAL_ANALYZE_MS. The same
|
|
48
|
-
* harness measures 1.90 s
|
|
49
|
-
* config.json change
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
48
|
+
* harness measures 1.90 s, then 1.22 s with the 2026-08-19
|
|
49
|
+
* config.json change, then a further paired 2.42x with the 2026-08-19
|
|
50
|
+
* fast-conversation shim, on the pinned 0.16.0 — so the real ceiling
|
|
51
|
+
* on this machine class is ~1,895/hour, ~2,951/hour and higher again
|
|
52
|
+
* respectively. The constant is kept as a conservative FLEET default
|
|
53
|
+
* because the fleet varies ~7x; do not quote 493 as this machine's
|
|
54
|
+
* ceiling.
|
|
53
55
|
* (The DOOH audience schema measured 12.48 s median and ranged to
|
|
54
56
|
* 23.8 s because it emits a free-text narrative. The SOP field set
|
|
55
57
|
* is both cheaper and far tighter — 6.0-7.5 s — which is a second,
|
|
@@ -113,9 +115,10 @@
|
|
|
113
115
|
* what the operator wants; this says what the machine can deliver.
|
|
114
116
|
*/
|
|
115
117
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
116
|
-
exports.DOOH_PERIODIC_MS = exports.LOOK_INTERVAL_MS = exports.LEGACY_LOOK_INTERVAL_MS = exports.LOOK_CAPTURE_CONCURRENCY = exports.MEASURED_LOOK_MS = exports.MIN_ANALYZE_INTERVAL_MS = exports.STILL_FLOOR_SLOT_SHARE = exports.MEASURED_LOCAL_ANALYZE_MS = void 0;
|
|
118
|
+
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.MIN_ANALYZE_INTERVAL_MS = exports.STILL_FLOOR_SLOT_SHARE = exports.MEASURED_LOCAL_ANALYZE_MS = void 0;
|
|
117
119
|
exports.periodicIntervalMsForFeedCount = periodicIntervalMsForFeedCount;
|
|
118
120
|
exports.lookIntervalMsForFeedCount = lookIntervalMsForFeedCount;
|
|
121
|
+
exports.streamFpsForLookIntervalMs = streamFpsForLookIntervalMs;
|
|
119
122
|
exports.stillSceneLoad = stillSceneLoad;
|
|
120
123
|
/**
|
|
121
124
|
* Measured median seconds per on-device analysis, in ms. See the header for the
|
|
@@ -147,25 +150,81 @@ exports.MEASURED_LOCAL_ANALYZE_MS = 7300;
|
|
|
147
150
|
* the box can afford, which costs hazard-persistence resolution against the
|
|
148
151
|
* 111 s constructive-notice bracket — the one thing this file exists to protect.
|
|
149
152
|
*
|
|
150
|
-
* ──
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
153
|
+
* ── AND IT MOVED AGAIN, 2026-08-19 (the shim) ──────────────────────────────
|
|
154
|
+
*
|
|
155
|
+
* `litert-lm serve` built `ConstrainedDecodingConfig(enable=True, …)` for every
|
|
156
|
+
* request even when nothing asked for a `response_format`, so every frame paid
|
|
157
|
+
* a constraint build for a grammar nothing used. Removing it (see
|
|
158
|
+
* `deploy/litertlm/trillboards_litertlm_shim.py`) is worth a further **paired
|
|
159
|
+
* 2.42x** on the shipped path — two stock serves on two ports, interleaved,
|
|
160
|
+
* n=16, 16/16 schema-valid and 16/16 BYTE-IDENTICAL generations on both arms.
|
|
161
|
+
* `bench-local-vlm.mjs --arms gpu-sop`, back to back in the same window, moves
|
|
162
|
+
* **1.32 -> 0.72 s/frame, i.e. 2,727 -> 5,000 analyses/hour**, at load 22-25
|
|
163
|
+
* where the same harness measures the stock arm at 1.32 s against the 1.22 s
|
|
164
|
+
* this file already records for it — so that window is close to the regime the
|
|
165
|
+
* 1.22 s was taken in, and 0.72 s is the nearest thing to a quotable per-frame
|
|
166
|
+
* number this machine class has.
|
|
167
|
+
*
|
|
168
|
+
* TWO CAVEATS THAT TRAVEL WITH IT. The ratio is itself load-dependent — 3.734x
|
|
169
|
+
* at 0.0% CPU idle against 2.42x at load 25 — because the removed work is
|
|
170
|
+
* CPU-bound and the retained work is GPU-bound, so a figure taken under
|
|
171
|
+
* contention flatters the change. And 0.72 s/frame is still BELOW the capacity
|
|
172
|
+
* target: 4 cameras resolving a 2 s bracket needs 0.5 s/frame (7,200
|
|
173
|
+
* analyses/hour), and this serves 2-3.
|
|
174
|
+
*
|
|
175
|
+
* ── IT IS NOT LOWERED HERE. IT IS NOW A BOOTSTRAP. (2026-08-19) ───────────
|
|
176
|
+
*
|
|
177
|
+
* This is ONE constant for a fleet whose measured spread on the SHIPPED path is
|
|
178
|
+
* 0.50 s/frame (M4 Max, post-#12567/#12594) against tens of seconds on a
|
|
179
|
+
* GPU-less x86 box. No constant is right for both: the fast desk's number would
|
|
180
|
+
* demand ~70x what the slow box can deliver, and the slow box's number makes
|
|
181
|
+
* every heartbeat worse than today. Picking the fast desk is exactly the error
|
|
182
|
+
* `.claude/lessons/2026-08-18-a-benchmark-with-no-machine-axis-is-a-spec-for-
|
|
183
|
+
* one-desk.md` was written about.
|
|
184
|
+
*
|
|
185
|
+
* So the fix was never a better constant, and as of 2026-08-19 it is built: the
|
|
186
|
+
* box DERIVES this for itself. `AnalyzeCostObserver` (analyzeCostObserver.ts)
|
|
187
|
+
* times every successful `analyze()` in `LocalSensingClient`, reports a rolling
|
|
188
|
+
* median over 16 frames, and `periodicIntervalMsForFeedCount` /
|
|
189
|
+
* `resolveDeployedCadence` take it as an optional input. A connector that has
|
|
190
|
+
* measured itself uses its OWN number; one that has not uses this one.
|
|
191
|
+
*
|
|
192
|
+
* NOTE WHAT THAT MAKES THIS CONSTANT. It is the value to assume BEFORE a machine
|
|
193
|
+
* has measured itself, so it should be conservative rather than accurate — and
|
|
194
|
+
* 7.30 s is exactly that. Guessing FAST on a slow box oversubscribes the single
|
|
195
|
+
* admission slot from the first minute; guessing slow costs only heartbeat
|
|
196
|
+
* resolution, and only until the observer settles (8 frames). It is therefore
|
|
197
|
+
* deliberately NOT lowered, and quoting it as a measurement of any machine is
|
|
198
|
+
* still what this block exists to stop.
|
|
199
|
+
*
|
|
200
|
+
* A CONSEQUENCE WORTH KNOWING: because this value does not move, neither does
|
|
201
|
+
* `sensingCatchRate.DEFAULT_SAMPLER.analyzeS`, so none of this required a
|
|
202
|
+
* corpus replay or a `.github/contracts/sensing-catch-rate-baseline.json`
|
|
203
|
+
* regeneration. If you ever DO move it, all of that becomes mandatory in the
|
|
204
|
+
* same change — and the catch-rate ratchet is shrink-only, so a baseline may
|
|
205
|
+
* never be regenerated merely to clear red.
|
|
206
|
+
*
|
|
207
|
+
* ── The idle-box objection, and why it does not block the derivation ────────
|
|
208
|
+
*
|
|
209
|
+
* The 2026-08-19 predecessor of this block held the per-machine derivation back
|
|
210
|
+
* for want of a per-frame number taken on an idle box, on the grounds that a
|
|
211
|
+
* figure measured at 0.0% CPU idle would hardcode one session's contention into
|
|
212
|
+
* every connector. That objection is answered twice over.
|
|
213
|
+
*
|
|
214
|
+
* First, it no longer applies to the DERIVATION at all: nothing global is
|
|
215
|
+
* derived from a measured number any more. Each box measures itself, in situ,
|
|
216
|
+
* under whatever load it actually runs at — which is the right load by
|
|
217
|
+
* definition, and is the one thing a bench on someone else's desk can never
|
|
218
|
+
* reproduce.
|
|
219
|
+
*
|
|
220
|
+
* Second, the verdict turned out not to depend on it. Three measurements of the
|
|
221
|
+
* shipped arm on one M4 Max across a 3x load range: 0.45 s at load 7.77, 0.50 s
|
|
222
|
+
* at load 11.87, 0.72 s at load 22-25. All three clear the 13.88 s/frame budget
|
|
223
|
+
* for 4 cameras by 19-31x, so "this class clears 4 cameras" is invariant across
|
|
224
|
+
* everything that could be measured. Load moves the quotable figure, not the
|
|
225
|
+
* verdict — and the post-merge path is markedly LESS load-sensitive than the one
|
|
226
|
+
* that motivated the objection, because the work #12594 removed was CPU-bound
|
|
227
|
+
* and what remains is GPU-bound.
|
|
169
228
|
*/
|
|
170
229
|
/**
|
|
171
230
|
* Share of the single admission slot the STILL-scene heartbeat floor may take.
|
|
@@ -219,8 +278,13 @@ exports.STILL_FLOOR_SLOT_SHARE = 0.5;
|
|
|
219
278
|
*/
|
|
220
279
|
exports.MIN_ANALYZE_INTERVAL_MS = 30000;
|
|
221
280
|
/**
|
|
222
|
-
* Measured wall-clock cost of ONE LOOK, in ms — a
|
|
223
|
-
* decode-and-detect that follows it.
|
|
281
|
+
* Measured wall-clock cost of ONE LOOK on the PER-GRAB capture path, in ms — a
|
|
282
|
+
* real RTSP grab, not the decode-and-detect that follows it.
|
|
283
|
+
*
|
|
284
|
+
* STILL LIVE, and still exactly right for the path it describes: the per-grab
|
|
285
|
+
* provider is what a feed runs when it cannot hold a persistent session (see
|
|
286
|
+
* `MEASURED_STREAM_LOOK_MS`), so this is the number `lookIntervalMsForFeedCount`
|
|
287
|
+
* uses in `'per_grab'` mode and nothing about it has changed.
|
|
224
288
|
*
|
|
225
289
|
* The cascade budget above quotes 3.6 ms for a look. That is the cost AFTER the
|
|
226
290
|
* frame is in hand: pull it down to ~178x100 grey and run the detector. Getting
|
|
@@ -253,6 +317,75 @@ exports.MIN_ANALYZE_INTERVAL_MS = 30000;
|
|
|
253
317
|
* the connector is not running.
|
|
254
318
|
*/
|
|
255
319
|
exports.MEASURED_LOOK_MS = 2000;
|
|
320
|
+
/**
|
|
321
|
+
* Measured wall-clock cost of ONE LOOK on the PERSISTENT capture path, in ms.
|
|
322
|
+
*
|
|
323
|
+
* `RTSPStreamCameraProvider` holds one long-lived ffmpeg per feed decoding into
|
|
324
|
+
* a bounded ring, so `getFrame()` is a memory read of the newest frame rather
|
|
325
|
+
* than a process start plus a DESCRIBE/SETUP/PLAY plus a wait for an IDR. The
|
|
326
|
+
* handshake is paid ONCE, at open.
|
|
327
|
+
*
|
|
328
|
+
* MEASURED 2026-08-18 against the same rig, same clip, same degraded profile
|
|
329
|
+
* (640x360 / 399 kbps / GOP 2 s), driving looks at the shipped cadence so a
|
|
330
|
+
* fresh frame is already in hand — which is the condition the connector runs
|
|
331
|
+
* in and the only one where "cost of a look" means anything:
|
|
332
|
+
*
|
|
333
|
+
* per-grab p50 2,000 ms
|
|
334
|
+
* persistent p50 0 ms (sub-millisecond; the ring read is a pointer)
|
|
335
|
+
*
|
|
336
|
+
* Reproduce: `node scripts/measure-persistent-capture.mjs --feeds 1
|
|
337
|
+
* --seconds 60 --pace-ms 500`. The rig is committed — see that file's header
|
|
338
|
+
* for why, and for why a `file://` fixture cannot stand in for it.
|
|
339
|
+
*
|
|
340
|
+
* It is 1 rather than 0 so that `lookIntervalMsForFeedCount` can never divide
|
|
341
|
+
* by it, and so nobody reads a zero as "free" — a look still costs the
|
|
342
|
+
* detector's `MEASURED_LOOK_DETECT_MS`, which is what actually bounds the
|
|
343
|
+
* persistent path.
|
|
344
|
+
*/
|
|
345
|
+
exports.MEASURED_STREAM_LOOK_MS = 1;
|
|
346
|
+
/**
|
|
347
|
+
* Cost of a look AFTER the frame is in hand, in ms: pull it down to ~178x100
|
|
348
|
+
* grey and run `SceneChangeDetector`. Measured 3.6 ms median / 7.1 ms p95.
|
|
349
|
+
*
|
|
350
|
+
* This is the number the cascade budget has always quoted, and until the
|
|
351
|
+
* capture path changed it was irrelevant — a 3.6 ms stage behind a 2,000 ms
|
|
352
|
+
* one bounds nothing. On the persistent path it is the ONLY per-look cost
|
|
353
|
+
* left, so it becomes the binding term and gets named here rather than living
|
|
354
|
+
* in a comment.
|
|
355
|
+
*/
|
|
356
|
+
exports.MEASURED_LOOK_DETECT_MS = 3.6;
|
|
357
|
+
/**
|
|
358
|
+
* Share of ONE core the motion detector may take across every feed.
|
|
359
|
+
*
|
|
360
|
+
* The one judgement in the persistent derivation, stated rather than hidden —
|
|
361
|
+
* the same shape as `STILL_FLOOR_SLOT_SHARE`. Detection is the cheapest stage
|
|
362
|
+
* on the box and it must not become the noisy neighbour: the connector also
|
|
363
|
+
* runs one ffmpeg decode per feed (measured on the rig at ~2.4% of a core per
|
|
364
|
+
* feed at this profile) and one local VLM slot, and the VLM is the thing the
|
|
365
|
+
* product is actually spending money on.
|
|
366
|
+
*
|
|
367
|
+
* A quarter of a core does not bind at any realistic feed count — see
|
|
368
|
+
* `lookIntervalMsForFeedCount` — which is the point: it is a guard rail, not a
|
|
369
|
+
* throttle, and it exists so that a future 64-feed host derives a cadence it
|
|
370
|
+
* can serve instead of one it merely asked for.
|
|
371
|
+
*/
|
|
372
|
+
exports.LOOK_DETECT_CPU_SHARE = 0.25;
|
|
373
|
+
/**
|
|
374
|
+
* The persistent session's frame interval, in ms — the freshest a look can
|
|
375
|
+
* possibly be, because a look reads the newest DECODED frame.
|
|
376
|
+
*
|
|
377
|
+
* `RTSPStreamCameraProvider` defaults `streamFps` to 4, i.e. 250 ms. Restated
|
|
378
|
+
* here rather than imported because `edge-sdk` must not depend on a platform
|
|
379
|
+
* package, and asserted against the real default by `connectorCadence.test.ts`
|
|
380
|
+
* so the two cannot drift — the same treatment `LOOK_CAPTURE_CONCURRENCY` gets.
|
|
381
|
+
*
|
|
382
|
+
* `LOOK_INTERVAL_MS` must never be finer than this: asking for a 100 ms look
|
|
383
|
+
* cadence against a 250 ms stream would hand the same frame to the motion
|
|
384
|
+
* detector twice, which the provider refuses (it throws rather than repeat a
|
|
385
|
+
* frame) and which would report as a stale-stream fault on a perfectly healthy
|
|
386
|
+
* camera.
|
|
387
|
+
*/
|
|
388
|
+
exports.MEASURED_STREAM_FRAME_INTERVAL_MS = 250;
|
|
256
389
|
/**
|
|
257
390
|
* Concurrent RTSP grabs one physical recorder will take —
|
|
258
391
|
* `DEFAULT_MAX_CONCURRENT_CAPTURES_PER_SOURCE` in `RTSPCameraProvider`, which
|
|
@@ -302,32 +435,59 @@ exports.LEGACY_LOOK_INTERVAL_MS = 30000;
|
|
|
302
435
|
* T Instant Brief Sustained State
|
|
303
436
|
* 30 s 8.4% 18.6% 100% 100%
|
|
304
437
|
* 5 s 50.2% 55.1% 100% 100%
|
|
305
|
-
* 2.44 s 89.1% 71.2% 100% 100%
|
|
306
438
|
* 2 s 95.5% 75.2% 100% 100%
|
|
307
439
|
* 1 s 100.0% 86.6% 100% 100%
|
|
308
440
|
* 0.75 s 100.0% 90.1% 100% 100%
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
* `
|
|
327
|
-
*
|
|
328
|
-
*
|
|
441
|
+
* 0.5 s 100.0% 93.2% 100% 100%
|
|
442
|
+
*
|
|
443
|
+
* ── 2 000 -> 500 ms ON 2026-08-18, AND THE REASON IS THE CAPTURE PATH ──────
|
|
444
|
+
*
|
|
445
|
+
* This constant was 2 000 ms, and the comment here said why in as many words:
|
|
446
|
+
* "the Brief group does NOT reach 90% until 0.75 s, and 0.75 s is well below
|
|
447
|
+
* the measured cost of a single grab. So 90% on every shape is NOT reachable by
|
|
448
|
+
* look cadence on the current capture architecture… Closing that gap needs a
|
|
449
|
+
* persistent RTSP session — one long-lived ffmpeg per feed instead of one per
|
|
450
|
+
* grab — which is an architecture change, not a number."
|
|
451
|
+
*
|
|
452
|
+
* That is now built (`RTSPStreamCameraProvider`), the constraint it named is
|
|
453
|
+
* gone, and this is the number that moves as a result. A look is a memory read
|
|
454
|
+
* of the newest decoded frame — `MEASURED_STREAM_LOOK_MS`, sub-millisecond —
|
|
455
|
+
* so the recorder no longer bounds the look cadence at all. Brief clears the
|
|
456
|
+
* 90% target at 0.75 s; 500 ms is the next value that is BOTH under it and a
|
|
457
|
+
* whole multiple of the persistent session's 250 ms frame interval
|
|
458
|
+
* (`MEASURED_STREAM_FRAME_INTERVAL_MS`), so a look always finds an unseen frame
|
|
459
|
+
* in hand and never blocks.
|
|
460
|
+
*
|
|
461
|
+
* ── WHY STOP AT 500 ms ────────────────────────────────────────────────────
|
|
462
|
+
*
|
|
463
|
+
* The same stopping rule that picked 2 000 ms: go until the last unmet target
|
|
464
|
+
* is met, and no further. At 500 ms every reporting group clears 90% — the
|
|
465
|
+
* first cadence in the product's history where that is true. Below it the
|
|
466
|
+
* corpus has nothing left to buy (Instant and Brief are saturated, Sustained
|
|
467
|
+
* and State were never look-limited) while the cost is real and linear: each
|
|
468
|
+
* halving doubles both the detector's CPU (`MEASURED_LOOK_DETECT_MS` per look
|
|
469
|
+
* per feed) and the session's JPEG-encode rate.
|
|
470
|
+
*
|
|
471
|
+
* ── AND IT MUST NOT COST ANALYSES ─────────────────────────────────────────
|
|
472
|
+
*
|
|
473
|
+
* Looking 4x more often must not analyse 4x more often, or the change has
|
|
474
|
+
* failed: one 7.30 s slot is 493 analyses/hour for the whole connector and
|
|
475
|
+
* there is no cloud path left to absorb an overflow. Three things hold that,
|
|
476
|
+
* and all three are asserted rather than assumed:
|
|
477
|
+
* - `MIN_ANALYZE_INTERVAL_MS` (30 s) is a SEPARATE constant and is what
|
|
478
|
+
* `MotionGate.minAnalyzeIntervalMs` floors on.
|
|
479
|
+
* - `periodicIntervalMsForFeedCount` floors on the ANALYZE floor, never on
|
|
480
|
+
* the look cadence, so the still-scene heartbeat does not move.
|
|
481
|
+
* - a still scene contributes exactly ZERO motion-triggered analyses, which
|
|
482
|
+
* is what makes the extra looks free at the model.
|
|
483
|
+
* The `sensing-catch-rate` guard's volume ratchet is the mechanical version:
|
|
484
|
+
* `volume.idleAnalyses` may only go DOWN.
|
|
485
|
+
*
|
|
486
|
+
* Do not raise or lower this without re-running the replay — the guard re-reads
|
|
487
|
+
* this constant and fails if it drifts from the cadence the corpus baseline was
|
|
488
|
+
* measured at.
|
|
329
489
|
*/
|
|
330
|
-
exports.LOOK_INTERVAL_MS =
|
|
490
|
+
exports.LOOK_INTERVAL_MS = 500;
|
|
331
491
|
/** The DOOH audience default (`DEFAULT_PERIODIC_MS`). The slow end of the clamp. */
|
|
332
492
|
exports.DOOH_PERIODIC_MS = 300000;
|
|
333
493
|
/**
|
|
@@ -340,11 +500,28 @@ exports.DOOH_PERIODIC_MS = 300000;
|
|
|
340
500
|
* Returns `null` when the count is not a positive integer, so a caller can omit
|
|
341
501
|
* the key entirely rather than write a cadence derived from a bad count.
|
|
342
502
|
*/
|
|
343
|
-
function periodicIntervalMsForFeedCount(feedCount
|
|
503
|
+
function periodicIntervalMsForFeedCount(feedCount,
|
|
504
|
+
/**
|
|
505
|
+
* What a frame ACTUALLY costs on this box, in ms, when this box has measured
|
|
506
|
+
* itself (`AnalyzeCostObserver.medianMs()`). Omitted or `null` falls back to
|
|
507
|
+
* `MEASURED_LOCAL_ANALYZE_MS`, which is the BOOTSTRAP — deliberately
|
|
508
|
+
* conservative, and not a measurement of any machine.
|
|
509
|
+
*
|
|
510
|
+
* This parameter is the whole reason a fleet spanning 0.45 s/frame to tens of
|
|
511
|
+
* seconds can share one code path: the equation was always per-machine, and
|
|
512
|
+
* until 2026-08-19 only its input was global.
|
|
513
|
+
*/
|
|
514
|
+
analyzeMs) {
|
|
344
515
|
const n = Number(feedCount);
|
|
345
516
|
if (!Number.isFinite(n) || n < 1)
|
|
346
517
|
return null;
|
|
347
|
-
const
|
|
518
|
+
const measured = Number(analyzeMs);
|
|
519
|
+
// A non-positive or non-finite measurement is not a fast box, it is a broken
|
|
520
|
+
// reading — fall back rather than derive a cadence from it.
|
|
521
|
+
const perFrameMs = Number.isFinite(measured) && measured > 0
|
|
522
|
+
? measured
|
|
523
|
+
: exports.MEASURED_LOCAL_ANALYZE_MS;
|
|
524
|
+
const required = Math.ceil((Math.trunc(n) * perFrameMs) / exports.STILL_FLOOR_SLOT_SHARE);
|
|
348
525
|
// MEASURED 2026-08-18, and left as it is ON PURPOSE — see below.
|
|
349
526
|
//
|
|
350
527
|
// The `DOOH_PERIODIC_MS` cap stops honouring `STILL_FLOOR_SLOT_SHARE` past
|
|
@@ -371,32 +548,107 @@ function periodicIntervalMsForFeedCount(feedCount) {
|
|
|
371
548
|
return Math.min(exports.DOOH_PERIODIC_MS, Math.max(exports.MIN_ANALYZE_INTERVAL_MS, required));
|
|
372
549
|
}
|
|
373
550
|
/**
|
|
374
|
-
* The fastest look cadence a connector running `feedCount` feeds
|
|
375
|
-
*
|
|
551
|
+
* The fastest look cadence a connector running `feedCount` feeds can actually
|
|
552
|
+
* SUSTAIN, in ms.
|
|
376
553
|
*
|
|
377
554
|
* Same shape as `periodicIntervalMsForFeedCount` and for the same reason: a
|
|
378
555
|
* cadence is only real if the machine can deliver it. That function divides one
|
|
379
|
-
* inference slot among N feeds; this one divides
|
|
380
|
-
*
|
|
556
|
+
* inference slot among N feeds; this one divides whatever the capture path's
|
|
557
|
+
* scarce resource is — and the whole point of the persistent session is that it
|
|
558
|
+
* CHANGES WHICH RESOURCE THAT IS.
|
|
559
|
+
*
|
|
560
|
+
* ── 'per_grab' — the RECORDER is the bound (unchanged) ────────────────────
|
|
381
561
|
*
|
|
382
562
|
* N feeds x (1 look / T) <= LOOK_CAPTURE_CONCURRENCY / MEASURED_LOOK_MS
|
|
383
563
|
* T >= N x MEASURED_LOOK_MS / LOOK_CAPTURE_CONCURRENCY
|
|
384
564
|
*
|
|
385
565
|
* Measured against that formula at 16 feeds on one recorder, GOP 2 s: predicted
|
|
386
|
-
* 16.0 s/feed, observed 16.0 s/feed.
|
|
566
|
+
* 16.0 s/feed, observed 16.0 s/feed. This is still exactly right for a feed that
|
|
567
|
+
* could not hold a persistent session and degraded — see
|
|
568
|
+
* `RTSPStreamCameraProvider`'s degrade path — so the branch stays live rather
|
|
569
|
+
* than being deleted as legacy.
|
|
570
|
+
*
|
|
571
|
+
* ── 'persistent' — the recorder drops out and the DETECTOR is the bound ───
|
|
572
|
+
*
|
|
573
|
+
* A look no longer touches the recorder, so `LOOK_CAPTURE_CONCURRENCY` and
|
|
574
|
+
* `MEASURED_LOOK_MS` have nothing to say about it. What is left is the cost
|
|
575
|
+
* AFTER the frame is in hand:
|
|
576
|
+
*
|
|
577
|
+
* N feeds x (1 look / T) x MEASURED_LOOK_DETECT_MS <= LOOK_DETECT_CPU_SHARE
|
|
578
|
+
* T >= N x 3.6 / 0.25 ms
|
|
579
|
+
*
|
|
580
|
+
* That is 230 ms at 16 feeds and 461 ms at 32 — both BELOW `LOOK_INTERVAL_MS`,
|
|
581
|
+
* so the clamp wins and a realistic connector simply gets the eval target. It
|
|
582
|
+
* begins to bind past ~34 feeds, which is the honest place for it to bind: a
|
|
583
|
+
* host with 34 cameras has other problems (its still-scene heartbeat pinned at
|
|
584
|
+
* `DOOH_PERIODIC_MS` since 21 feeds) and `getSensingHealth()` reports the
|
|
585
|
+
* effective interval either way.
|
|
387
586
|
*
|
|
388
587
|
* Never faster than `LOOK_INTERVAL_MS` (the eval target — looking finer than
|
|
389
588
|
* that buys nothing the corpus can see) and never slower than
|
|
390
589
|
* `LEGACY_LOOK_INTERVAL_MS`, so this can only make a connector look MORE often
|
|
391
|
-
* than it did, never less. Returns `null` on an uncountable feed count so a
|
|
392
|
-
* omit the key rather than derive a cadence from nothing.
|
|
590
|
+
* than it did, never less. Returns `null` on an uncountable feed count so a
|
|
591
|
+
* caller can omit the key rather than derive a cadence from nothing.
|
|
393
592
|
*/
|
|
394
|
-
function lookIntervalMsForFeedCount(feedCount) {
|
|
593
|
+
function lookIntervalMsForFeedCount(feedCount, captureMode = 'per_grab') {
|
|
395
594
|
const n = Number(feedCount);
|
|
396
595
|
if (!Number.isFinite(n) || n < 1)
|
|
397
596
|
return null;
|
|
398
|
-
const
|
|
399
|
-
|
|
597
|
+
const feeds = Math.trunc(n);
|
|
598
|
+
if (captureMode === 'persistent') {
|
|
599
|
+
const sustainable = Math.ceil((feeds * exports.MEASURED_LOOK_DETECT_MS) / exports.LOOK_DETECT_CPU_SHARE);
|
|
600
|
+
return Math.min(exports.LEGACY_LOOK_INTERVAL_MS, Math.max(exports.LOOK_INTERVAL_MS, sustainable));
|
|
601
|
+
}
|
|
602
|
+
// The per-grab branch keeps its OWN floor at `MEASURED_LOOK_MS`, not at
|
|
603
|
+
// `LOOK_INTERVAL_MS`, and that is deliberate rather than tidy.
|
|
604
|
+
//
|
|
605
|
+
// Those were the same 2 000 ms until this function learned about capture mode,
|
|
606
|
+
// so the distinction did not exist and the eval target did the flooring. Now
|
|
607
|
+
// that `LOOK_INTERVAL_MS` is 500 ms, flooring here would ask a 1-feed per-grab
|
|
608
|
+
// connector for a look every 1 000 ms instead of every 2 000 ms — DOUBLE the
|
|
609
|
+
// RTSP session churn on a recorder, derived from a constant that describes a
|
|
610
|
+
// capture path this branch is not on. And a feed reaches this branch by
|
|
611
|
+
// DEGRADING (`RTSPStreamCameraProvider` could not hold a persistent session),
|
|
612
|
+
// i.e. because that recorder has already refused something; doubling its
|
|
613
|
+
// session rate is precisely the wrong response.
|
|
614
|
+
//
|
|
615
|
+
// So: this change moves the per-grab cadence by zero milliseconds at every
|
|
616
|
+
// feed count, which `connectorCadence.test.ts` asserts against the values that
|
|
617
|
+
// shipped.
|
|
618
|
+
const sustainable = Math.ceil((feeds * exports.MEASURED_LOOK_MS) / exports.LOOK_CAPTURE_CONCURRENCY);
|
|
619
|
+
return Math.min(exports.LEGACY_LOOK_INTERVAL_MS, Math.max(exports.MEASURED_LOOK_MS, sustainable));
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Decoded frames a persistent session must produce per LOOK.
|
|
623
|
+
*
|
|
624
|
+
* TWO, and the second one is not waste. `RTSPStreamCameraProvider.getFrame()`
|
|
625
|
+
* refuses to hand back a frame it has already served — that refusal is the
|
|
626
|
+
* whole reason a frozen camera cannot report as healthy — so a look that
|
|
627
|
+
* arrives before the next frame does BLOCKS, and if it blocks past the
|
|
628
|
+
* staleness deadline it throws on a camera that is working perfectly.
|
|
629
|
+
*
|
|
630
|
+
* Running the stream at exactly the look rate makes that a coin flip on every
|
|
631
|
+
* tick, because neither clock is locked to the other: the session's frame
|
|
632
|
+
* cadence follows the CAMERA's timestamps and the look cadence follows a Node
|
|
633
|
+
* timer, and they drift. One frame of headroom removes the race for the cost of
|
|
634
|
+
* one extra scale+encode per look — the cheapest stage in the pipeline, and one
|
|
635
|
+
* that never touches the recorder or the model.
|
|
636
|
+
*/
|
|
637
|
+
exports.STREAM_LOOK_HEADROOM = 2;
|
|
638
|
+
/**
|
|
639
|
+
* Frames per second a persistent session should decode to serve a given look
|
|
640
|
+
* cadence. Derived, so the two can never be set independently and drift into
|
|
641
|
+
* the blocking case above.
|
|
642
|
+
*
|
|
643
|
+
* At the shipped 500 ms look cadence this is 4 fps — which is also the
|
|
644
|
+
* provider's own default (`MEASURED_STREAM_FRAME_INTERVAL_MS`), and
|
|
645
|
+
* `connectorCadence.test.ts` asserts the agreement rather than trusting it.
|
|
646
|
+
*/
|
|
647
|
+
function streamFpsForLookIntervalMs(lookIntervalMs) {
|
|
648
|
+
const t = Number(lookIntervalMs);
|
|
649
|
+
if (!Number.isFinite(t) || t <= 0)
|
|
650
|
+
return 1000 / exports.MEASURED_STREAM_FRAME_INTERVAL_MS;
|
|
651
|
+
return (exports.STREAM_LOOK_HEADROOM * 1000) / t;
|
|
400
652
|
}
|
|
401
653
|
/**
|
|
402
654
|
* The still-scene analyses/hour a whole connector demands at a heartbeat, and
|
|
@@ -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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"}
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
* 493 analyses/hour however many cameras it runs. (The SINGLE SLOT is now a
|
|
30
30
|
* measured fact — two concurrent sessions on one engine make the second return
|
|
31
31
|
* an EMPTY generation, silently; see CloudSensingManager. The 7.30 s is NOT:
|
|
32
|
-
* the shipped path measures 1.90 s on the pinned 0.16.0,
|
|
33
|
-
* 2026-08-19 config.json change
|
|
32
|
+
* the shipped path measures 1.90 s on the pinned 0.16.0, 1.22 s with the
|
|
33
|
+
* 2026-08-19 config.json change, and a further paired 2.42x with the 2026-08-19
|
|
34
|
+
* fast-conversation shim (0.72 s/frame through bench-local-vlm). The argument below is unaffected — the profile's
|
|
34
35
|
* 5-30 s values still oversubscribe by ~4-24x rather than ~23x — but do not
|
|
35
36
|
* quote 493/hour as this machine's ceiling; see MEASURED_LOCAL_ANALYZE_MS.) That ceiling is what
|
|
36
37
|
* `connectorCadence.periodicIntervalMsForFeedCount` already computes, and it is
|
|
@@ -144,5 +145,10 @@ export declare function resolveDeployedCadence(input: {
|
|
|
144
145
|
requestedMs: unknown;
|
|
145
146
|
mode: SensingRunModeName;
|
|
146
147
|
feedCount: number;
|
|
148
|
+
/**
|
|
149
|
+
* This machine's own measured per-frame analyze cost in ms, or null/undefined
|
|
150
|
+
* while it is still on the bootstrap constant. See `AnalyzeCostObserver`.
|
|
151
|
+
*/
|
|
152
|
+
analyzeMs?: number | null;
|
|
147
153
|
}): DeployedCadence;
|
|
148
154
|
//# sourceMappingURL=deployedCadence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployedCadence.d.ts","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"deployedCadence.d.ts","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAQH;;;;;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;;;;;;;GAOG;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;CAC3B,GAAG,eAAe,CAoFlB"}
|
package/dist/deployedCadence.js
CHANGED
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
* 493 analyses/hour however many cameras it runs. (The SINGLE SLOT is now a
|
|
31
31
|
* measured fact — two concurrent sessions on one engine make the second return
|
|
32
32
|
* an EMPTY generation, silently; see CloudSensingManager. The 7.30 s is NOT:
|
|
33
|
-
* the shipped path measures 1.90 s on the pinned 0.16.0,
|
|
34
|
-
* 2026-08-19 config.json change
|
|
33
|
+
* the shipped path measures 1.90 s on the pinned 0.16.0, 1.22 s with the
|
|
34
|
+
* 2026-08-19 config.json change, and a further paired 2.42x with the 2026-08-19
|
|
35
|
+
* fast-conversation shim (0.72 s/frame through bench-local-vlm). The argument below is unaffected — the profile's
|
|
35
36
|
* 5-30 s values still oversubscribe by ~4-24x rather than ~23x — but do not
|
|
36
37
|
* quote 493/hour as this machine's ceiling; see MEASURED_LOCAL_ANALYZE_MS.) That ceiling is what
|
|
37
38
|
* `connectorCadence.periodicIntervalMsForFeedCount` already computes, and it is
|
|
@@ -115,7 +116,9 @@ function resolveDeployedCadence(input) {
|
|
|
115
116
|
// measured. periodicIntervalMsForFeedCount returns null on a bad count; a
|
|
116
117
|
// connector with no countable feeds gets the DOOH default rather than a
|
|
117
118
|
// cadence derived from nothing.
|
|
118
|
-
|
|
119
|
+
// `analyzeMs` is what THIS box measured for itself; undefined/null means it has
|
|
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;
|
|
119
122
|
// A cadence finer than the connector may ANALYZE is not deliverable, so it is
|
|
120
123
|
// a floor in every case. This read `LOOK_INTERVAL_MS` until 2026-08-18, when
|
|
121
124
|
// the two constants split: both were 30 000 ms, so the substitution changes no
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployedCadence.js","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"deployedCadence.js","sourceRoot":"","sources":["../src/deployedCadence.ts"],"names":[],"mappings":"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"}
|
|
@@ -36,6 +36,16 @@ export interface DeployedCadenceSourceOptions {
|
|
|
36
36
|
mode: SensingRunModeName;
|
|
37
37
|
/** Total feeds this connector runs (drives the LOCAL single-slot floor). */
|
|
38
38
|
feedCount: number;
|
|
39
|
+
/**
|
|
40
|
+
* This machine's own per-frame analyze cost, read AT RESOLVE TIME.
|
|
41
|
+
*
|
|
42
|
+
* A getter and not a number on purpose: the observer settles minutes after the
|
|
43
|
+
* connector starts and then keeps rolling, so a value captured at construction
|
|
44
|
+
* would pin every future cadence to the cold-start estimate — the one reading
|
|
45
|
+
* we specifically know is high. Omitted leaves the conservative bootstrap
|
|
46
|
+
* constant governing, which is exactly today's behaviour.
|
|
47
|
+
*/
|
|
48
|
+
observedAnalyzeMs?: () => number | null;
|
|
39
49
|
}
|
|
40
50
|
/**
|
|
41
51
|
* Build the connector's cadence source. Returns `undefined` when there are no
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployedCadenceSource.d.ts","sourceRoot":"","sources":["../src/deployedCadenceSource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,4BAA4B;IAC3C,kFAAkF;IAClF,cAAc,EAAE,oBAAoB,EAAE,CAAC;IACvC,IAAI,EAAE,kBAAkB,CAAC;IACzB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"deployedCadenceSource.d.ts","sourceRoot":"","sources":["../src/deployedCadenceSource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,4BAA4B;IAC3C,kFAAkF;IAClF,cAAc,EAAE,oBAAoB,EAAE,CAAC;IACvC,IAAI,EAAE,kBAAkB,CAAC;IACzB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,4BAA4B,GACpC,aAAa,GAAG,SAAS,CA6C3B"}
|
|
@@ -62,10 +62,21 @@ function buildDeployedCadenceSource(options) {
|
|
|
62
62
|
// running cadence, and returning null is how the manager is told so.
|
|
63
63
|
if (!anyAnswered)
|
|
64
64
|
return null;
|
|
65
|
+
// Read the machine's own cost NOW, not at construction. A throwing getter
|
|
66
|
+
// must not take the cadence down with it: an unmeasured box is a box on
|
|
67
|
+
// the bootstrap, which is the state it was in a moment ago anyway.
|
|
68
|
+
let analyzeMs = null;
|
|
69
|
+
try {
|
|
70
|
+
analyzeMs = options.observedAnalyzeMs ? options.observedAnalyzeMs() : null;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
analyzeMs = null;
|
|
74
|
+
}
|
|
65
75
|
return (0, deployedCadence_1.resolveDeployedCadence)({
|
|
66
76
|
requestedMs: fastest,
|
|
67
77
|
mode: options.mode,
|
|
68
78
|
feedCount: options.feedCount,
|
|
79
|
+
analyzeMs,
|
|
69
80
|
});
|
|
70
81
|
},
|
|
71
82
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployedCadenceSource.js","sourceRoot":"","sources":["../src/deployedCadenceSource.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;
|
|
1
|
+
{"version":3,"file":"deployedCadenceSource.js","sourceRoot":"","sources":["../src/deployedCadenceSource.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;AAiCH,gEA+CC;AA5ED,uDAI2B;AAoB3B;;;;GAIG;AACH,SAAgB,0BAA0B,CACxC,OAAqC;IAErC,MAAM,OAAO,GAAG,CAAC,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3C,OAAO;QACL,KAAK,CAAC,OAAO;YACX,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAC/B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,CACvD,CAAC;YAEF,qEAAqE;YACrE,gDAAgD;YAChD,IAAI,OAAO,GAAkB,IAAI,CAAC;YAClC,IAAI,WAAW,GAAG,KAAK,CAAC;YACxB,KAAK,MAAM,OAAO,IAAI,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,OAAO;oBAAE,SAAS;gBACvB,WAAW,GAAG,IAAI,CAAC;gBACnB,MAAM,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC;gBACrC,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;oBAAE,SAAS;gBACxE,IAAI,OAAO,KAAK,IAAI,IAAI,EAAE,GAAG,OAAO;oBAAE,OAAO,GAAG,EAAE,CAAC;YACrD,CAAC;YAED,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,IAAI,CAAC,WAAW;gBAAE,OAAO,IAAI,CAAC;YAE9B,0EAA0E;YAC1E,wEAAwE;YACxE,mEAAmE;YACnE,IAAI,SAAS,GAAkB,IAAI,CAAC;YACpC,IAAI,CAAC;gBACH,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7E,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,GAAG,IAAI,CAAC;YACnB,CAAC;YAED,OAAO,IAAA,wCAAsB,EAAC;gBAC5B,WAAW,EAAE,OAAO;gBACpB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS;aACV,CAAC,CAAC;QACL,CAAC;KACF,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.64";
|
|
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.64';
|
|
67
67
|
exports._SDK_VERSION = SDK_VERSION;
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
69
|
// ANSI helpers (no external deps)
|