Implement pure Rust visual snapshots (#132)
Some checks failed
CI / rust-skia (Rust only) (push) Successful in 2m48s
CI / required (push) Failing after 54s

This commit is contained in:
2026-08-18 11:57:50 +02:00
parent 64edd8df46
commit 11a8c37f21
16 changed files with 1793 additions and 17 deletions

View File

@@ -85,6 +85,7 @@ pub struct AgentControlTarget {
audit: Arc<MemoryPolicyAudit>,
observability: Mutex<Option<Arc<Observability>>>,
build: Mutex<Option<Arc<dyn crate::build::BuildControl>>>,
vision: Mutex<Option<Arc<dyn crate::vision::VisionControl>>>,
behavior: BehaviorIngress,
commands: mpsc::Sender<RuntimeControlCommand>,
command_capacity: usize,
@@ -129,6 +130,7 @@ impl AgentControlTarget {
audit,
observability: Mutex::new(None),
build: Mutex::new(None),
vision: Mutex::new(None),
behavior,
commands,
command_capacity,
@@ -149,6 +151,11 @@ impl AgentControlTarget {
*lock(&self.build) = Some(build);
}
/// Attaches synthetic viewport cancellation and metadata to operator control.
pub fn attach_vision_control(&self, vision: Arc<dyn crate::vision::VisionControl>) {
*lock(&self.vision) = Some(vision);
}
pub fn update_session(&self, session: SessionStatus) {
let mut state = lock(&self.state);
state.session = session;
@@ -245,6 +252,7 @@ impl AgentControlTarget {
let state = lock(&self.state).clone();
let usage = self.policy.global_budget_usage();
let build = lock(&self.build);
let vision = lock(&self.vision);
Ok(ControlPayload::Runtime(RuntimeView {
grid_state: state.session.state.as_str().to_owned(),
generation: state.session.generation,
@@ -265,6 +273,13 @@ impl AgentControlTarget {
build_orphan_ids: build
.as_ref()
.map_or_else(Vec::new, |build| build.build_orphans()),
active_visual_capture: vision
.as_ref()
.and_then(|vision| vision.active_capture()),
visual_progress: vision.as_ref().and_then(|vision| vision.capture_progress()),
visual_image_sha256: vision
.as_ref()
.and_then(|vision| vision.last_image_sha256()),
}))
}
ControlRequest::ListSessions { page } => {
@@ -345,6 +360,12 @@ impl AgentControlTarget {
ControlRequest::PauseAutonomy => {
let response = self.enqueue("pause", RuntimeControlCommand::Pause)?;
self.behavior.pause();
let vision = lock(&self.vision).clone();
if let Some((vision, active)) =
vision.and_then(|vision| vision.active_capture().map(|active| (vision, active)))
{
let _ = vision.cancel_capture(&active);
}
Ok(response)
}
ControlRequest::ResumeAutonomy => {
@@ -356,10 +377,13 @@ impl AgentControlTarget {
let build_cancelled = lock(&self.build)
.as_ref()
.is_some_and(|build| build.cancel_build(&action_id));
let vision_cancelled = lock(&self.vision)
.as_ref()
.is_some_and(|vision| vision.cancel_capture(&action_id));
let cancelled = self.behavior.cancel_action(&action_id).map_err(|_| {
control_error(ControlErrorCode::InvalidRequest, "invalid action ID", false)
})?;
if !cancelled && !build_cancelled {
if !cancelled && !build_cancelled && !vision_cancelled {
return Err(control_error(
ControlErrorCode::NotFound,
"active behavior action not found",