replicas-computer 0.3.10 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2937,7 +2937,7 @@ async function computerStatusCommand() {
2937
2937
  }
2938
2938
 
2939
2939
  // src/version.ts
2940
- var COMPUTER_USE_VERSION = "0.3.10";
2940
+ var COMPUTER_USE_VERSION = "0.3.11";
2941
2941
  var COMPUTER_USE_PROTOCOL_VERSION = 1;
2942
2942
 
2943
2943
  // src/program.ts
@@ -3592,7 +3592,8 @@ var DESKTOP_WEBRTC_FAILURE_REASONS = {
3592
3592
  codec_unsupported: "browser cannot receive H.264 over WebRTC",
3593
3593
  negotiation_failed: "negotiation failed",
3594
3594
  video_timeout: "WebRTC did not decode video in time",
3595
- video_stalled: "WebRTC video stopped arriving"
3595
+ video_stalled: "WebRTC video stopped arriving",
3596
+ full_chroma_undecodable: "WebRTC could not decode H.264 High 4:4:4"
3596
3597
  };
3597
3598
  function isDesktopWebRtcFailure(value) {
3598
3599
  if (!isRecord(value) || typeof value.reasonCode !== "string" || !Object.hasOwn(DESKTOP_WEBRTC_FAILURE_REASONS, value.reasonCode) || typeof value.turnConfigured !== "boolean") return false;
@@ -42,7 +42,8 @@ var DESKTOP_WEBRTC_FAILURE_REASONS = {
42
42
  codec_unsupported: "browser cannot receive H.264 over WebRTC",
43
43
  negotiation_failed: "negotiation failed",
44
44
  video_timeout: "WebRTC did not decode video in time",
45
- video_stalled: "WebRTC video stopped arriving"
45
+ video_stalled: "WebRTC video stopped arriving",
46
+ full_chroma_undecodable: "WebRTC could not decode H.264 High 4:4:4"
46
47
  };
47
48
 
48
49
  // src/input-validation.ts
@@ -99,6 +100,13 @@ diagnostics.classList.toggle("visible", showDiagnostics);
99
100
  var webrtcFrameTimeoutMs = 6e3;
100
101
  var webrtcSkipsAfterFailure = 3;
101
102
  var pointerIntervalMs = 4;
103
+ var fullChromaFailureKey = "replicas-desktop-4:4:4-failed";
104
+ var skipFullChroma = false;
105
+ var fullChromaFailure;
106
+ try {
107
+ skipFullChroma = localStorage.getItem(fullChromaFailureKey) === navigator.userAgent;
108
+ } catch {
109
+ }
102
110
  var socket;
103
111
  var config;
104
112
  var transport;
@@ -397,38 +405,40 @@ var unlockAudio = () => {
397
405
  });
398
406
  });
399
407
  };
408
+ var captureFailure = (reasonCode, current) => {
409
+ const captured = {
410
+ id: crypto.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`,
411
+ reasonCode,
412
+ elapsedMs: Math.round(performance.now() - connectionStartedAt),
413
+ connectionState: peer?.connectionState ?? "unavailable",
414
+ iceConnectionState: peer?.iceConnectionState ?? "unavailable",
415
+ iceGatheringState: peer?.iceGatheringState ?? "unavailable",
416
+ signalingState: peer?.signalingState ?? "unavailable",
417
+ iceTransportPolicy: request.iceTransportPolicy,
418
+ turnConfigured: current.iceServers?.some((server) => /^turns?:/i.test(server.urls)) ?? false,
419
+ serverVersion: current.serverVersion ?? "unknown",
420
+ serverUnavailableReason: current.webrtcUnavailableReason ?? "",
421
+ localCandidateTypes: [...localCandidateTypes].join(","),
422
+ remoteCandidateTypes: [...remoteCandidateTypes].join(","),
423
+ iceErrorCodes: [...iceErrorCodes].join(","),
424
+ candidateErrors,
425
+ errorName: negotiationErrorName,
426
+ path: route.path,
427
+ codec: route.codec,
428
+ framesDecoded: inbound?.framesDecoded ?? 0,
429
+ packetsReceived: inbound?.packetsReceived ?? 0,
430
+ packetsLost: inbound?.packetsLost ?? 0,
431
+ bytesReceived: inbound?.bytesReceived ?? 0,
432
+ lastFrameAgeMs: lastFrameAt ? Math.round(performance.now() - lastFrameAt) : -1,
433
+ encoderFps: telemetry?.encoder.fps ?? -1
434
+ };
435
+ state.lastWebRtcFailure = captured;
436
+ return captured;
437
+ };
400
438
  var useFallback = (reasonCode, failed = false) => {
401
439
  if (transport === "websocket" || !config) return;
402
440
  const reason = reasonCode === "websocket_selected" ? "WebSocket transport selected" : reasonCode === "recent_failure" ? "recent WebRTC failure" : DESKTOP_WEBRTC_FAILURE_REASONS[reasonCode];
403
- if (request.transport === "webrtc" && reasonCode !== "websocket_selected" && reasonCode !== "recent_failure") {
404
- failure = {
405
- id: crypto.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(36).slice(2)}`,
406
- reasonCode,
407
- elapsedMs: Math.round(performance.now() - connectionStartedAt),
408
- connectionState: peer?.connectionState ?? "unavailable",
409
- iceConnectionState: peer?.iceConnectionState ?? "unavailable",
410
- iceGatheringState: peer?.iceGatheringState ?? "unavailable",
411
- signalingState: peer?.signalingState ?? "unavailable",
412
- iceTransportPolicy: request.iceTransportPolicy,
413
- turnConfigured: config.iceServers?.some((server) => /^turns?:/i.test(server.urls)) ?? false,
414
- serverVersion: config.serverVersion ?? "unknown",
415
- serverUnavailableReason: config.webrtcUnavailableReason ?? "",
416
- localCandidateTypes: [...localCandidateTypes].join(","),
417
- remoteCandidateTypes: [...remoteCandidateTypes].join(","),
418
- iceErrorCodes: [...iceErrorCodes].join(","),
419
- candidateErrors,
420
- errorName: negotiationErrorName,
421
- path: route.path,
422
- codec: route.codec,
423
- framesDecoded: inbound?.framesDecoded ?? 0,
424
- packetsReceived: inbound?.packetsReceived ?? 0,
425
- packetsLost: inbound?.packetsLost ?? 0,
426
- bytesReceived: inbound?.bytesReceived ?? 0,
427
- lastFrameAgeMs: lastFrameAt ? Math.round(performance.now() - lastFrameAt) : -1,
428
- encoderFps: telemetry?.encoder.fps ?? -1
429
- };
430
- state.lastWebRtcFailure = failure;
431
- }
441
+ if (request.transport === "webrtc" && reasonCode !== "websocket_selected" && reasonCode !== "recent_failure") failure = captureFailure(reasonCode, config);
432
442
  closePeer();
433
443
  if (failed) webrtcSkips = webrtcSkipsAfterFailure;
434
444
  note("fallback to socket", reason);
@@ -504,6 +514,10 @@ var negotiate = async (sdp) => {
504
514
  try {
505
515
  await pc.setRemoteDescription({ type: "offer", sdp });
506
516
  if (peer !== pc) return;
517
+ if (skipFullChroma) {
518
+ const codecs = RTCRtpReceiver.getCapabilities("video")?.codecs.filter((codec) => h264ProfileLabel(codec.sdpFmtpLine ?? "") !== H264_PROFILES.high444.label);
519
+ if (codecs) pc.getTransceivers().find((transceiver) => transceiver.receiver.track.kind === "video")?.setCodecPreferences(codecs);
520
+ }
507
521
  const answer = await pc.createAnswer();
508
522
  if (peer !== pc) return;
509
523
  await pc.setLocalDescription(answer);
@@ -573,7 +587,16 @@ var handleServerMessage = (message, viaSocket) => {
573
587
  useFallback("recent_failure");
574
588
  } else if (!transport) {
575
589
  showStatus("Establishing connection\u2026", true);
576
- webrtcDeadline ??= setTimeout(() => useFallback("video_timeout", true), webrtcFrameTimeoutMs);
590
+ webrtcDeadline ??= setTimeout(() => {
591
+ if (skipFullChroma || !inbound?.packetsReceived || !route.codec.endsWith(H264_PROFILES.high444.label)) {
592
+ useFallback("video_timeout", true);
593
+ return;
594
+ }
595
+ skipFullChroma = true;
596
+ if (config) fullChromaFailure = captureFailure("full_chroma_undecodable", config);
597
+ note("retrying WebRTC without 4:4:4");
598
+ socket?.close();
599
+ }, webrtcFrameTimeoutMs);
577
600
  }
578
601
  };
579
602
  var handleServerText = (data, viaSocket) => {
@@ -794,6 +817,15 @@ var reportWebRtcStats = async (pc, stats) => {
794
817
  lastFrameAt = now;
795
818
  clearTimeout(webrtcDeadline);
796
819
  webrtcDeadline = void 0;
820
+ if (fullChromaFailure) {
821
+ try {
822
+ localStorage.setItem(fullChromaFailureKey, navigator.userAgent);
823
+ } catch {
824
+ }
825
+ failure = fullChromaFailure;
826
+ fullChromaFailure = void 0;
827
+ announceTransport();
828
+ }
797
829
  }
798
830
  if (inbound && (inbound.id !== counters.id || counters.framesDecoded < inbound.framesDecoded)) inbound = void 0;
799
831
  if (!inbound) inboundHistory = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-computer",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "description": "Replicas workspace computer-use runtime",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",