Compress computer-use captures before embedding in history

Reduce the size of computer-use capture attachments stored by the _a0_connector plugin so Windows screenshots remain usable. - optimize capture images before embedding them in history - convert large captures to JPEG data URLs instead of keeping full PNG payloads - keep the existing capture-path fallback when inline payloads are missing - preserve the current user-facing computer_use_remote flow while shrinking the history payload

Alessandro committed Apr 20, 2026 at 03:56 UTC 20107ff92154742221cd7daf468182339da07a57
1 file changed +37 -7
plugins/_a0_connector/tools/computer_use_remote.py
+37 -7
@@ -3,10 +3,14 @@ from __future__ import annotations
3
4 import asyncio
5 import base64
6 +import io
7 +import math
8 from pathlib import Path
9 import uuid
10 from typing import Any
11
12 +from PIL import Image
13 +
14 from helpers import history
15 from helpers.tool import Response, Tool
16 from helpers.ws import NAMESPACE
@@ -22,6 +26,8 @@ from plugins._a0_connector.helpers.ws_runtime import (
26 COMPUTER_USE_OP_TIMEOUT = 180.0
27 COMPUTER_USE_OP_EVENT = "connector_computer_use_op"
28 CAPTURE_TOKENS_ESTIMATE = 1500
29 +CAPTURE_MAX_PIXELS = 768_000
30 +CAPTURE_JPEG_QUALITY = 75
31 _AUTO_CAPTURE_ACTIONS = {
32 "start_session",
33 "move",
@@ -307,13 +313,13 @@ class ComputerUseRemote(Tool):
313 return f"Computer use status={status}, trust_mode={trust_mode or 'unknown'}, active_contexts={active_text}."
314
315 def _record_capture(self, data: dict[str, Any]) -> str:
310 - image_b64 = self._capture_image_base64(data)
316 + mime_type, image_b64 = self._capture_image_data(data)
317 width = data.get("width", "?")
318 height = data.get("height", "?")
319 summary = f"Computer-use capture {width}x{height}."
320 content = [
321 {"type": "text", "text": summary},
316 - {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_b64}"}},
322 + {"type": "image_url", "image_url": {"url": f"data:{mime_type};base64,{image_b64}"}},
323 ]
324 raw_message = history.RawMessage(raw_content=content, preview=summary)
325 self.agent.hist_add_message(False, content=raw_message, tokens=CAPTURE_TOKENS_ESTIMATE)
@@ -393,18 +399,42 @@ class ComputerUseRemote(Tool):
399 return preview
400 return ""
401
396 - def _capture_image_base64(self, data: dict[str, Any]) -> str:
402 + def _capture_image_data(self, data: dict[str, Any]) -> tuple[str, str]:
403 + image_bytes = self._capture_image_bytes(data)
404 + optimized_bytes = self._optimize_capture_image(image_bytes)
405 + if optimized_bytes is not None:
406 + return "image/jpeg", base64.b64encode(optimized_bytes).decode("utf-8")
407 + return "image/png", base64.b64encode(image_bytes).decode("utf-8")
408 +
409 + def _capture_image_bytes(self, data: dict[str, Any]) -> bytes:
410 inline_payload = str(data.get("png_base64", "") or "").strip()
411 if inline_payload:
412 try:
400 - base64.b64decode(inline_payload, validate=True)
413 + return base64.b64decode(inline_payload, validate=True)
414 except Exception:
415 pass
403 - else:
404 - return inline_payload
416
417 image_path, _display_path = self._resolve_capture_path(data)
407 - return base64.b64encode(image_path.read_bytes()).decode("utf-8")
418 + return image_path.read_bytes()
419 +
420 + def _optimize_capture_image(self, image_bytes: bytes) -> bytes | None:
421 + try:
422 + image = Image.open(io.BytesIO(image_bytes))
423 + current_pixels = image.width * image.height
424 + if current_pixels > CAPTURE_MAX_PIXELS:
425 + scale = math.sqrt(CAPTURE_MAX_PIXELS / current_pixels)
426 + resized = (
427 + max(1, int(image.width * scale)),
428 + max(1, int(image.height * scale)),
429 + )
430 + image = image.resize(resized, Image.Resampling.LANCZOS)
431 + if image.mode not in {"RGB", "L"}:
432 + image = image.convert("RGB")
433 + output = io.BytesIO()
434 + image.save(output, format="JPEG", quality=CAPTURE_JPEG_QUALITY, optimize=True)
435 + return output.getvalue()
436 + except Exception:
437 + return None
438
439 def _resolve_capture_path(self, data: dict[str, Any]) -> tuple[Path, str]:
440 candidates = [