Skip to main content

adk_computer_use/runtime/
mod.rs

1//! The [`ComputerUseRuntime`] boundary and its concrete adapters.
2//!
3//! [`ComputerUseRuntime`] is the extension point driven by the deterministic
4//! graph in [`crate::build_reference_graph`]. The [`mcp`](crate::runtime::mcp) submodule provides
5//! [`ComputerUseMcpRuntime`], backed by a live `computer-use-mcp` server.
6//! Tests and portable examples can supply an in-process implementation instead.
7
8/// Binds MCP responses back to the request that produced them.
9pub mod binding;
10
11pub mod mcp;
12
13pub use mcp::{ComputerUseMcpConfig, ComputerUseMcpRuntime, TraceCorrelation};
14
15use crate::{
16    ActionEnvelope, ActionPreview, ComputerUseError, ControlLease, ExecutionReceipt,
17    TargetReservation,
18};
19use async_trait::async_trait;
20use serde_json::Value;
21
22/// What is actually known about an action's effect after execution.
23///
24/// `verify` previously returned `bool`, computed as `receipt.status == Committed`. That
25/// collapsed two different claims: that the runtime accepted the action, and that the
26/// intended effect was observed. A committed action whose effect did not occur was reported
27/// as completed, and the reference graph labelled the node and its output "verification".
28#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum VerificationOutcome {
30    /// The declared postcondition was observed to hold, with evidence bound to it.
31    Verified,
32    /// The runtime committed the action, but no independent postcondition evidence is
33    /// available — either none was declared, or the receipt carried none.
34    CommittedUnverified {
35        /// Why verification could not be performed, for the operator reading the result.
36        reason: String,
37    },
38    /// The action did not commit, or the evidence contradicts the postcondition.
39    Failed {
40        /// What went wrong.
41        reason: String,
42    },
43}
44
45impl VerificationOutcome {
46    /// Whether the postcondition was independently observed.
47    ///
48    /// Deliberately false for [`VerificationOutcome::CommittedUnverified`]: a caller asking
49    /// "was this verified?" must not be told yes because the action merely committed.
50    pub fn is_verified(&self) -> bool {
51        matches!(self, VerificationOutcome::Verified)
52    }
53
54    /// Whether the action was performed, whether or not its effect was verified.
55    pub fn is_committed(&self) -> bool {
56        matches!(
57            self,
58            VerificationOutcome::Verified | VerificationOutcome::CommittedUnverified { .. }
59        )
60    }
61
62    /// A stable status string for the graph's `result.status` field.
63    pub fn status(&self) -> &'static str {
64        match self {
65            VerificationOutcome::Verified => "completed",
66            VerificationOutcome::CommittedUnverified { .. } => "committed_unverified",
67            VerificationOutcome::Failed { .. } => "verification_failed",
68        }
69    }
70}
71
72/// Runtime boundary implemented by the computer-use MCP server or an in-process adapter.
73///
74/// The [`crate::build_reference_graph`] workflow drives this trait in a fixed,
75/// safe order: parallel observation ([`discover_capabilities`](Self::discover_capabilities),
76/// [`observe_visual`](Self::observe_visual), [`observe_semantic`](Self::observe_semantic)),
77/// then [`preview_action`](Self::preview_action), optional
78/// [`reserve_target`](Self::reserve_target), [`acquire_lease`](Self::acquire_lease),
79/// exactly one [`execute_action`](Self::execute_action),
80/// [`verify`](Self::verify), and [`release_target`](Self::release_target).
81///
82/// The reference graph validates leases, reservations, receipts, envelope expiry, and
83/// approval bindings independently of the implementation. After a reservation is accepted,
84/// it calls [`release_target`](Self::release_target) on every later success or error path.
85///
86/// Implementations must treat the runtime (not graph or model state) as
87/// authoritative for policy, identity, lease ownership, exact preview binding, and
88/// idempotency. Implementations that expose these methods outside the reference graph must
89/// enforce the same invariants at that direct-call boundary.
90///
91/// # Errors
92///
93/// Every method returns [`ComputerUseError`]. Transport faults map to
94/// [`ComputerUseError::Mcp`], payload decoding failures to
95/// [`ComputerUseError::Decode`], and identity checks to
96/// [`ComputerUseError::IdentityMismatch`]. The cancellation control methods
97/// default to [`ComputerUseError::Unsupported`] so adapters can opt in.
98#[async_trait]
99pub trait ComputerUseRuntime: Send + Sync {
100    /// Enumerate the execution capabilities available for the target.
101    async fn discover_capabilities(&self) -> Result<Value, ComputerUseError>;
102    /// Capture a fresh visual (screenshot/annotation) observation frame.
103    async fn observe_visual(&self) -> Result<Value, ComputerUseError>;
104    /// Capture a fresh semantic (accessibility/window-tree) observation frame.
105    async fn observe_semantic(&self) -> Result<Value, ComputerUseError>;
106    /// Preview a proposed action, returning the runtime-bound envelope, policy, and route.
107    async fn preview_action(
108        &self,
109        proposed_action: Value,
110    ) -> Result<ActionPreview, ComputerUseError>;
111    /// Reserve a non-authoritative planner intent for multi-agent conflict checks.
112    ///
113    /// Returns `Ok(None)` when the adapter does not model reservations.
114    async fn reserve_target(
115        &self,
116        _envelope: &ActionEnvelope,
117    ) -> Result<Option<TargetReservation>, ComputerUseError> {
118        Ok(None)
119    }
120    /// Release a previously acquired [`TargetReservation`].
121    ///
122    /// The graph reports cleanup failures, including when another operation already failed,
123    /// so implementations should return an error instead of hiding an uncertain release.
124    async fn release_target(
125        &self,
126        _reservation: &TargetReservation,
127    ) -> Result<(), ComputerUseError> {
128        Ok(())
129    }
130    /// Acquire the one-writer control lease required before any mutation.
131    async fn acquire_lease(
132        &self,
133        envelope: &ActionEnvelope,
134    ) -> Result<ControlLease, ComputerUseError>;
135    /// Execute the previewed action exactly once under the supplied lease.
136    async fn execute_action(
137        &self,
138        envelope: &ActionEnvelope,
139        lease: &ControlLease,
140        approval_grant_id: Option<&str>,
141    ) -> Result<ExecutionReceipt, ComputerUseError>;
142    /// Report whether the action's postcondition was independently observed to hold.
143    ///
144    /// A committed receipt is an acknowledgement that the runtime accepted and performed the
145    /// action. It is not evidence that the intended effect occurred, so the two are reported
146    /// separately: see [`VerificationOutcome`].
147    ///
148    /// `postcondition` is the envelope's declared expected state, or `None` when the action
149    /// declared none — in which case there is nothing to verify and the honest answer is
150    /// [`VerificationOutcome::CommittedUnverified`].
151    async fn verify(
152        &self,
153        receipt: &ExecutionReceipt,
154        postcondition: Option<&crate::ActionPostcondition>,
155    ) -> Result<VerificationOutcome, ComputerUseError>;
156
157    /// Pause the session's desktop authority. Defaults to unsupported.
158    async fn pause_session(
159        &self,
160        _session_id: &str,
161        _reason: &str,
162    ) -> Result<(), ComputerUseError> {
163        Err(ComputerUseError::Unsupported { operation: "pause_session" })
164    }
165
166    /// Stop the session's desktop authority. Defaults to unsupported.
167    async fn stop_session(&self, _session_id: &str, _reason: &str) -> Result<(), ComputerUseError> {
168        Err(ComputerUseError::Unsupported { operation: "stop_session" })
169    }
170
171    /// Revoke all desktop authority immediately. Defaults to unsupported.
172    async fn emergency_stop(&self, _reason: &str) -> Result<(), ComputerUseError> {
173        Err(ComputerUseError::Unsupported { operation: "emergency_stop" })
174    }
175}