livedesk 0.1.459 → 0.1.461

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.
@@ -492,12 +492,13 @@ function check(id, ok, detail) {
492
492
  }
493
493
 
494
494
  export function classifyMacPhysicalGate(snapshot = {}) {
495
+ const schemaVersion = Math.floor(finite(snapshot.schemaVersion));
495
496
  const requestedFps = Math.max(1, finite(snapshot.requestedFps, 30));
496
497
  const minimumFps = requestedFps * 0.8;
497
- const telemetry = snapshot.telemetry || {};
498
498
  const helper = snapshot.helper || {};
499
499
  const monitor = snapshot.monitor || {};
500
500
  const modifier = snapshot.modifier || {};
501
+ const lifecycle = snapshot.lifecycle || {};
501
502
  const transport = snapshot.transport || {};
502
503
  const diagnostic = snapshot.diagnostic || {};
503
504
  const abortedSignal = String(diagnostic.abortedSignal || '').trim();
@@ -510,18 +511,65 @@ export function classifyMacPhysicalGate(snapshot = {}) {
510
511
  const cleanupErrors = Array.isArray(diagnostic.cleanupErrors)
511
512
  ? diagnostic.cleanupErrors.map(value => String(value || '').trim()).filter(Boolean)
512
513
  : [];
513
- const captureFps = Math.max(0, finite(telemetry.captureFps));
514
- const compressionFps = Math.max(0, finite(telemetry.compressionFps));
515
- const stdoutFps = Math.max(0, finite(telemetry.stdoutFps));
516
- const idleOptimized = finite(telemetry.idleSkipped) > 0
517
- && finite(telemetry.backpressureSkipped) === 0;
518
- const captureAtTarget = captureFps >= minimumFps;
519
- const compressionKeepsUp = idleOptimized
520
- || (captureFps > 0 && compressionFps >= captureFps * 0.8);
521
- const stdoutKeepsUp = (idleOptimized && compressionFps <= 0)
522
- || (compressionFps > 0 && stdoutFps >= compressionFps * 0.8);
523
- const deliveredAtTarget = idleOptimized
524
- || finite(snapshot.deliveredFps) >= minimumFps;
514
+ const captureStages = snapshot.stages || {};
515
+ const requiredStageNames = ['initial', 'switched'];
516
+ const stageEvidenceAvailable = requiredStageNames.every(name =>
517
+ captureStages[name]
518
+ && typeof captureStages[name] === 'object'
519
+ && captureStages[name].telemetry
520
+ && typeof captureStages[name].telemetry === 'object'
521
+ && Number.isFinite(Number(captureStages[name].deliveredFps))
522
+ );
523
+ const stageEvaluations = requiredStageNames.map(name => {
524
+ const stage = captureStages[name] || {};
525
+ const stageTelemetry = stage.telemetry || {};
526
+ const captureFps = Math.max(0, finite(stageTelemetry.captureFps));
527
+ const compressionFps = Math.max(0, finite(stageTelemetry.compressionFps));
528
+ const stdoutFps = Math.max(0, finite(stageTelemetry.stdoutFps));
529
+ const deliveredFps = Math.max(0, finite(stage.deliveredFps));
530
+ const idleSkipped = Math.max(0, finite(stageTelemetry.idleSkipped));
531
+ const backpressureSkipped = Math.max(0, finite(stageTelemetry.backpressureSkipped));
532
+ // One incidental idle skip must never mask a 3-4 fps regression. A static
533
+ // screen is treated as intentionally idle only when most of a current
534
+ // one-second capture window was suppressed and keepalive output remained.
535
+ const idleDominant = captureFps >= minimumFps
536
+ && backpressureSkipped === 0
537
+ && idleSkipped >= Math.max(10, captureFps * 0.6);
538
+ return {
539
+ name,
540
+ telemetry: stageTelemetry,
541
+ captureFps,
542
+ compressionFps,
543
+ stdoutFps,
544
+ deliveredFps,
545
+ idleSkipped,
546
+ backpressureSkipped,
547
+ idleDominant,
548
+ telemetryCurrent: finite(stageTelemetry.version) >= 2
549
+ && Number.isFinite(Number(stageTelemetry.ageMs))
550
+ && finite(stageTelemetry.ageMs) <= 3_000,
551
+ hardwareAccelerated: stageTelemetry.hardwareAccelerated === true,
552
+ captureAtTarget: captureFps >= minimumFps,
553
+ compressionKeepsUp: (idleDominant && compressionFps >= 1)
554
+ || (captureFps > 0 && compressionFps >= captureFps * 0.8),
555
+ stdoutKeepsUp: (idleDominant && stdoutFps >= 1)
556
+ || (compressionFps > 0 && stdoutFps >= compressionFps * 0.8),
557
+ deliveredAtTarget: (idleDominant && deliveredFps >= 1)
558
+ || deliveredFps >= minimumFps
559
+ };
560
+ });
561
+ const stageMetricDetail = metric => stageEvaluations
562
+ .map(stage => `${stage.name}=${finite(stage[metric]).toFixed(1)}`)
563
+ .join(' ');
564
+ const idleOptimized = stageEvaluations.some(stage => stage.idleDominant);
565
+ const captureAtTarget = stageEvidenceAvailable
566
+ && stageEvaluations.every(stage => stage.captureAtTarget);
567
+ const compressionKeepsUp = stageEvidenceAvailable
568
+ && stageEvaluations.every(stage => stage.compressionKeepsUp);
569
+ const stdoutKeepsUp = stageEvidenceAvailable
570
+ && stageEvaluations.every(stage => stage.stdoutKeepsUp);
571
+ const deliveredAtTarget = stageEvidenceAvailable
572
+ && stageEvaluations.every(stage => stage.deliveredAtTarget);
525
573
  const helperPids = Array.isArray(helper.observedPids)
526
574
  ? helper.observedPids.map(Number).filter(pid => Number.isInteger(pid) && pid > 1)
527
575
  : [];
@@ -568,8 +616,53 @@ export function classifyMacPhysicalGate(snapshot = {}) {
568
616
  cpuPercentPeak: Math.max(0, finite(processInfo?.cpuPercentPeak)),
569
617
  rssBytesPeak: Math.max(0, Math.round(finite(processInfo?.rssBytesPeak)))
570
618
  }));
571
- const expectedModifierAcks = Math.max(1, finite(modifier.expectedAcks, 8));
619
+ const expectedModifierSteps = buildMacModifierProbeSteps(1).map(step => ({
620
+ inputSeq: step.inputSeq,
621
+ type: step.type,
622
+ code: step.code
623
+ }));
624
+ const modifierApplied = Array.isArray(modifier.applied) ? modifier.applied : [];
625
+ const modifierSequenceMatches = modifierApplied.length === expectedModifierSteps.length
626
+ && modifierApplied.every((actual, index) => {
627
+ const expected = expectedModifierSteps[index];
628
+ return actual?.inputSeq === expected.inputSeq
629
+ && actual?.type === expected.type
630
+ && actual?.code === expected.code;
631
+ });
632
+ const lifecycleOrder = Array.isArray(lifecycle.order)
633
+ ? lifecycle.order.map(value => String(value || '').trim()).filter(Boolean)
634
+ : [];
635
+ const expectedLifecycleOrder = [
636
+ 'wall',
637
+ 'control',
638
+ 'paused',
639
+ 'blocked',
640
+ 'resumed',
641
+ 'control-resumed',
642
+ 'control-stopped',
643
+ 'wall-restored',
644
+ 'stopped'
645
+ ];
646
+ const lifecycleHelperCounts = [
647
+ finite(lifecycle.wallHelperCount, -1),
648
+ finite(lifecycle.controlHelperCount, -1),
649
+ finite(lifecycle.pausedHelperCount, -1),
650
+ finite(lifecycle.resumedControlHelperCount, -1),
651
+ finite(lifecycle.controlStoppedHelperCount, -1),
652
+ finite(lifecycle.restoredWallHelperCount, -1),
653
+ finite(lifecycle.finalHelperCount, -1)
654
+ ];
655
+ const lifecycleCompletedAtMs = Date.parse(String(lifecycle.completedAt || ''));
656
+ const registrySampledAtMs = Date.parse(String(lifecycle.registrySampledAt || ''));
657
+ const lifecycleRegistryFresh = Number.isFinite(lifecycleCompletedAtMs)
658
+ && Number.isFinite(registrySampledAtMs)
659
+ && registrySampledAtMs >= lifecycleCompletedAtMs;
572
660
  const checks = [
661
+ check(
662
+ 'report-schema',
663
+ schemaVersion === 3,
664
+ `v${schemaVersion || 'missing'}`
665
+ ),
573
666
  check(
574
667
  'diagnostic-run',
575
668
  !runError,
@@ -580,6 +673,51 @@ export function classifyMacPhysicalGate(snapshot = {}) {
580
673
  cleanupErrors.length === 0,
581
674
  cleanupErrors.length === 0 ? 'completed' : cleanupErrors.join('; ')
582
675
  ),
676
+ check(
677
+ 'capture-lifecycle-order',
678
+ lifecycleOrder.join('>') === expectedLifecycleOrder.join('>'),
679
+ lifecycleOrder.join(' -> ') || 'missing'
680
+ ),
681
+ check(
682
+ 'capture-lifecycle-generation',
683
+ finite(lifecycle.wallGeneration) > 0
684
+ && finite(lifecycle.controlGeneration) > finite(lifecycle.wallGeneration)
685
+ && finite(lifecycle.resumedControlGeneration) > finite(lifecycle.controlGeneration)
686
+ && finite(lifecycle.restoredWallGeneration) > finite(lifecycle.resumedControlGeneration),
687
+ `${finite(lifecycle.wallGeneration)} -> ${finite(lifecycle.controlGeneration)}`
688
+ + ` -> ${finite(lifecycle.resumedControlGeneration)}`
689
+ + ` -> ${finite(lifecycle.restoredWallGeneration)}`
690
+ ),
691
+ check(
692
+ 'capture-lifecycle-pause',
693
+ lifecycle.pauseStopConfirmed === true
694
+ && lifecycle.pauseStopAcknowledged === true
695
+ && lifecycle.blockedWhilePaused === true
696
+ && lifecycle.resumeAccepted === true,
697
+ `stop=${lifecycle.pauseStopConfirmed === true}/${lifecycle.pauseStopAcknowledged === true} `
698
+ + `blocked=${lifecycle.blockedWhilePaused === true} resume=${lifecycle.resumeAccepted === true}`
699
+ ),
700
+ check(
701
+ 'capture-lifecycle-helper',
702
+ lifecycleHelperCounts.join(',') === '1,1,0,1,0,1,0',
703
+ lifecycleHelperCounts.join(' -> ')
704
+ ),
705
+ check(
706
+ 'capture-lifecycle-registry',
707
+ lifecycleRegistryFresh
708
+ && finite(lifecycle.registryMaxConcurrent, -1) === 1
709
+ && finite(lifecycle.registryFinalHelperCount, -1) === 0,
710
+ `completed=${String(lifecycle.completedAt || '-')} `
711
+ + `sampled=${String(lifecycle.registrySampledAt || '-')} `
712
+ + `max=${finite(lifecycle.registryMaxConcurrent, -1)} `
713
+ + `final=${finite(lifecycle.registryFinalHelperCount, -1)}`
714
+ ),
715
+ check(
716
+ 'capture-lifecycle-stop',
717
+ lifecycle.controlStopConfirmed === true
718
+ && lifecycle.finalStopConfirmed === true,
719
+ `control=${lifecycle.controlStopConfirmed === true} final=${lifecycle.finalStopConfirmed === true}`
720
+ ),
583
721
  check(
584
722
  'first-frame',
585
723
  finite(snapshot.firstFrameMs) > 0 && finite(snapshot.firstFrameMs) < 8_000,
@@ -591,6 +729,11 @@ export function classifyMacPhysicalGate(snapshot = {}) {
591
729
  && finite(snapshot.monitorSwitchFirstFrameMs) < 8_000,
592
730
  `${Math.round(finite(snapshot.monitorSwitchFirstFrameMs))}ms`
593
731
  ),
732
+ check(
733
+ 'capture-stage-evidence',
734
+ stageEvidenceAvailable,
735
+ stageEvidenceAvailable ? requiredStageNames.join(',') : 'initial and switched stages required'
736
+ ),
594
737
  check(
595
738
  'native-profile',
596
739
  snapshot.platformProfile === 'macos-screencapturekit-nv12-videotoolbox',
@@ -598,31 +741,57 @@ export function classifyMacPhysicalGate(snapshot = {}) {
598
741
  ),
599
742
  check(
600
743
  'telemetry-current',
601
- finite(telemetry.version) >= 2
602
- && Number.isFinite(Number(telemetry.ageMs))
603
- && finite(telemetry.ageMs) <= 3_000,
604
- `v${finite(telemetry.version)} age=${Number.isFinite(Number(telemetry.ageMs)) ? Math.round(finite(telemetry.ageMs)) : '-'}ms`
744
+ stageEvidenceAvailable
745
+ && stageEvaluations.every(stage => stage.telemetryCurrent),
746
+ stageEvaluations
747
+ .map(stage =>
748
+ `${stage.name}=v${finite(stage.telemetry.version)}`
749
+ + `/age=${Number.isFinite(Number(stage.telemetry.ageMs))
750
+ ? Math.round(finite(stage.telemetry.ageMs))
751
+ : '-'}ms`
752
+ )
753
+ .join(' ')
605
754
  ),
606
755
  check(
607
756
  'hardware-videotoolbox',
608
- telemetry.hardwareAccelerated === true,
609
- telemetry.hardwareAccelerated === true ? 'hardware' : 'not-confirmed'
757
+ stageEvidenceAvailable
758
+ && stageEvaluations.every(stage => stage.hardwareAccelerated),
759
+ stageEvaluations
760
+ .map(stage => `${stage.name}=${stage.hardwareAccelerated ? 'hardware' : 'not-confirmed'}`)
761
+ .join(' ')
762
+ ),
763
+ check(
764
+ 'screencapturekit-fps',
765
+ captureAtTarget,
766
+ `${stageMetricDetail('captureFps')} target=${requestedFps}`
610
767
  ),
611
- check('screencapturekit-fps', captureAtTarget, `${captureFps.toFixed(1)}/${requestedFps}`),
612
768
  check(
613
769
  'videotoolbox-fps',
614
770
  compressionKeepsUp,
615
- `${compressionFps.toFixed(1)} after ${captureFps.toFixed(1)}${idleOptimized ? ' idle-optimized' : ''}`
771
+ stageEvaluations
772
+ .map(stage =>
773
+ `${stage.name}=${stage.compressionFps.toFixed(1)}`
774
+ + `/${stage.captureFps.toFixed(1)}`
775
+ + `${stage.idleDominant ? ' idle-dominant' : ''}`
776
+ )
777
+ .join(' ')
616
778
  ),
617
779
  check(
618
780
  'stdout-fps',
619
781
  stdoutKeepsUp,
620
- `${stdoutFps.toFixed(1)} after ${compressionFps.toFixed(1)}${idleOptimized ? ' idle-optimized' : ''}`
782
+ stageEvaluations
783
+ .map(stage =>
784
+ `${stage.name}=${stage.stdoutFps.toFixed(1)}`
785
+ + `/${stage.compressionFps.toFixed(1)}`
786
+ + `${stage.idleDominant ? ' idle-dominant' : ''}`
787
+ )
788
+ .join(' ')
621
789
  ),
622
790
  check(
623
791
  'delivered-fps',
624
792
  deliveredAtTarget,
625
- `${finite(snapshot.deliveredFps).toFixed(1)}/${requestedFps}${idleOptimized ? ' idle-optimized' : ''}`
793
+ `${stageMetricDetail('deliveredFps')} target=${requestedFps}`
794
+ + `${idleOptimized ? ' idle-dominant-stage' : ''}`
626
795
  ),
627
796
  check(
628
797
  'single-helper',
@@ -656,6 +825,13 @@ export function classifyMacPhysicalGate(snapshot = {}) {
656
825
  finite(monitor.nextGeneration) > finite(monitor.initialGeneration),
657
826
  `${finite(monitor.initialGeneration)} -> ${finite(monitor.nextGeneration)}`
658
827
  ),
828
+ check(
829
+ 'monitor-native-identity',
830
+ finite(monitor.initialDisplayId) > 0
831
+ && finite(monitor.nextDisplayId) > 0
832
+ && finite(monitor.initialDisplayId) !== finite(monitor.nextDisplayId),
833
+ `${finite(monitor.initialDisplayId)} -> ${finite(monitor.nextDisplayId)}`
834
+ ),
659
835
  check(
660
836
  'monitor-pixels',
661
837
  Boolean(monitor.initialHash)
@@ -665,9 +841,12 @@ export function classifyMacPhysicalGate(snapshot = {}) {
665
841
  ),
666
842
  check(
667
843
  'modifier-native-ack',
668
- finite(modifier.appliedAcks) === expectedModifierAcks
844
+ modifier.expectedAcks === expectedModifierSteps.length
845
+ && modifier.appliedAcks === expectedModifierSteps.length
846
+ && modifierSequenceMatches
669
847
  && (!Array.isArray(modifier.errors) || modifier.errors.length === 0),
670
- `${finite(modifier.appliedAcks)}/${expectedModifierAcks} native ACK`
848
+ `${finite(modifier.appliedAcks)}/${expectedModifierSteps.length} native ACK, `
849
+ + `sequence=${modifierSequenceMatches ? 'exact' : 'invalid'}`
671
850
  ),
672
851
  check(
673
852
  'active-transport',
@@ -682,10 +861,18 @@ export function classifyMacPhysicalGate(snapshot = {}) {
682
861
 
683
862
  const failed = checks.filter(item => !item.ok);
684
863
  const bottleneckByCheck = {
864
+ 'report-schema': 'report-schema',
685
865
  'diagnostic-run': 'diagnostic-run',
686
866
  'diagnostic-cleanup': 'diagnostic-cleanup',
867
+ 'capture-lifecycle-order': 'capture-lifecycle-order',
868
+ 'capture-lifecycle-generation': 'capture-generation',
869
+ 'capture-lifecycle-pause': 'capture-pause-resume',
870
+ 'capture-lifecycle-helper': 'capture-helper-lifecycle',
871
+ 'capture-lifecycle-registry': 'capture-helper-registry',
872
+ 'capture-lifecycle-stop': 'capture-stop',
687
873
  'first-frame': 'capture-startup',
688
874
  'monitor-first-frame': 'monitor-restart',
875
+ 'capture-stage-evidence': 'capture-stage-evidence',
689
876
  'native-profile': 'capture-fallback',
690
877
  'telemetry-current': 'native-telemetry-stale',
691
878
  'hardware-videotoolbox': 'videotoolbox-software',
@@ -700,6 +887,7 @@ export function classifyMacPhysicalGate(snapshot = {}) {
700
887
  'resource-drained': 'resource-not-drained',
701
888
  'two-monitors': 'monitor-topology',
702
889
  'monitor-generation': 'monitor-generation',
890
+ 'monitor-native-identity': 'monitor-binding',
703
891
  'monitor-pixels': 'monitor-binding',
704
892
  'modifier-native-ack': 'remote-input',
705
893
  'active-transport': 'transport-telemetry'