@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.
Files changed (71) hide show
  1. package/deploy/litertlm/test_trillboards_litertlm_shim.py +132 -0
  2. package/deploy/litertlm/trillboards-litertlm-shim.pth +1 -0
  3. package/deploy/litertlm/trillboards_litertlm_shim.py +214 -0
  4. package/deploy/macos/bench-local-vlm.sh +8 -4
  5. package/deploy/macos/build-macos-pkg.sh +4 -0
  6. package/deploy/macos/scripts/setup-litertlm.sh +99 -2
  7. package/deploy/systemd/setup-litertlm.sh +99 -2
  8. package/deploy/windows/build-windows.ps1 +4 -0
  9. package/deploy/windows/register-litertlm-task.ps1 +22 -0
  10. package/deploy/windows/setup-litertlm.ps1 +131 -3
  11. package/deploy/windows/trillboards-edge.cmd +19 -0
  12. package/dist/CloudSensingManager.d.ts +65 -0
  13. package/dist/CloudSensingManager.d.ts.map +1 -1
  14. package/dist/CloudSensingManager.js +65 -30
  15. package/dist/CloudSensingManager.js.map +1 -1
  16. package/dist/DurableVlmCandidateSpool.d.ts +21 -0
  17. package/dist/DurableVlmCandidateSpool.d.ts.map +1 -1
  18. package/dist/DurableVlmCandidateSpool.js +126 -1
  19. package/dist/DurableVlmCandidateSpool.js.map +1 -1
  20. package/dist/EdgeAgent.js +1 -1
  21. package/dist/LiteRtVlmProvider.d.ts +74 -7
  22. package/dist/LiteRtVlmProvider.d.ts.map +1 -1
  23. package/dist/LiteRtVlmProvider.js.map +1 -1
  24. package/dist/MosaicSource.d.ts +18 -0
  25. package/dist/MosaicSource.d.ts.map +1 -1
  26. package/dist/MosaicSource.js +23 -0
  27. package/dist/MosaicSource.js.map +1 -1
  28. package/dist/VlmCandidateBuffer.d.ts +45 -6
  29. package/dist/VlmCandidateBuffer.d.ts.map +1 -1
  30. package/dist/VlmCandidateBuffer.js +76 -7
  31. package/dist/VlmCandidateBuffer.js.map +1 -1
  32. package/dist/analyzeCostObserver.d.ts +96 -0
  33. package/dist/analyzeCostObserver.d.ts.map +1 -0
  34. package/dist/analyzeCostObserver.js +130 -0
  35. package/dist/analyzeCostObserver.js.map +1 -0
  36. package/dist/config.d.ts +8 -0
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/config.js.map +1 -1
  39. package/dist/connectorCadence.d.ts +286 -57
  40. package/dist/connectorCadence.d.ts.map +1 -1
  41. package/dist/connectorCadence.js +313 -61
  42. package/dist/connectorCadence.js.map +1 -1
  43. package/dist/deployedCadence.d.ts +8 -2
  44. package/dist/deployedCadence.d.ts.map +1 -1
  45. package/dist/deployedCadence.js +6 -3
  46. package/dist/deployedCadence.js.map +1 -1
  47. package/dist/deployedCadenceSource.d.ts +10 -0
  48. package/dist/deployedCadenceSource.d.ts.map +1 -1
  49. package/dist/deployedCadenceSource.js +11 -0
  50. package/dist/deployedCadenceSource.js.map +1 -1
  51. package/dist/diagnose.d.ts +1 -1
  52. package/dist/diagnose.js +1 -1
  53. package/dist/localInferenceSchema.d.ts +8 -3
  54. package/dist/localInferenceSchema.d.ts.map +1 -1
  55. package/dist/localInferenceSchema.js +8 -3
  56. package/dist/localInferenceSchema.js.map +1 -1
  57. package/dist/localSensing.d.ts +22 -1
  58. package/dist/localSensing.d.ts.map +1 -1
  59. package/dist/localSensing.js +101 -7
  60. package/dist/localSensing.js.map +1 -1
  61. package/dist/localVlmBench.d.ts +82 -0
  62. package/dist/localVlmBench.d.ts.map +1 -1
  63. package/dist/localVlmBench.js +88 -0
  64. package/dist/localVlmBench.js.map +1 -1
  65. package/dist/sensingCatchRate.d.ts.map +1 -1
  66. package/dist/sensingCatchRate.js +75 -10
  67. package/dist/sensingCatchRate.js.map +1 -1
  68. package/dist/sensingRunMode.d.ts.map +1 -1
  69. package/dist/sensingRunMode.js +38 -2
  70. package/dist/sensingRunMode.js.map +1 -1
  71. package/package.json +1 -1
@@ -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 (and 1.22 s with the 2026-08-19
49
- * config.json change) on the pinned 0.16.0, so the real ceiling on
50
- * this machine class is ~1,895/hour and ~2,951/hour respectively.
51
- * The constant is kept as a conservative FLEET default because the
52
- * fleet varies ~7x; do not quote 493 as this machine's ceiling.
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
- * ── Why it is NOT simply lowered here ──────────────────────────────────────
151
- *
152
- * Because this is ONE constant for a fleet that varies ~7x, and every number
153
- * above is one desk. `cctv-local-inference-hardware-floor.md` §7 measures
154
- * 34.78 s/frame on the `windows-latest` class. Replacing 7.30 s with 1.22 s (the
155
- * M4 Max WITH the 2026-08-19 config.json change) would demand ~28x what that box
156
- * can deliver, and replacing it with the slow box's number makes every heartbeat
157
- * worse than today. A single global constant cannot be right for both, and
158
- * picking the fast desk is exactly the error `.claude/lessons/2026-08-18-a-
159
- * benchmark-with-no-machine-axis-is-a-spec-for-one-desk.md` was written about.
160
- *
161
- * The fix is to DERIVE this per machine — the connector already measures its own
162
- * frame cost, and `deployedCadence` already exists to reconcile a deployed
163
- * cadence with what a machine can afford. Until that lands, treat 7.30 s as a
164
- * deliberately conservative fleet default and NOT as a measurement of any
165
- * machine: quoting it as one is what this block exists to stop. Changing the
166
- * value also moves `sensingCatchRate.DEFAULT_SAMPLER.analyzeS`, so it requires
167
- * re-running `replay-catch-rate.mjs` and regenerating
168
- * `.github/contracts/sensing-catch-rate-baseline.json` in the same change.
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 real RTSP grab, not the
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
- * Two things fall out of that table and both are load bearing:
311
- *
312
- * - The Instant group — "alert me if the door opens after hours", a 2.2 s
313
- * median event — clears the 90% target at 2.4 s and is at 8.4% today. This
314
- * is the promise the 30 s cadence was silently failing.
315
- *
316
- * - The Brief group does NOT reach 90% until 0.75 s, and 0.75 s is well below
317
- * the measured cost of a single grab (`MEASURED_LOOK_MS`). So 90% on every
318
- * shape is NOT reachable by look cadence on the current capture
319
- * architecture, and this constant does not pretend otherwise. Closing that
320
- * gap needs a persistent RTSP session — one long-lived ffmpeg per feed
321
- * instead of one per grab — which is an architecture change, not a number.
322
- *
323
- * 2 000 ms is therefore the fastest cadence that is BOTH an eval output and
324
- * physically deliverable: it is the measured single-grab floor, and at it the
325
- * Instant group clears the target. Do not lower it without first lowering
326
- * `MEASURED_LOOK_MS`, and do not raise it without re-running the replay — the
327
- * `sensing-catch-rate` guard re-reads this constant and fails if it drifts from
328
- * the cadence the corpus was measured at.
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 = 2000;
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 required = Math.ceil((Math.trunc(n) * exports.MEASURED_LOCAL_ANALYZE_MS) / exports.STILL_FLOOR_SLOT_SHARE);
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 against ONE
375
- * recorder can actually SUSTAIN, in ms.
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 one recorder's capture
380
- * capacity among N feeds.
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 caller can
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 sustainable = Math.ceil((Math.trunc(n) * exports.MEASURED_LOOK_MS) / exports.LOOK_CAPTURE_CONCURRENCY);
399
- return Math.min(exports.LEGACY_LOOK_INTERVAL_MS, Math.max(exports.LOOK_INTERVAL_MS, sustainable));
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":"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"}
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, and 1.22 s with the
33
- * 2026-08-19 config.json change. The argument below is unaffected — the profile's
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;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;CACnB,GAAG,eAAe,CAkFlB"}
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"}
@@ -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, and 1.22 s with the
34
- * 2026-08-19 config.json change. The argument below is unaffected — the profile's
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
- const floorMs = (0, connectorCadence_1.periodicIntervalMsForFeedCount)(input.feedCount) ?? connectorCadence_1.DOOH_PERIODIC_MS;
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;;;AA+FH,wDAsFC;AAnLD,yDAI4B;AAU5B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACU,QAAA,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAwC/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,KAItC;IACC,MAAM,WAAW,GAAG,gBAAgB,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IAExD,sEAAsE;IACtE,0EAA0E;IAC1E,wEAAwE;IACxE,gCAAgC;IAChC,MAAM,OAAO,GAAG,IAAA,iDAA8B,EAAC,KAAK,CAAC,SAAS,CAAC,IAAI,mCAAgB,CAAC;IAEpF,8EAA8E;IAC9E,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,0EAA0E;IAC1E,wDAAwD;IACxD,EAAE;IACF,6EAA6E;IAC7E,6EAA6E;IAC7E,+CAA+C;IAC/C,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,sBAAc,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,0CAAuB,CAAC,CAAC,CAAC;IAE5F,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,IAAI,cAAc,KAAK,0CAAuB,IAAI,OAAO,IAAI,0CAAuB;YAAE,MAAM,GAAG,eAAe,CAAC;;YAC1G,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"}
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;CACnB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,4BAA4B,GACpC,aAAa,GAAG,SAAS,CAkC3B"}
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;;AAuBH,gEAoCC;AAvDD,uDAI2B;AAU3B;;;;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,OAAO,IAAA,wCAAsB,EAAC;gBAC5B,WAAW,EAAE,OAAO;gBACpB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,SAAS,EAAE,OAAO,CAAC,SAAS;aAC7B,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
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"}
@@ -31,7 +31,7 @@ export interface DiagnosticReport {
31
31
  skipped: number;
32
32
  };
33
33
  }
34
- declare const SDK_VERSION = "0.2.61";
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.61';
66
+ const SDK_VERSION = '0.2.64';
67
67
  exports._SDK_VERSION = SDK_VERSION;
68
68
  // ---------------------------------------------------------------------------
69
69
  // ANSI helpers (no external deps)