Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 12 additions & 27 deletions crates/computer-use-mcp/src/permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,7 @@
//! effect after the app restarts (macOS shows its own "Quit & Reopen" dialog);
//! callers must persist any restart-continuity marker *before* requesting.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PermissionKind {
Accessibility,
ScreenRecording,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PermissionStatus {
pub accessibility: bool,
pub screen_recording: bool,
}
pub use tcode_core::permissions::{ComputerUsePermissions, PermissionKind, PermissionStatus};

/// The explicit user-facing action to perform for a missing permission.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -59,24 +46,22 @@ impl PermissionGrantFlow {
}
}

impl PermissionStatus {
pub fn granted(&self, kind: PermissionKind) -> bool {
match kind {
PermissionKind::Accessibility => self.accessibility,
PermissionKind::ScreenRecording => self.screen_recording,
}
}

pub fn all_granted(&self) -> bool {
self.accessibility && self.screen_recording
}
}

/// Non-prompting snapshot of both TCC grants for this process.
pub fn check() -> PermissionStatus {
imp::check()
}

/// Non-prompting host snapshot including whether these grants apply at all.
pub fn host_status() -> ComputerUsePermissions {
if cfg!(target_os = "macos") {
ComputerUsePermissions::MacOs(check())
} else if cfg!(target_os = "windows") {
ComputerUsePermissions::NotRequired
} else {
ComputerUsePermissions::Unsupported
}
}

/// Fire the native request for one permission kind. The system prompt may
/// complete asynchronously or stop appearing after an earlier attempt. Callers
/// should offer [`open_settings_pane`] as a later, explicit fallback instead of
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod acp;
pub mod attachments;
pub mod git;
pub mod permissions;
pub mod project;
pub mod provider_models;
pub mod provider_status;
Expand Down
38 changes: 38 additions & 0 deletions crates/core/src/permissions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//! Computer-use permission facts, reported by the machine running the agent.

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PermissionKind {
Accessibility,
ScreenRecording,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PermissionStatus {
pub accessibility: bool,
pub screen_recording: bool,
}

impl PermissionStatus {
pub fn granted(&self, kind: PermissionKind) -> bool {
match kind {
PermissionKind::Accessibility => self.accessibility,
PermissionKind::ScreenRecording => self.screen_recording,
}
}

pub fn all_granted(&self) -> bool {
self.accessibility && self.screen_recording
}
}

/// Platform semantics are supplied by the host, never inferred by a client.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "platform", content = "status", rename_all = "snake_case")]
pub enum ComputerUsePermissions {
MacOs(PermissionStatus),
NotRequired,
Unsupported,
}
3 changes: 3 additions & 0 deletions crates/protocol/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub enum Query {
Hosting {
action: HostingAction,
},
/// Read the agent host's system grants without requesting access.
ComputerUsePermissions,
Ping,
/// Records strictly before the absolute event cursor, oldest first.
SessionHistoryPage {
Expand Down Expand Up @@ -98,6 +100,7 @@ pub const MAX_THREAD_EXPORT_BYTES: usize = 8 * 1024 * 1024;
#[serde(tag = "type", content = "content", rename_all = "snake_case")]
pub enum QueryResponse {
Hosting(HostingState),
ComputerUsePermissions(tcode_core::permissions::ComputerUsePermissions),
Pong,
SessionHistoryPage {
records: Vec<crate::SessionEventRecord>,
Expand Down
5 changes: 5 additions & 0 deletions crates/runtime/src/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,11 @@ fn dispatch_query(
message: "this host has no remote hosting controls".into(),
})
}),
Query::ComputerUsePermissions => cx.spawn_background(async {
Ok(QueryResponse::ComputerUsePermissions(
computer_use_mcp::permissions::host_status(),
))
}),
Query::Ping => cx.spawn_background(async { Ok(QueryResponse::Pong) }),
Query::ListActiveWorkspace { session_id } => {
let cwd = app
Expand Down
Loading