diff --git a/native/computer-use-macos/Sources/OrcaComputerUseMacOS/main.swift b/native/computer-use-macos/Sources/OrcaComputerUseMacOS/main.swift index 61ee27715..0c56041ee 100644 --- a/native/computer-use-macos/Sources/OrcaComputerUseMacOS/main.swift +++ b/native/computer-use-macos/Sources/OrcaComputerUseMacOS/main.swift @@ -1044,11 +1044,7 @@ private func screenCaptureTrusted() -> Bool { } private func screenCaptureTrustedSettled() -> Bool { - PermissionTrustSettling.settleWithFallback( - finalTimeoutMs: 500, - probe: screenCaptureTrusted, - fallbackProbe: screenCaptureTrustedByCaptureProbe - ) + PermissionTrustSettling.settle(timeoutMs: 2_000, probe: screenCaptureTrusted).settled } private func permissionStatusSnapshotSettled() -> PermissionStatusSnapshot { @@ -1058,40 +1054,6 @@ private func permissionStatusSnapshotSettled() -> PermissionStatusSnapshot { ) } -private func screenCaptureTrustedByCaptureProbe() -> Bool { - guard let infos = CGWindowListCopyWindowInfo( - [.optionOnScreenOnly], - kCGNullWindowID - ) as? [[String: Any]] else { - return false - } - let windows = infos.compactMap { info -> ScreenCaptureProbeWindow? in - guard let layer = info[kCGWindowLayer as String] as? Int, - let ownerPid = info[kCGWindowOwnerPID as String] as? NSNumber, - let number = info[kCGWindowNumber as String] as? NSNumber - else { - return nil - } - return ScreenCaptureProbeWindow( - layer: layer, - ownerPid: ownerPid.int32Value, - windowId: number.uint32Value - ) - } - guard let windowId = ScreenCaptureProbeWindowSelection.firstCrossProcessNormalWindow( - ownPid: ProcessInfo.processInfo.processIdentifier, - windows: windows - ) else { - return false - } - return CGWindowListCreateImage( - .null, - [.optionIncludingWindow], - CGWindowID(windowId), - [.boundsIgnoreFraming] - ) != nil -} - private func requestScreenCaptureAccess() -> Bool { CGRequestScreenCaptureAccess() } diff --git a/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/PermissionTrustSettling.swift b/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/PermissionTrustSettling.swift index 806126644..0f7d0bd8c 100644 --- a/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/PermissionTrustSettling.swift +++ b/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/PermissionTrustSettling.swift @@ -41,33 +41,4 @@ public enum PermissionTrustSettling { waitedMs += interval } } - - public static func settleWithFallback( - initialTimeoutMs: Int = defaultTimeoutMs, - finalTimeoutMs: Int, - intervalMs: Int = defaultIntervalMs, - sleepMs: (Int) -> Void = { interval in - Thread.sleep(forTimeInterval: TimeInterval(interval) / 1_000) - }, - probe: () -> Bool, - fallbackProbe: () -> Bool - ) -> Bool { - if settle( - timeoutMs: initialTimeoutMs, - intervalMs: intervalMs, - sleepMs: sleepMs, - probe: probe - ).settled { - return true - } - if fallbackProbe() { - return true - } - return settle( - timeoutMs: finalTimeoutMs, - intervalMs: intervalMs, - sleepMs: sleepMs, - probe: probe - ).settled - } } diff --git a/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/ScreenCaptureProbeWindowSelection.swift b/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/ScreenCaptureProbeWindowSelection.swift deleted file mode 100644 index 36b4346a8..000000000 --- a/native/computer-use-macos/Sources/OrcaComputerUseMacOSCore/ScreenCaptureProbeWindowSelection.swift +++ /dev/null @@ -1,22 +0,0 @@ -public struct ScreenCaptureProbeWindow: Equatable { - public let layer: Int - public let ownerPid: Int32 - public let windowId: UInt32 - - public init(layer: Int, ownerPid: Int32, windowId: UInt32) { - self.layer = layer - self.ownerPid = ownerPid - self.windowId = windowId - } -} - -public enum ScreenCaptureProbeWindowSelection { - public static func firstCrossProcessNormalWindow( - ownPid: Int32, - windows: [ScreenCaptureProbeWindow] - ) -> UInt32? { - windows.first { window in - window.layer == 0 && window.ownerPid != ownPid - }?.windowId - } -} diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCapturePermissionPreflightSafetyTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCapturePermissionPreflightSafetyTests.swift new file mode 100644 index 000000000..95deec57a --- /dev/null +++ b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCapturePermissionPreflightSafetyTests.swift @@ -0,0 +1,31 @@ +import XCTest + +final class ScreenCapturePermissionPreflightSafetyTests: XCTestCase { + func testPermissionPreflightCannotRequestOrCaptureTheScreen() throws { + let source = try computerUseSource() + let start = try XCTUnwrap(source.range(of: "private func screenCaptureTrusted()")) + let end = try XCTUnwrap( + source.range(of: "private func requestScreenCaptureAccess()", range: start.upperBound.. String { + let testFile = URL(fileURLWithPath: #filePath) + let packageRoot = testFile + .deletingLastPathComponent() + .deletingLastPathComponent() + .deletingLastPathComponent() + let mainPath = packageRoot + .appendingPathComponent("Sources") + .appendingPathComponent("OrcaComputerUseMacOS") + .appendingPathComponent("main.swift") + return try String(contentsOf: mainPath, encoding: .utf8) + } +} diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCapturePermissionSettlingTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCapturePermissionSettlingTests.swift deleted file mode 100644 index d53991d36..000000000 --- a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCapturePermissionSettlingTests.swift +++ /dev/null @@ -1,79 +0,0 @@ -import Testing -@testable import OrcaComputerUseMacOSCore - -@Suite("Screen Capture permission settling") -struct ScreenCapturePermissionSettlingTests { - @Test("initial preflight success skips fallback") - func initialSuccess() { - var fallbackCalls = 0 - let trusted = PermissionTrustSettling.settleWithFallback( - finalTimeoutMs: 500, - sleepMs: { _ in }, - probe: { true }, - fallbackProbe: { - fallbackCalls += 1 - return true - } - ) - - #expect(trusted) - #expect(fallbackCalls == 0) - } - - @Test("capture fallback can establish trust") - func fallbackSuccess() { - var fallbackCalls = 0 - let trusted = PermissionTrustSettling.settleWithFallback( - initialTimeoutMs: 200, - finalTimeoutMs: 200, - intervalMs: 100, - sleepMs: { _ in }, - probe: { false }, - fallbackProbe: { - fallbackCalls += 1 - return true - } - ) - - #expect(trusted) - #expect(fallbackCalls == 1) - } - - @Test("preflight is retried after fallback failure") - func finalPreflightSuccess() { - var probeCalls = 0 - let trusted = PermissionTrustSettling.settleWithFallback( - initialTimeoutMs: 100, - finalTimeoutMs: 200, - intervalMs: 100, - sleepMs: { _ in }, - probe: { - probeCalls += 1 - return probeCalls == 4 - }, - fallbackProbe: { false } - ) - - #expect(trusted) - #expect(probeCalls == 4) - } - - @Test("persistent denial remains denied") - func persistentDenial() { - var fallbackCalls = 0 - let trusted = PermissionTrustSettling.settleWithFallback( - initialTimeoutMs: 100, - finalTimeoutMs: 100, - intervalMs: 100, - sleepMs: { _ in }, - probe: { false }, - fallbackProbe: { - fallbackCalls += 1 - return false - } - ) - - #expect(!trusted) - #expect(fallbackCalls == 1) - } -} diff --git a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCaptureProbeWindowSelectionTests.swift b/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCaptureProbeWindowSelectionTests.swift deleted file mode 100644 index 96afd6fbc..000000000 --- a/native/computer-use-macos/Tests/OrcaComputerUseMacOSTests/ScreenCaptureProbeWindowSelectionTests.swift +++ /dev/null @@ -1,44 +0,0 @@ -import Testing -@testable import OrcaComputerUseMacOSCore - -@Suite("Screen capture probe window selection") -struct ScreenCaptureProbeWindowSelectionTests { - @Test("own process windows cannot prove cross-app capture permission") - func excludesOwnWindows() { - let selected = ScreenCaptureProbeWindowSelection.firstCrossProcessNormalWindow( - ownPid: 42, - windows: [ - .init(layer: 0, ownerPid: 42, windowId: 1), - .init(layer: 0, ownerPid: 84, windowId: 2) - ] - ) - - #expect(selected == 2) - } - - @Test("non-normal windows are skipped") - func excludesNonNormalWindows() { - let selected = ScreenCaptureProbeWindowSelection.firstCrossProcessNormalWindow( - ownPid: 42, - windows: [ - .init(layer: 1, ownerPid: 84, windowId: 1), - .init(layer: 0, ownerPid: 84, windowId: 2) - ] - ) - - #expect(selected == 2) - } - - @Test("no cross-process normal window returns nil") - func noCandidate() { - let selected = ScreenCaptureProbeWindowSelection.firstCrossProcessNormalWindow( - ownPid: 42, - windows: [ - .init(layer: 0, ownerPid: 42, windowId: 1), - .init(layer: 1, ownerPid: 84, windowId: 2) - ] - ) - - #expect(selected == nil) - } -}