Keep macOS Computer Use runtime permission checks quiet (#2764)

This commit is contained in:
Neil 2026-05-30 20:17:52 -07:00 committed by GitHub
parent be2dfe4bac
commit 0adb030bbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 47 additions and 26 deletions

View File

@ -157,7 +157,6 @@ enum ScreenshotStatus {
final class Provider {
private var snapshots: [String: Snapshot] = [:]
private var hasPromptedForAccessibility = false
func handle(method: String, params: [String: JSONValue]) throws -> Any {
switch method {
@ -301,14 +300,6 @@ final class Provider {
}
}
private func promptForAccessibilityOnce() {
guard !hasPromptedForAccessibility else {
return
}
hasPromptedForAccessibility = true
_ = promptForAccessibility()
}
private func listApps() -> [AppDescriptor] {
var seen = Set<String>()
return NSWorkspace.shared.runningApplications
@ -467,8 +458,12 @@ final class Provider {
restoreWindow: Bool
) throws -> Snapshot {
guard accessibilityTrusted() else {
promptForAccessibilityOnce()
throw ProviderError.coded("permission_denied", "Accessibility permission is required for Orca Computer Use.")
// Why: agents retry failed observations. Only the explicit setup flow
// should open macOS privacy prompts/settings; runtime calls stay quiet.
throw ProviderError.coded(
"permission_denied",
"Accessibility permission is required for Orca Computer Use. Run `orca computer permissions` or open Settings > Computer Use, grant Accessibility to Orca Computer Use, then retry."
)
}
let appElement = AXUIElementCreateApplication(app.pid)
enableManualAccessibilityIfNeeded(appElement, app: app)
@ -480,7 +475,14 @@ final class Provider {
allowRecovery: restoreWindow
)
let focusedTitle = stringAttribute(focused, kAXTitleAttribute as String) ?? app.name
guard let capture = WindowCapture.resolve(candidates: windowCandidates, titleHint: focusedTitle, windowId: windowId, windowIndex: windowIndex) else {
let canCaptureScreenshot = includeScreenshot && screenCaptureTrusted()
guard let capture = WindowCapture.resolve(
candidates: windowCandidates,
titleHint: focusedTitle,
windowId: windowId,
windowIndex: windowIndex,
captureImage: canCaptureScreenshot
) else {
throw ProviderError.coded("window_not_found", "app '\(app.name)' has no on-screen window")
}
guard let window = matchingWindow(appElement: appElement, capture: capture, focused: focused, explicitTarget: windowId != nil || windowIndex != nil) else {
@ -492,7 +494,7 @@ final class Provider {
let screenshot = includeScreenshot ? capture.screenshotPayload() : nil
let screenshotStatus: ScreenshotStatus = if screenshot != nil {
.captured
} else if includeScreenshot && !screenCaptureTrusted() {
} else if includeScreenshot && !canCaptureScreenshot {
.failed("Screen Recording permission is required for Orca Computer Use; grant permission or pass --no-screenshot to inspect accessibility state only.")
} else if includeScreenshot {
.failed("window screenshot capture returned no image; retry with --no-screenshot if accessibility state is sufficient.")
@ -817,11 +819,6 @@ private func accessibilityTrusted() -> Bool {
AXIsProcessTrusted()
}
private func promptForAccessibility() -> Bool {
let options = ["AXTrustedCheckOptionPrompt": true] as CFDictionary
return AXIsProcessTrustedWithOptions(options)
}
private func screenCaptureTrusted() -> Bool {
CGPreflightScreenCaptureAccess()
}
@ -1628,19 +1625,37 @@ private struct WindowCapture {
let title: String?
let image: CapturedImage?
static func resolve(pid: pid_t, titleHint: String?, windowId: CGWindowID?, windowIndex: Int?) -> WindowCapture? {
resolve(candidates: candidates(pid: pid), titleHint: titleHint, windowId: windowId, windowIndex: windowIndex)
static func resolve(
pid: pid_t,
titleHint: String?,
windowId: CGWindowID?,
windowIndex: Int?,
captureImage: Bool
) -> WindowCapture? {
resolve(
candidates: candidates(pid: pid),
titleHint: titleHint,
windowId: windowId,
windowIndex: windowIndex,
captureImage: captureImage
)
}
static func resolve(candidates: [WindowCandidate], titleHint: String?, windowId: CGWindowID?, windowIndex: Int?) -> WindowCapture? {
static func resolve(
candidates: [WindowCandidate],
titleHint: String?,
windowId: CGWindowID?,
windowIndex: Int?,
captureImage: Bool
) -> WindowCapture? {
if let windowId {
guard let candidate = candidates.first(where: { $0.windowId == windowId }) else { return nil }
return WindowCapture(candidate: candidate)
return WindowCapture(candidate: candidate, captureImage: captureImage)
}
if let windowIndex {
let visibleWindows = candidates.filter { $0.layer == 0 }
guard visibleWindows.indices.contains(windowIndex) else { return nil }
return WindowCapture(candidate: visibleWindows[windowIndex])
return WindowCapture(candidate: visibleWindows[windowIndex], captureImage: captureImage)
}
guard let best = candidates.sorted(by: { lhs, rhs in
if let titleHint, lhs.title == titleHint, rhs.title != titleHint { return true }
@ -1649,15 +1664,21 @@ private struct WindowCapture {
}).first else {
return nil
}
return WindowCapture(candidate: best)
return WindowCapture(candidate: best, captureImage: captureImage)
}
private init(candidate: WindowCandidate) {
private init(candidate: WindowCandidate, captureImage: Bool) {
self.windowId = candidate.windowId
self.layer = candidate.layer
self.bounds = candidate.bounds
self.title = candidate.title
self.image = Self.captureImage(windowId: candidate.windowId, bounds: candidate.bounds)
// Why: probing image APIs before TCC preflight can raise Screen
// Recording prompts, even for --no-screenshot calls.
if captureImage {
self.image = Self.captureImage(windowId: candidate.windowId, bounds: candidate.bounds)
} else {
self.image = nil
}
}
static func candidates(pid: pid_t) -> [WindowCandidate] {