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}