@rynx-ai/server 0.1.11-beta.50 → 0.1.11-beta.51

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.
@@ -228,7 +228,6 @@ export class DesktopBrowserHostRegistry {
228
228
  : hostUnavailable("Desktop Browser Host lease ended"));
229
229
  }
230
230
  if (lease.pending.size >= DESKTOP_BROWSER_HOST_MAX_PENDING_COMMANDS) {
231
- this.failLease(lease, CLOSE_BACKPRESSURE, "Desktop Browser Host command capacity exceeded");
232
231
  return Promise.reject(new SessionBrowserHostError("capacity", "Desktop Browser Host command capacity exceeded"));
233
232
  }
234
233
  const commandId = this.nextId("command");
@@ -245,7 +244,6 @@ export class DesktopBrowserHostRegistry {
245
244
  const encodedBytes = Buffer.byteLength(encoded, "utf8");
246
245
  if (encodedBytes > DESKTOP_BROWSER_HOST_MAX_CONTROL_FRAME_BYTES ||
247
246
  lease.socket.bufferedAmount + encodedBytes > MAX_SOCKET_BUFFERED_BYTES) {
248
- this.failLease(lease, CLOSE_BACKPRESSURE, "Desktop Browser Host output capacity exceeded");
249
247
  return Promise.reject(new SessionBrowserHostError("capacity", "Desktop Browser Host output capacity exceeded"));
250
248
  }
251
249
  return new Promise((resolve, reject) => {
@@ -874,12 +872,23 @@ class DesktopBrowserSurfaceSource {
874
872
  function handleDesktopConnection(socket, options) {
875
873
  let leaseId;
876
874
  let alive = true;
875
+ let missedProbes = 0;
876
+ let lastHeartbeatAt = Date.now();
877
877
  const handshakeTimer = setTimeout(() => {
878
878
  closeSocket(socket, CLOSE_UNSUPPORTED, "Desktop Browser Host hello timed out");
879
879
  }, options.handshakeTimeoutMilliseconds);
880
880
  handshakeTimer.unref?.();
881
881
  const heartbeat = setInterval(() => {
882
- if (!alive) {
882
+ const now = Date.now();
883
+ const elapsed = now - lastHeartbeatAt;
884
+ lastHeartbeatAt = now;
885
+ // A paused local event loop is not evidence that the remote Host is dead.
886
+ const delayed = elapsed < 0 || elapsed > options.heartbeatIntervalMilliseconds * 1.5;
887
+ if (alive)
888
+ missedProbes = 0;
889
+ else if (!delayed)
890
+ missedProbes++;
891
+ if (missedProbes >= 3) {
883
892
  socket.terminate();
884
893
  return;
885
894
  }
@@ -894,6 +903,7 @@ function handleDesktopConnection(socket, options) {
894
903
  heartbeat.unref?.();
895
904
  socket.on("pong", () => { alive = true; });
896
905
  socket.on("message", (data, binary) => {
906
+ alive = true;
897
907
  try {
898
908
  if (binary) {
899
909
  if (!leaseId)
@@ -988,7 +998,10 @@ function commandDeadlineError(command, message) {
988
998
  return new SessionBrowserHostError(isReadOnlyCommand(command) ? "command_timeout" : "outcome_unknown", message);
989
999
  }
990
1000
  function isRecoverableDeadline(command) {
991
- return isReadOnlyCommand(command) || ["page.navigate", "page.reload", "page.back", "page.forward", "page.activate"].includes(command.method);
1001
+ // Page state can be read back after an uncertain action; do not tear down
1002
+ // every Browser on the Host. Browser/Surface lifecycle still requires fencing.
1003
+ return isReadOnlyCommand(command) || command.method.startsWith("page.") ||
1004
+ ["surface.input", "surface.frame.ack", "surface.driver"].includes(command.method);
992
1005
  }
993
1006
  function isReadOnlyCommand(command) {
994
1007
  return command.method === "browser.snapshot" ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/server",
3
- "version": "0.1.11-beta.50",
3
+ "version": "0.1.11-beta.51",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -27,15 +27,15 @@
27
27
  "@koa/router": "^15.4.0",
28
28
  "koa": "^3.2.0",
29
29
  "ws": "^8.21.0",
30
- "@rynx-ai/core": "0.1.11-beta.50",
31
- "@rynx-ai/plugin-sdk": "0.1.11-beta.50",
32
- "@rynx-ai/protocol": "0.1.11-beta.50",
33
- "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.50",
34
- "@rynx-ai/runtime": "0.1.11-beta.50"
30
+ "@rynx-ai/core": "0.1.11-beta.51",
31
+ "@rynx-ai/plugin-sdk": "0.1.11-beta.51",
32
+ "@rynx-ai/protocol": "0.1.11-beta.51",
33
+ "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.51",
34
+ "@rynx-ai/runtime": "0.1.11-beta.51"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/ws": "^8.18.1",
38
- "@rynx-ai/control-web": "0.1.11-beta.50"
38
+ "@rynx-ai/control-web": "0.1.11-beta.51"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "rm -rf dist && tsc -p tsconfig.json && mkdir -p dist/control-web && cp -R ../control-web/dist/. dist/control-web/",