@melaya/runner 1.1.18 → 1.1.19

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.
@@ -1356,6 +1356,12 @@ export async function startBrowserBridge(opts) {
1356
1356
  },
1357
1357
  };
1358
1358
  }
1359
+ // Post an (unchanged) heartbeat frame at least this often so the viewer's
1360
+ // frame watchdog never trips on a STATIC page. Without it, a page that is not
1361
+ // animating (the agent is reasoning, or the page is idle) produced ZERO frames
1362
+ // and the client showed "The live view was interrupted" even though capture was
1363
+ // running fine. Must be shorter than the client's interrupt threshold.
1364
+ const FRAME_HEARTBEAT_MS = 2_000;
1359
1365
  // sessionId -> WatchLease
1360
1366
  const watchLeases = new Map();
1361
1367
  function postFrame(lease, jpegB64, tabs) {
@@ -1479,14 +1485,19 @@ export async function startBrowserBridge(opts) {
1479
1485
  }
1480
1486
  const { image_b64: jpegB64 } = await takeScreenshot(targetLease);
1481
1487
  const changed = jpegB64 !== lease.lastJpegB64;
1488
+ const heartbeatDue = Date.now() - lease.lastPostAt >= FRAME_HEARTBEAT_MS;
1482
1489
  if (changed) {
1483
1490
  lease.lastJpegB64 = jpegB64;
1484
1491
  postFrame(lease, jpegB64, tabsPayload);
1492
+ lease.lastPostAt = Date.now();
1485
1493
  }
1486
- else if (tabsPayload !== undefined) {
1487
- // Tabs changed but frame did not; send frame anyway so the tab update
1488
- // is not dropped (the frame body carries the tabs field).
1494
+ else if (tabsPayload !== undefined || heartbeatDue) {
1495
+ // Tabs changed but frame did not, OR the page is static and the viewer's
1496
+ // watchdog is about to trip: send the (unchanged) frame anyway so the tab
1497
+ // update is not dropped and the live view never shows "interrupted" while
1498
+ // capture is actually running. This is the static-page heartbeat.
1489
1499
  postFrame(lease, jpegB64, tabsPayload);
1500
+ lease.lastPostAt = Date.now();
1490
1501
  }
1491
1502
  // Adaptive FPS: 250 ms (4 fps) when changed, 500 ms (2 fps) when
1492
1503
  // the page is static. The cap is 4 fps; do not go lower than 2 fps
@@ -1576,6 +1587,7 @@ export async function startBrowserBridge(opts) {
1576
1587
  timer: null,
1577
1588
  lastJpegB64: "",
1578
1589
  lastTabsJson: "",
1590
+ lastPostAt: 0,
1579
1591
  };
1580
1592
  watchLeases.set(sessionId, lease);
1581
1593
  log(`[watch] frame producer started for session ${sessionId.slice(0, 16)} run=${matchedRunId.slice(0, 10)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melaya/runner",
3
- "version": "1.1.18",
3
+ "version": "1.1.19",
4
4
  "description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,